diff --git a/Cargo.lock b/Cargo.lock index 04e404295af..5f0bf0be508 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1697,7 +1697,7 @@ dependencies = [ [[package]] name = "nym-client-wasm" -version = "0.7.2" +version = "0.7.3" dependencies = [ "console_error_panic_hook", "crypto", diff --git a/clients/webassembly/Cargo.toml b/clients/webassembly/Cargo.toml index fad6bdbfe8e..0e67336d537 100644 --- a/clients/webassembly/Cargo.toml +++ b/clients/webassembly/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "nym-client-wasm" authors = ["Dave Hrycyszyn ", "Jedrzej Stuczynski "] -version = "0.7.2" +version = "0.7.3" edition = "2018" keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"] license = "Apache-2.0" diff --git a/clients/webassembly/client.js b/clients/webassembly/client.js index df4e8116b46..dd63395716c 100644 --- a/clients/webassembly/client.js +++ b/clients/webassembly/client.js @@ -1,5 +1,9 @@ import * as wasm from "."; +/** + * A Nym identity, consisting of a public/private keypair and a Nym + * gateway address. + */ export class Identity { // in the future this should allow for loading from local storage constructor() { @@ -10,6 +14,10 @@ export class Identity { } } +/** + * A Client which connects to a Nym gateway via websocket. All communication + * with the Nym network happens through this connection. + */ export class Client { constructor(directoryUrl, identity, authToken) { this.authToken = authToken @@ -19,10 +27,17 @@ export class Client { this.topologyEndpoint = directoryUrl + "/api/presence/topology"; } + /** + * @return {string} a user-pubkey@nym-gateway recipient address + */ formatAsRecipient() { return `${this.identity.address}@${this.gateway.mixAddress}` } + /** + * Get the current network topology, then connect to this client's Nym gateway + * via websocket. + */ async start() { await this.updateTopology(); this._getInitialGateway(); @@ -34,6 +49,11 @@ export class Client { return this.authToken !== null } + /** + * Update the Nym network topology. + * + * @returns an object containing the current Nym network topology + */ async updateTopology() { let response = await http('get', this.topologyEndpoint); let topology = JSON.parse(response); // make sure it's a valid json @@ -42,9 +62,11 @@ export class Client { return topology; } - /* Gets the address of a Nym gateway to send the Sphinx packet to. - At present we choose the first gateway as the network should only be running - one. Later, we will implement multiple gateways. */ + /** + * Gets the address of a Nym gateway to send the Sphinx packet to. + * At present we choose the first gateway as the network should only be + * running one. Later, we will implement multiple gateways. + */ _getInitialGateway() { if (this.gateway !== null) { console.error("tried to re-initialise gateway data"); @@ -60,6 +82,9 @@ export class Client { } } + /** + * Connect to the client's defined Nym gateway via websocket. + */ connect() { return new Promise((resolve, reject) => { const conn = new WebSocket(this.gateway.socketAddress); @@ -84,22 +109,39 @@ export class Client { }) } + /** + * Sends a registration request to a Nym gateway node. Use it only if you + * haven't registered this client before. + */ sendRegisterRequest() { - const registerReq = makeRegisterRequest(this.identity.address); + const registerReq = buildRegisterRequest(this.identity.address); this.gateway.conn.send(registerReq); this.onRegisterRequestSend(); } + /** + * Authenticates with a Nym gateway for this client. + * + * @param {string} token + */ sendAuthenticateRequest(token) { - const authenticateReq = makeAuthenticateRequest(this.identity.address, token); + const authenticateReq = buildAuthenticateRequest(this.identity.address, token); this.conn.send(authenticateReq); this.onAuthenticateRequestSend(); } - /* - NOTE: this currently does not implement chunking and messages over ~1KB - will cause a panic. This will be fixed in a future version. - */ + /** + * Sends a message up the websocket to this client's Nym gateway. + * + * NOTE: this currently does not implement chunking and messages over ~1KB + * will cause a panic. This will be fixed in a future version. + * + * `message` must be text at the moment. Binary `Blob` and `ArrayBuffer` + * will be supported soon. + * + * @param {*} message + * @param {string} recipient + */ sendMessage(message, recipient) { if (this.gateway === null || this.gateway.conn === null) { console.error("Client was not initialised"); @@ -116,6 +158,15 @@ export class Client { this.onMessageSend(); } + /** + * A callback triggered when a message is received from this client's Nym + * gateway. + * + * The `event` may be a binary blob which was the payload of a Sphinx packet, + * or it may be a JSON control message (for example, the result of an + * authenticate request). + * @param {*} event + */ onMessage(event) { if (event.data instanceof Blob) { this.onBlobResponse(event); @@ -133,10 +184,17 @@ export class Client { // all the callbacks that can be overwritten + /** + * A callback that fires when network topology is updated. + */ onUpdatedTopology() { console.log("Default: Updated topology") } + /** + * + * @param {*} event + */ onConnect(event) { console.log("Default: Established gateway connection", event); } @@ -200,20 +258,42 @@ export class Client { reader.readAsText(event.data); } - // Alternatively you may use default implementation and treat everything as text + /** + * @callback that makes a best-effort attempt to return decrypted Sphinx bytes as text. + * + * Note that no checks are performed to determine whether something is + * really text. If the received data is in fact binary, you'll get + * binary-as-text from this callback. + */ onText(data) { console.log("Default: parsed the following data", data); } } -function makeRegisterRequest(address) { +/** + * Build a JSON registration request. + * + * @param {string} address + */ +function buildRegisterRequest(address) { return JSON.stringify({ "type": "register", "address": address }); } -function makeAuthenticateRequest(address, token) { +/** + * Build a JSON authentication request. + * + * @param {string} address + * @param {string} token + */ +function buildAuthenticateRequest(address, token) { return JSON.stringify({ "type": "authenticate", "address": address, "token": token }); } +/** + * Make an HTTP request. + * @param {string} method + * @param {string} url + */ function http(method, url) { return new Promise(function (resolve, reject) { let xhr = new XMLHttpRequest(); diff --git a/clients/webassembly/js-example/index.js b/clients/webassembly/js-example/index.js index bd2a9caa2b9..687aab165c4 100644 --- a/clients/webassembly/js-example/index.js +++ b/clients/webassembly/js-example/index.js @@ -36,30 +36,50 @@ async function main() { await nymClient.start(); } -// Create a Sphinx packet and send it to the mixnet through the gateway node. +/** + * 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 + */ function sendMessageTo(nymClient) { var message = document.getElementById("message").value; var recipient = document.getElementById("recipient").value; nymClient.sendMessage(message, recipient); displaySend(message); } - +/** + * Display messages that have been sent up the websocket. Colours them blue. + * + * @param {string} message + */ function displaySend(message) { let timestamp = new Date().toISOString().substr(11, 12); let out = "

" + timestamp + " sent >>> " + message + "

"; document.getElementById("output").innerHTML = out + document.getElementById("output").innerHTML; } +/** + * Display received text messages in the browser. Colour them green. + * + * @param {string} message + */ function displayReceived(message) { let timestamp = new Date().toISOString().substr(11, 12); let out = "

" + timestamp + " received >>> " + message + "

"; document.getElementById("output").innerHTML = out + document.getElementById("output").innerHTML; } + +/** + * Display the nymClient's sender address in the user interface + * + * @param {Client} nymClient + */ function displaySenderAddress(nymClient) { document.getElementById("sender").value = nymClient.formatAsRecipient(); } - // Let's get started! main(); \ No newline at end of file diff --git a/clients/webassembly/js-example/package-lock.json b/clients/webassembly/js-example/package-lock.json index 8ef029f36be..6c51a77bafc 100644 --- a/clients/webassembly/js-example/package-lock.json +++ b/clients/webassembly/js-example/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@nymproject/nym-client-wasm": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@nymproject/nym-client-wasm/-/nym-client-wasm-0.7.2.tgz", - "integrity": "sha512-Xyjkigo3IvUFsyItQkZnZjQj49VGVNeNFDk8TORx+iQNifcGlMA1T7swPcBE11xt0oTsJk9P5mFYSrMljPRrqA==" + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@nymproject/nym-client-wasm/-/nym-client-wasm-0.7.3.tgz", + "integrity": "sha512-sCLX4BGK+BjWh1lP+URexWsJoZQtyXSy2AmCaDUgCJm2rVFWhgeS2odkg2S+tdTeTeYO9Fu2YPvD8P3CFIelLg==" }, "@types/events": { "version": "3.0.0", @@ -3140,9 +3140,6 @@ "path-key": "^2.0.0" } }, - "nym-client-wasm": { - "version": "file:../pkg" - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/clients/webassembly/js-example/package.json b/clients/webassembly/js-example/package.json index 6503bddf166..a91b577b06a 100644 --- a/clients/webassembly/js-example/package.json +++ b/clients/webassembly/js-example/package.json @@ -34,6 +34,6 @@ "webpack-dev-server": "^3.11.0" }, "dependencies": { - "@nymproject/nym-client-wasm": "^0.7.2" + "@nymproject/nym-client-wasm": "^0.7.3" } }