Feature/wasm js demo (#191)
* Moved `nym-sphinx-wasm` into `wasm` folder and renamed its examples * A proposal for some structural cleanup * desktop client: beginning of a Rust example for chunking. * Simplifying desktop client readme * Tarting up webassembly README * A more visual webassembly client example * clients/webassembly: grouping a few things into named functions for clarity * clients/webassembly: put URLs at top of file, prepping for drop-down action * mixnode + provider run: changed startup error messages a bit * clients/webassembly: scrolling from the bottom in the demo
This commit is contained in:
+7
-7
@@ -5,11 +5,13 @@ opt-level = "s"
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"common/clients/directory-client",
|
||||
"common/clients/mix-client",
|
||||
"common/clients/multi-tcp-client",
|
||||
"common/clients/provider-client",
|
||||
"common/clients/validator-client",
|
||||
"clients/desktop",
|
||||
"clients/webassembly",
|
||||
"common/client-libs/directory-client",
|
||||
"common/client-libs/mix-client",
|
||||
"common/client-libs/multi-tcp-client",
|
||||
"common/client-libs/provider-client",
|
||||
"common/client-libs/validator-client",
|
||||
"common/config",
|
||||
"common/crypto",
|
||||
"common/healthcheck",
|
||||
@@ -18,8 +20,6 @@ members = [
|
||||
"common/topology",
|
||||
"gateway",
|
||||
"mixnode",
|
||||
"nym-client",
|
||||
"nym-sphinx-wasm",
|
||||
"sfw-provider",
|
||||
"sfw-provider/sfw-provider-requests",
|
||||
"validator",
|
||||
|
||||
@@ -28,17 +28,17 @@ tokio = { version = "0.2", features = ["full"] }
|
||||
tokio-tungstenite = "0.10.1"
|
||||
|
||||
## internal
|
||||
config = {path = "../common/config"}
|
||||
crypto = {path = "../common/crypto"}
|
||||
directory-client = { path = "../common/clients/directory-client" }
|
||||
healthcheck = { path = "../common/healthcheck" }
|
||||
mix-client = { path = "../common/clients/mix-client" }
|
||||
multi-tcp-client = { path = "../common/clients/multi-tcp-client" }
|
||||
nymsphinx = { path = "../common/nymsphinx" }
|
||||
pemstore = {path = "../common/pemstore"}
|
||||
provider-client = { path = "../common/clients/provider-client" }
|
||||
sfw-provider-requests = { path = "../sfw-provider/sfw-provider-requests" }
|
||||
topology = {path = "../common/topology" }
|
||||
config = {path = "../../common/config"}
|
||||
crypto = {path = "../../common/crypto"}
|
||||
directory-client = { path = "../../common/client-libs/directory-client" }
|
||||
healthcheck = { path = "../../common/healthcheck" }
|
||||
mix-client = { path = "../../common/client-libs/mix-client" }
|
||||
multi-tcp-client = { path = "../../common/client-libs/multi-tcp-client" }
|
||||
nymsphinx = { path = "../../common/nymsphinx" }
|
||||
pemstore = {path = "../../common/pemstore"}
|
||||
provider-client = { path = "../../common/client-libs/provider-client" }
|
||||
sfw-provider-requests = { path = "../../sfw-provider/sfw-provider-requests" }
|
||||
topology = {path = "../../common/topology" }
|
||||
|
||||
[build-dependencies]
|
||||
built = "0.3.2"
|
||||
@@ -0,0 +1,3 @@
|
||||
# Nym Desktop Client
|
||||
|
||||
The Nym Desktop Client communicates with the remote, decentralised nodes which make up the Nym system as a whole.
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use config::NymConfig;
|
||||
use std::time;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
fn setup_logging() {
|
||||
let mut log_builder = pretty_env_logger::formatted_timed_builder();
|
||||
if let Ok(s) = ::std::env::var("RUST_LOG") {
|
||||
log_builder.parse_filters(&s);
|
||||
} else {
|
||||
// default to 'Info'
|
||||
log_builder.filter(None, log::LevelFilter::Info);
|
||||
}
|
||||
|
||||
log_builder
|
||||
.filter_module("hyper", log::LevelFilter::Warn)
|
||||
.filter_module("tokio_reactor", log::LevelFilter::Warn)
|
||||
.filter_module("reqwest", log::LevelFilter::Warn)
|
||||
.filter_module("mio", log::LevelFilter::Warn)
|
||||
.filter_module("want", log::LevelFilter::Warn)
|
||||
.init();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// totally optional
|
||||
setup_logging();
|
||||
|
||||
let mut rt = Runtime::new().unwrap();
|
||||
|
||||
// note: I have manually initialised the client with this config (because the keys weren't
|
||||
// saved, etc - a really dodgy setup but good enough for a quick test)
|
||||
// so to run it, you need to do the usual `nym-client init --id native-local`
|
||||
// then optionally modify config.toml to increase rates, etc. if you want
|
||||
let config = nym_client::config::Config::load_from_file(None, Some("native-local")).unwrap();
|
||||
let mut client = nym_client::client::NymClient::new(config);
|
||||
|
||||
let input_data = std::fs::read("trailer.mp4").unwrap();
|
||||
|
||||
client.start();
|
||||
let address = client.as_mix_destination();
|
||||
client.send_message(address, input_data);
|
||||
|
||||
loop {
|
||||
let mut messages = rt.block_on(async {
|
||||
tokio::time::delay_for(time::Duration::from_secs(2)).await;
|
||||
client.check_for_messages_async().await
|
||||
});
|
||||
|
||||
if !messages.is_empty() {
|
||||
assert_eq!(messages.len(), 1);
|
||||
std::fs::write("downloaded.mp4", messages.pop().unwrap()).unwrap();
|
||||
println!("done!");
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,8 @@ slice_as_array = "1.1.0"
|
||||
wasm-bindgen = "0.2"
|
||||
|
||||
# internal
|
||||
crypto = { path = "../common/crypto" }
|
||||
nymsphinx = { path = "../common/nymsphinx" }
|
||||
crypto = { path = "../../common/crypto" }
|
||||
nymsphinx = { path = "../../common/nymsphinx" }
|
||||
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This is a Rust crate which is set up to automatically cross-compile the contents of `lib.rs` to WebAssembly (aka wasm).
|
||||
|
||||
Wasm is pretty close to bare metal. Browser-based or server-side JavaScript (or other wasm-using environments) can use the wasm output from this crate to create Sphinx packets at much higher speeds than would be possible using (interpreted) JavaScript. This helps browser-based and mobile applications get stronger privacy, in a way that wasn't previously possible.
|
||||
Wasm is pretty close to bare metal. Browser-based or server-side JavaScript (or other wasm-using environments) can use the wasm output from this crate to create Sphinx packets at much higher speeds than would be possible using (interpreted) JavaScript. This enables browser-based and mobile applications get stronger privacy, in a way that wasn't previously possible.
|
||||
|
||||
### Compiling
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nym WebAssembly Demo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>
|
||||
<label for="fname">Recipient address: </label><input type="text" id="recipient" name="recipient"
|
||||
value="HNka47Wji3LF23wGtU5oAFwoaM3tfbp2fBLGBPLcwUZ5">
|
||||
</p>
|
||||
<label for="fname">Text to send: </label><input type="text" id="sendtext" name="sendtext" value="Hello mixnet!">
|
||||
<button id="send-button">Send</button>
|
||||
|
||||
<p>Send messages to the mixnet 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>
|
||||
<span id="output"></div>
|
||||
</p>
|
||||
<script src="./bootstrap.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
import * as wasm from "nym-sphinx-wasm";
|
||||
|
||||
console.log("Retrieving topology...");
|
||||
|
||||
class Route {
|
||||
constructor(nodes) {
|
||||
this.nodes = nodes;
|
||||
@@ -29,20 +27,48 @@ class NodeData {
|
||||
}
|
||||
}
|
||||
|
||||
async function getTopology() {
|
||||
let topologyURL = 'http://127.0.0.1:8080/api/presence/topology'; // TODO change this DAVE!!!!
|
||||
let response = await makeRequest('get', topologyURL);
|
||||
async function main() {
|
||||
var gatewayUrl = "ws://127.0.0.1:1793";
|
||||
var directoryUrl = "http://127.0.0.1:8080/api/presence/topology";
|
||||
|
||||
// Get the topology, then the mixnode and provider data
|
||||
const topology = await getTopology(directoryUrl);
|
||||
|
||||
// Set up a websocket connection to the gateway node
|
||||
var connection = await connectWebsocket(gatewayUrl).then(function (c) {
|
||||
return c
|
||||
}).catch(function (err) {
|
||||
console.log("Websocket ERROR: " + err);
|
||||
})
|
||||
|
||||
// Set up the send button
|
||||
const sendButton = document.querySelector('#send-button');
|
||||
sendButton.onclick = function () {
|
||||
sendMessageToMixnet(connection, topology);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a Sphinx packet and send it to the mixnet through the Gateway node.
|
||||
function sendMessageToMixnet(connection, topology) {
|
||||
let route = constructRoute(topology);
|
||||
var recipient = document.getElementById("recipient").value;
|
||||
var sendText = document.getElementById("sendtext").value;
|
||||
let packet = wasm.create_sphinx_packet(JSON.stringify(route), sendText, recipient);
|
||||
connection.send(packet);
|
||||
displaySend(packet);
|
||||
display("Sent a Sphinx packet containing message: " + sendText);
|
||||
}
|
||||
|
||||
async function getTopology(directoryUrl) {
|
||||
let response = await http('get', directoryUrl);
|
||||
let topology = JSON.parse(response);
|
||||
return topology;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Get the topology, then the mixnode and provider data
|
||||
const topology = await getTopology();
|
||||
// Construct a route from the current network topology so we can get wasm to build us a Sphinx packet
|
||||
function constructRoute(topology) {
|
||||
const mixnodes = topology.mixNodes;
|
||||
const provider = topology.mixProviderNodes[0];
|
||||
|
||||
// Construct a route so we can get wasm to build us a Sphinx packet
|
||||
let nodes = [];
|
||||
mixnodes.forEach(node => {
|
||||
let n = new NodeData(node.host, node.pubKey);
|
||||
@@ -50,29 +76,23 @@ async function main() {
|
||||
});
|
||||
let p = new NodeData(provider.mixnetListener, provider.pubKey)
|
||||
nodes.push(p);
|
||||
let route = new Route(nodes);
|
||||
|
||||
// Create the packet
|
||||
let packet = wasm.create_sphinx_packet(JSON.stringify(route), "THIS IS THE MESSAGE", "C2fdNoUybRuGrVYUM6QRejiELPQCohGbxjhKpU4UZ4ci");
|
||||
|
||||
// Set up a websocket connection to the gateway node
|
||||
var port = "1793" // gateway websocket listens on 1793 by default, change if yours is different
|
||||
var url = "ws://127.0.0.1:" + port;
|
||||
|
||||
connectWebsocket(url).then(function (server) {
|
||||
server.send("hello gateway");
|
||||
console.log(packet);
|
||||
server.send(packet);
|
||||
}).catch(function (err) {
|
||||
console.log("Websocket ERROR: " + err);
|
||||
})
|
||||
return new Route(nodes);
|
||||
}
|
||||
|
||||
// Let's get started!
|
||||
main();
|
||||
|
||||
// utility functions below here, nothing too interesting...
|
||||
|
||||
function makeRequest(method, url) {
|
||||
function display(message) {
|
||||
document.getElementById("output").innerHTML = "<p>" + message + "</p >" + document.getElementById("output").innerHTML;
|
||||
}
|
||||
|
||||
function displaySend(message) {
|
||||
document.getElementById("output").innerHTML = "<p style='color: blue; word-break: break-all;'>sent >>> " + message + "</p >" + document.getElementById("output").innerHTML;
|
||||
}
|
||||
|
||||
function http(method, url) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open(method, url);
|
||||
@@ -96,12 +116,6 @@ function makeRequest(method, url) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function print(name, obj) {
|
||||
console.log(name + ": " + JSON.stringify(obj));
|
||||
}
|
||||
|
||||
|
||||
function connectWebsocket(url) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var server = new WebSocket(url);
|
||||
@@ -19,10 +19,10 @@ tokio = { version = "0.2", features = ["full"] }
|
||||
|
||||
## internal
|
||||
crypto = { path = "../crypto" }
|
||||
directory-client = { path = "../clients/directory-client" }
|
||||
multi-tcp-client = { path = "../clients/multi-tcp-client" }
|
||||
directory-client = { path = "../client-libs/directory-client" }
|
||||
multi-tcp-client = { path = "../client-libs/multi-tcp-client" }
|
||||
nymsphinx = {path = "../nymsphinx" }
|
||||
provider-client = { path = "../clients/provider-client" }
|
||||
provider-client = { path = "../client-libs/provider-client" }
|
||||
sfw-provider-requests = { path = "../../sfw-provider/sfw-provider-requests" }
|
||||
topology = {path = "../topology" }
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ futures = "0.3"
|
||||
futures-channel = "0.3"
|
||||
futures-util = { version = "0.3", default-features = false, features = ["async-await", "sink", "std"] }
|
||||
log = "0.4"
|
||||
multi-tcp-client = { path = "../common/clients/multi-tcp-client" }
|
||||
multi-tcp-client = { path = "../common/client-libs/multi-tcp-client" }
|
||||
pretty_env_logger = "0.3"
|
||||
tokio = { version = "0.2", features = ["full"] }
|
||||
tokio-tungstenite = "0.10.1"
|
||||
|
||||
+2
-2
@@ -22,8 +22,8 @@ tokio = { version = "0.2", features = ["full"] }
|
||||
## internal
|
||||
config = {path = "../common/config"}
|
||||
crypto = {path = "../common/crypto"}
|
||||
directory-client = { path = "../common/clients/directory-client" }
|
||||
multi-tcp-client = { path = "../common/clients/multi-tcp-client" }
|
||||
directory-client = { path = "../common/client-libs/directory-client" }
|
||||
multi-tcp-client = { path = "../common/client-libs/multi-tcp-client" }
|
||||
nymsphinx = {path = "../common/nymsphinx" }
|
||||
pemstore = {path = "../common/pemstore"}
|
||||
topology = {path = "../common/topology"}
|
||||
|
||||
@@ -130,7 +130,7 @@ impl MixNode {
|
||||
|
||||
if let Some(duplicate_node_key) = self.check_if_same_ip_node_exists() {
|
||||
error!(
|
||||
"Our announce-host is identical to one of existing nodes! (its key is {:?}",
|
||||
"Our announce-host is identical to an existing node's announce-host! (its key is {:?}",
|
||||
duplicate_node_key
|
||||
);
|
||||
return;
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# Nym Client
|
||||
|
||||
The Nym Client communicates with the remote, decentralised nodes which make up the Nym system as a whole.
|
||||
|
||||
It needs to handle communication with 3 types of remote nodes. Here's the development status of each:
|
||||
|
||||
- [ ] Directory nodes
|
||||
- [ ] Mix nodes
|
||||
- [ ] Validator nodes
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Hello wasm-pack!</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
|
||||
<script src="./bootstrap.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -27,7 +27,7 @@ hmac = "0.7.1"
|
||||
## internal
|
||||
crypto = { path = "../common/crypto" }
|
||||
config = { path = "../common/config" }
|
||||
directory-client = { path = "../common/clients/directory-client" }
|
||||
directory-client = { path = "../common/client-libs/directory-client" }
|
||||
nymsphinx = { path = "../common/nymsphinx" }
|
||||
pemstore = { path = "../common/pemstore" }
|
||||
sfw-provider-requests = { path = "./sfw-provider-requests" }
|
||||
@@ -36,7 +36,7 @@ topology = { path = "../common/topology"}
|
||||
# this dependency is due to requiring to know content of loop message. however, mix-client module itself
|
||||
# is going to be removed or renamed at some point as it no longer servers its purpose of sending traffic to mix network
|
||||
# and only provides utility functions
|
||||
mix-client = { path = "../common/clients/mix-client" }
|
||||
mix-client = { path = "../common/client-libs/mix-client" }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -127,7 +127,7 @@ impl ServiceProvider {
|
||||
|
||||
if let Some(duplicate_provider_key) = self.check_if_same_ip_provider_exists() {
|
||||
error!(
|
||||
"Our announce-host is identical to one of existing nodes! (its key is {:?}",
|
||||
"Our announce-host is identical to an existing node's announce-host! (its key is {:?}",
|
||||
duplicate_provider_key
|
||||
);
|
||||
return;
|
||||
|
||||
@@ -28,7 +28,7 @@ tokio = { version = "0.2", features = ["full"] }
|
||||
## internal
|
||||
crypto = {path = "../common/crypto"}
|
||||
config = {path = "../common/config"}
|
||||
directory-client = { path = "../common/clients/directory-client" }
|
||||
directory-client = { path = "../common/client-libs/directory-client" }
|
||||
healthcheck = {path = "../common/healthcheck" }
|
||||
topology = {path = "../common/topology"}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user