diff --git a/download_cmix_binaries.sh b/download_cmix_binaries.sh index d38cf4e509a72844c8babcc22947abc91b891ae2..e9a4d39352e748d32d878fc65734b9a7588ba06b 100755 --- a/download_cmix_binaries.sh +++ b/download_cmix_binaries.sh @@ -86,13 +86,14 @@ for BRANCH in $(echo "forcedbranch" $FBRANCH $FBRANCH2 $DEFAULTBRANCH); do GPULIB_URL=${GPULIB_URL:="${REPOS_API}server/$BRANCH_URL/libpowmosm75.so?job=build"} GPULIB2_URL=${GPULIB2_URL:="${REPOS_API}server/$BRANCH_URL/libpow.fatbin?job=build"} CLIENT_REG_URL=${CLIENT_REG_URL:="${REPOS_API}client-registrar/$BRANCH_URL/registration$BIN"} + XXDK_WASM_URL=${XXDK_WASM_URL:="${REPOS_API}xxdk-wasm/$BRANCH_URL/xxdk.wasm?job=build"} else UDB_URL=${UDB_URL:="${REPOS_API}/$BRANCH/udb$BIN"} SERVER_URL=${SERVER_URL:="${REPOS_API}/$BRANCH/server$BIN"} GW_URL=${GW_URL:="${REPOS_API}/$BRANCH/gateway$BIN"} PERMISSIONING_URL=${PERMISSIONING_URL:="${REPOS_API}/$BRANCH/registration.stateless$BIN"} CLIENT_URL=${CLIENT_URL:="${REPOS_API}/$BRANCH/client$BIN"} - CLIENT_REG_URL=${CLIENT_REG_URL:="${REPOS_API}client-registrar/$BRANCH_URL/registration$BIN"} + XXDK_WASM_URL=${XXDK_WASM_URL:="${REPOS_API}/$BRANCH/xxdk.wasm?job=build"} fi set -x @@ -122,11 +123,16 @@ for BRANCH in $(echo "forcedbranch" $FBRANCH $FBRANCH2 $DEFAULTBRANCH); do curl -s -f -L -H "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" -o "$download_path/client" ${CLIENT_URL} fi - # Silently download the client registrar binary to the provisioning directory + # Silently download the client registrar binary to the provisioning directory if [ ! -f $download_path/client-registrar ] && [[ "$CLIENT_REG_URL" != *"forcedbranch"* ]]; then curl -s -f -L -H "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" -o "$download_path/client-registrar" ${CLIENT_REG_URL} fi + # Silently download the client registrar binary to the provisioning directory + if [ ! -f $download_path/xxdk.wasm ] && [[ "$XXDK_WASM_URL" != *"forcedbranch"* ]]; then + curl -s -f -L -H "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" -o "$download_path/xxdk.wasm" ${XXDK_WASM_URL} + fi + if [[ $2 == "d" ]]; then # Silently download the Server binary to the provisioning directory if [ ! -f $download_path/server-cuda ] && [[ "$SERVER_GPU_URL" != *"forcedbranch"* ]]; then @@ -161,6 +167,7 @@ fi unset GPULIB_URL unset GPULIB2_URL unset CLIENT_REG_URL + unset XXDK_WASM_URL done # Make binaries executable diff --git a/wasm/README.md b/wasm/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ebb0478a036caa237074f94f6beded193d354277 --- /dev/null +++ b/wasm/README.md @@ -0,0 +1,56 @@ +# Running WASM Tests + +To run WASM tests, servers and gateways are run like normal, but the client is +served by an HTTP server in the browser. + +The `testServer` directory contains a basic HTTP server (`clientServer.go`) and +various directories each containing a different example. Each example contains +one or more cMix clients running in javascript. The `assets` directory contains +files used by all the examples. + +To run the server, first ensure the compiled bindings are in the `bin` +directory. Then run the server + +```shell +$ GOOS=js GOARCH=wasm go build -o examples/xxdk.wasm +$ cd testServer/ +$ go run clientServer.go +``` + +Navigate to http://localhost:9090 to see a list of files in the server and +navigate to a `.html` file in any of the examples to open a client. + +To start the network, run `run.sh` in the console. + + +## `wasm_exec.js` + +`wasm_exec.js` is provided by Go and is used to import the WebAssembly module in +the browser. It can be retrieved from Go using the following command. + +```shell +$ cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" test/assets/ +``` + +Note that this repository makes edits to `wasm_exec.js` and you must either use +the one in this repository or add the following lines in the `go` `importObject` +on `global.Go`. + +```javascript +global.Go = class { + constructor() { + // ... + this.importObject = { + go: { + // ... + // func Throw(exception string, message string) + 'gitlab.com/elixxir/xxdk-wasm/utils.throw': (sp) => { + const exception = loadString(sp + 8) + const message = loadString(sp + 24) + throw globalThis[exception](message) + }, + } + } + } +} +``` diff --git a/wasm/client-registrar.yaml b/wasm/client-registrar.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bfc9d3b0cd31cf3ed5d8d7728c23f89f4caeb567 --- /dev/null +++ b/wasm/client-registrar.yaml @@ -0,0 +1,36 @@ +# ================================== +# Client Registrar Configuration +# ================================== + +# Log message level (0 = info, 1 = debug, >1 = trace) +logLevel: 0 +# Path to log file +logPath: "results/client-registrar.log" + +# Public address, used in NDF it gives to client +publicAddress: "localhost:11421" +# The listening port of this server +port: 11421 + +# === REQUIRED FOR ENABLING TLS === +# Path to the registration server private key file +keyPath: "../keys/cmix.rip.key" +# Path to the registration server certificate file +certPath: "../keys/cmix.rip.crt" + +# Maximum number of connections per period +userRegCapacity: 1000 +# How often the number of connections is reset +userRegLeakPeriod: "24h" + +# Database connection information +dbUsername: "cmix" +dbPassword: "" +dbName: "cmix_server" +dbAddress: "" + +# List of client codes to be added to the database (for testing) +clientRegCodes: + - "AAAA" + - "BBBB" + - "CCCC" diff --git a/wasm/gateway-1.yaml b/wasm/gateway-1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2a38d5c404db1dda39924d10d2ee081abd7303cc --- /dev/null +++ b/wasm/gateway-1.yaml @@ -0,0 +1,45 @@ +# START YAML === +################################################################################ +## Copyright © 2019 Privategrity Corporation # +## # +## All rights reserved. # +################################################################################ + +# Output log file +log: "results/gateways/gateway-1.log" + +# Log message level +logLevel: 1 + +# The public IP address and port of the node associated with this gateway +cmixAddress: "127.0.0.1:50000" + +# Port for Gateway to listen on. Gateway must be the only listener on this port. +# Required field. +port: 8440 + +# The public IPv4 address of the Gateway, as reported to the network, to use +# instead of dynamically looking up Gateway's own IP address. If a port is not +# included, then the port flag is used instead. +overridePublicIP: "localhost" + +# The number of seconds a message should remain in the globals before being +# deleted from the user's message queue +messageTimeout: "1800s" + +# Path to where the IDF is saved. This is used by the wrapper management script. +idfPath: "results/gateways/gateway-1-idf.json" + +# === REQUIRED FOR ENABLING TLS === +# Path to the private key file +keyPath: "../keys/cmix.rip.key" +# Path to the certificate file +certPath: "../keys/cmix.rip.crt" +# Path to the permissioning certificate +schedulingCertPath: "../keys/cmix.rip.crt" +# Path to the certificate file +cmixCertPath: "../keys/cmix.rip.crt" + +devMode: true + +# === END YAML diff --git a/wasm/gateway-2.yaml b/wasm/gateway-2.yaml new file mode 100644 index 0000000000000000000000000000000000000000..574bbef1056c8082c046a8e929bd9c9f9c5faa76 --- /dev/null +++ b/wasm/gateway-2.yaml @@ -0,0 +1,45 @@ +# START YAML === +################################################################################ +## Copyright © 2019 Privategrity Corporation # +## # +## All rights reserved. # +################################################################################ + +# Output log file +log: "results/gateways/gateway-2.log" + +# Log message level +logLevel: 1 + +# The public IP address and port of the node associated with this gateway +cmixAddress: "127.0.0.1:50001" + +# Port for Gateway to listen on. Gateway must be the only listener on this port. +# Required field. +port: 8441 + +# The public IPv4 address of the Gateway, as reported to the network, to use +# instead of dynamically looking up Gateway's own IP address. If a port is not +# included, then the port flag is used instead. +overridePublicIP: "localhost" + +# The number of seconds a message should remain in the globals before being +# deleted from the user's message queue +messageTimeout: "1800s" + +# Path to where the IDF is saved. This is used by the wrapper management script. +idfPath: "results/gateways/gateway-2-idf.json" + +# === REQUIRED FOR ENABLING TLS === +# Path to the private key file +keyPath: "../keys/cmix.rip.key" +# Path to the certificate file +certPath: "../keys/cmix.rip.crt" +# Path to the permissioning certificate +schedulingCertPath: "../keys/cmix.rip.crt" +# Path to the certificate file +cmixCertPath: "../keys/cmix.rip.crt" + +devMode: true + +# === END YAML diff --git a/wasm/gateway-3.yaml b/wasm/gateway-3.yaml new file mode 100644 index 0000000000000000000000000000000000000000..42c61354d38a0cd730bc60aee811472734d5617c --- /dev/null +++ b/wasm/gateway-3.yaml @@ -0,0 +1,45 @@ +# START YAML === +################################################################################ +## Copyright © 2019 Privategrity Corporation # +## # +## All rights reserved. # +################################################################################ + +# Output log file +log: "results/gateways/gateway-3.log" + +# Log message level +logLevel: 1 + +# The public IP address and port of the node associated with this gateway +cmixAddress: "127.0.0.1:50002" + +# Port for Gateway to listen on. Gateway must be the only listener on this port. +# Required field. +port: 8442 + +# The public IPv4 address of the Gateway, as reported to the network, to use +# instead of dynamically looking up Gateway's own IP address. If a port is not +# included, then the port flag is used instead. +overridePublicIP: "localhost" + +# The number of seconds a message should remain in the globals before being +# deleted from the user's message queue +messageTimeout: "1800s" + +# Path to where the IDF is saved. This is used by the wrapper management script. +idfPath: "results/gateways/gateway-3-idf.json" + +# === REQUIRED FOR ENABLING TLS === +# Path to the private key file +keyPath: "../keys/cmix.rip.key" +# Path to the certificate file +certPath: "../keys/cmix.rip.crt" +# Path to the permissioning certificate +schedulingCertPath: "../keys/cmix.rip.crt" +# Path to the certificate file +cmixCertPath: "../keys/cmix.rip.crt" + +devMode: true + +# === END YAML diff --git a/wasm/gateway-4.yaml b/wasm/gateway-4.yaml new file mode 100644 index 0000000000000000000000000000000000000000..cdfc018cd8f50d3c691bdc2b006140057f628ea9 --- /dev/null +++ b/wasm/gateway-4.yaml @@ -0,0 +1,45 @@ +# START YAML === +################################################################################ +## Copyright © 2019 Privategrity Corporation # +## # +## All rights reserved. # +################################################################################ + +# Output log file +log: "results/gateways/gateway-4.log" + +# Log message level +logLevel: 1 + +# The public IP address and port of the node associated with this gateway +cmixAddress: "127.0.0.1:50003" + +# Port for Gateway to listen on. Gateway must be the only listener on this port. +# Required field. +port: 8443 + +# The public IPv4 address of the Gateway, as reported to the network, to use +# instead of dynamically looking up Gateway's own IP address. If a port is not +# included, then the port flag is used instead. +overridePublicIP: "localhost" + +# The number of seconds a message should remain in the globals before being +# deleted from the user's message queue +messageTimeout: "1800s" + +# Path to where the IDF is saved. This is used by the wrapper management script. +idfPath: "results/gateways/gateway-4-idf.json" + +# === REQUIRED FOR ENABLING TLS === +# Path to the private key file +keyPath: "../keys/cmix.rip.key" +# Path to the certificate file +certPath: "../keys/cmix.rip.crt" +# Path to the permissioning certificate +schedulingCertPath: "../keys/cmix.rip.crt" +# Path to the certificate file +cmixCertPath: "../keys/cmix.rip.crt" + +devMode: true + +# === END YAML diff --git a/wasm/gateway-5.yaml b/wasm/gateway-5.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3bd10a8d15052fa9ef16a1e342b0977363c64847 --- /dev/null +++ b/wasm/gateway-5.yaml @@ -0,0 +1,45 @@ +# START YAML === +################################################################################ +## Copyright © 2019 Privategrity Corporation # +## # +## All rights reserved. # +################################################################################ + +# Output log file +log: "results/gateways/gateway-5.log" + +# Log message level +logLevel: 1 + +# The public IP address and port of the node associated with this gateway +cmixAddress: "127.0.0.1:50004" + +# Port for Gateway to listen on. Gateway must be the only listener on this port. +# Required field. +port: 8444 + +# The public IPv4 address of the Gateway, as reported to the network, to use +# instead of dynamically looking up Gateway's own IP address. If a port is not +# included, then the port flag is used instead. +overridePublicIP: "localhost" + +# The number of seconds a message should remain in the globals before being +# deleted from the user's message queue +messageTimeout: "1800s" + +# Path to where the IDF is saved. This is used by the wrapper management script. +idfPath: "results/gateways/gateway-5-idf.json" + +# === REQUIRED FOR ENABLING TLS === +# Path to the private key file +keyPath: "../keys/cmix.rip.key" +# Path to the certificate file +certPath: "../keys/cmix.rip.crt" +# Path to the permissioning certificate +schedulingCertPath: "../keys/cmix.rip.crt" +# Path to the certificate file +cmixCertPath: "../keys/cmix.rip.crt" + +devMode: true + +# === END YAML diff --git a/wasm/network.sh b/wasm/network.sh new file mode 100644 index 0000000000000000000000000000000000000000..fa821fba884d86308475a246f3f4154b1226f11e --- /dev/null +++ b/wasm/network.sh @@ -0,0 +1,109 @@ +# This script is used to start a basic 5 node network for running clients on. It is meant to be `source`'d into a script +# which will run clients on the network, such as `client-session-tests.sh` or the main `run.sh`. +# +# You **must** source it, because otherwise the `trap finish EXIT` instruction will cause the network to stop when +# network.sh returns to your script or shell. Sourcing it will "import" the commands into your script instead, causing +# the trap instruction to stop the network when your script/shell exits. + +echo "STARTING SERVERS..." + +# Copy udbContact into place when running locally. +cp udbContact.bin results/udbContact.bin + +PERMCMD="../bin/permissioning --logLevel $DEBUGLEVEL -c permissioning.yaml " +$PERMCMD > results/permissioning-console.txt 2>&1 & +PIDVAL=$! +echo "$PERMCMD -- $PIDVAL" + + +# Run Client Registrar +CLIENT_REG_CMD="../bin/client-registrar \ +-l 2 -c client-registrar.yaml" +$CLIENT_REG_CMD > results/client-registrat-console.txt 2>&1 & +PIDVAL=$! +echo "$CLIENT_REG_CMD -- $PIDVAL" + +for SERVERID in $(seq 5 -1 1) +do + IDX=$(($SERVERID - 1)) + SERVERCMD="../bin/server --logLevel $DEBUGLEVEL --config server-$SERVERID.yaml" + if [ $SERVERID -eq 5 ] && [ -n "$NSYSENABLED" ] + then + SERVERCMD="nsys profile --session-new=gputest --trace=cuda -o server-$SERVERID $SERVERCMD" + fi + $SERVERCMD > $SERVERLOGS/server-$SERVERID-console.txt 2>&1 & + PIDVAL=$! + echo "$SERVERCMD -- $PIDVAL" +done + +# Start gateways +for GWID in $(seq 5 -1 1) +do + IDX=$(($GWID - 1)) + GATEWAYCMD="../bin/gateway --logLevel $DEBUGLEVEL --config gateway-$GWID.yaml" + $GATEWAYCMD > $GATEWAYLOGS/gateway-$GWID-console.txt 2>&1 & + PIDVAL=$! + echo "$GATEWAYCMD -- $PIDVAL" +done + +jobs -p > results/serverpids + +finish() { + echo "STOPPING SERVERS AND GATEWAYS..." + if [ -n "$NSYSENABLED" ] + then + nsys stop --session=gputest + fi + # NOTE: jobs -p doesn't work in a signal handler + for job in $(cat results/serverpids) + do + echo "KILLING $job" + kill $job || true + done + + sleep 5 + + for job in $(cat results/serverpids) + do + echo "KILL -9 $job" + kill -9 $job || true + done + #tail $SERVERLOGS/* + #tail $CLIENTCLEAN/* + #diff -aruN clients.goldoutput $CLIENTCLEAN +} + +trap finish EXIT +trap finish INT + +# Sleeps can die in a fire on the sun, we wait for the servers to start running +# rounds +rm rid.txt || true +touch rid.txt +cnt=0 +echo -n "Waiting for a round to run" +while [ ! -s rid.txt ] && [ $cnt -lt 120 ]; do + sleep 1 + grep -a "RID 1 ReceiveFinishRealtime END" results/servers/server-* > rid.txt || true + cnt=$(($cnt + 1)) + echo -n "." +done + +# Start a user discovery bot server +echo "STARTING UDB..." +UDBCMD="../bin/udb --logLevel $DEBUGLEVEL --skipVerification --protoUserPath udbProto.json --config udb.yaml -l 1" +$UDBCMD >> $UDBOUT 2>&1 & +PIDVAL=$! +echo $PIDVAL >> results/serverpids +echo "$UDBCMD -- $PIDVAL" +rm rid.txt || true +while [ ! -s rid.txt ] && [ $cnt -lt 30 ]; do + sleep 1 + grep -a "Sending Poll message" results/udb-console.txt > rid.txt || true + cnt=$(($cnt + 1)) + echo -n "." +done + +echo "localhost:8440" > results/startgwserver.txt + +echo "DONE LETS DO STUFF" \ No newline at end of file diff --git a/wasm/permissioning.yaml b/wasm/permissioning.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e6189cd1987a0c274b279a35888622c9652cd5a6 --- /dev/null +++ b/wasm/permissioning.yaml @@ -0,0 +1,90 @@ +# ================================== +# Permissioning Server Configuration +# ================================== + +# Log message level +logLevel: 1 + +# Path to log file +logPath: "results/permissioning.log" + +# The listening pofrt of this server +port: 18000 + +# Database connection information +dbUsername: "" +dbPassword: "" +dbName: "" +dbAddress: "0.0.0.0:6969" + +minimumNodes: 5 + +minGatewayVersion: "3.0.0" +minServerVersion: "3.0.0" +minClientVersion: "4.0.0" +nodeMetricInterval: 3 + +# For testing, use the sequence as the country code. Do not use the geobinning database +disableGeoBinning: true + +# For testing, do not exclude node or gateway IPs which are local to the machine +allowLocalIPs: true +# Disable pruning of NDF for offline nodes +# if set to false, network will sleep for five minutes on start +disableNDFPruning: true +permissiveIPChecking: true + +# How long rounds will be tracked by gateways. Rounds (and messages as an extension) +# prior to this period are not guaranteed to be delivered to clients. +# Expects duration in"h". (Defaults to 1 weeks (168 hours) +messageRetentionLimit: "168h" + +# Path to the file containing the round ID +roundIdPath: "results/roundId.txt" + +# Path to the file containing the update ID +updateIdPath: "results/updateId.txt" + +# Public address used in NDF to give to client +registrationAddress: "localhost:11421" + +# Path to whitelisted IPs for client ratelimiting +whitelistedIpAddressesPath: "whitelist.txt" + +# === REQUIRED FOR ENABLING TLS === +# Path to the registration server private key file +keyPath: "../keys/cmix.rip.key" +# Path to the registration server certificate file +certPath: "../keys/cmix.rip.crt" + +fullNdfOutputPath: "results/permissions-ndfoutput.json" + +# === REQUIRED FOR ENABLING TLS === +# Path to the UDB certificate file +udbCertPath: "../keys/cmix.rip.crt" +# "Location of the user discovery contact file. +udContactPath: "results/udbContact.bin" +udbAddress: "127.0.0.1:18001" + +# Time interval (in minutes) in which the database is +# checked for banned nodes +BanTrackerInterval: "3" + +groups: + cmix: + prime: "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF" + generator: "2" + e2e: + prime: "E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AEDF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF453B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE92096EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F278DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873847AEF49F66E43873" + generator: "2" + + +# Path to file with config for scheduling algorithm within the user directory +schedulingConfigPath: "registration.json" + +# Path to JSON file with list of Node registration codes (in order of network +# placement) +RegCodesFilePath: "regCodes.json" + +# Set address space size for ephemeral IDs +addressSpace: 32 diff --git a/wasm/regCodes.json b/wasm/regCodes.json new file mode 100644 index 0000000000000000000000000000000000000000..fea6a9d6e7f50348e01fb324a74de2e200506b35 --- /dev/null +++ b/wasm/regCodes.json @@ -0,0 +1 @@ +[{"RegCode": "qpol", "Order": "CR"},{"RegCode": "yiiq", "Order": "GB"},{"RegCode": "vydz", "Order": "SK"},{"RegCode": "gwxs", "Order": "HR"},{"RegCode": "nahv", "Order": "IQ"}, {"RegCode": "doko", "Order": "RU"}] diff --git a/wasm/registration.json b/wasm/registration.json new file mode 100644 index 0000000000000000000000000000000000000000..0c4a121947b1427f52c71c26eb83aaa5e03b7af7 --- /dev/null +++ b/wasm/registration.json @@ -0,0 +1,12 @@ +{ + "TeamSize": 5, + "PrecomputationTimeout": 30000, + "RealtimeTimeout": 15000, + "DebugTrackRounds": true, + "BatchSize": 32, + "MinimumDelay": 1000, + "RealtimeDelay": 2000, + "Threshold": 0.3, + "NodeCleanUpInterval": 180000, + "ResourceQueueTimeout": 300000 +} diff --git a/wasm/run.sh b/wasm/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..790799ec8540405f614c098ca9ad4ea5c9dbaba1 --- /dev/null +++ b/wasm/run.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -e + +# --- Define variables to use for the test & local network --- + +DEBUGLEVEL=${DEBUGLEVEL-1} +SERVERLOGS=results/servers +GATEWAYLOGS=results/gateways +UDBOUT=results/udb-console.txt + +# --- Setup a local network --- + +rm -rf results.bak || true +mv results results.bak || rm -rf results || true +rm -rf client*.log blob* rick*.bin ben*.bin +mkdir results + +mkdir -p $SERVERLOGS +mkdir -p $GATEWAYLOGS + +# Start the network +source network.sh + +# This remains commented out while using HTTP +#echo "DOWNLOADING TLS Cert..." +#CMD="openssl s_client -showcerts -connect $(tr -d '[:space:]' <results/startgwserver.txt)" +#echo "$CMD" +#eval "$CMD" </dev/null 2>&1 >"results/startgwcert.bin" +#CMD="cat results/startgwcert.bin | openssl x509 -outform PEM" +#echo "$CMD" +#eval "$CMD" >"results/startgwcert.pem" +#head "results/startgwcert.pem" +# +#echo "DOWNLOADING NDF..." +#CLIENTCMD="../bin/client getndf -v $DEBUGLEVEL --gwhost $(tr -d '[:space:]' <results/startgwserver.txt) --cert results/startgwcert.pem" +#eval "$CLIENTCMD" >>results/ndf.json 2>&1 & +#echo "$CLIENTCMD -- $PIDVAL" +#wait $PIDVAL + +while :; do + sleep 10 +done diff --git a/wasm/server-1.yaml b/wasm/server-1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fc8b704b210b9dfa19e88ac61bacaa61e1148e3d --- /dev/null +++ b/wasm/server-1.yaml @@ -0,0 +1,39 @@ +# START YAML === +################################################################################ +## Copyright © 2019 Privategrity Corporation # +## # +## All rights reserved. # +################################################################################ +# registration code used for first time registration. Unique. Provided by xx network +registrationCode: "qpol" +useGPU: false +devMode: true +rawPermAddr: true +logLevel: 1 +cmix: + paths: + idf: "results/servers/nodeID-1.json" + cert: "../keys/cmix.rip.crt" + key: "../keys/cmix.rip.key" + log: "results/servers/server-1.log" + errOutput: "results/servers/server-1.err" + ipListOutput: "results/servers/iplist-1.txt" + port: 50000 + overridePublicIP: "127.0.0.1" +database: + name: "node1" + username: "cmix_server" + password: "" + address: "" +gateway: + paths: + cert: "../keys/cmix.rip.crt" +scheduling: + paths: + cert: "../keys/cmix.rip.crt" + address: "127.0.0.1:18000" +metrics: + # location of stored metrics data. Modification to set to permissioning + # server instead of saving will be made at a later date + log: "results/servers/server-1-metrics.log" +# === END YAML diff --git a/wasm/server-2.yaml b/wasm/server-2.yaml new file mode 100644 index 0000000000000000000000000000000000000000..37765bc314816bd9928ff96803653145aeea2671 --- /dev/null +++ b/wasm/server-2.yaml @@ -0,0 +1,39 @@ +# START YAML === +################################################################################ +## Copyright © 2019 Privategrity Corporation # +## # +## All rights reserved. # +################################################################################ +# registration code used for first time registration. Unique. Provided by xx network +registrationCode: "yiiq" +useGPU: false +devMode: true +rawPermAddr: true +logLevel: 1 +cmix: + paths: + idf: "results/servers/nodeID-2.json" + cert: "../keys/cmix.rip.crt" + key: "../keys/cmix.rip.key" + log: "results/servers/server-2.log" + errOutput: "results/servers/server-2.err" + ipListOutput: "results/servers/iplist-2.txt" + port: 50001 + overridePublicIP: "127.0.0.1" +database: + name: "node2" + username: "cmix_server" + password: "" + address: "" +gateway: + paths: + cert: "../keys/cmix.rip.crt" +scheduling: + paths: + cert: "../keys/cmix.rip.crt" + address: "127.0.0.1:18000" +metrics: + # location of stored metrics data. Modification to set to permissioning + # server instead of saving will be made at a later date + log: "results/servers/server-2-metrics.log" +# === END YAML diff --git a/wasm/server-3.yaml b/wasm/server-3.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f17b4255f1b9519ee5c3e4e787264a4cb1f3a645 --- /dev/null +++ b/wasm/server-3.yaml @@ -0,0 +1,39 @@ +# START YAML === +################################################################################ +## Copyright © 2019 Privategrity Corporation # +## # +## All rights reserved. # +################################################################################ +# registration code used for first time registration. Unique. Provided by xx network +registrationCode: "vydz" +useGPU: false +devMode: true +rawPermAddr: true +logLevel: 1 +cmix: + paths: + idf: "results/servers/nodeID-3.json" + cert: "../keys/cmix.rip.crt" + key: "../keys/cmix.rip.key" + log: "results/servers/server-3.log" + errOutput: "results/servers/server-3.err" + ipListOutput: "results/servers/iplist-3.txt" + port: 50002 + overridePublicIP: "127.0.0.1" +database: + name: "node3" + username: "cmix_server" + password: "" + address: "" +gateway: + paths: + cert: "../keys/cmix.rip.crt" +scheduling: + paths: + cert: "../keys/cmix.rip.crt" + address: "127.0.0.1:18000" +metrics: + # location of stored metrics data. Modification to set to permissioning + # server instead of saving will be made at a later date + log: "results/servers/server-3-metrics.log" +# === END YAML diff --git a/wasm/server-4.yaml b/wasm/server-4.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b13f6fd5a329fb44e4748a76f4aed4407482523a --- /dev/null +++ b/wasm/server-4.yaml @@ -0,0 +1,39 @@ +# START YAML === +################################################################################ +## Copyright © 2019 Privategrity Corporation # +## # +## All rights reserved. # +################################################################################ +# registration code used for first time registration. Unique. Provided by xx network +registrationCode: "gwxs" +useGPU: false +devMode: true +rawPermAddr: true +logLevel: 1 +cmix: + paths: + idf: "results/servers/nodeID-4.json" + cert: "../keys/cmix.rip.crt" + key: "../keys/cmix.rip.key" + log: "results/servers/server-4.log" + errOutput: "results/servers/server-4.err" + ipListOutput: "results/servers/iplist-4.txt" + port: 50003 + overridePublicIP: "127.0.0.1" +database: + name: "node4" + username: "cmix_server" + password: "" + address: "" +gateway: + paths: + cert: "../keys/cmix.rip.crt" +scheduling: + paths: + cert: "../keys/cmix.rip.crt" + address: "127.0.0.1:18000" +metrics: + # location of stored metrics data. Modification to set to permissioning + # server instead of saving will be made at a later date + log: "results/servers/server-4-metrics.log" +# === END YAML diff --git a/wasm/server-5.yaml b/wasm/server-5.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d820326869ac148f9823eb929ed686719970c4c8 --- /dev/null +++ b/wasm/server-5.yaml @@ -0,0 +1,39 @@ +# START YAML === +################################################################################ +## Copyright © 2019 Privategrity Corporation # +## # +## All rights reserved. # +################################################################################ +# registration code used for first time registration. Unique. Provided by xx network +registrationCode: "nahv" +useGPU: false +devMode: true +rawPermAddr: true +logLevel: 1 +cmix: + paths: + idf: "results/servers/nodeID-5.json" + cert: "../keys/cmix.rip.crt" + key: "../keys/cmix.rip.key" + log: "results/servers/server-5.log" + errOutput: "results/servers/server-5.err" + ipListOutput: "results/servers/iplist-5.txt" + port: 50004 + overridePublicIP: "127.0.0.1" +database: + name: "node5" + username: "cmix_server" + password: "" + address: "" +gateway: + paths: + cert: "../keys/cmix.rip.crt" +scheduling: + paths: + cert: "../keys/cmix.rip.crt" + address: "127.0.0.1:18000" +metrics: + # location of stored metrics data. Modification to set to permissioning + # server instead of saving will be made at a later date + log: "results/servers/server-5-metrics.log" +# === END YAML diff --git a/wasm/testServer/assets/styles.css b/wasm/testServer/assets/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..292d39800aebbc29f525f89c7fe21c1a10c6080b --- /dev/null +++ b/wasm/testServer/assets/styles.css @@ -0,0 +1,138 @@ +/****************************************************************************** + * Copyright © 2022 xx foundation * + * * + * Use of this source code is governed by a license that can be found in the * + * LICENSE file. * + ******************************************************************************/ + +/****************************************************************************** + * CSS Reset (meyerweb reset, v2.0 | 20110126) * + ******************************************************************************/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +body { + line-height: 1; +} + +ol, ul { + list-style: none; +} + +blockquote, q { + quotes: none; +} + +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +/****************************************************************************** + * Global Styles * + ******************************************************************************/ +body { + font-family: "Roboto", "Franklin Gothic Medium", Tahoma, sans-serif; + padding:25px; +} + +form { +} + +form div { + margin:1em 0; +} + +samp, var, code { + border-radius:.385em; + font-family:Consolas, monaco, monospace; + white-space:pre; + background:#e5e5e5; + color:#000; + font-size:1em; + padding:.125em .25em; +} + +div.toolbar { + float:right; + display: flex; +} + +div.toolbar input { + margin:2px; +} + +/*======================================== + Headers + ========================================*/ +h1, h2, h3, h4, h5, h6 { + color:#000; + font-weight:700; + line-height:1em; +} + +h1 { + font-size:3em; + margin:0.5em 0; +} + +/*======================================== + HTML Console + ========================================*/ + +div#logOutput { + font-family: "Roboto Mono", monaco, Consolas, "Lucida Console", monospace; + display: block; + font-size: 1em; + line-height: 1.5em; + border-radius:9px; + background:#f5f5f5; + border:1px solid #6ce26c; + border-left:3px solid #6ce26c; + margin:25px 0; + max-height:60vh; + padding:15px; + white-space:pre-wrap; + width:auto; + overflow-y: scroll; +} + +div#logOutput p{ + padding:0.1em 0; +} + +div#logOutput p.error{ + color: #ff0000; + font-weight: bold; +} \ No newline at end of file diff --git a/wasm/testServer/assets/utils.js b/wasm/testServer/assets/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..b657062c12ea1a6de7502ac7c52f8f1099a7f4b0 --- /dev/null +++ b/wasm/testServer/assets/utils.js @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +// Function to download data to a file. +function download(filename, data) { + const file = new Blob([data], {type: 'text/plain'}); + let a = document.createElement("a"), + url = URL.createObjectURL(file); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + setTimeout(function() { + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + }, 0); +} + +// sleepUntil waits until the condition f is met or until the timeout is +// reached. +async function sleepUntil(f, timeoutMs) { + return new Promise((resolve, reject) => { + const timeWas = new Date(); + const wait = setInterval(function() { + if (f()) { + console.log("resolved after", new Date() - timeWas, "ms"); + clearInterval(wait); + resolve(); + } else if (new Date() - timeWas > timeoutMs) { // Timeout + console.log("rejected after", new Date() - timeWas, "ms"); + clearInterval(wait); + reject(); + } + }, 20); + }); +} + +// newHtmlConsole returns an object that allows for printing log messages or +// error messages to an element. +function newHtmlConsole(elem) { + return { + log: function (message) { + console.log(message) + elem.innerHTML += "<p>" + message + "</p>" + }, + error: function (message) { + console.error(message) + elem.innerHTML += "<p class='error'>" + message + "</p>" + } + }; +} \ No newline at end of file diff --git a/wasm/testServer/assets/wasm_exec.js b/wasm/testServer/assets/wasm_exec.js new file mode 100644 index 0000000000000000000000000000000000000000..bba4a84051b5fb6bf284df03d21556eb56500062 --- /dev/null +++ b/wasm/testServer/assets/wasm_exec.js @@ -0,0 +1,650 @@ +/* + * Copyright © 2020 xx network SEZC /// + * /// + * Use of this source code is governed by a license that can be found in the /// + * LICENSE file /// + */ + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +(() => { + // Map multiple JavaScript environments to a single common API, + // preferring web standards over Node.js API. + // + // Environments considered: + // - Browsers + // - Node.js + // - Electron + // - Parcel + // - Webpack + + if (typeof global !== "undefined") { + // global already exists + } else if (typeof window !== "undefined") { + window.global = window; + } else if (typeof self !== "undefined") { + self.global = self; + } else { + throw new Error("cannot export Go (neither global, window nor self is defined)"); + } + + if (!global.require && typeof require !== "undefined") { + global.require = require; + } + + if (!global.fs && global.require) { + const fs = require("fs"); + if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) { + global.fs = fs; + } + } + + const enosys = () => { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + return err; + }; + + if (!global.fs) { + let outputBuf = ""; + global.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused + writeSync(fd, buf) { + outputBuf += decoder.decode(buf); + const nl = outputBuf.lastIndexOf("\n"); + if (nl != -1) { + console.log(outputBuf.substr(0, nl)); + outputBuf = outputBuf.substr(nl + 1); + } + return buf.length; + }, + write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + callback(enosys()); + return; + } + const n = this.writeSync(fd, buf); + callback(null, n); + }, + chmod(path, mode, callback) { callback(enosys()); }, + chown(path, uid, gid, callback) { callback(enosys()); }, + close(fd, callback) { callback(enosys()); }, + fchmod(fd, mode, callback) { callback(enosys()); }, + fchown(fd, uid, gid, callback) { callback(enosys()); }, + fstat(fd, callback) { callback(enosys()); }, + fsync(fd, callback) { callback(null); }, + ftruncate(fd, length, callback) { callback(enosys()); }, + lchown(path, uid, gid, callback) { callback(enosys()); }, + link(path, link, callback) { callback(enosys()); }, + lstat(path, callback) { callback(enosys()); }, + mkdir(path, perm, callback) { callback(enosys()); }, + open(path, flags, mode, callback) { callback(enosys()); }, + read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, + readdir(path, callback) { callback(enosys()); }, + readlink(path, callback) { callback(enosys()); }, + rename(from, to, callback) { callback(enosys()); }, + rmdir(path, callback) { callback(enosys()); }, + stat(path, callback) { callback(enosys()); }, + symlink(path, link, callback) { callback(enosys()); }, + truncate(path, length, callback) { callback(enosys()); }, + unlink(path, callback) { callback(enosys()); }, + utimes(path, atime, mtime, callback) { callback(enosys()); }, + }; + } + + if (!global.process) { + global.process = { + getuid() { return -1; }, + getgid() { return -1; }, + geteuid() { return -1; }, + getegid() { return -1; }, + getgroups() { throw enosys(); }, + pid: -1, + ppid: -1, + umask() { throw enosys(); }, + cwd() { throw enosys(); }, + chdir() { throw enosys(); }, + } + } + + if (!global.crypto && global.require) { + const nodeCrypto = require("crypto"); + global.crypto = { + getRandomValues(b) { + nodeCrypto.randomFillSync(b); + }, + }; + } + if (!global.crypto) { + throw new Error("global.crypto is not available, polyfill required (getRandomValues only)"); + } + + if (!global.performance) { + global.performance = { + now() { + const [sec, nsec] = process.hrtime(); + return sec * 1000 + nsec / 1000000; + }, + }; + } + + if (!global.TextEncoder && global.require) { + global.TextEncoder = require("util").TextEncoder; + } + if (!global.TextEncoder) { + throw new Error("global.TextEncoder is not available, polyfill required"); + } + + if (!global.TextDecoder && global.require) { + global.TextDecoder = require("util").TextDecoder; + } + if (!global.TextDecoder) { + throw new Error("global.TextDecoder is not available, polyfill required"); + } + + // End of polyfills for common API. + + const encoder = new TextEncoder("utf-8"); + const decoder = new TextDecoder("utf-8"); + + global.Go = class { + constructor() { + this.argv = ["js"]; + this.env = {}; + this.exit = (code) => { + if (code !== 0) { + console.warn("exit code:", code); + } + }; + this._exitPromise = new Promise((resolve) => { + this._resolveExitPromise = resolve; + }); + this._pendingEvent = null; + this._scheduledTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + + const setInt64 = (addr, v) => { + this.mem.setUint32(addr + 0, v, true); + this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); + } + + const getInt64 = (addr) => { + const low = this.mem.getUint32(addr + 0, true); + const high = this.mem.getInt32(addr + 4, true); + return low + high * 4294967296; + } + + const loadValue = (addr) => { + const f = this.mem.getFloat64(addr, true); + if (f === 0) { + return undefined; + } + if (!isNaN(f)) { + return f; + } + + const id = this.mem.getUint32(addr, true); + return this._values[id]; + } + + const storeValue = (addr, v) => { + const nanHead = 0x7FF80000; + + if (typeof v === "number" && v !== 0) { + if (isNaN(v)) { + this.mem.setUint32(addr + 4, nanHead, true); + this.mem.setUint32(addr, 0, true); + return; + } + this.mem.setFloat64(addr, v, true); + return; + } + + if (v === undefined) { + this.mem.setFloat64(addr, 0, true); + return; + } + + let id = this._ids.get(v); + if (id === undefined) { + id = this._idPool.pop(); + if (id === undefined) { + id = this._values.length; + } + this._values[id] = v; + this._goRefCounts[id] = 0; + this._ids.set(v, id); + } + this._goRefCounts[id]++; + let typeFlag = 0; + switch (typeof v) { + case "object": + if (v !== null) { + typeFlag = 1; + } + break; + case "string": + typeFlag = 2; + break; + case "symbol": + typeFlag = 3; + break; + case "function": + typeFlag = 4; + break; + } + this.mem.setUint32(addr + 4, nanHead | typeFlag, true); + this.mem.setUint32(addr, id, true); + } + + const loadSlice = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + return new Uint8Array(this._inst.exports.mem.buffer, array, len); + } + + const loadSliceOfValues = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + const a = new Array(len); + for (let i = 0; i < len; i++) { + a[i] = loadValue(array + i * 8); + } + return a; + } + + const loadString = (addr) => { + const saddr = getInt64(addr + 0); + const len = getInt64(addr + 8); + return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); + } + + const timeOrigin = Date.now() - performance.now(); + this.importObject = { + go: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + + // func wasmExit(code int32) + "runtime.wasmExit": (sp) => { + sp >>>= 0; + const code = this.mem.getInt32(sp + 8, true); + this.exited = true; + delete this._inst; + delete this._values; + delete this._goRefCounts; + delete this._ids; + delete this._idPool; + this.exit(code); + }, + + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + "runtime.wasmWrite": (sp) => { + sp >>>= 0; + const fd = getInt64(sp + 8); + const p = getInt64(sp + 16); + const n = this.mem.getInt32(sp + 24, true); + fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); + }, + + // func resetMemoryDataView() + "runtime.resetMemoryDataView": (sp) => { + sp >>>= 0; + this.mem = new DataView(this._inst.exports.mem.buffer); + }, + + // func nanotime1() int64 + "runtime.nanotime1": (sp) => { + sp >>>= 0; + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); + }, + + // func walltime() (sec int64, nsec int32) + "runtime.walltime": (sp) => { + sp >>>= 0; + const msec = (new Date).getTime(); + setInt64(sp + 8, msec / 1000); + this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); + }, + + // func scheduleTimeoutEvent(delay int64) int32 + "runtime.scheduleTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this._nextCallbackTimeoutID; + this._nextCallbackTimeoutID++; + this._scheduledTimeouts.set(id, setTimeout( + () => { + this._resume(); + while (this._scheduledTimeouts.has(id)) { + // for some reason Go failed to register the timeout event, log and try again + // (temporary workaround for https://github.com/golang/go/issues/28975) + console.warn("scheduleTimeoutEvent: missed timeout event"); + this._resume(); + } + }, + getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early + )); + this.mem.setInt32(sp + 16, id, true); + }, + + // func clearTimeoutEvent(id int32) + "runtime.clearTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this.mem.getInt32(sp + 8, true); + clearTimeout(this._scheduledTimeouts.get(id)); + this._scheduledTimeouts.delete(id); + }, + + // func getRandomData(r []byte) + "runtime.getRandomData": (sp) => { + sp >>>= 0; + crypto.getRandomValues(loadSlice(sp + 8)); + }, + + // func finalizeRef(v ref) + "syscall/js.finalizeRef": (sp) => { + sp >>>= 0; + const id = this.mem.getUint32(sp + 8, true); + this._goRefCounts[id]--; + if (this._goRefCounts[id] === 0) { + const v = this._values[id]; + this._values[id] = null; + this._ids.delete(v); + this._idPool.push(id); + } + }, + + // func stringVal(value string) ref + "syscall/js.stringVal": (sp) => { + sp >>>= 0; + storeValue(sp + 24, loadString(sp + 8)); + }, + + // func valueGet(v ref, p string) ref + "syscall/js.valueGet": (sp) => { + sp >>>= 0; + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 32, result); + }, + + // func valueSet(v ref, p string, x ref) + "syscall/js.valueSet": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); + }, + + // func valueDelete(v ref, p string) + "syscall/js.valueDelete": (sp) => { + sp >>>= 0; + Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); + }, + + // func valueIndex(v ref, i int) ref + "syscall/js.valueIndex": (sp) => { + sp >>>= 0; + storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); + }, + + // valueSetIndex(v ref, i int, x ref) + "syscall/js.valueSetIndex": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); + }, + + // func valueCall(v ref, m string, args []ref) (ref, bool) + "syscall/js.valueCall": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const m = Reflect.get(v, loadString(sp + 16)); + const args = loadSliceOfValues(sp + 32); + const result = Reflect.apply(m, v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, result); + this.mem.setUint8(sp + 64, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, err); + this.mem.setUint8(sp + 64, 0); + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + "syscall/js.valueInvoke": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.apply(v, undefined, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + "syscall/js.valueNew": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.construct(v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueLength(v ref) int + "syscall/js.valueLength": (sp) => { + sp >>>= 0; + setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); + }, + + // valuePrepareString(v ref) (ref, int) + "syscall/js.valuePrepareString": (sp) => { + sp >>>= 0; + const str = encoder.encode(String(loadValue(sp + 8))); + storeValue(sp + 16, str); + setInt64(sp + 24, str.length); + }, + + // valueLoadString(v ref, b []byte) + "syscall/js.valueLoadString": (sp) => { + sp >>>= 0; + const str = loadValue(sp + 8); + loadSlice(sp + 16).set(str); + }, + + // func valueInstanceOf(v ref, t ref) bool + "syscall/js.valueInstanceOf": (sp) => { + sp >>>= 0; + this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0); + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + "syscall/js.copyBytesToGo": (sp) => { + sp >>>= 0; + const dst = loadSlice(sp + 8); + const src = loadValue(sp + 32); + if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + // func copyBytesToJS(dst ref, src []byte) (int, bool) + "syscall/js.copyBytesToJS": (sp) => { + sp >>>= 0; + const dst = loadValue(sp + 8); + const src = loadSlice(sp + 16); + if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + "debug": (value) => { + console.log(value); + }, + + // func throw(exception string, message string) + 'gitlab.com/elixxir/xxdk-wasm/utils.throw': (sp) => { + const exception = loadString(sp + 8) + const message = loadString(sp + 24) + throw globalThis[exception](message) + }, + } + }; + } + + async run(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new Error("Go.run: WebAssembly.Instance expected"); + } + this._inst = instance; + this.mem = new DataView(this._inst.exports.mem.buffer); + this._values = [ // JS values that Go currently has references to, indexed by reference id + NaN, + 0, + null, + true, + false, + global, + this, + ]; + this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id + this._ids = new Map([ // mapping from JS values to reference ids + [0, 1], + [null, 2], + [true, 3], + [false, 4], + [global, 5], + [this, 6], + ]); + this._idPool = []; // unused ids that have been garbage collected + this.exited = false; // whether the Go program has exited + + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. + let offset = 4096; + + const strPtr = (str) => { + const ptr = offset; + const bytes = encoder.encode(str + "\0"); + new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); + offset += bytes.length; + if (offset % 8 !== 0) { + offset += 8 - (offset % 8); + } + return ptr; + }; + + const argc = this.argv.length; + + const argvPtrs = []; + this.argv.forEach((arg) => { + argvPtrs.push(strPtr(arg)); + }); + argvPtrs.push(0); + + const keys = Object.keys(this.env).sort(); + keys.forEach((key) => { + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); + }); + argvPtrs.push(0); + + const argv = offset; + argvPtrs.forEach((ptr) => { + this.mem.setUint32(offset, ptr, true); + this.mem.setUint32(offset + 4, 0, true); + offset += 8; + }); + + // The linker guarantees global data starts from at least wasmMinDataAddr. + // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr. + const wasmMinDataAddr = 4096 + 8192; + if (offset >= wasmMinDataAddr) { + throw new Error("total length of command line and environment variables exceeds limit"); + } + + this._inst.exports.run(argc, argv); + if (this.exited) { + this._resolveExitPromise(); + } + await this._exitPromise; + } + + _resume() { + if (this.exited) { + throw new Error("Go program has already exited"); + } + this._inst.exports.resume(); + if (this.exited) { + this._resolveExitPromise(); + } + } + + _makeFuncWrapper(id) { + const go = this; + return function () { + const event = { id: id, this: this, args: arguments }; + go._pendingEvent = event; + go._resume(); + return event.result; + }; + } + } + + if ( + typeof module !== "undefined" && + global.require && + global.require.main === module && + global.process && + global.process.versions && + !global.process.versions.electron + ) { + if (process.argv.length < 3) { + console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); + process.exit(1); + } + + const go = new Go(); + go.argv = process.argv.slice(2); + go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); + go.exit = process.exit; + WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { + process.on("exit", (code) => { // Node.js exits if no event handler is pending + if (code === 0 && !go.exited) { + // deadlock, make Go print error and stack traces + go._pendingEvent = { id: 0 }; + go._resume(); + } + }); + return go.run(result.instance); + }).catch((err) => { + console.error(err); + process.exit(1); + }); + } +})(); diff --git a/wasm/testServer/clientServer.go b/wasm/testServer/clientServer.go new file mode 100644 index 0000000000000000000000000000000000000000..c637bd458a3f2ea833dc7050e8b0779ffd1bd1ca --- /dev/null +++ b/wasm/testServer/clientServer.go @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +package main + +import ( + "fmt" + "net/http" +) + +func main() { + port := "9090" + root := "" + fmt.Printf("Starting server on port %s from %s\n", port, root) + fmt.Printf("\thttp://localhost:%s\n", port) + + err := http.ListenAndServe(":"+port, http.FileServer(http.Dir(root))) + if err != nil { + panic(fmt.Sprintf("Failed to start server: %+v", err)) + } +} diff --git a/wasm/testServer/sendE2E/README.md b/wasm/testServer/sendE2E/README.md new file mode 100644 index 0000000000000000000000000000000000000000..1a5bb7cc2a434dbc54bfb9b12e7ea93c5d6e94c5 --- /dev/null +++ b/wasm/testServer/sendE2E/README.md @@ -0,0 +1,27 @@ +# SendE2E Test + +This test sets up two clients in the browser and has one client send an E2E +message to the other. + +## Running the Test + +1. First, compile the WASM binary and start the local HTTP server. Refer to wasm + test [README](../README.md) for details on how to do this. Open the two + clients, the sender and receiver. + +2. Next, start the network using the `run.sh` script. This will start all the + gateways and client registrar using localhost as their public IP addresses + and the NDF will be provided by the permissioning server rather than + downloaded from a gateway. + +3. Once rounds are running, on the receiver webpage, navigate to the results + folder in integration and select the `permissions-ndfoutput.json` file. Doing + this will start the client. Once the client generates keys and joins the + network, it will prompt its contact file for download. Copy the contents of + this file into the `recipientContactFile` const in `sender.html`. + +4. On the sender webpage (make sure to refresh the page), select the NDF file as + described for the recipient above. This will start the sender client. Once it + generates its keys and joins the network, it will add the receiver client as + a partner, they will exchange requests and confirmations, and finally, the + sender will send an E2E message to the recipient. \ No newline at end of file diff --git a/wasm/testServer/sendE2E/receiver.html b/wasm/testServer/sendE2E/receiver.html new file mode 100644 index 0000000000000000000000000000000000000000..3fb9d3400150e2b8db60c03fc69cab5b80f57238 --- /dev/null +++ b/wasm/testServer/sendE2E/receiver.html @@ -0,0 +1,80 @@ +<!--//////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +/////////////////////////////////////////////////////////////////////////////--> + +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Receiver</title> + + <link rel="stylesheet" type="text/css" href="../assets/styles.css"> + <link rel="shortcut icon" type="image/x-icon" href="receiver.ico"/> + + + <script type="text/javascript" src="sendE2ejs"></script> + <script type="text/javascript" src="../assets/utils.js"></script> + <script type="text/javascript" src="../assets/wasm_exec.js"></script> + <script> + const go = new Go(); + const binPath = '../../../bin/xxdk.wasm' + WebAssembly.instantiateStreaming(fetch(binPath), go.importObject).then((result) => { + go.run(result.instance); + + // Set log level + LogLevel(0); + + // Output log to file that can be downloaded using a button + const logFile = LogToFile(0, "receiver.log", 1000000); + document.getElementById('logFileDownload').addEventListener( + 'click', () => download(logFile.Name(), logFile.GetFile())) + + // Get element to print log messages to + const logOutput = document.getElementById("logOutput") + const htmlConsole = newHtmlConsole(logOutput) + + // Get button that will stop the network follower + const stopNetworkFollowerBtn = document.getElementById("stopNetworkFollowerBtn") + + // Client specific parameters + const recipientContactFile = ''; + const myContactFileName = 'receiverContact.xxc'; + const statePath = 'statePathRecipient'; + const statePass = 'password'; + + document.getElementById('ndfInput').addEventListener('change', e => { + let reader = new FileReader(); + reader.onload = async function (e) { + try { + await SendE2e(htmlConsole, stopNetworkFollowerBtn, + e.target.result, recipientContactFile, + myContactFileName, statePath, statePass); + } catch (e) { + htmlConsole.error(e) + } + }; + reader.readAsText(e.target.files[0]); + }, false); + }); + </script> +</head> +<body> +<h1 style="margin-top: 0">SendE2E: Receiver</h1> +<div class="toolbar"> + <input type="button" value="Clear localStorage" onclick="localStorage.clear();"> + <input type="button" value="Download Log File" id="logFileDownload"> + <input type="button" value="Stop Network Follower" id="stopNetworkFollowerBtn" style="display:none;" > +</div> +<p>Selecting an NDF will start the client.</p> +<form id="startCmix"> + <div> + <label for="ndfInput">Select NDF file to use <code>ndf</code> variable in JS:</label><br/> + <input type="file" id="ndfInput" required/> + </div> +</form> +<div id="logOutput"></div> +</body> +</html> \ No newline at end of file diff --git a/wasm/testServer/sendE2E/receiver.ico b/wasm/testServer/sendE2E/receiver.ico new file mode 100644 index 0000000000000000000000000000000000000000..3a7b5826dfdad175084ea63406202334dfe29bf4 Binary files /dev/null and b/wasm/testServer/sendE2E/receiver.ico differ diff --git a/wasm/testServer/sendE2E/sendE2ejs b/wasm/testServer/sendE2E/sendE2ejs new file mode 100644 index 0000000000000000000000000000000000000000..ae635574f02b184909f1431314b44a016970e3c9 --- /dev/null +++ b/wasm/testServer/sendE2E/sendE2ejs @@ -0,0 +1,228 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +async function SendE2e(htmlConsole, stopNetworkFollowerBtn, ndf, + recipientContactFile, myContactFileName, statePath, + statePassString) { + + let enc = new TextEncoder(); + let dec = new TextDecoder(); + + const statePass = enc.encode(statePassString); + + console.log("Starting client with:" + + "\n\trecipientContactFile: " + recipientContactFile + + "\n\tmyContactFileName: " + myContactFileName + + "\n\tstatePath: " + statePath + + "\n\tstatePass: " + statePassString) + htmlConsole.log("Starting client with path " + statePath) + + // Check if state exists + if (localStorage.getItem(statePath) === null) { + + htmlConsole.log("No state found at " + statePath + ". Calling NewCmix.") + + // Initialize the state + NewCmix(ndf, statePath, statePass, ''); + } else { + htmlConsole.log("State found at " + statePath) + } + + + //////////////////////////////////////////////////////////////////////////// + // Login to your client session // + //////////////////////////////////////////////////////////////////////////// + + // Login with the same statePath and statePass used to call NewCmix + htmlConsole.log("Starting to load cmix with path " + statePath) + let net; + try { + net = await LoadCmix(statePath, statePass, GetDefaultCMixParams()); + } catch (e) { + htmlConsole.error("Failed to load Cmix: " + e) + return + } + htmlConsole.log("Loaded Cmix.") + console.log("Loaded Cmix: " + JSON.stringify(net)) + + // Get reception identity (automatically created if one does not exist) + const identityStorageKey = "identityStorageKey"; + let identity; + try { + htmlConsole.log("Getting reception identity.") + identity = LoadReceptionIdentity(identityStorageKey, net.GetID()); + } catch (e) { + htmlConsole.log("No reception identity found. Generating a new one.") + + // If no extant xxdk.ReceptionIdentity, generate and store a new one + identity = await net.MakeReceptionIdentity(); + + StoreReceptionIdentity(identityStorageKey, identity, net.GetID()); + } + + // Print contact to console. + const myContactFile = dec.decode(GetContactFromReceptionIdentity(identity)) + htmlConsole.log("My contact file: " + encodeURIComponent(myContactFile)) + + // Start contact file download + if (myContactFileName !== '') { + download(myContactFileName, myContactFile); + } + + let confirm = false; + let confirmContact; + let e2eClient; + let authCallbacks = { + Confirm: function (contact, receptionId, ephemeralId, roundId) { + confirm = true; + confirmContact = contact + + htmlConsole.log("Confirm: from " + Uint8ArrayToBase64(receptionId) + " on round " + roundId) + }, + Request: async function (contact, receptionId, ephemeralId, roundId) { + htmlConsole.log("Request: from " + Uint8ArrayToBase64(receptionId) + " on round " + roundId) + + htmlConsole.log("Calling confirm on contact " + dec.decode(contact)) + const rid = await e2eClient.Confirm(contact) + htmlConsole.log("Called confirm on round " + rid) + } + } + + // Create an E2E client + // Pass in auth object which controls auth callbacks for this client + const params = GetDefaultE2EParams(); + htmlConsole.log("Logging in E2E") + e2eClient = Login(net.GetID(), authCallbacks, identity, new Uint8Array()); + htmlConsole.log("Logged in E2E") + + //////////////////////////////////////////////////////////////////////////// + // Start network threads // + //////////////////////////////////////////////////////////////////////////// + + // Set networkFollowerTimeout to a value of your choice (seconds) + net.StartNetworkFollower(5000); + + htmlConsole.log("Started network follower") + + stopNetworkFollowerBtn.style.display = 'block'; + stopNetworkFollowerBtn.addEventListener('click', async () => { + htmlConsole.log("Stopping network follower") + try { + await net.StopNetworkFollower() + } catch (e) { + htmlConsole.log("Failed to stop network follower: " + e) + } + }) + + // Wait for network to become healthy + htmlConsole.log("Waiting for network to be healthy") + await net.WaitForNetwork(25000).then( + () => { + htmlConsole.log("Network is healthy") + }, + () => { + htmlConsole.error("Timed out. Network is not healthy.") + throw new Error("Timed out. Network is not healthy.") + } + ) + + + //////////////////////////////////////////////////////////////////////////// + // Register a listener for messages // + //////////////////////////////////////////////////////////////////////////// + + let listener = { + Hear: function (item) { + htmlConsole.log("Listener heard: " + dec.decode(item)) + const message = JSON.parse(dec.decode(item)) + htmlConsole.log("Listener message: " + atob(message.Payload)) + }, + Name: function () { + return "Listener"; + } + } + + // Listen for all types of messages using catalog.NoType + // Listen for messages from all users using id.ZeroUser + let zerUser = Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3]); + e2eClient.RegisterListener(zerUser, 0, listener); + htmlConsole.log("Registered listener.") + + //////////////////////////////////////////////////////////////////////////// + // Connect with the recipient // + //////////////////////////////////////////////////////////////////////////// + + // Check that the partner exists + if (recipientContactFile !== '') { + let exists = false; + htmlConsole.log("getting ID from contact") + const recipientContactID = GetIDFromContact(enc.encode(recipientContactFile)); + const recipientContactIDBase64 = Uint8ArrayToBase64(recipientContactID) + + htmlConsole.log("Checking if partner " + recipientContactIDBase64 + " exists") + + const partnerIDS = dec.decode(e2eClient.GetAllPartnerIDs()) + console.log("partnerIDS: " + partnerIDS) + + let partners = JSON.parse(partnerIDS); + for (let i = 0; i < partners.length; i++) { + console.log("Checking partner #" + i + ": " + partners[i] + " matches recipient " + recipientContactIDBase64) + if (partners[i] === recipientContactIDBase64) { + htmlConsole.log("Already partner with " + recipientContactIDBase64) + exists = true; + break + } + } + + // If the partner does not exist, send a request + if (exists === false) { + htmlConsole.log("Partner does not exist, Request being sent to " + recipientContactIDBase64) + const factList = enc.encode('[]') + const rid = await e2eClient.Request(enc.encode(recipientContactFile), factList) + htmlConsole.log("Request sent on round " + rid) + + + htmlConsole.log("Waiting to receive confirmation.") + try { + await sleepUntil(() => confirm === true, 35000); + htmlConsole.log("Received confirmation: " + confirm) + } catch { + htmlConsole.error("Timed out. Did not receive confirm: " + confirm) + throw new Error("Did not receive confirm: " + confirm) + } + + console.log("confirmContact: " + confirmContact) + console.log("confirmContact: " + dec.decode(confirmContact)) + const confirmContactID = GetIDFromContact(confirmContact) + if (!Uint8ArrayEquals(recipientContactID, confirmContactID)) { + htmlConsole.log("contact ID from confirmation " + + Uint8ArrayToBase64(confirmContactID) + + " does not match recipient ID " + recipientContactIDBase64) + throw new Error("contact ID from confirmation " + + Uint8ArrayToBase64(confirmContactID) + + " does not match recipient ID " + recipientContactIDBase64) + } + } else { + htmlConsole.log("Partner exists") + } + + //////////////////////////////////////////////////////////////////////////// + // Send a message to the recipient // + //////////////////////////////////////////////////////////////////////////// + + // Test message + const msgBody = "If this message is sent successfully, we'll have established contact with the recipient." + + htmlConsole.log("Sending E2E message: " + msgBody) + const e2eSendReport = await e2eClient.SendE2E(2, recipientContactID, enc.encode(msgBody), params) + + htmlConsole.log("Send E2e message. Report: " + dec.decode(e2eSendReport)) + } else { + htmlConsole.log("No recipient specified. Waiting for request") + } +} \ No newline at end of file diff --git a/wasm/testServer/sendE2E/sender.html b/wasm/testServer/sendE2E/sender.html new file mode 100644 index 0000000000000000000000000000000000000000..c88b0e950f83b8c3f5030312a833215cbd599852 --- /dev/null +++ b/wasm/testServer/sendE2E/sender.html @@ -0,0 +1,79 @@ +<!--//////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +/////////////////////////////////////////////////////////////////////////////--> + +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Sender</title> + + <link rel="stylesheet" type="text/css" href="../assets/styles.css"> + <link rel="shortcut icon" type="image/x-icon" href="sender.ico"/> + + <script type="text/javascript" src="sendE2ejs"></script> + <script type="text/javascript" src="../assets/utils.js"></script> + <script type="text/javascript" src="../assets/wasm_exec.js"></script> + <script> + const go = new Go(); + const binPath = '../../../bin/xxdk.wasm' + WebAssembly.instantiateStreaming(fetch(binPath), go.importObject).then((result) => { + go.run(result.instance); + + // Set log level + LogLevel(0); + + // Output log to file that can be downloaded using a button + const logFile = LogToFile(0, "sender.log", 1000000); + document.getElementById('logFileDownload').addEventListener( + 'click', () => download(logFile.Name(), logFile.GetFile())) + + // Get element to print log messages to + const logOutput = document.getElementById("logOutput") + const htmlConsole = newHtmlConsole(logOutput) + + // Get button that will stop the network follower + const stopNetworkFollowerBtn = document.getElementById("stopNetworkFollowerBtn") + + // Client specific parameters + const recipientContactFile = '<xxc(2)8xlGWi5QO7ypchZrwF0HIZwNv7MhrwojO1Zw6Mjmsx4DrgZ7Ugdw/BAr6UIgauCEAsxLTzY9qk+fDN6Q4lLf5bOCRKRavadoNTRpbG6GWmps0e1aMs6VsGflG4i95G4zWEu3tkGptr65SPC5c5/V1V9aYvf1rLSl8UwtiANaHlDdxy4ZEZbiibg7EnVi3mKZ3h4orsflDiWSwwSsdFbv9SxNmhzaFD4qpR0bi4jRyinyR4Ty6j8tA+ozcVo+Lt+gPeiUQqCt8NORS3Kcoe54yi4Lf9ylKxBumioRNytdyvo93r/SAX44w8j2tFTa+zYLz/Y09hPz391GHa3MDbDliPQgzRyJrAQRVJV9YcGTgi6ESDOFG3IoJbaNioHles7+2y/hqOKh3kCkbRtNozqGsp91PGiatCLx3bpRJq7h+o4Zk0yjZLf6e92JtFN4IBkHVc9RsiQEH8hy6tY9XLfvMOfr8xhWJ1gvIDxsS/Ice/lYkOs3zPCXwQXTBCAmZGZtsGxjMcSElz0d6cIVFBNgaGh3clWiWzz9nb62ofz0FcQ1O5n13L1Ir/41DyJU6GdeE5/+zvfqIKqSlAAAAgA76aDSbZXRB/a463h2byeQMw==xxc>'; + const myContactFileName = ''; + const statePath = 'statePathSender'; + const statePass = 'password'; + + document.getElementById('ndfInput').addEventListener('change', e => { + let reader = new FileReader(); + reader.onload = async function (e) { + try { + await SendE2e(htmlConsole, stopNetworkFollowerBtn, + e.target.result, recipientContactFile, + myContactFileName, statePath, statePass); + } catch (e) { + htmlConsole.error(e) + } + }; + reader.readAsText(e.target.files[0]); + }, false); + }); + </script> +</head> +<body> +<h1 style="margin-top: 0">SendE2E: Sender</h1> +<div class="toolbar"> + <input type="button" value="Clear localStorage" onclick="localStorage.clear();"> + <input type="button" value="Download Log File" id="logFileDownload"> + <input type="button" value="Stop Network Follower" id="stopNetworkFollowerBtn" style="display:none;" > +</div> +<p>Selecting an NDF will start the client.</p> +<form id="startCmix"> + <div> + <label for="ndfInput">Select NDF file to use <code>ndf</code> variable in JS:</label><br/> + <input type="file" id="ndfInput" required/> + </div> +</form> +<div id="logOutput"></div> +</body> +</html> \ No newline at end of file diff --git a/wasm/testServer/sendE2E/sender.ico b/wasm/testServer/sendE2E/sender.ico new file mode 100644 index 0000000000000000000000000000000000000000..149d6cb2fdb5e20b4a4f80a5797878f73e14d25e Binary files /dev/null and b/wasm/testServer/sendE2E/sender.ico differ diff --git a/wasm/udb.yaml b/wasm/udb.yaml new file mode 100644 index 0000000000000000000000000000000000000000..14b3ed1ed5398db64c1d56f33d99f499c898ca03 --- /dev/null +++ b/wasm/udb.yaml @@ -0,0 +1,10 @@ +# UDB Config for E2E Integration test +log: "./results/udb.log" +sessionPath: "./results/udbsession" +sessionPass: "hello" +certPath: "../keys/cmix.rip.crt" +port: "18001" +keyPath: "../keys/cmix.rip.key" +permCertPath: "../keys/cmix.rip.crt" +permAddress: "0.0.0.0:18000" +devMode: true \ No newline at end of file diff --git a/wasm/udbContact.bin b/wasm/udbContact.bin new file mode 100644 index 0000000000000000000000000000000000000000..10b08460a8ec87eaa6474298845d1deaa9482a5c --- /dev/null +++ b/wasm/udbContact.bin @@ -0,0 +1 @@ +<xxc(2)uu49Kr9myQUG0gG8VKwalxmem65pfPu4D2Lk1u8pi/UDrgZ7Ugdw/BAr6QTgt3dDqBSTfbLpgWOpOQvmR1bACzi52YiM90WbtsF4Iy5RZ64mRVbzN+erifSAaH8JAWSoK23KmMJH9F/HqBBwMjWJmpmkpxR0wo4bHDoBnHulqnBf/WxsHBeLk6HRbWqXWljV3LnO0ugSZDxa3cNu8mjfMNnxgoQr3JWY/bgiAHUGvUSHGxfMgmRvbxuFe3TRO9ZNVGhIC291GsvA+6kKnIjC2NuHu+UZ5Gmk86fw/1kbVK6lxFZsWN7dDgDrodUMc0Bnq2fQ+pmwP26x2Wj5tUHUKK+VHqmY+DGHg/RObjpgnr1qctl3/+gpL83Q3TyWwAjH/73tVww/M6lDnFYxAb62k0NYtVKZVFPDqrocFyAlDXaReg1it9VNJSPYiUM4TCimMmtYL6AgcXdMeD5E7UX1vsvmA5Ll9jZNOmoyPWLYt5W9Gvl79Xqgzqlf0t1DxR4ZmFtG/NwH3S1gCARcIlYgCb66j2gifEMH7aAQWyW8YLm2hEZmwLxISnIPjEL3gIOzNHtmGGXN23jUIwAAAgA790tNCG8KTm8tuEBiH/sjKg==xxc> \ No newline at end of file diff --git a/wasm/udbProto.json b/wasm/udbProto.json new file mode 100644 index 0000000000000000000000000000000000000000..940cadfe9b1bb56946fd38a8c677207245add9e3 --- /dev/null +++ b/wasm/udbProto.json @@ -0,0 +1 @@ +{"TransmissionID":"MhaUvxgJp0REgqp/FNes3ddICg1vKmIIUr+hABI9PXgD","TransmissionSalt":"aUdWmk0Dtu8YZ9N4fUCl80QAmCZ+eY96BDTAN9//9kI=","TransmissionRSA":{"N":744095517605335188486192402593567285818149781037814392671915097997165799160704009076529206117565015316892087089782583938102479243183663906078668582528984516276493241709164519970005583971481012694667579696383360567823504989650655410246207331522294633002054530951336779742521949130565223443549835766371552961634577790108359687869697030104209247319401651289712207512551543251668177804743453344668589351336794674335174328494860289416890175422687829958938924106165398205285038813958087994742173028066268997679203164659373760933342345626244738488110458781897756397149360694648367300111068967014678500898983090333707836644483738687322761365319344259236535454689996343110842585871572930571278463402471136616034421156328875234527272277980275379473515969296415235308154781609704415193243251254222667078013823356321213274280009612841451973719445431427792198300346805888677562529255025870805189890421627704987545754460307965087998094692006804411392783507966480991167024256590215549839475685306270811326657913050667578064503246626321836700579972796860953792588080819942544062977767967992236600692121009380090485897336412623665757415839315721348591326702012914394412545952829437103713850347584948953528257126120160975904938795178616150713777831589,"E":65537,"D":702540524327712978286466077105790056678969955922255232545891489063744574128612569535199928085456619203859372153966109927796010011603759273638246729040785738035622202988204724082335711421080162694477101925219543187134214539033120295707837878642675511638740378895522619590888057835298904908929813505588790799198357720349344223969868061274671644969751681902643121660346855086187525184614960443841773399647021439245580083376969805274103382587388114414289085660271241356003819929067314812253558149711712876228635033944612512096562618373058664318305855593748378360108198295661336701847393296488506669516858822963804260311691759542739227343536282374650987690037136673732778544975139599300829103845745104309687652878356279709829981494578600603924386279381105751731671579921080691074783353253958778076575957155595416051906390745893233111609000272562409467803170191115741547810223834894875859746256757717726366292758015729401293357270389003259918967902654657293283011346796119435418406947470723708887763547764820828540891209479365431423513260945988353481802346962185241475598840911175187647890159503133204568937310632267175230680232531126383217383814916312952923810037599497876585604599444464808553067231902765607317662766023981051800036686273,"Primes":[26431524235468771028852443590104055376636364921067197450041684258482439988835561020261125258562308094700689754910670346007026107201010688328852518422104352082149765577020457481232265846864075786524112374194109435619505107638299597352847727718122930754740563865787954820820245269180427904294490102595163613363903301612449832540143489589841021282833070290450230168473246516211511298499533872232550844877914066189330777819461606196011609435557144318950621971682270122109937087991576359208132704648705570764167630153594184009208403478937807655656357669009470285691425163502536227383519714393737089527860969984842067786101,28151820189273258942344071129053694480161436139819710188331124791415914831471008775403858034120373831438530464789505032183420165894885328994613924494905439083295166072103442061113078040409562754110291758200572034754286027961921301572668934253552291343681689448082037497152344335274886686268374921532985704635156460979764429070570728443686028271768081803840073189209047377853848238258439123998890243019814395279696459993201039305339332983028931741597790390904270131316198444444886804187393230626448411790408221074812330007942755799986304653723364502190308487509016389841021566288050103973334556368552133193493944408689],"Precomputed":{"Dp":275861918871181765777119358768805009042514512504538856765315959424477607341860685381671569904887601458340659358208317693345833000068530918670905329824669680092015802595205653556965833639852721943062588521732316919659756986505285939077892576089782636316013025988357127995499454722056436616528544641577916467658114626896496413590157420685280964301964082558981299651123802082620103577729849834552463156636605601011676641111307179731631610447855207198410446444461491425045347943699562532590182186851925025599137266354249078570861467256564390137921306217899471678791138011134699170397294423689155274685397614624288178673,"Dq":20251853375093108718674238635432892563921007495734318881533472838489627548476924176040866851955981272517828971313395551327029420652154717499734014133036480629583806088703921134828221879139253331786377393413250059516388743370809492102858684015410780592477790114275535038844384485571048533054744709867618963802570860877854857148803386838061270563205181572605460893517398533291080291147479606024866554731101049481309326072897084076010928068387048779916221764340337818499984617266561381665606317822215493877818423040314663633588189342602717841897580646356474723470712524458623415234392941421286158880506566848352312481377,"Qinv":1782249293930479585148549445278205034617546516712012566064256819261113180298258405511667713490475276726336613818509018461173201977639000556416891523605685233456051292911470649097170785609362912595510275601045219404262259673943809118153699614833210258102652927663612925023313592281316017977621743103307097046836057862305643762370317914029974246165942098634086048350280652529323604758408742898452312792547897909618079071765102987807077305433629474092046709583007609210604882495077254479038254736382806594706188378137791260031991135639188023310238199612626314094372849297487936335153146414944425343861682455207985266708,"CRTValues":null}},"ReceptionID":"uu49Kr9myQUG0gG8VKwalxmem65pfPu4D2Lk1u8pi/UD","ReceptionSalt":"M8xD+2gHGbqwKPwHFxs7+IjutQYuyBUkX2H257o5BdU=","ReceptionRSA":{"N":850387188153991188889446100749739241848829271482462061108409439312531431988795734813327514693008344548532502397064837726907012068926804456615485530991075075479632026273101933309061970039508777559060326644210787796911046233949155489473727669005294322689793791335544727360305644959942063168476363583581030898050353126928764965273874658694962134566452334451854924568242626514389265043292391299760010067072010852660228863317130740474026897759770820934809759670185849936472580423333979237659958984231856174610567212253308506984182380860548206401655999782332308043025450559980137052816211530713615583429240205892728859426128000967414395838939110894033790769681039750313146913688429864118346214901692066118374534016813397369786419474818398950057293996832967235881835337432259690040386208608584839830051784956186429114899224875646403109211367267143599678019107255952565689487312514740269900292167402469336239686704437448306260169629496444993371773823439258565787685710030837201534059640894021404396138451751712905956843779570695671045422782022593718074598748351627333401529057059641321731684199811309173775075814578877100319468485440759413419208114295109676086784903373038008038594143762297734223790838124082684159063469085109435783518725031,"E":65537,"D":564922212042972891506779603709986684962272914422874876092540423872769289935398323184885484645154711027502930510414145293747815500326018670791589385580030144211006599750553715770887147559547944650942359905229184557030627277529401445675232029624235148495438489607025204008203409747486116303247866721674280821649117660056115481226310038842219913218786872835656314004723732098768107056926939591645201310559136617365280437344369150983684163873340894930173985790635539446178597340291613196667556255191759193646646393943456253239701974694076434107586512390304739235381525566166520085882164295171867519962140940902875266686494581933486332733070115573951932064475826442525759304559384157338056633545437785084083189928354125207030609304055082642325819334882909664939190272656190536034921638933681026317152328190142016810293928283213410554345222497221023348512487512997673475708771529517243600219742073183452616019438402558324904073954972062329692828835961585134598049832023174978649417520451705037067815379933732830551892585260348438752787466224668527842810502446714266623795601014094978116799695556231619140778349831856996205593248896859777976571100881350185386707051672977669257878795867903988402998091514995549897864288962757393054768438537,"Primes":[29492971595987200184565588575492524180677323915637743461334512913337954089756992535478030745356298455923396638365524831317118482754099964262507886663300281433334915984551779676428190574578201359535331987821184071989738056990202803783701055959370528270478305175226218851763791555786561228871062383690072445559534386478623474224364245986294525688152961853703225064960942669169364540334937714823971415905428637789133243443480147178878208486722743719938775945712937478778805521783173263515825112840086735855547887576584469859603118910509756820711149404941056507505214523564857945496996487150674719706390219045171074335669,28833553966793039902622472693344948085468484145845354555175845509525852913656457209339298633228826325187527465622133573224700820135296269687184379379824668957542611483930310268532473436266625079565533379890421336412596736616538404624204368463036294562591617517553183068875112160214949467183247851787117317409259574446000362822944034217039330133706829603389396809485151845126091352794981119059018300220209826040784745553271153888708759107151118112035826198322288728622562498893857193711017030398086686353945134010647373894524028231986696810322055915091233789726845203397655178875279735934668248673328323303568542440299],"Precomputed":{"Dp":22035237410890233868459863048338975639819722683815281575052938413439628332162765147926847055043275613688284730725665248110879449899362264829236899315936009893392193130347405158251161198776593825925012295095354961090338647794761436856568384348544759704654930999358405268422632307384972924785564942206454328040993656009976019887941091363945732186709946704404815833892496723925079492767447780739975286934240402358132188919389129905454498658046281432577050584888444445861180895892596225766397251174372446422126467723384631073675430939059008540612500276377295754916960329986927526454681065555835446395523094977597412985733,"Dq":16841607730729779627880254737037774275779080108991259477426970506807599516834294855936468737964805708655851677434354230175954764404521738920387232291067463077265226781891943132572792211121754246391635530802528781571848010706640373056663308127699305062117691053480260736325118535987736173516864180026715457076559142313393867416303731172135824915975669274116088772252187506773681691029370847575861277330815144740242001614038173411351927897550159472187183222786780178092858880596561535853910492143960790906343282877269046075993405262988094570992390564561887627916194430414303984731460217763692273970657921724531238912593,"Qinv":13386450555509069245317666985331292478728039484440837065189457918756926085772889573452664818969207436096145088071795396654392004940595219762405312750934346518716740936238520072004578261328949676162223098531690599793600587771458065485838287464893640290027597304483114889945915722313306140200593797334834667938307085618314432134506785949791967130287157125647210497214516165218726084383497011560605024384546734297139331536623591040849235382041138776467271730696249828686335872619825082175542573979118200539219034800857165800266206532665733326893998872307865475986277696080650713536968589910998008126592314634991854532329,"CRTValues":null}},"Precanned":false,"RegistrationTimestamp":1640900456461843040,"RegCode":"","TransmissionRegValidationSig":"Zln6nmiiuvO2vZGe4jMrHyEWyd3j89GYUjnugb88V+1Q/mdn9BFV/qmiBpnoNyqAn5XOpHmkYOw0DkBX2Tc3w5NQWHiLgcrAMHlZUkinFZ2/jWeLNGuWTFmauu2q+z1C4R/reAjlRiyo2OuKBZxFoE3mbw85mWYoiyyMbEfyNCHK2ScCmdapHP5kSzWeUi2WjaNi+LUBqmWMLX9nWZs24YD6VapNnStcj7w14XTWtUJ/oXtgwI/uMDa3ZvLwCO6JwsRR3VGq98Dbky4eJhXwCnFTW/Jy/ZR5n3xPccU3Eb+/0eHyYwZGORe0Tr7qGq0YI9wFNfjv3rpEmqqScq9sHg==","ReceptionRegValidationSig":"ABBNeRb2gEVIi/LU3c4rwTxauQpJ0PV0Dp78JT3QGXyoThMjDIgpCZHZRr8clexXkDSNZ0GZCKLQtv48e2TJbO15oodzKZq9fS6HPNc/D2l17DBbNCFTy+kM7MKIqHU4dN2h/fhjYgQ0k3WaR04NGSFtX4FSyeDqh8LbQAlrvKn13Z1fCId6kt9Sp3GTWtRMm2RD3hRNYLzrFHqAiH/rDcMy/d4Ar9wTwKlIcFyX3+1ENjAxTcsnjno7+aa5tT8XI0HdlcvrMDkx+m2kbHquqGJM9hCg/XxhrLFqsjY0F0bVp7J2UeHoLw6s6OgEl9Eg5I0k51K+SSMa1Mse1zggkg==","CmixDhPrivateKey":{"Value":23134336198863750741748703660400387868824411194091704843877765878704684400020511760943730932032181255742725205976185967794840498055119688330124475657025083380879051516507316264855378759159543513707252259922784173347685874456358247463151784175729466898154272096842618045994973870223785531415217217965394756900460790339174016946558150507580144972427210564566872862707000226886100833409714665643615263325876515987666291734559374206190550344559412403144311029314745799204676946278572048079685978558133653417488694953610922501137922416052437955367323383147526279158425720871674296239641638893436600760625905793653337291326,"Fingerprint":1019130854791890857},"CmixDhPublicKey":{"Value":40661473203398365346021139834751279657661515042218153062933068745659041507124553367048607412838109996778705972152516147090017024944177990861357148498071195775787956973205443551118903422765769934453067811496751124796541305224646917714432655244199681118218184307551604728113020909007420531039281826284338933975869040846161322930224431922676228280139352116332784218225442881629816602619617561152949947075502732590198688997850331029595028876889786739167152345971604837504050490241011792155573239919262293917635508122225378077992880296300468988471302542680432268331360185693269950450132051419581315000110594207999508607050616011689445128093546507459669376840509120845943383094511932011628510767784841945662325523012877900497552502527594150800481434891933803449151469858078545202819003974303280259033864766743161709598642156724209578764288690522108924880552091308234645729967740909969025514152089886697815454891469560150611996697804062619010044123824507982370347027313396405015494843434897303661589566168416699605512854149233601685403032629550047093556970695648101495563847168540708660964238960062140890332386702253196064038177801032833477139196626595095716721717130200091320630907308046939829403546847081437153253274137106108481349472925,"Fingerprint":1019130854791890857},"E2eDhPrivateKey":{"Value":15868054927490624034769207675284372396111367138702747294809526549406434197505594302868553416930040949078939726209524016245099170354423124146109791439676150153811847753697222698706991672115541614080484720172498069738416608757527621565114235134343850506971256805006649306801742780960331446133932778313421336826492419515327757859355790985926801091202339643161980910811435573134233363961673385626782920679443568800015505367010870530111147205683029126252427303195582360585233280495455148410685790997024364716148825012857917467229638326785383545555885827743352446807694303549475074691875396849487056555719913457474008032697,"Fingerprint":16801541511233098363},"E2eDhPublicKey":{"Value":147139791407699234997192910595075527435460061524772944343748681206265869846642332345707990677406040145177534321538182440717890359993428686109525202118205223891431899168261959123977071363023161983520620405235747590084693661804115691677488049367521195795602971625363864492999496527190606383593289607097185210792999982667279095678826515909506042508081456493456997354040504436530126940434391612132572210469911068602346111602950742796752446044197938946885845119733654743975968520762482827365434031479613917149883640347855512864879556669871625702373555191119636414088476652878563700716501799409607427689929171266147047756204689439519092680494886131474665331718064247744119146288064620933248987203778241513546175445638858254194548267620960510274776658377038971793051901595492445943697519511203993381901627766295075444537422179881027670118336783809271801530698319821686375879565780071214330909899155111647279589474822393047955732167728825532445888096165721311471522851,"Fingerprint":16801541511233098363}} \ No newline at end of file diff --git a/wasm/whitelist.txt b/wasm/whitelist.txt new file mode 100644 index 0000000000000000000000000000000000000000..b13380509def3eae45d04009b5e89b0843b2d88b --- /dev/null +++ b/wasm/whitelist.txt @@ -0,0 +1 @@ +["0.0.0.0", "127.0.0.1", "::1"] \ No newline at end of file