Skip to content

Commit cbbeb66

Browse files
authored
Feature/wasm client topology injection (#3311)
* added cargo config file to explicitly specify build target * wip * Config option to disable topology refreshing * extracted common parsing code * helper trait for working on wasm topology * wasm topology parsing * restored (slightly modified) old js-example * wip * Moved message preparation into a trait * wip * long-winded way of sending test packet * standalone NymNodeTester * finishing the test upon receiving all packets even if timeout wasnt reached * initial round of cleanup * sending multiple test packets in normal NymClient * javascript-side cleanup * starting mixnode test on btn click * Improved NymNodeTester constructors * improved error handling and constructors * tester utils error handling * further cleanup + using BTreeMap for NymTopology mixnodes * handling missed errors * splitting up 'test_node' * split up and cleaned up generation of test result * clippy + fixed example * post rebase fixes * another broken test * prevent running multiple parallel tests * cargo fmt * Added nym- prefix to node tester utils
1 parent f24bb5c commit cbbeb66

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+5156
-263
lines changed

Cargo.lock

+18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ members = [
4848
"common/ledger",
4949
"common/mixnode-common",
5050
"common/network-defaults",
51+
"common/node-tester-utils",
5152
"common/nonexhaustive-delayqueue",
5253
"common/nymcoconut",
5354
"common/nymsphinx",
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build]
2+
target = "wasm32-unknown-unknown"

clients/webassembly/Cargo.toml

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ default = ["console_error_panic_hook"]
1717
offline-test = []
1818

1919
[dependencies]
20+
bs58 = "0.4.0"
2021
futures = "0.3"
2122
js-sys = "0.3"
2223
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
@@ -28,15 +29,21 @@ tokio = { version = "1.24.1", features = ["sync"] }
2829
url = "2.2"
2930
wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] }
3031
wasm-bindgen-futures = "0.4"
32+
thiserror = "1.0.40"
33+
34+
wasm-timer = { git = "https://github.com/mmsinclair/wasm-timer", rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6"}
3135

3236
# internal
37+
nym-node-tester-utils = { path = "../../common/node-tester-utils" }
3338
nym-client-core = { path = "../../common/client-core", default-features = false, features = ["wasm"] }
3439
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
3540
nym-coconut-interface = { path = "../../common/coconut-interface" }
3641
nym-credentials = { path = "../../common/credentials" }
3742
nym-credential-storage = { path = "../../common/credential-storage" }
3843
nym-crypto = { path = "../../common/crypto" }
3944
nym-sphinx = { path = "../../common/nymsphinx" }
45+
nym-topology = { path = "../../common/topology" }
46+
nym-gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
4047
nym-validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
4148
wasm-utils = { path = "../../common/wasm-utils" }
4249
nym-task = { path = "../../common/task" }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
dist
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// A dependency graph that contains any wasm must all be imported
2+
// asynchronously. This `bootstrap.js` file does the single async import, so
3+
// that no one else needs to worry about it again.
4+
import('./index.js')
5+
.catch(e => console.error('Error importing `index.js`:', e));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Nym WebAssembly Demo</title>
8+
</head>
9+
10+
<body>
11+
<p>
12+
<label>Sender: </label><input disabled="true" size="85" type="text" id="sender" value="">
13+
</p>
14+
15+
<p>
16+
<label>Recipient: </label><input size="85" type="text" id="recipient" value="">
17+
</p>
18+
<p>
19+
<label>Message: </label><input type="text" id="message" value="Hello mixnet!">
20+
</p>
21+
<p>
22+
<button id="send-button">Send</button>
23+
</p>
24+
25+
<div>
26+
<label>Mixnode Identity: </label>
27+
<input type="text" size = "60" id="mixnode_identity" value="...">
28+
<button id="magic-button">✨ Magic Test Button ✨</button>
29+
</div>
30+
31+
32+
<p>Send messages from your browser, through the mixnet, and to the recipient using the "send" button.</p>
33+
<p><span style='color: blue;'>Sent</span> messages show in blue, <span style='color: green;'>received</span>
34+
messages show in green.</p>
35+
36+
<hr>
37+
<p>
38+
<span id="output"></span>
39+
</p>
40+
<script src="./bootstrap.js"></script>
41+
</body>
42+
43+
</html>
+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Copyright 2020-2023 Nym Technologies SA
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
class WebWorkerClient {
16+
worker = null;
17+
18+
constructor() {
19+
this.worker = new Worker('./worker.js');
20+
21+
this.worker.onmessage = (ev) => {
22+
if (ev.data && ev.data.kind) {
23+
switch (ev.data.kind) {
24+
case 'Ready':
25+
const {selfAddress} = ev.data.args;
26+
displaySenderAddress(selfAddress);
27+
break;
28+
case 'ReceiveMessage':
29+
const {message, senderTag, isTestPacket } = ev.data.args;
30+
displayReceived(message, senderTag, isTestPacket);
31+
break;
32+
case 'DisableMagicTestButton':
33+
const magicButton = document.querySelector('#magic-button');
34+
magicButton.setAttribute('disabled', "true")
35+
break;
36+
case 'DisplayTesterResults':
37+
const {score, sentPackets, receivedPackets, receivedAcks, duplicatePackets, duplicateAcks} = ev.data.args;
38+
const resultText = `Test score: ${score}. Sent ${sentPackets} packets. Received ${receivedPackets} packets and ${receivedAcks} acks back. We also got ${duplicatePackets} duplicate packets and ${duplicateAcks} duplicate acks.`
39+
displayReceivedRawString(resultText)
40+
break;
41+
}
42+
}
43+
};
44+
}
45+
46+
sendMessage = (message, recipient) => {
47+
if (!this.worker) {
48+
console.error('Could not send message because worker does not exist');
49+
return;
50+
}
51+
52+
this.worker.postMessage({
53+
kind: 'SendMessage',
54+
args: {
55+
message, recipient,
56+
},
57+
});
58+
};
59+
60+
sendTestPacket = (mixnodeIdentity) => {
61+
if (!this.worker) {
62+
console.error('Could not send message because worker does not exist');
63+
return;
64+
}
65+
66+
this.worker.postMessage({
67+
kind: 'TestPacket',
68+
args: {
69+
mixnodeIdentity,
70+
},
71+
});
72+
}
73+
}
74+
75+
let client = null;
76+
77+
async function main() {
78+
client = new WebWorkerClient();
79+
80+
const sendButton = document.querySelector('#send-button');
81+
sendButton.onclick = function () {
82+
sendMessageTo();
83+
};
84+
85+
const magicButton = document.querySelector('#magic-button');
86+
magicButton.onclick = function () {
87+
sendTestPacket();
88+
}
89+
}
90+
91+
/**
92+
* Create a Sphinx packet and send it to the mixnet through the gateway node.
93+
*
94+
* Message and recipient are taken from the values in the user interface.
95+
*
96+
*/
97+
async function sendMessageTo() {
98+
const message = document.getElementById('message').value;
99+
const recipient = document.getElementById('recipient').value;
100+
101+
await client.sendMessage(message, recipient);
102+
displaySend(message);
103+
}
104+
105+
async function sendTestPacket() {
106+
const mixnodeIdentity = document.getElementById('mixnode_identity').value;
107+
108+
await client.sendTestPacket(mixnodeIdentity)
109+
displaySend(`sending test packets to: ${mixnodeIdentity}...`);
110+
}
111+
112+
/**
113+
* Display messages that have been sent up the websocket. Colours them blue.
114+
*
115+
* @param {string} message
116+
*/
117+
function displaySend(message) {
118+
let timestamp = new Date().toISOString().substr(11, 12);
119+
120+
let sendDiv = document.createElement('div');
121+
let paragraph = document.createElement('p');
122+
paragraph.setAttribute('style', 'color: blue');
123+
let paragraphContent = document.createTextNode(timestamp + ' sent >>> ' + message);
124+
paragraph.appendChild(paragraphContent);
125+
126+
sendDiv.appendChild(paragraph);
127+
document.getElementById('output').appendChild(sendDiv);
128+
}
129+
130+
/**
131+
* Display received text messages in the browser. Colour them green.
132+
*
133+
* @param {Uint8Array} raw
134+
*/
135+
function displayReceived(raw, sender_tag, isTestPacket) {
136+
let content = new TextDecoder().decode(raw);
137+
if (sender_tag !== undefined) {
138+
console.log("this message also contained some surbs from", sender_tag)
139+
}
140+
141+
if (isTestPacket) {
142+
const decoded = JSON.parse(content)
143+
content = `Received packet ${decoded.msg_id} / ${decoded.total_msgs} for node ${decoded.encoded_node_identity} (test: ${decoded.test_id})`
144+
}
145+
146+
displayReceivedRawString(content)
147+
}
148+
149+
150+
function displayReceivedRawString(raw) {
151+
let timestamp = new Date().toISOString().substr(11, 12);
152+
let receivedDiv = document.createElement('div');
153+
let paragraph = document.createElement('p');
154+
paragraph.setAttribute('style', 'color: green');
155+
let paragraphContent = document.createTextNode(timestamp + ' received >>> ' + raw);
156+
paragraph.appendChild(paragraphContent);
157+
receivedDiv.appendChild(paragraph);
158+
document.getElementById('output').appendChild(receivedDiv);
159+
}
160+
161+
/**
162+
* Display the nymClient's sender address in the user interface
163+
*
164+
* @param {String} address
165+
*/
166+
function displaySenderAddress(address) {
167+
document.getElementById('sender').value = address;
168+
}
169+
170+
main();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "create-wasm-app",
3+
"version": "0.1.0",
4+
"description": "create an app to consume rust-generated wasm packages",
5+
"main": "index.js",
6+
"bin": {
7+
"create-wasm-app": ".bin/create-wasm-app.js"
8+
},
9+
"scripts": {
10+
"build": "webpack --config webpack.config.js",
11+
"start": "webpack-dev-server --port 8001"
12+
},
13+
"repository": {
14+
"type": "git",
15+
"url": "git+https://github.com/rustwasm/create-wasm-app.git"
16+
},
17+
"keywords": [
18+
"webassembly",
19+
"wasm",
20+
"rust",
21+
"webpack"
22+
],
23+
"author": "Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
24+
"license": "Apache-2.0",
25+
"bugs": {
26+
"url": "https://github.com/nymtech/nym/issues"
27+
},
28+
"homepage": "https://nymtech.net/docs",
29+
"devDependencies": {
30+
"copy-webpack-plugin": "^10.2.4",
31+
"hello-wasm-pack": "^0.1.0",
32+
"webpack": "^5.70.0",
33+
"webpack-cli": "^4.9.2",
34+
"webpack-dev-server": "^4.7.4"
35+
},
36+
"dependencies": {
37+
"@nymproject/nym-client-wasm": "file:../pkg"
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const CopyWebpackPlugin = require('copy-webpack-plugin');
2+
const path = require('path');
3+
4+
module.exports = {
5+
performance: {
6+
hints: false,
7+
maxEntrypointSize: 512000,
8+
maxAssetSize: 512000
9+
},
10+
entry: {
11+
bootstrap: './bootstrap.js',
12+
worker: './worker.js',
13+
},
14+
output: {
15+
path: path.resolve(__dirname, 'dist'),
16+
filename: '[name].js',
17+
},
18+
// mode: 'development',
19+
mode: 'production',
20+
plugins: [
21+
new CopyWebpackPlugin({
22+
patterns: [
23+
'index.html',
24+
{
25+
from: 'node_modules/@nymproject/nym-client-wasm/*.(js|wasm)',
26+
to: '[name][ext]',
27+
},
28+
],
29+
}),
30+
31+
],
32+
experiments: { syncWebAssembly: true },
33+
};

0 commit comments

Comments
 (0)