// Copyright 2020-2023 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. importScripts("nym_client_wasm.js"); console.log("Initializing worker"); // wasm_bindgen creates a global variable (with the exports attached) that is in scope after `importScripts` const { NymNodeTester, WasmGateway, WasmMixNode, WasmNymTopology, default_debug, NymClientBuilder, NymClient, set_panic_hook, Config, GatewayEndpointConfig, ClientStorage, current_network_topology, make_key, make_key2, } = wasm_bindgen; let client = null; let tester = null; const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9"; function dummyTopology() { const l1Mixnode = new WasmMixNode( 1, "n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47", "178.79.143.65", 1789, "4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz", "8ndjk5oZ6HxUZNScLJJ7hk39XtUqGexdKgW7hSX6kpWG", 1, "1.10.0" ); const l2Mixnode = new WasmMixNode( 2, "n1z93z44vf8ssvdhujjvxcj4rd5e3lz0l60wdk70", "109.74.197.180", 1789, "7sVjiMrPYZrDWRujku9QLxgE8noT7NTgBAqizCsu7AoK", "GepXwRnKZDd8x2nBWAajGGBVvF3mrpVMQBkgfrGuqRCN", 2, "1.10.0" ); const l3Mixnode = new WasmMixNode( 3, "n1ptg680vnmef2cd8l0s9uyc4f0hgf3x8sed6w77", "176.58.101.80", 1789, "FoM5Mx9Pxk1g3zEqkS3APgtBeTtTo3M8k7Yu4bV6kK1R", "DeYjrDC2AcQRVFshiKnbUo6bRvPyZ33QGYR2DLeFJ9qD", 3, "1.10.0" ); const gateway = new WasmGateway( "n16evnn8glr0sham3matj8rg2s24m6x56ayk87ts", "85.159.212.96", 1789, 9000, "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9", "BtYjoWihiuFihGKQypmpSspbhmWDPxzqeTVSd8ciCpWL", "1.10.1" ); const mixnodes = new Map(); mixnodes.set(1, [l1Mixnode]); mixnodes.set(2, [l2Mixnode]); mixnodes.set(3, [l3Mixnode]); const gateways = [gateway]; return new WasmNymTopology(mixnodes, gateways); } function printAndDisplayTestResult(result) { result.log_details(); self.postMessage({ kind: "DisplayTesterResults", args: { score: result.score(), sentPackets: result.sent_packets, receivedPackets: result.received_packets, receivedAcks: result.received_acks, duplicatePackets: result.duplicate_packets, duplicateAcks: result.duplicate_acks, }, }); } async function testWithTester() { // A) construct with hardcoded topology const topology = dummyTopology(); const nodeTester = await new NymNodeTester(topology, preferredGateway); // B) first get topology directly from nym-api // const validator = 'https://qwerty-validator-api.qa.nymte.ch/api'; // const topology = await current_network_topology(validator) // const nodeTester = await new NymNodeTester(topology, preferredGateway); // // C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers) // const validator = 'https://qwerty-validator-api.qa.nymte.ch/api'; // const nodeTester = await NymNodeTester.new_with_api(validator, preferredGateway) // B) first get topology directly from nym-api // const validator = 'https://qwerty-validator-api.qa.nymte.ch/api'; // const topology = await current_network_topology(validator) // const nodeTester = await new NymNodeTester(topology, undefined, preferredGateway); // // C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers) // const validator = 'https://qwerty-validator-api.qa.nymte.ch/api'; // const nodeTester = await NymNodeTester.new_with_api(validator, undefined, preferredGateway) // D, E, F) you also don't have to specify the gateway. if you don't, a random one (from your topology) will be used // const topology = dummyTopology() // const nodeTester = await new NymNodeTester(topology); self.onmessage = async (event) => { if (event.data && event.data.kind) { switch (event.data.kind) { case "TestPacket": { const { mixnodeIdentity } = event.data.args; console.log("starting node test..."); let result = await nodeTester.test_node(mixnodeIdentity); printAndDisplayTestResult(result); } } } }; } async function testerReconnection() { const validator = "https://qwerty-validator-api.qa.nymte.ch/api"; const nodeTester = await NymNodeTester.new_with_api(validator); self.onmessage = async (event) => { if (event.data && event.data.kind) { switch (event.data.kind) { case "TestPacket": { const { mixnodeIdentity } = event.data.args; console.log("starting node test..."); let result1 = await nodeTester.test_node(mixnodeIdentity); console.log("sleeping for 5s"); await new Promise((r) => setTimeout(r, 5000)); await nodeTester.disconnect_from_gateway(); console.log("sleeping for 5s"); await new Promise((r) => setTimeout(r, 5000)); await nodeTester.reconnect_to_gateway(); let result2 = await nodeTester.test_node(mixnodeIdentity); printAndDisplayTestResult(result1); printAndDisplayTestResult(result2); } } } }; } async function testWithNymClient() { const topology = dummyTopology(); let received = 0; const onMessageHandler = (message) => { received += 1; self.postMessage({ kind: "ReceiveMessage", args: { message, senderTag: undefined, isTestPacket: true, }, }); // it's really up to the user to create proper callback here... console.log(`received ${received} packets so far`); }; console.log("Instantiating WASM client..."); let clientBuilder = NymClientBuilder.new_tester( topology, onMessageHandler, preferredGateway ); console.log("Web worker creating WASM client..."); let local_client = await clientBuilder.start_client(); console.log("WASM client running!"); const selfAddress = local_client.self_address(); // set the global (I guess we don't have to anymore?) client = local_client; console.log(`Client address is ${selfAddress}`); self.postMessage({ kind: "Ready", args: { selfAddress, }, }); // Set callback to handle messages passed to the worker. self.onmessage = async (event) => { console.log(event); if (event.data && event.data.kind) { switch (event.data.kind) { case "SendMessage": { const { message, recipient } = event.data.args; let uint8Array = new TextEncoder().encode(message); await client.send_regular_message(uint8Array, recipient); break; } case "TestPacket": { const { mixnodeIdentity } = event.data.args; const req = await client.try_construct_test_packet_request( mixnodeIdentity ); await client.change_hardcoded_topology(req.injectable_topology()); await client.try_send_test_packets(req); break; } } } }; } async function normalNymClientUsage() { self.postMessage({ kind: "DisableMagicTestButton" }); // only really useful if you want to adjust some settings like traffic rate // (if not needed you can just pass a null) const debug = default_debug(); debug.disable_main_poisson_packet_distribution = true; debug.disable_loop_cover_traffic_stream = true; debug.use_extended_packet_size = false; // debug.average_packet_delay_ms = BigInt(10); // debug.average_ack_delay_ms = BigInt(10); // debug.ack_wait_addition_ms = BigInt(3000); // debug.ack_wait_multiplier = 10; debug.topology_refresh_rate_ms = BigInt(60000); const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9"; const validator = "https://qwerty-validator-api.qa.nymte.ch/api"; const config = new Config("my-awesome-wasm-client", validator, debug); const onMessageHandler = (message) => { console.log(message); self.postMessage({ kind: "ReceiveMessage", args: { message, }, }); }; console.log("Instantiating WASM client..."); let localClient = await new NymClient(config, onMessageHandler); console.log("WASM client running!"); const selfAddress = localClient.self_address(); // set the global (I guess we don't have to anymore?) client = localClient; console.log(`Client address is ${selfAddress}`); self.postMessage({ kind: "Ready", args: { selfAddress, }, }); // Set callback to handle messages passed to the worker. self.onmessage = async (event) => { console.log(event); if (event.data && event.data.kind) { switch (event.data.kind) { case "SendMessage": { const { message, recipient } = event.data.args; let uint8Array = new TextEncoder().encode(message); await client.send_regular_message(uint8Array, recipient); break; } } } }; } async function messWithStorage() { self.onmessage = async (event) => { if (event.data && event.data.kind) { switch (event.data.kind) { case "TestPacket": { const { mixnodeIdentity } = event.data.args; console.log("button clicked...", mixnodeIdentity); let id1 = "one"; let id2 = "two"; console.log("making store1 NO-ENC"); let _storage1 = await ClientStorage.new_unencrypted(id1); console.log("making store2 ENC"); let _storage2 = await new ClientStorage(id2, "my-secret-password"); // // // // console.log("attempting to use store1 WITH PASSWORD") // let _storage1_alt = await new ClientStorage(id1, "password"); // // // // console.log("attempting to use store2 WITHOUT PASSWORD") // let _storage2_alt = await ClientStorage.new_unencrypted(id2); // // // // console.log("attempting to use store2 with WRONG PASSWORD") // let _storage2_bad = await new ClientStorage(id2, "bad-password") // // console.log("read1: ", await storage1.read()); // console.log("read2: ", await storage2.read()); // // console.log("store1: ", await storage1.store("FOOMP")); // // console.log("read1: ", await storage1.read()); // console.log("read2: ", await storage2.read()); } } } }; } async function main() { // load WASM package await wasm_bindgen("nym_client_wasm_bg.wasm"); console.log("Loaded WASM"); // sets up better stack traces in case of in-rust panics set_panic_hook(); // show reconnection capabilities // await testerReconnection() // run test on simplified and dedicated tester: await testWithTester(); // 'Normal' client setup (to send 'normal' messages) // await normalNymClientUsage() } // Let's get started! main();