Compare commits

..

28 Commits

Author SHA1 Message Date
Simon Wicky 4b3dcb6eca Feature Noise XKpsk3 : Fix localnet (#4465)
* add custom mixnet option for gateways

* add custom mixnet option for mixnode

* change topology construction as to not miss described_nodes

* adapt localnet scripts to new topology

* add snake_case alias to described_nodes
2024-03-19 15:49:29 +01:00
Simon Wicky 5a9c391ba2 change license 2024-03-13 11:35:47 +01:00
Simon Wicky 011be0d31b remove outdated comments 2024-03-13 11:16:55 +01:00
Simon Wicky 4d273ea613 change to psk structure and port checking to allow same ip 2024-03-12 12:18:25 +01:00
Simon Wicky 6b1657adae apply jstuczyn suggestion 2024-03-12 12:18:25 +01:00
Simon Wicky 3846d3c9b5 comment change according to review 2024-03-12 12:18:25 +01:00
Simon Wicky 2e12c20327 update cargo lock 2024-03-12 12:18:25 +01:00
Simon Wicky a0e3610681 change key retrieval to use bond info 2024-03-12 12:18:25 +01:00
Simon Wicky 30a1f5582a change mix_host in Node to a vec 2024-03-12 12:18:25 +01:00
Simon Wicky 7ec35fe010 Merge commit '0e56d8c2f733f37ae5cdeace779b58083b12baeb' into simon/noise_nodes 2024-03-11 09:32:09 +01:00
Simon Wicky 49588e0abc Merge branch 'develop' into simon/noise_nodes 2024-03-04 17:08:25 +01:00
Simon Wicky e57fcf4a0e Merge branch 'develop' into simon/noise_nodes 2024-02-14 09:34:52 +01:00
Simon Wicky e2fa1ae986 apply octlol's type suggestion 2024-02-14 09:20:52 +01:00
Simon Wicky 293cf2fd3f apply some of octlol's suggestion 2024-02-14 09:20:52 +01:00
Simon Wicky 06f1271a25 enable noise support in self described API 2024-02-14 09:20:52 +01:00
Simon Wicky e4425f9136 add noise connection in nodes 2024-02-14 09:20:52 +01:00
Simon Wicky 54feb9ea0a add main noise logic 2024-02-14 09:20:52 +01:00
Simon Wicky a16b21ce3c cargo fmt 2024-02-14 09:20:52 +01:00
Simon Wicky 6924f29d32 changes on nym-connect and wasm client 2024-02-14 09:20:52 +01:00
Simon Wicky fbbd634a39 apply octlol suggestion 2024-02-14 09:20:52 +01:00
Simon Wicky 8c29fba5ef move topology control into its own crate 2024-02-14 09:20:52 +01:00
Simon Wicky 0a826b8146 add topology access to nodes 2024-02-14 09:20:52 +01:00
Simon Wicky 580457aeb1 add epoch in validator client 2024-02-14 09:20:52 +01:00
Simon Wicky d4a2be8a9e augment topology with described nodes 2024-02-14 09:20:52 +01:00
Simon Wicky 7363cdab68 apply octlol unwrap or default suggestion 2024-02-06 09:57:54 +01:00
Simon Wicky 59087413c7 adapt noise support to PR chain merge order 2024-01-30 11:04:19 +01:00
Simon Wicky 0d841e5c75 augment Nym-API described with Noise Info 2024-01-30 09:38:41 +01:00
Simon Wicky 786ff31ce5 augment self described API with NoiseInformation field 2024-01-30 09:37:56 +01:00
198 changed files with 2849 additions and 41146 deletions
+2 -10
View File
@@ -14,20 +14,12 @@ inputs:
description: 'The tag/release to process. Uses the release id when trigger from a release.'
required: false
default: ''
repo:
description: 'The repo to use. Defaults to "nym".'
required: false
default: 'nym'
owner:
description: 'The repo owner to use. Defaults to "nymtech".'
required: false
default: 'nymtech'
outputs:
hashes:
description: 'A string containing JSON with the release asset hashes and signatures'
runs:
using: 'node20'
main: 'dist/index.js'
using: 'node16'
main: 'index.js'
branding:
icon: 'hash'
color: 'green'
@@ -11,14 +11,10 @@ function getBinInfo(path) {
let mode = fs.statSync(path).mode
fs.chmodSync(path, mode | 0o111)
const cmd = `${path} build-info --output=json`;
console.log(`🚚 Running ${cmd}... (for max of 3 seconds, then SIGTERM)`);
const raw = execSync(cmd, { stdio: 'pipe', encoding: "utf8", timeout: 3000 });
const raw = execSync(`${path} build-info --output=json`, { stdio: 'pipe', encoding: "utf8" });
const parsed = JSON.parse(raw)
console.log(` ✅ ok`);
return parsed
} catch (_) {
console.log(` ❌ failed`);
return undefined
}
}
@@ -28,11 +24,8 @@ async function run(assets, algorithm, filename, cache) {
console.warn("cache is set to 'false', but we we no longer support it")
}
const directory = path.join(process.env.RUNNER_TEMP || '.tmp', process.env.GITHUB_RUN_ID || '');
console.log('Temporary directory: ', directory);
try {
fs.mkdirSync(directory, { recursive: true });
fs.mkdirSync('.tmp');
} catch(e) {
// ignore
}
@@ -47,13 +40,13 @@ async function run(assets, algorithm, filename, cache) {
let sig = null;
// cache in `${WORKING_DIR}/.tmp/`
const cacheFilename = path.join(directory, `${asset.name}`);
const cacheFilename = path.resolve(`.tmp/${asset.name}`);
if(!fs.existsSync(cacheFilename)) {
console.log(`⬇️ Downloading ${asset.browser_download_url}... to ${cacheFilename} [${numAwaiting} of ${assets.length}]`);
console.log(`Downloading ${asset.browser_download_url}... to ${cacheFilename}`);
buffer = Buffer.from(await fetch(asset.browser_download_url).then(res => res.arrayBuffer()));
fs.writeFileSync(cacheFilename, buffer);
} else {
console.log(`💾 Loading from ${cacheFilename}`);
console.log(`Loading from ${cacheFilename}`);
buffer = Buffer.from(fs.readFileSync(cacheFilename));
// console.log('Reading signature from content');
@@ -138,7 +131,6 @@ async function run(assets, algorithm, filename, cache) {
}
}
}
console.log(`Completed hashing ${assets.length} files`);
return hashes;
}
@@ -150,7 +142,7 @@ export async function createHashes({ assets, algorithm, filename, cache }) {
return output;
}
export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrId, algorithm = 'sha256', filename = 'hashes.json', cache = false, upload = true, owner = 'nymtech', repo = 'nym' }) {
export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrId, algorithm = 'sha256', filename = 'hashes.json', cache = false, upload = true }) {
console.log("🚀🚀🚀 Getting releases");
let auth;
@@ -165,6 +157,8 @@ export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrI
auth: process.env.GITHUB_TOKEN,
request: { fetch }
});
const owner = "nymtech";
const repo = "nym";
let releases;
if(cache) {
@@ -218,14 +212,7 @@ export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrI
releasesToProcess.forEach(release => {
const {tag_name, name} = release;
const matches = tag_name.match(/(\S+)-v([0-9]+\.[0-9]+(\.\S+)?)/);
if(!matches || matches.length < 2) {
console.warn('Could not match version structure in tag name = ', tag_name);
return;
}
const tagComponents = matches.slice(1);
const tagComponents = tag_name.split('-v');
const componentName = tagComponents[0];
const componentVersion = 'v' + tagComponents[1];
@@ -1,450 +0,0 @@
export const id = 37;
export const ids = [37];
export const modules = {
/***/ 4037:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "toFormData": () => (/* binding */ toFormData)
/* harmony export */ });
/* harmony import */ var fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2777);
/* harmony import */ var formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8010);
let s = 0;
const S = {
START_BOUNDARY: s++,
HEADER_FIELD_START: s++,
HEADER_FIELD: s++,
HEADER_VALUE_START: s++,
HEADER_VALUE: s++,
HEADER_VALUE_ALMOST_DONE: s++,
HEADERS_ALMOST_DONE: s++,
PART_DATA_START: s++,
PART_DATA: s++,
END: s++
};
let f = 1;
const F = {
PART_BOUNDARY: f,
LAST_BOUNDARY: f *= 2
};
const LF = 10;
const CR = 13;
const SPACE = 32;
const HYPHEN = 45;
const COLON = 58;
const A = 97;
const Z = 122;
const lower = c => c | 0x20;
const noop = () => {};
class MultipartParser {
/**
* @param {string} boundary
*/
constructor(boundary) {
this.index = 0;
this.flags = 0;
this.onHeaderEnd = noop;
this.onHeaderField = noop;
this.onHeadersEnd = noop;
this.onHeaderValue = noop;
this.onPartBegin = noop;
this.onPartData = noop;
this.onPartEnd = noop;
this.boundaryChars = {};
boundary = '\r\n--' + boundary;
const ui8a = new Uint8Array(boundary.length);
for (let i = 0; i < boundary.length; i++) {
ui8a[i] = boundary.charCodeAt(i);
this.boundaryChars[ui8a[i]] = true;
}
this.boundary = ui8a;
this.lookbehind = new Uint8Array(this.boundary.length + 8);
this.state = S.START_BOUNDARY;
}
/**
* @param {Uint8Array} data
*/
write(data) {
let i = 0;
const length_ = data.length;
let previousIndex = this.index;
let {lookbehind, boundary, boundaryChars, index, state, flags} = this;
const boundaryLength = this.boundary.length;
const boundaryEnd = boundaryLength - 1;
const bufferLength = data.length;
let c;
let cl;
const mark = name => {
this[name + 'Mark'] = i;
};
const clear = name => {
delete this[name + 'Mark'];
};
const callback = (callbackSymbol, start, end, ui8a) => {
if (start === undefined || start !== end) {
this[callbackSymbol](ui8a && ui8a.subarray(start, end));
}
};
const dataCallback = (name, clear) => {
const markSymbol = name + 'Mark';
if (!(markSymbol in this)) {
return;
}
if (clear) {
callback(name, this[markSymbol], i, data);
delete this[markSymbol];
} else {
callback(name, this[markSymbol], data.length, data);
this[markSymbol] = 0;
}
};
for (i = 0; i < length_; i++) {
c = data[i];
switch (state) {
case S.START_BOUNDARY:
if (index === boundary.length - 2) {
if (c === HYPHEN) {
flags |= F.LAST_BOUNDARY;
} else if (c !== CR) {
return;
}
index++;
break;
} else if (index - 1 === boundary.length - 2) {
if (flags & F.LAST_BOUNDARY && c === HYPHEN) {
state = S.END;
flags = 0;
} else if (!(flags & F.LAST_BOUNDARY) && c === LF) {
index = 0;
callback('onPartBegin');
state = S.HEADER_FIELD_START;
} else {
return;
}
break;
}
if (c !== boundary[index + 2]) {
index = -2;
}
if (c === boundary[index + 2]) {
index++;
}
break;
case S.HEADER_FIELD_START:
state = S.HEADER_FIELD;
mark('onHeaderField');
index = 0;
// falls through
case S.HEADER_FIELD:
if (c === CR) {
clear('onHeaderField');
state = S.HEADERS_ALMOST_DONE;
break;
}
index++;
if (c === HYPHEN) {
break;
}
if (c === COLON) {
if (index === 1) {
// empty header field
return;
}
dataCallback('onHeaderField', true);
state = S.HEADER_VALUE_START;
break;
}
cl = lower(c);
if (cl < A || cl > Z) {
return;
}
break;
case S.HEADER_VALUE_START:
if (c === SPACE) {
break;
}
mark('onHeaderValue');
state = S.HEADER_VALUE;
// falls through
case S.HEADER_VALUE:
if (c === CR) {
dataCallback('onHeaderValue', true);
callback('onHeaderEnd');
state = S.HEADER_VALUE_ALMOST_DONE;
}
break;
case S.HEADER_VALUE_ALMOST_DONE:
if (c !== LF) {
return;
}
state = S.HEADER_FIELD_START;
break;
case S.HEADERS_ALMOST_DONE:
if (c !== LF) {
return;
}
callback('onHeadersEnd');
state = S.PART_DATA_START;
break;
case S.PART_DATA_START:
state = S.PART_DATA;
mark('onPartData');
// falls through
case S.PART_DATA:
previousIndex = index;
if (index === 0) {
// boyer-moore derrived algorithm to safely skip non-boundary data
i += boundaryEnd;
while (i < bufferLength && !(data[i] in boundaryChars)) {
i += boundaryLength;
}
i -= boundaryEnd;
c = data[i];
}
if (index < boundary.length) {
if (boundary[index] === c) {
if (index === 0) {
dataCallback('onPartData', true);
}
index++;
} else {
index = 0;
}
} else if (index === boundary.length) {
index++;
if (c === CR) {
// CR = part boundary
flags |= F.PART_BOUNDARY;
} else if (c === HYPHEN) {
// HYPHEN = end boundary
flags |= F.LAST_BOUNDARY;
} else {
index = 0;
}
} else if (index - 1 === boundary.length) {
if (flags & F.PART_BOUNDARY) {
index = 0;
if (c === LF) {
// unset the PART_BOUNDARY flag
flags &= ~F.PART_BOUNDARY;
callback('onPartEnd');
callback('onPartBegin');
state = S.HEADER_FIELD_START;
break;
}
} else if (flags & F.LAST_BOUNDARY) {
if (c === HYPHEN) {
callback('onPartEnd');
state = S.END;
flags = 0;
} else {
index = 0;
}
} else {
index = 0;
}
}
if (index > 0) {
// when matching a possible boundary, keep a lookbehind reference
// in case it turns out to be a false lead
lookbehind[index - 1] = c;
} else if (previousIndex > 0) {
// if our boundary turned out to be rubbish, the captured lookbehind
// belongs to partData
const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength);
callback('onPartData', 0, previousIndex, _lookbehind);
previousIndex = 0;
mark('onPartData');
// reconsider the current character even so it interrupted the sequence
// it could be the beginning of a new sequence
i--;
}
break;
case S.END:
break;
default:
throw new Error(`Unexpected state entered: ${state}`);
}
}
dataCallback('onHeaderField');
dataCallback('onHeaderValue');
dataCallback('onPartData');
// Update properties for the next call
this.index = index;
this.state = state;
this.flags = flags;
}
end() {
if ((this.state === S.HEADER_FIELD_START && this.index === 0) ||
(this.state === S.PART_DATA && this.index === this.boundary.length)) {
this.onPartEnd();
} else if (this.state !== S.END) {
throw new Error('MultipartParser.end(): stream ended unexpectedly');
}
}
}
function _fileName(headerValue) {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);
if (!m) {
return;
}
const match = m[2] || m[3] || '';
let filename = match.slice(match.lastIndexOf('\\') + 1);
filename = filename.replace(/%22/g, '"');
filename = filename.replace(/&#(\d{4});/g, (m, code) => {
return String.fromCharCode(code);
});
return filename;
}
async function toFormData(Body, ct) {
if (!/multipart/i.test(ct)) {
throw new TypeError('Failed to fetch');
}
const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
if (!m) {
throw new TypeError('no or bad content-type header, no multipart boundary');
}
const parser = new MultipartParser(m[1] || m[2]);
let headerField;
let headerValue;
let entryValue;
let entryName;
let contentType;
let filename;
const entryChunks = [];
const formData = new formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__/* .FormData */ .Ct();
const onPartData = ui8a => {
entryValue += decoder.decode(ui8a, {stream: true});
};
const appendToFile = ui8a => {
entryChunks.push(ui8a);
};
const appendFileToFormData = () => {
const file = new fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__/* .File */ .$B(entryChunks, filename, {type: contentType});
formData.append(entryName, file);
};
const appendEntryToFormData = () => {
formData.append(entryName, entryValue);
};
const decoder = new TextDecoder('utf-8');
decoder.decode();
parser.onPartBegin = function () {
parser.onPartData = onPartData;
parser.onPartEnd = appendEntryToFormData;
headerField = '';
headerValue = '';
entryValue = '';
entryName = '';
contentType = '';
filename = null;
entryChunks.length = 0;
};
parser.onHeaderField = function (ui8a) {
headerField += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderValue = function (ui8a) {
headerValue += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderEnd = function () {
headerValue += decoder.decode();
headerField = headerField.toLowerCase();
if (headerField === 'content-disposition') {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);
if (m) {
entryName = m[2] || m[3] || '';
}
filename = _fileName(headerValue);
if (filename) {
parser.onPartData = appendToFile;
parser.onPartEnd = appendFileToFormData;
}
} else if (headerField === 'content-type') {
contentType = headerValue;
}
headerValue = '';
headerField = '';
};
for await (const chunk of Body) {
parser.write(chunk);
}
parser.end();
return formData;
}
/***/ })
};
File diff suppressed because one or more lines are too long
@@ -1,57 +0,0 @@
'use strict';
const fs = require('fs');
const crypto = require('crypto');
const {parentPort} = require('worker_threads');
const handlers = {
hashFile: (algorithm, filePath) => new Promise((resolve, reject) => {
const hasher = crypto.createHash(algorithm);
fs.createReadStream(filePath)
// TODO: Use `Stream.pipeline` when targeting Node.js 12.
.on('error', reject)
.pipe(hasher)
.on('error', reject)
.on('finish', () => {
const {buffer} = new Uint8Array(hasher.read());
resolve({value: buffer, transferList: [buffer]});
});
}),
hash: async (algorithm, input) => {
const hasher = crypto.createHash(algorithm);
if (Array.isArray(input)) {
for (const part of input) {
hasher.update(part);
}
} else {
hasher.update(input);
}
const {buffer} = new Uint8Array(hasher.digest());
return {value: buffer, transferList: [buffer]};
}
};
parentPort.on('message', async message => {
try {
const {method, args} = message;
const handler = handlers[method];
if (handler === undefined) {
throw new Error(`Unknown method '${method}'`);
}
const {value, transferList} = await handler(...args);
parentPort.postMessage({id: message.id, value}, transferList);
} catch (error) {
const newError = {message: error.message, stack: error.stack};
for (const [key, value] of Object.entries(error)) {
if (typeof value !== 'object') {
newError[key] = value;
}
}
parentPort.postMessage({id: message.id, error: newError});
}
});
@@ -0,0 +1,15 @@
import core from "@actions/core";
import github from "@actions/github";
import { createHashesFromReleaseTagOrNameOrId } from './create-hashes.mjs';
const algorithm = core.getInput('hash-type');
const filename = core.getInput("file-name");
// use the release id from the payload if it is set
const releaseTagOrNameOrId = core.getInput("release-tag-or-name-or-id") || github.context.payload.release?.id;
try {
await createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrId, algorithm, filename })
} catch (error) {
core.setFailed(error.message);
}
@@ -1,28 +1,26 @@
{
"name": "nym-hash-release",
"name": "ghaction-generate-release-hashes",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "nym-hash-release",
"name": "ghaction-generate-release-hashes",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
"@octokit/auth-action": "^4.0.1",
"@octokit/rest": "^20.0.2",
"@octokit/auth-action": "^4.0.0",
"@octokit/rest": "^20.0.1",
"hasha": "^5.2.0",
"node-fetch": "^3.2.10"
},
"devDependencies": {
"@vercel/ncc": "^0.38.1"
}
},
"node_modules/@actions/core": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz",
"integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==",
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
@@ -48,12 +46,12 @@
}
},
"node_modules/@octokit/auth-action": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@octokit/auth-action/-/auth-action-4.0.1.tgz",
"integrity": "sha512-mJLOcFFafIivLZ7BEkGDCTFoHPJv7BeL5Zwy7j5qMDU0b/DKshhi6GCU9tw3vmKhOxTNquYfvwqsEfPpemaaxg==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-action/-/auth-action-4.0.0.tgz",
"integrity": "sha512-sMm9lWZdiX6e89YFaLrgE9EFs94k58BwIkvjOtozNWUqyTmsrnWFr/M5LolaRzZ7Kmb5FbhF9hi7FEeE274SoQ==",
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/types": "^12.0.0"
"@octokit/types": "^11.0.0"
},
"engines": {
"node": ">= 18"
@@ -68,16 +66,16 @@
}
},
"node_modules/@octokit/auth-action/node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="
"version": "18.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz",
"integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw=="
},
"node_modules/@octokit/auth-action/node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-11.1.0.tgz",
"integrity": "sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==",
"dependencies": {
"@octokit/openapi-types": "^20.0.0"
"@octokit/openapi-types": "^18.0.0"
}
},
"node_modules/@octokit/auth-token": {
@@ -193,14 +191,14 @@
}
},
"node_modules/@octokit/rest": {
"version": "20.0.2",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz",
"integrity": "sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ==",
"version": "20.0.1",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.1.tgz",
"integrity": "sha512-wROV21RwHQIMNb2Dgd4+pY+dVy1Dwmp85pBrgr6YRRDYRBu9Gb+D73f4Bl2EukZSj5hInq2Tui9o7gAQpc2k2Q==",
"dependencies": {
"@octokit/core": "^5.0.0",
"@octokit/plugin-paginate-rest": "^9.0.0",
"@octokit/plugin-paginate-rest": "^8.0.0",
"@octokit/plugin-request-log": "^4.0.0",
"@octokit/plugin-rest-endpoint-methods": "^10.0.0"
"@octokit/plugin-rest-endpoint-methods": "^9.0.0"
},
"engines": {
"node": ">= 18"
@@ -263,30 +261,17 @@
"integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw=="
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest": {
"version": "9.2.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz",
"integrity": "sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==",
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-8.0.0.tgz",
"integrity": "sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ==",
"dependencies": {
"@octokit/types": "^12.6.0"
"@octokit/types": "^11.0.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": "5"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"dependencies": {
"@octokit/openapi-types": "^20.0.0"
"@octokit/core": ">=5"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-request-log": {
@@ -301,30 +286,17 @@
}
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz",
"integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==",
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-9.0.0.tgz",
"integrity": "sha512-KquMF/VB1IkKNiVnzJKspY5mFgGyLd7HzdJfVEGTJFzqu9BRFNWt+nwTCMuUiWc72gLQhRWYubTwOkQj+w/1PA==",
"dependencies": {
"@octokit/types": "^12.6.0"
"@octokit/types": "^11.0.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": "5"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"dependencies": {
"@octokit/openapi-types": "^20.0.0"
"@octokit/core": ">=5"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/request": {
@@ -371,15 +343,6 @@
"@octokit/openapi-types": "^12.11.0"
}
},
"node_modules/@vercel/ncc": {
"version": "0.38.1",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.1.tgz",
"integrity": "sha512-IBBb+iI2NLu4VQn3Vwldyi2QwaXt5+hTyh58ggAMoCGE6DJmPvwL3KPBWcJl1m9LYPChBLE980Jw+CS4Wokqxw==",
"dev": true,
"bin": {
"ncc": "dist/ncc/cli.js"
}
},
"node_modules/before-after-hook": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
+13 -2
View File
@@ -2,6 +2,17 @@
"name": "nym-hash-release",
"version": "1.0.0",
"description": "Generate hashes and signatures for assets in Nym releases",
"main": "dist/index.js",
"type": "module"
"main": "index.js",
"type": "module",
"scripts": {
"local": "node run-local.mjs"
},
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
"@octokit/auth-action": "^4.0.0",
"@octokit/rest": "^20.0.1",
"hasha": "^5.2.0",
"node-fetch": "^3.2.10"
}
}
@@ -0,0 +1,6 @@
import {createHashesFromReleaseTagOrNameOrId} from './create-hashes.mjs';
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 119065724, cache: true, upload: false});
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: '119065724', cache: true, upload: false});
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'nym-connect-v1.1.19-snickers', cache: true, upload: false});
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'Nym Connect v1.1.19-snickers', cache: true, upload: false});
@@ -1,14 +0,0 @@
# nym-hash-release
This is the source code for the custom GitHub Action to calculate hashes.
It is in a subdirectory to avoid issues with `package.json`.
## Build
The following will bundle all code and dependencies into the `dist` folder, and copy it into place for GitHub Actions.
```
npm run build
npm run dist:copy
```
@@ -1,21 +0,0 @@
import core from "@actions/core";
import github from "@actions/github";
import { createHashesFromReleaseTagOrNameOrId } from './create-hashes.mjs';
const algorithm = core.getInput('hash-type');
const filename = core.getInput("file-name");
const owner = core.getInput("owner");
const repo = core.getInput("repo");
async function main() {
// use the release id from the payload if it is set
const releaseTagOrNameOrId = core.getInput("release-tag-or-name-or-id") || github.context.payload.release?.id;
try {
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId, algorithm, filename, owner, repo})
} catch (error) {
core.setFailed(error.message);
}
}
main().catch(error => core.setFailed(error.message));
@@ -1,23 +0,0 @@
{
"name": "nym-hash-release",
"version": "1.0.0",
"description": "Generate hashes and signatures for assets in Nym releases",
"main": "index.js",
"type": "module",
"scripts": {
"local": "node run-local.mjs",
"build": "ncc build index.js -o dist",
"dist:copy": "mkdir -p ../dist && cp dist/*.js ../dist"
},
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/github": "^5.1.1",
"@octokit/auth-action": "^4.0.1",
"@octokit/rest": "^20.0.2",
"hasha": "^5.2.0",
"node-fetch": "^3.2.10"
},
"devDependencies": {
"@vercel/ncc": "^0.38.1"
}
}
@@ -1,11 +0,0 @@
import {createHashesFromReleaseTagOrNameOrId} from './create-hashes.mjs';
const cache = true;
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'nym-binaries-v2024.1-marabou', cache, upload: false});
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'nym-vpn-desktop-v0.0.8', cache, upload: false, repo: 'nym-vpn-client'});
// await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 119065724, cache: true, upload: false});
// await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: '119065724', cache: true, upload: false});
// await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'nym-connect-v1.1.19-snickers', cache: true, upload: false});
// await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'Nym Connect v1.1.19-snickers', cache: true, upload: false});
+6 -3
View File
@@ -8,8 +8,8 @@ on:
required: true
type: string
workflow_dispatch:
inputs:
release_tag:
release_tag:
tag:
description: 'Release tag'
required: true
type: string
@@ -24,7 +24,10 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 18
- uses: nymtech/nym/.github/actions/nym-hash-releases@develop
- name: Install packages
run: cd ./.github/actions/nym-hash-releases && npm i
- uses: ./.github/actions/nym-hash-releases
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
Generated
+127 -169
View File
@@ -557,9 +557,9 @@ dependencies = [
"bitflags 1.3.2",
"bytes",
"futures-util",
"http 0.2.9",
"http-body 0.4.5",
"hyper 0.14.27",
"http",
"http-body",
"hyper",
"itoa",
"matchit",
"memchr",
@@ -587,8 +587,8 @@ dependencies = [
"async-trait",
"bytes",
"futures-util",
"http 0.2.9",
"http-body 0.4.5",
"http",
"http-body",
"mime",
"rustversion",
"tower-layer",
@@ -1429,6 +1429,14 @@ dependencies = [
"thiserror",
]
[[package]]
name = "cpu-cycles"
version = "0.1.0"
dependencies = [
"cfg-if",
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.2.9"
@@ -1908,12 +1916,12 @@ dependencies = [
[[package]]
name = "darling"
version = "0.20.5"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8"
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
dependencies = [
"darling_core 0.20.5",
"darling_macro 0.20.5",
"darling_core 0.20.3",
"darling_macro 0.20.3",
]
[[package]]
@@ -1946,9 +1954,9 @@ dependencies = [
[[package]]
name = "darling_core"
version = "0.20.5"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3"
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
dependencies = [
"fnv",
"ident_case",
@@ -1982,11 +1990,11 @@ dependencies = [
[[package]]
name = "darling_macro"
version = "0.20.5"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77"
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
"darling_core 0.20.5",
"darling_core 0.20.3",
"quote",
"syn 2.0.38",
]
@@ -3011,7 +3019,7 @@ dependencies = [
"futures-core",
"futures-sink",
"gloo-utils",
"http 0.2.9",
"http",
"js-sys",
"pin-project",
"serde",
@@ -3080,7 +3088,7 @@ dependencies = [
"futures-core",
"futures-sink",
"futures-util",
"http 0.2.9",
"http",
"indexmap 1.9.3",
"slab",
"tokio",
@@ -3290,17 +3298,6 @@ dependencies = [
"itoa",
]
[[package]]
name = "http"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-api-client"
version = "0.1.0"
@@ -3322,30 +3319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
dependencies = [
"bytes",
"http 0.2.9",
"pin-project-lite 0.2.13",
]
[[package]]
name = "http-body"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
dependencies = [
"bytes",
"http 1.1.0",
]
[[package]]
name = "http-body-util"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
dependencies = [
"bytes",
"futures-core",
"http 1.1.0",
"http-body 1.0.0",
"http",
"pin-project-lite 0.2.13",
]
@@ -3413,8 +3387,8 @@ dependencies = [
"futures-core",
"futures-util",
"h2",
"http 0.2.9",
"http-body 0.4.5",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
@@ -3426,25 +3400,6 @@ dependencies = [
"want",
]
[[package]]
name = "hyper"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http 1.1.0",
"http-body 1.0.0",
"httparse",
"httpdate",
"itoa",
"pin-project-lite 0.2.13",
"smallvec",
"tokio",
]
[[package]]
name = "hyper-rustls"
version = "0.24.1"
@@ -3452,8 +3407,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97"
dependencies = [
"futures-util",
"http 0.2.9",
"hyper 0.14.27",
"http",
"hyper",
"rustls 0.21.10",
"tokio",
"tokio-rustls 0.24.1",
@@ -3465,28 +3420,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
dependencies = [
"hyper 0.14.27",
"hyper",
"pin-project-lite 0.2.13",
"tokio",
"tokio-io-timeout",
]
[[package]]
name = "hyper-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
dependencies = [
"bytes",
"futures-util",
"http 1.1.0",
"http-body 1.0.0",
"hyper 1.2.0",
"pin-project-lite 0.2.13",
"socket2 0.5.4",
"tokio",
]
[[package]]
name = "iana-time-zone"
version = "0.1.58"
@@ -3811,7 +3750,7 @@ dependencies = [
"curl-sys",
"event-listener",
"futures-lite",
"http 0.2.9",
"http",
"log",
"once_cell",
"polling",
@@ -4624,7 +4563,7 @@ dependencies = [
"bytes",
"encoding_rs",
"futures-util",
"http 0.2.9",
"http",
"httparse",
"log",
"memchr",
@@ -5261,10 +5200,7 @@ dependencies = [
"dirs 4.0.0",
"futures",
"gloo-timers",
"http-body-util",
"humantime-serde",
"hyper 1.2.0",
"hyper-util",
"log",
"nym-bandwidth-controller",
"nym-config",
@@ -5273,13 +5209,13 @@ dependencies = [
"nym-explorer-client",
"nym-gateway-client",
"nym-gateway-requests",
"nym-metrics",
"nym-network-defaults",
"nym-nonexhaustive-delayqueue",
"nym-pemstore",
"nym-sphinx",
"nym-task",
"nym-topology",
"nym-topology-control",
"nym-validator-client",
"rand 0.7.3",
"reqwest",
@@ -5587,7 +5523,7 @@ dependencies = [
"dotenvy",
"futures",
"humantime-serde",
"hyper 0.14.27",
"hyper",
"ipnetwork 0.16.0",
"log",
"nym-api-requests",
@@ -5603,10 +5539,13 @@ dependencies = [
"nym-network-defaults",
"nym-network-requester",
"nym-node",
"nym-noise",
"nym-pemstore",
"nym-sphinx",
"nym-statistics-common",
"nym-task",
"nym-topology",
"nym-topology-control",
"nym-types",
"nym-validator-client",
"nym-wireguard",
@@ -5678,9 +5617,7 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
"tokio",
"tungstenite",
"wasmtimer",
"zeroize",
]
@@ -5784,26 +5721,22 @@ dependencies = [
"url",
]
[[package]]
name = "nym-metrics"
version = "0.1.0"
dependencies = [
"dashmap",
"lazy_static",
"log",
"prometheus",
]
[[package]]
name = "nym-mixnet-client"
version = "0.1.0"
dependencies = [
"futures",
"log",
"nym-crypto",
"nym-noise",
"nym-sphinx",
"nym-task",
"nym-topology-control",
"nym-validator-client",
"rand 0.7.3",
"tokio",
"tokio-util",
"url",
]
[[package]]
@@ -5834,22 +5767,23 @@ dependencies = [
"anyhow",
"axum",
"bs58 0.5.0",
"cfg-if",
"clap 4.4.7",
"colored",
"cpu-cycles",
"cupid",
"dirs 4.0.0",
"futures",
"humantime-serde",
"lazy_static",
"log",
"nym-bin-common",
"nym-config",
"nym-contracts-common",
"nym-crypto",
"nym-metrics",
"nym-mixnet-client",
"nym-mixnode-common",
"nym-node",
"nym-noise",
"nym-nonexhaustive-delayqueue",
"nym-pemstore",
"nym-sphinx",
@@ -5857,8 +5791,10 @@ dependencies = [
"nym-sphinx-types",
"nym-task",
"nym-topology",
"nym-topology-control",
"nym-types",
"nym-validator-client",
"opentelemetry",
"rand 0.7.3",
"serde",
"serde_json",
@@ -5867,6 +5803,7 @@ dependencies = [
"tokio",
"tokio-util",
"toml 0.5.11",
"tracing",
"url",
]
@@ -5875,12 +5812,13 @@ name = "nym-mixnode-common"
version = "0.1.0"
dependencies = [
"bytes",
"cfg-if",
"cpu-cycles",
"futures",
"humantime-serde",
"log",
"nym-bin-common",
"nym-crypto",
"nym-metrics",
"nym-network-defaults",
"nym-sphinx-acknowledgements",
"nym-sphinx-addressing",
@@ -5895,6 +5833,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-util",
"tracing",
"url",
]
@@ -6022,7 +5961,7 @@ dependencies = [
"dashmap",
"fastrand 2.0.1",
"hmac 0.12.1",
"hyper 0.14.27",
"hyper",
"ipnetwork 0.16.0",
"mime",
"nym-config",
@@ -6103,6 +6042,24 @@ dependencies = [
"wasmtimer",
]
[[package]]
name = "nym-noise"
version = "0.1.0"
dependencies = [
"bytes",
"futures",
"log",
"nym-crypto",
"nym-topology",
"pin-project",
"semver 0.11.0",
"sha2 0.10.8",
"snow",
"thiserror",
"tokio",
"tokio-util",
]
[[package]]
name = "nym-nonexhaustive-delayqueue"
version = "0.1.0"
@@ -6175,7 +6132,7 @@ dependencies = [
"dotenvy",
"futures",
"hex",
"http 0.2.9",
"http",
"httpcodec",
"libp2p",
"log",
@@ -6195,6 +6152,7 @@ dependencies = [
"nym-sphinx",
"nym-task",
"nym-topology",
"nym-topology-control",
"nym-validator-client",
"parking_lot 0.12.1",
"pretty_env_logger",
@@ -6260,6 +6218,7 @@ dependencies = [
"nym-socks5-client-core",
"nym-sphinx",
"nym-topology",
"nym-topology-control",
"rand 0.7.3",
"serde",
"serde_json",
@@ -6572,6 +6531,29 @@ dependencies = [
"wasm-utils",
]
[[package]]
name = "nym-topology-control"
version = "0.1.0"
dependencies = [
"async-trait",
"futures",
"gloo-timers",
"log",
"nym-explorer-client",
"nym-network-defaults",
"nym-sphinx",
"nym-task",
"nym-topology",
"nym-validator-client",
"rand 0.7.3",
"serde",
"tap",
"tokio",
"tokio-stream",
"url",
"wasmtimer",
]
[[package]]
name = "nym-tun"
version = "0.1.0"
@@ -6887,9 +6869,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.98"
version = "0.9.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d"
dependencies = [
"cc",
"libc",
@@ -6915,7 +6897,7 @@ checksum = "a819b71d6530c4297b49b3cae2939ab3a8cc1b9f382826a1bc29dd0ca3864906"
dependencies = [
"async-trait",
"bytes",
"http 0.2.9",
"http",
"isahc",
"opentelemetry_api",
]
@@ -6929,7 +6911,7 @@ dependencies = [
"async-trait",
"futures",
"futures-executor",
"http 0.2.9",
"http",
"isahc",
"once_cell",
"opentelemetry",
@@ -7514,21 +7496,6 @@ dependencies = [
"yansi",
]
[[package]]
name = "prometheus"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c"
dependencies = [
"cfg-if",
"fnv",
"lazy_static",
"memchr",
"parking_lot 0.12.1",
"protobuf",
"thiserror",
]
[[package]]
name = "prometheus-client"
version = "0.19.0"
@@ -7650,17 +7617,11 @@ dependencies = [
"prost 0.12.1",
]
[[package]]
name = "protobuf"
version = "2.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
[[package]]
name = "psl"
version = "2.1.22"
version = "2.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc74a6e6a56708be1cf5c4c4d1a0dc21d33b2dcaa24e731b7fa9c287ce4f916f"
checksum = "fa35143bed048dcb22457ef82f8ba3008b842e9158e2cfcc904f5a4e2571cd4c"
dependencies = [
"psl-types",
]
@@ -8056,13 +8017,13 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.3"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.6",
"regex-automata 0.4.3",
"regex-syntax 0.8.2",
]
@@ -8077,9 +8038,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.6"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
@@ -8110,9 +8071,9 @@ dependencies = [
"futures-core",
"futures-util",
"h2",
"http 0.2.9",
"http-body 0.4.5",
"hyper 0.14.27",
"http",
"http-body",
"hyper",
"hyper-rustls",
"ipnet",
"js-sys",
@@ -8271,7 +8232,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfac3a1df83f8d4fc96aa41dba3b86c786417b7fc0f52ec76295df2ba781aa69"
dependencies = [
"http 0.2.9",
"http",
"log",
"regex",
"rocket",
@@ -8291,8 +8252,8 @@ dependencies = [
"cookie",
"either",
"futures",
"http 0.2.9",
"hyper 0.14.27",
"http",
"hyper",
"indexmap 2.0.2",
"log",
"memchr",
@@ -8902,9 +8863,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.6.0"
version = "3.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b0ed1662c5a68664f45b76d18deb0e234aff37207086803165c961eb695e981"
checksum = "f5c9fdb6b00a489875b22efd4b78fe2b363b72265cc5f6eb2e2b9ee270e6140c"
dependencies = [
"base64 0.21.4",
"chrono",
@@ -8919,11 +8880,11 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.6.0"
version = "3.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "568577ff0ef47b879f736cd66740e022f3672788cdf002a05a4e609ea5a6fb15"
checksum = "dbff351eb4b33600a2e138dfa0b10b65a238ea8ff8fb2387c422c5022a3e8298"
dependencies = [
"darling 0.20.5",
"darling 0.20.3",
"proc-macro2",
"quote",
"syn 2.0.38",
@@ -9096,9 +9057,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.13.1"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
[[package]]
name = "snafu"
@@ -9947,10 +9908,7 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
dependencies = [
"futures-util",
"log",
"rustls 0.21.10",
"rustls-native-certs",
"tokio",
"tokio-rustls 0.24.1",
"tungstenite",
]
@@ -10053,9 +10011,9 @@ dependencies = [
"futures-core",
"futures-util",
"h2",
"http 0.2.9",
"http-body 0.4.5",
"hyper 0.14.27",
"http",
"http-body",
"hyper",
"hyper-timeout",
"percent-encoding",
"pin-project",
@@ -10098,8 +10056,8 @@ dependencies = [
"bytes",
"futures-core",
"futures-util",
"http 0.2.9",
"http-body 0.4.5",
"http",
"http-body",
"http-range-header",
"httpdate",
"mime",
@@ -10377,7 +10335,7 @@ dependencies = [
"byteorder",
"bytes",
"data-encoding",
"http 0.2.9",
"http",
"httparse",
"log",
"rand 0.8.5",
+7 -20
View File
@@ -32,7 +32,7 @@ members = [
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common",
# "common/cosmwasm-smart-contracts/ephemera",
# "common/cosmwasm-smart-contracts/ephemera",
"common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
@@ -57,7 +57,6 @@ members = [
"common/nonexhaustive-delayqueue",
"common/nymcoconut",
"common/nym-id",
"common/nym-metrics",
"common/nymsphinx",
"common/nymsphinx/acknowledgements",
"common/nymsphinx/addressing",
@@ -113,10 +112,9 @@ members = [
"tools/nymvisor",
"tools/ts-rs-cli",
"wasm/client",
# "wasm/full-nym-wasm",
# "wasm/full-nym-wasm",
"wasm/mix-fetch",
"wasm/node-tester",
"common/nym-metrics",
]
default-members = [
@@ -132,16 +130,7 @@ default-members = [
"nym-validator-rewarder",
]
exclude = [
"explorer",
"contracts",
"nym-wallet",
"nym-connect/mobile/src-tauri",
"nym-connect/desktop",
"nym-vpn/ui/src-tauri",
"cpu-cycles",
"sdk/ffi/cpp",
]
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "nym-vpn/ui/src-tauri", "cpu-cycles", "sdk/ffi/cpp"]
[workspace.package]
authors = ["Nym Technologies SA"]
@@ -172,7 +161,7 @@ log = "0.4"
once_cell = "1.7.2"
parking_lot = "0.12.1"
rand = "0.8.5"
reqwest = { version = "0.11.22", default-features = false }
reqwest = { version = "0.11.22", default_features = false }
schemars = "0.8.1"
serde = "1.0.152"
serde_json = "1.0.91"
@@ -191,12 +180,10 @@ utoipa-swagger-ui = "3.1.5"
url = "2.4"
zeroize = "1.6.0"
prometheus = { version = "0.13.0" }
# coconut/DKG related
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch = "feature/gt-serialization-0.8.0" }
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch ="feature/gt-serialization-0.8.0" }
group = "0.13.0"
ff = "0.13.0"
@@ -221,9 +208,9 @@ cw-controllers = { version = "=1.1.0" }
bip32 = "0.5.1"
# temporarily using a fork again (yay.) because we need staking and slashing support
cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch = "nym-temp/all-validator-features" }
cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch ="nym-temp/all-validator-features" }
#cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch = "nym-temp/all-validator-features" } # unfortuntely we need a fork by yours truly to get the staking support
tendermint = "0.34" # same version as used by cosmrs
tendermint = "0.34" # same version as used by cosmrs
tendermint-rpc = "0.34" # same version as used by cosmrs
prost = "0.12"
@@ -1667,9 +1667,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"dev": true,
"funding": [
{
@@ -1705,9 +1705,9 @@
}
},
"node_modules/fs-monkey": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz",
"integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
"integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
"dev": true
},
"node_modules/fs.realpath": {
@@ -2430,12 +2430,12 @@
}
},
"node_modules/memfs": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
"integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
"dev": true,
"dependencies": {
"fs-monkey": "^1.0.4"
"fs-monkey": "1.0.3"
},
"engines": {
"node": ">= 4.0.0"
@@ -4047,13 +4047,13 @@
}
},
"node_modules/webpack-dev-middleware": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
"integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz",
"integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==",
"dev": true,
"dependencies": {
"colorette": "^2.0.10",
"memfs": "^3.4.3",
"memfs": "^3.4.1",
"mime-types": "^2.1.31",
"range-parser": "^1.2.1",
"schema-utils": "^4.0.0"
@@ -5800,9 +5800,9 @@
}
},
"follow-redirects": {
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"dev": true
},
"forwarded": {
@@ -5818,9 +5818,9 @@
"dev": true
},
"fs-monkey": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz",
"integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
"integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
"dev": true
},
"fs.realpath": {
@@ -6346,12 +6346,12 @@
"dev": true
},
"memfs": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
"integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
"dev": true,
"requires": {
"fs-monkey": "^1.0.4"
"fs-monkey": "1.0.3"
}
},
"merge-descriptors": {
@@ -7547,13 +7547,13 @@
}
},
"webpack-dev-middleware": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
"integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz",
"integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==",
"dev": true,
"requires": {
"colorette": "^2.0.10",
"memfs": "^3.4.3",
"memfs": "^3.4.1",
"mime-types": "^2.1.31",
"range-parser": "^1.2.1",
"schema-utils": "^4.0.0"
+1
View File
@@ -34,6 +34,7 @@ nym-sphinx = { path = "../../common/nymsphinx" }
nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
nym-pemstore = { path = "../../common/pemstore" }
nym-topology = { path = "../../common/topology" }
nym-topology-control = { path = "../../common/topology-control" }
nym-socks5-client-core = { path = "../../common/socks5-client-core" }
nym-id = { path = "../../common/nym-id" }
+2 -2
View File
@@ -16,11 +16,11 @@ use nym_client_core::client::base_client::storage::gateway_details::{
OnDiskGatewayDetails, PersistedGatewayDetails,
};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
use nym_client_core::config::{GatewayEndpointConfig, GroupBy, TopologyStructure};
use nym_client_core::config::{GatewayEndpointConfig, TopologyStructure};
use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet;
use nym_sphinx::params::{PacketSize, PacketType};
use nym_topology_control::geo_aware_provider::{CountryGroup, GroupBy};
use std::error::Error;
use std::net::IpAddr;
use std::sync::OnceLock;
+1 -1
View File
@@ -12,9 +12,9 @@ use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_client_core::cli_helpers::client_run::CommonClientRunArgs;
use nym_client_core::client::base_client::storage::OnDiskPersistent;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
use nym_socks5_client_core::NymClient;
use nym_sphinx::addressing::clients::Recipient;
use nym_topology_control::geo_aware_provider::CountryGroup;
use std::net::IpAddr;
#[derive(Args, Clone)]
+4 -22
View File
@@ -27,7 +27,7 @@ tap = "1.0.1"
thiserror = { workspace = true }
url = { workspace = true, features = ["serde"] }
tungstenite = { workspace = true, default-features = false }
tokio = { workspace = true, features = ["macros"] }
tokio = { workspace = true, features = ["macros"]}
time = "0.3.17"
zeroize = { workspace = true }
@@ -38,30 +38,17 @@ nym-crypto = { path = "../crypto" }
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
nym-gateway-client = { path = "../client-libs/gateway-client" }
nym-gateway-requests = { path = "../../gateway/gateway-requests" }
nym-metrics = { path = "../nym-metrics" }
nym-nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
nym-sphinx = { path = "../nymsphinx" }
nym-pemstore = { path = "../pemstore" }
nym-topology = { path = "../topology", features = ["serializable"] }
nym-topology-control = { path = "../topology-control" }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-task = { path = "../task" }
nym-credential-storage = { path = "../credential-storage" }
nym-network-defaults = { path = "../network-defaults" }
si-scale = "0.2.2"
### For serving prometheus metrics
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper]
version = "1"
features = ["server", "http1"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.http-body-util]
version = "0.1"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.hyper-util]
version = "0.1"
features = ["tokio"]
###
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.11"
features = ["time"]
@@ -72,7 +59,6 @@ features = ["time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.20.1"
features = ["rustls-tls-native-roots"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
workspace = true
@@ -106,15 +92,11 @@ tempfile = "3.1.0"
[build-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
sqlx = { workspace = true, features = [
"runtime-tokio-rustls",
"sqlite",
"macros",
"migrate",
] }
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
[features]
default = []
cli = ["clap"]
fs-surb-storage = ["sqlx"]
wasm = ["nym-gateway-client/wasm"]
@@ -3,7 +3,6 @@
use super::packet_statistics_control::PacketStatisticsReporter;
use super::received_buffer::ReceivedBufferMessage;
use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider;
use crate::client::base_client::storage::gateway_details::GatewayDetailsStore;
use crate::client::base_client::storage::MixnetClientStorage;
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
@@ -22,10 +21,6 @@ use crate::client::replies::reply_controller::{ReplyControllerReceiver, ReplyCon
use crate::client::replies::reply_storage::{
CombinedReplyStorage, PersistentReplyStorage, ReplyStorageBackend, SentReplyKeys,
};
use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider;
use crate::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use crate::config::{Config, DebugConfig};
use crate::error::ClientCoreError;
use crate::init::{
@@ -50,6 +45,9 @@ use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender,
use nym_task::{TaskClient, TaskHandle};
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::HardcodedTopologyProvider;
use nym_topology_control::geo_aware_provider::GeoAwareTopologyProvider;
use nym_topology_control::nym_api_provider::NymApiTopologyProvider;
use nym_topology_control::{TopologyAccessor, TopologyRefresher, TopologyRefresherConfig};
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::fmt::Debug;
use std::os::raw::c_int as RawFd;
@@ -3,7 +3,6 @@
use crate::client::mix_traffic::BatchMixMessageSender;
use crate::client::packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter};
use crate::client::topology_control::TopologyAccessor;
use crate::{config, spawn_future};
use futures::task::{Context, Poll};
use futures::{Future, Stream, StreamExt};
@@ -13,6 +12,7 @@ use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::cover::generate_loop_cover_packet;
use nym_sphinx::params::{PacketSize, PacketType};
use nym_sphinx::utils::sample_poisson_duration;
use nym_topology_control::TopologyAccessor;
use rand::{rngs::OsRng, CryptoRng, Rng};
use std::pin::Pin;
use std::sync::Arc;
@@ -300,7 +300,7 @@ impl KeyManager {
/// Gets an atomically reference counted pointer to [`SharedKey`].
pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
self.gateway_shared_key.clone()
self.gateway_shared_key.as_ref().map(Arc::clone)
}
pub fn remove_gateway_key(self) -> KeyManagerBuilder {
-1
View File
@@ -11,5 +11,4 @@ pub(crate) mod packet_statistics_control;
pub mod real_messages_control;
pub mod received_buffer;
pub mod replies;
pub mod topology_control;
pub(crate) mod transmission_buffer;
@@ -3,30 +3,8 @@ use std::{
time::{Duration, Instant},
};
use log::{info, warn};
use nym_metrics::{inc, inc_by, metrics};
use si_scale::helpers::bibytes2;
// Metrics server
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use http_body_util::Full;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use hyper::body::Bytes;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use hyper::server::conn::http1;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use hyper::service::service_fn;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use hyper::{Request, Response};
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use hyper_util::rt::TokioIo;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use std::convert::Infallible;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use std::net::SocketAddr;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use tokio::net::TcpListener;
use crate::spawn_future;
// Time interval between reporting packet statistics
@@ -75,60 +53,42 @@ impl PacketStatistics {
PacketStatisticsEvent::RealPacketSent(packet_size) => {
self.real_packets_sent += 1;
self.real_packets_sent_size += packet_size;
inc!("real_packets_sent");
inc_by!("real_packets_sent_size", packet_size);
}
PacketStatisticsEvent::CoverPacketSent(packet_size) => {
self.cover_packets_sent += 1;
self.cover_packets_sent_size += packet_size;
inc!("cover_packets_sent");
inc_by!("cover_packets_sent_size", packet_size);
}
PacketStatisticsEvent::RealPacketReceived(packet_size) => {
self.real_packets_received += 1;
self.real_packets_received_size += packet_size;
inc!("real_packets_received");
inc_by!("real_packets_received_size", packet_size);
}
PacketStatisticsEvent::CoverPacketReceived(packet_size) => {
self.cover_packets_received += 1;
self.cover_packets_received_size += packet_size;
inc!("cover_packets_received");
inc_by!("cover_packets_received_size", packet_size);
}
PacketStatisticsEvent::AckReceived(packet_size) => {
self.total_acks_received += 1;
self.total_acks_received_size += packet_size;
inc!("total_acks_received");
inc_by!("total_acks_received_size", packet_size);
}
PacketStatisticsEvent::RealAckReceived(packet_size) => {
self.real_acks_received += 1;
self.real_acks_received_size += packet_size;
inc!("real_acks_received");
inc_by!("real_acks_received_size", packet_size);
}
PacketStatisticsEvent::CoverAckReceived(packet_size) => {
self.cover_acks_received += 1;
self.cover_acks_received_size += packet_size;
inc!("cover_acks_received");
inc_by!("cover_acks_received_size", packet_size);
}
PacketStatisticsEvent::RealPacketQueued => {
self.real_packets_queued += 1;
inc!("real_packets_queued");
}
PacketStatisticsEvent::RetransmissionQueued => {
self.retransmissions_queued += 1;
inc!("retransmissions_queued");
}
PacketStatisticsEvent::ReplySurbRequestQueued => {
self.reply_surbs_queued += 1;
inc!("reply_surbs_queued");
}
PacketStatisticsEvent::AdditionalReplySurbRequestQueued => {
self.additional_reply_surbs_queued += 1;
inc!("additional_reply_surbs_queued");
}
}
}
@@ -505,33 +465,6 @@ impl PacketStatisticsControl {
let snapshot_interval = Duration::from_millis(SNAPSHOT_INTERVAL_MS);
let mut snapshot_interval = tokio::time::interval(snapshot_interval);
cfg_if::cfg_if! {
if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
log::warn!("Metrics server is not supported on wasm32-unknown-unknown");
let listener = None;
} else {
let mut metrics_port = 18000;
let listener: Option<TcpListener>;
loop {
let addr = SocketAddr::from(([0, 0, 0, 0], metrics_port));
match TcpListener::bind(addr).await {
Ok(l) => {
info!("###############################");
info!("Metrics endpoint is at: {:?}", l.local_addr());
info!("###############################");
listener = Some(l);
break;
},
Err(err) => {
log::warn!("Failed to bind metrics server: {:?}", err);
metrics_port += 1;
}
};
}
}
}
loop {
tokio::select! {
stats_event = self.stats_rx.recv() => match stats_event {
@@ -544,27 +477,6 @@ impl PacketStatisticsControl {
break;
}
},
// conditional will disable the branch if we're in wasm32-unknown-unknown
result = listener.as_ref().unwrap().accept(), if listener.is_some() => {
cfg_if::cfg_if! {
if #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] {
if let Ok((stream, _)) = result {
let io = TokioIo::new(stream);
tokio::task::spawn(async move {
if let Err(err) = http1::Builder::new()
.serve_connection(io, service_fn(serve_metrics))
.await
{
warn!("Error serving connection: {:?}", err);
}
});
} else {
warn!("Error accepting connection");
}
}
}
}
_ = snapshot_interval.tick() => {
self.update_history();
self.update_rates();
@@ -589,9 +501,3 @@ impl PacketStatisticsControl {
})
}
}
async fn serve_metrics(
_: Request<hyper::body::Incoming>,
) -> Result<Response<Full<Bytes>>, Infallible> {
Ok(Response::new(Full::new(Bytes::from(metrics!()))))
}
@@ -7,7 +7,6 @@ use crate::client::real_messages_control::real_traffic_stream::{
};
use crate::client::real_messages_control::{AckActionSender, Action};
use crate::client::replies::reply_storage::{ReceivedReplySurbsMap, SentReplyKeys, UsedSenderTags};
use crate::client::topology_control::{TopologyAccessor, TopologyReadPermit};
use log::{debug, error, info, trace, warn};
use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient;
@@ -20,6 +19,7 @@ use nym_sphinx::preparer::{MessagePreparer, PreparedFragment};
use nym_sphinx::Delay;
use nym_task::connections::TransmissionLane;
use nym_topology::{NymTopology, NymTopologyError};
use nym_topology_control::{TopologyAccessor, TopologyReadPermit};
use rand::{CryptoRng, Rng};
use std::collections::HashMap;
use std::sync::Arc;
@@ -17,7 +17,6 @@ use crate::{
client::{
inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender,
real_messages_control::acknowledgement_control::AcknowledgementControllerConnectors,
topology_control::TopologyAccessor,
},
spawn_future,
};
@@ -28,6 +27,7 @@ use nym_sphinx::acknowledgements::AckKey;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType;
use nym_task::connections::{ConnectionCommandReceiver, LaneQueueLengths};
use nym_topology_control::TopologyAccessor;
use rand::{rngs::OsRng, CryptoRng, Rng};
use std::sync::Arc;
@@ -5,7 +5,6 @@ use self::sending_delay_controller::SendingDelayController;
use crate::client::mix_traffic::BatchMixMessageSender;
use crate::client::packet_statistics_control::{PacketStatisticsEvent, PacketStatisticsReporter};
use crate::client::real_messages_control::acknowledgement_control::SentPacketNotificationSender;
use crate::client::topology_control::TopologyAccessor;
use crate::client::transmission_buffer::TransmissionBuffer;
use crate::config;
use futures::task::{Context, Poll};
@@ -22,6 +21,7 @@ use nym_sphinx::utils::sample_poisson_duration;
use nym_task::connections::{
ConnectionCommand, ConnectionCommandReceiver, ConnectionId, LaneQueueLengths, TransmissionLane,
};
use nym_topology_control::TopologyAccessor;
use rand::{CryptoRng, Rng};
use std::pin::Pin;
use std::sync::Arc;
+3 -21
View File
@@ -1,14 +1,12 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{client::topology_control::geo_aware_provider::CountryGroup, error::ClientCoreError};
use crate::error::ClientCoreError;
use nym_config::defaults::NymNetworkDetails;
use nym_crypto::asymmetric::identity;
use nym_gateway_client::client::GatewayConfig;
use nym_sphinx::{
addressing::clients::Recipient,
params::{PacketSize, PacketType},
};
use nym_sphinx::params::{PacketSize, PacketType};
use nym_topology_control::geo_aware_provider::GroupBy;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -541,22 +539,6 @@ pub enum TopologyStructure {
GeoAware(GroupBy),
}
#[allow(clippy::large_enum_variant)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum GroupBy {
CountryGroup(CountryGroup),
NymAddress(Recipient),
}
impl std::fmt::Display for GroupBy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupBy::CountryGroup(group) => write!(f, "group: {}", group),
GroupBy::NymAddress(address) => write!(f, "address: {}", address),
}
}
}
impl Default for Topology {
fn default() -> Self {
Topology {
@@ -1,15 +1,15 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::topology_control::geo_aware_provider::CountryGroup;
use crate::config::{
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection, GroupBy,
ReplySurbs, Topology, TopologyStructure, Traffic,
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection, ReplySurbs,
Topology, TopologyStructure, Traffic,
};
use nym_sphinx::{
addressing::clients::Recipient,
params::{PacketSize, PacketType},
};
use nym_topology_control::geo_aware_provider::{CountryGroup, GroupBy};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -49,7 +49,6 @@ workspace = true
# the choice of this particular tls feature was arbitrary;
# if you reckon a different one would be more appropriate, feel free to change it
# features = ["native-tls"]
features = ["rustls-tls-native-roots"]
# wasm-only dependencies
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
@@ -20,7 +20,7 @@ use nym_gateway_requests::authentication::encrypted_address::EncryptedAddressByt
use nym_gateway_requests::iv::IV;
use nym_gateway_requests::registration::handshake::{client_handshake, SharedKeys};
use nym_gateway_requests::{
BinaryRequest, ClientControlRequest, ServerResponse, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION,
BinaryRequest, ClientControlRequest, ServerResponse, CREDENTIAL_UPDATE_V1_PROTOCOL_VERSION,
CURRENT_PROTOCOL_VERSION,
};
use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
@@ -438,7 +438,6 @@ impl<C, St> GatewayClient<C, St> {
ws_stream,
self.local_identity.as_ref(),
self.gateway_identity,
!self.disabled_credentials_mode,
)
.await
.map_err(GatewayClientError::RegistrationFailure),
@@ -495,13 +494,8 @@ impl<C, St> GatewayClient<C, St> {
.derive_destination_address();
let encrypted_address = EncryptedAddressBytes::new(&self_address, shared_key, &iv);
let msg = ClientControlRequest::new_authenticate(
self_address,
encrypted_address,
iv,
!self.disabled_credentials_mode,
)
.into();
let msg =
ClientControlRequest::new_authenticate(self_address, encrypted_address, iv).into();
match self.send_websocket_message(msg).await? {
ServerResponse::Authenticate {
@@ -605,7 +599,7 @@ impl<C, St> GatewayClient<C, St> {
});
};
if gateway_protocol < CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION {
if gateway_protocol < CREDENTIAL_UPDATE_V1_PROTOCOL_VERSION {
return Err(GatewayClientError::OutdatedGatewayCredentialVersion {
negotiated_protocol: Some(gateway_protocol),
});
@@ -69,10 +69,6 @@ impl PacketRouter {
}
Ok(())
}
pub fn mark_as_success(&mut self) {
self.shutdown.mark_as_success();
}
}
impl GatewayPacketRouter for PacketRouter {
@@ -44,7 +44,9 @@ pub(crate) fn ws_fd(_conn: &WsConn) -> Option<RawFd> {
#[cfg(unix)]
match _conn.get_ref() {
MaybeTlsStream::Plain(stream) => Some(stream.as_raw_fd()),
&_ => None,
&_ => unreachable!(
"If tls features are enabled, the inner stream needs to be unpacked into raw fd"
),
}
#[cfg(not(unix))]
None
@@ -97,7 +99,7 @@ impl PartiallyDelegated {
pub(crate) fn split_and_listen_for_mixnet_messages(
conn: WsConn,
mut packet_router: PacketRouter,
packet_router: PacketRouter,
shared_key: Arc<SharedKeys>,
mut shutdown: TaskClient,
) -> Self {
@@ -140,7 +142,6 @@ impl PartiallyDelegated {
if match ret_err {
Err(err) => stream_sender.send(Err(err)),
Ok(_) => {
packet_router.mark_as_success();
shutdown.mark_as_success();
stream_sender.send(Ok(stream))
}
@@ -16,3 +16,11 @@ tokio-util = { workspace = true, features = ["codec"] }
# internal
nym-sphinx = { path = "../../nymsphinx" }
nym-task = { path = "../../task" }
nym-topology-control = { path = "../../topology-control" }
nym-noise = { path = "../../nymnoise"}
nym-crypto = { path = "../../crypto" }
nym-validator-client = { path = "../validator-client"}
[dev-dependencies]
url = { workspace = true }
rand = "0.7.3"
+87 -22
View File
@@ -4,11 +4,15 @@
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use nym_crypto::asymmetric::encryption;
use nym_noise::upgrade_noise_initiator_with_topology;
use nym_sphinx::addressing::nodes::NymNodeRoutingAddress;
use nym_sphinx::framing::codec::NymCodec;
use nym_sphinx::framing::packet::FramedNymPacket;
use nym_sphinx::params::PacketType;
use nym_sphinx::NymPacket;
use nym_topology_control::accessor::TopologyAccessor;
use nym_validator_client::NymApiClient;
use std::collections::HashMap;
use std::io;
use std::net::SocketAddr;
@@ -59,6 +63,9 @@ pub trait SendWithoutResponse {
pub struct Client {
conn_new: HashMap<NymNodeRoutingAddress, ConnectionSender>,
config: Config,
topology_access: TopologyAccessor,
api_client: NymApiClient,
local_identity: Arc<encryption::KeyPair>,
}
struct ConnectionSender {
@@ -76,10 +83,18 @@ impl ConnectionSender {
}
impl Client {
pub fn new(config: Config) -> Client {
pub fn new(
config: Config,
topology_access: TopologyAccessor,
api_client: NymApiClient,
local_identity: Arc<encryption::KeyPair>,
) -> Client {
Client {
conn_new: HashMap::new(),
config,
topology_access,
api_client,
local_identity,
}
}
@@ -88,25 +103,60 @@ impl Client {
receiver: mpsc::Receiver<FramedNymPacket>,
connection_timeout: Duration,
current_reconnection: &AtomicU32,
topology_access: TopologyAccessor,
api_client: NymApiClient,
local_identity: Arc<encryption::KeyPair>,
) {
let connection_fut = TcpStream::connect(address);
let conn = match tokio::time::timeout(connection_timeout, connection_fut).await {
Ok(stream_res) => match stream_res {
Ok(stream) => {
debug!("Managed to establish connection to {}", address);
// if we managed to connect, reset the reconnection count (whatever it might have been)
current_reconnection.store(0, Ordering::Release);
Framed::new(stream, NymCodec)
Ok(stream_res) => {
match stream_res {
Ok(stream) => {
debug!("Managed to establish connection to {}", address);
// if we managed to connect, reset the reconnection count (whatever it might have been)
current_reconnection.store(0, Ordering::Release);
//Get the topology, because we need the keys for the handshake
let Some(topology) = topology_access.current_topology().await else {
error!("Cannot perform Noise handshake to {address}, due to topology error");
return;
};
let epoch_id = match api_client.get_current_epoch_id().await {
Ok(id) => id,
Err(err) => {
error!("Cannot perform Noise handshake to {address}, due to epoch id error - {err}");
return;
}
};
let noise_stream = match upgrade_noise_initiator_with_topology(
stream,
Default::default(),
&topology,
epoch_id,
local_identity.private_key(),
)
.await
{
Ok(noise_stream) => noise_stream,
Err(err) => {
error!("Failed to perform Noise handshake with {address} - {err}");
return;
}
};
debug!("Noise initiator handshake completed for {:?}", address);
Framed::new(noise_stream, NymCodec)
}
Err(err) => {
debug!(
"failed to establish connection to {} (err: {})",
address, err
);
return;
}
}
Err(err) => {
debug!(
"failed to establish connection to {} (err: {})",
address, err
);
return;
}
},
}
Err(_) => {
debug!(
"failed to connect to {} within {:?}",
@@ -175,6 +225,10 @@ impl Client {
// copy the value before moving into another task
let initial_connection_timeout = self.config.initial_connection_timeout;
let topology_access_clone = self.topology_access.clone();
let api_client_clone = self.api_client.clone();
let local_id_key = self.local_identity.clone();
tokio::spawn(async move {
// before executing the manager, wait for what was specified, if anything
if let Some(backoff) = backoff {
@@ -187,6 +241,9 @@ impl Client {
receiver,
initial_connection_timeout,
&current_reconnection_attempt,
topology_access_clone,
api_client_clone,
local_id_key,
)
.await
});
@@ -253,15 +310,23 @@ impl SendWithoutResponse for Client {
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::OsRng;
use url::Url;
fn dummy_client() -> Client {
Client::new(Config {
initial_reconnection_backoff: Duration::from_millis(10_000),
maximum_reconnection_backoff: Duration::from_millis(300_000),
initial_connection_timeout: Duration::from_millis(1_500),
maximum_connection_buffer_size: 128,
use_legacy_version: false,
})
let mut rng = OsRng;
Client::new(
Config {
initial_reconnection_backoff: Duration::from_millis(10_000),
maximum_reconnection_backoff: Duration::from_millis(300_000),
initial_connection_timeout: Duration::from_millis(1_500),
maximum_connection_buffer_size: 128,
use_legacy_version: false,
},
TopologyAccessor::new(),
NymApiClient::new(Url::parse("http://dummy.url").unwrap()),
Arc::new(encryption::KeyPair::new(&mut rng)),
)
}
#[test]
@@ -5,8 +5,11 @@ use crate::client::{Client, Config, SendWithoutResponse};
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use nym_crypto::asymmetric::encryption;
use nym_sphinx::forwarding::packet::MixPacket;
use std::time::Duration;
use nym_topology_control::accessor::TopologyAccessor;
use nym_validator_client::NymApiClient;
use std::sync::Arc;
pub type MixForwardingSender = mpsc::UnboundedSender<MixPacket>;
type MixForwardingReceiver = mpsc::UnboundedReceiver<MixPacket>;
@@ -21,26 +24,22 @@ pub struct PacketForwarder {
impl PacketForwarder {
pub fn new(
initial_reconnection_backoff: Duration,
maximum_reconnection_backoff: Duration,
initial_connection_timeout: Duration,
maximum_connection_buffer_size: usize,
use_legacy_version: bool,
client_config: Config,
topology_access: TopologyAccessor,
api_client: NymApiClient,
local_identity: Arc<encryption::KeyPair>,
shutdown: nym_task::TaskClient,
) -> (PacketForwarder, MixForwardingSender) {
let client_config = Config::new(
initial_reconnection_backoff,
maximum_reconnection_backoff,
initial_connection_timeout,
maximum_connection_buffer_size,
use_legacy_version,
);
let (packet_sender, packet_receiver) = mpsc::unbounded();
(
PacketForwarder {
mixnet_client: Client::new(client_config),
mixnet_client: Client::new(
client_config,
topology_access,
api_client,
local_identity,
),
packet_receiver,
shutdown,
},
@@ -13,7 +13,7 @@ use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, FreePassRequest, VerifyCredentialBody,
VerifyCredentialResponse,
};
use nym_api_requests::models::{DescribedGateway, MixNodeBondAnnotated};
use nym_api_requests::models::{DescribedGateway, DescribedNymNode, MixNodeBondAnnotated};
use nym_api_requests::models::{
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse,
@@ -291,6 +291,18 @@ impl NymApiClient {
Ok(self.nym_api.get_gateways_described().await?)
}
pub async fn get_cached_described_nodes(
&self,
) -> Result<Vec<DescribedNymNode>, ValidatorClientError> {
Ok(self.nym_api.get_nym_nodes_described().await?)
}
pub async fn get_current_epoch_id(
&self,
) -> Result<nym_mixnet_contract_common::EpochId, ValidatorClientError> {
Ok(self.nym_api.get_current_epoch().await?.current_epoch_id())
}
pub async fn get_gateway_core_status_count(
&self,
identity: IdentityKeyRef<'_>,
@@ -15,16 +15,16 @@ pub use nym_api_requests::{
VerifyCredentialBody, VerifyCredentialResponse,
},
models::{
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
StakeSaturationResponse, UptimeResponse,
ComputeRewardEstParam, DescribedGateway, DescribedNymNode, GatewayBondAnnotated,
GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
InclusionProbabilityResponse, MixNodeBondAnnotated, MixnodeCoreStatusResponse,
MixnodeStatusReportResponse, MixnodeStatusResponse, MixnodeUptimeHistoryResponse,
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
},
};
pub use nym_coconut_dkg_common::types::EpochId;
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, Interval, MixId};
use nym_name_service_common::response::NamesListResponse;
use nym_service_provider_directory_common::response::ServicesListResponse;
@@ -97,6 +97,14 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_nym_nodes_described(&self) -> Result<Vec<DescribedNymNode>, NymAPIError> {
self.get_json(
&[routes::API_VERSION, routes::NYM_NODES, routes::DESCRIBED],
NO_PARAMS,
)
.await
}
async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
self.get_json(
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
@@ -144,6 +152,14 @@ pub trait NymApiClientExt: ApiClient {
.await
}
async fn get_current_epoch(&self) -> Result<Interval, NymAPIError> {
self.get_json(
&[routes::API_VERSION, routes::EPOCH, routes::CURRENT],
NO_PARAMS,
)
.await
}
async fn get_gateway_report(
&self,
identity: IdentityKeyRef<'_>,
@@ -6,8 +6,12 @@ use nym_network_defaults::NYM_API_VERSION;
pub const API_VERSION: &str = NYM_API_VERSION;
pub const MIXNODES: &str = "mixnodes";
pub const GATEWAYS: &str = "gateways";
pub const NYM_NODES: &str = "nym-nodes";
pub const DESCRIBED: &str = "described";
pub const EPOCH: &str = "epoch";
pub const CURRENT: &str = "current";
pub const DETAILED: &str = "detailed";
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
pub const ACTIVE: &str = "active";
+1 -1
View File
@@ -837,7 +837,7 @@ mod tests {
let share3 = chunks3.clone().try_into().unwrap();
let shares = vec![share1, share2, share3];
let chunks = &[chunks1, chunks2, chunks3];
let chunks = vec![chunks1, chunks2, chunks3];
for (i, pk_i) in pks.iter().enumerate() {
let mut ciphertext_chunk_i = Vec::with_capacity(NUM_CHUNKS);
+1 -8
View File
@@ -34,13 +34,6 @@ impl MultiIpPacketCodec {
}
}
pub fn bundle_one_packet(packet: Bytes) -> Bytes {
let mut bundled_packets = BytesMut::new();
bundled_packets.extend_from_slice(&(packet.len() as u16).to_be_bytes());
bundled_packets.extend_from_slice(&packet);
bundled_packets.freeze()
}
// Append a packet to the buffer and return the buffer if it's full
pub fn append_packet(&mut self, packet: Bytes) -> Option<Bytes> {
let mut bundled_packets = BytesMut::new();
@@ -54,7 +47,7 @@ impl MultiIpPacketCodec {
}
// Flush the current buffer and return it.
pub fn flush_current_buffer(&mut self) -> Bytes {
fn flush_current_buffer(&mut self) -> Bytes {
let mut output_buffer = BytesMut::new();
std::mem::swap(&mut output_buffer, &mut self.buffer);
output_buffer.freeze()
+4 -12
View File
@@ -2,22 +2,14 @@ use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::net::{Ipv4Addr, Ipv6Addr};
// The current version of the protocol.
// The idea here is that we add new request response types at least one version before we start
// using them.
// Also, depending on the version in the client connect message the IPR could respond with a
// matching older version.
pub use v6::request;
pub use v6::response;
pub mod codec;
pub mod v6;
pub mod request;
pub mod response;
// version 3: initial version
// version 4: IPv6 support
// version 5: Add severity level to info response
// version 6: Increase the available IPs
pub const CURRENT_VERSION: u8 = 6;
pub const CURRENT_VERSION: u8 = 5;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair {
@@ -33,7 +25,7 @@ impl IpPair {
impl Display for IpPair {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "IPv4: {}, IPv6: {}", self.ipv4, self.ipv6)
writeln!(f, "IPv4: {}, IPV6: {}", self.ipv4, self.ipv6)
}
}
@@ -83,34 +83,6 @@ impl IpPacketRequest {
}
}
pub fn new_ping(reply_to: Recipient) -> (Self, u64) {
let request_id = generate_random();
(
Self {
version: CURRENT_VERSION,
data: IpPacketRequestData::Ping(PingRequest {
request_id,
reply_to,
}),
},
request_id,
)
}
pub fn new_health_request(reply_to: Recipient) -> (Self, u64) {
let request_id = generate_random();
(
Self {
version: CURRENT_VERSION,
data: IpPacketRequestData::Health(HealthRequest {
request_id,
reply_to,
}),
},
request_id,
)
}
pub fn id(&self) -> Option<u64> {
match &self.data {
IpPacketRequestData::StaticConnect(request) => Some(request.request_id),
@@ -144,35 +144,6 @@ impl IpPacketResponse {
}
}
pub fn new_pong(request_id: u64, reply_to: Recipient) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::Pong(PongResponse {
request_id,
reply_to,
}),
}
}
pub fn new_health_response(
request_id: u64,
reply_to: Recipient,
build_info: nym_bin_common::build_information::BinaryBuildInformationOwned,
routable: Option<bool>,
) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::Health(HealthResponse {
request_id,
reply_to,
reply: HealthResponseReply {
build_info,
routable,
},
}),
}
}
pub fn id(&self) -> Option<u64> {
match &self.data {
IpPacketResponseData::StaticConnect(response) => Some(response.request_id),
-2
View File
@@ -1,2 +0,0 @@
pub mod request;
pub mod response;
+9 -1
View File
@@ -25,6 +25,9 @@ tokio-util = { workspace = true, features = ["codec"] }
url = { workspace = true }
thiserror = { workspace = true }
## tracing
tracing = { version = "0.1.37", optional = true }
nym-crypto = { path = "../crypto" }
nym-network-defaults = { path = "../network-defaults" }
nym-sphinx-acknowledgements = { path = "../nymsphinx/acknowledgements" }
@@ -36,4 +39,9 @@ nym-sphinx-types = { path = "../nymsphinx/types" }
nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client" }
nym-bin-common = { path = "../bin-common" }
nym-metrics = { path = "../nym-metrics" }
cfg-if = "1.0.0"
cpu-cycles = { path = "../../cpu-cycles", optional = true }
[features]
cpucycles = ["cpu-cycles", "tracing"]
+37
View File
@@ -2,3 +2,40 @@
// SPDX-License-Identifier: Apache-2.0
pub mod packet_processor;
pub mod verloc;
pub fn cpu_cycles() -> Result<i64, Box<dyn std::error::Error>> {
cfg_if::cfg_if! {
if #[cfg(feature = "cpucycles")] {
Ok(cpu_cycles::cpucycles()?)
} else {
Err("`cpucycles` feature is not turned on!".into())
}
}
}
#[macro_export]
macro_rules! measure {
( $x:expr ) => {{
cfg_if::cfg_if! {
if #[cfg(feature = "cpucycles")] {
let start_cycles = $crate::cpu_cycles();
// if the block needs to return something, we can return it
let r = $x;
let end_cycles = $crate::cpu_cycles();
let name = if let Some(meta) = tracing::Span::current().metadata() {
meta.name()
} else {
"measure"
};
match (start_cycles, end_cycles) {
(Ok(start), Ok(end)) => log::trace!("{} cpucycles: {}", name, end - start),
(Err(e), _) => error!("{e}"),
(_, Err(e)) => error!("{e}"),
}
r
} else {
$x
}
}
}};
}
@@ -1,9 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::measure;
use crate::packet_processor::error::MixProcessingError;
use log::*;
use nym_metrics::nanos;
use nym_sphinx_acknowledgements::surb_ack::SurbAck;
use nym_sphinx_addressing::nodes::NymNodeRoutingAddress;
use nym_sphinx_forwarding::packet::MixPacket;
@@ -15,6 +15,8 @@ use nym_sphinx_types::{
};
use std::convert::TryFrom;
use std::sync::Arc;
#[cfg(feature = "cpucycles")]
use tracing::instrument;
type ForwardAck = MixPacket;
@@ -49,11 +51,15 @@ impl SphinxPacketProcessor {
}
/// Performs a fresh sphinx unwrapping using no cache.
#[cfg_attr(
feature = "cpucycles",
instrument(skip(self, packet), fields(cpucycles))
)]
fn perform_initial_packet_processing(
&self,
packet: NymPacket,
) -> Result<NymProcessedPacket, MixProcessingError> {
nanos!("perform_initial_packet_processing", {
measure!({
packet.process(&self.sphinx_key).map_err(|err| {
debug!("Failed to unwrap NymPacket packet: {err}");
MixProcessingError::NymPacketProcessingError(err)
@@ -62,12 +68,17 @@ impl SphinxPacketProcessor {
}
/// Takes the received framed packet and tries to unwrap it from the sphinx encryption.
#[cfg_attr(
feature = "cpucycles",
instrument(skip(self, received), fields(cpucycles))
)]
fn perform_initial_unwrapping(
&self,
received: FramedNymPacket,
) -> Result<NymProcessedPacket, MixProcessingError> {
nanos!("perform_initial_unwrapping", {
measure!({
let packet = received.into_inner();
self.perform_initial_packet_processing(packet)
})
}
@@ -212,12 +223,16 @@ impl SphinxPacketProcessor {
}
}
#[cfg_attr(
feature = "cpucycles",
instrument(skip(self, received), fields(cpucycles))
)]
pub fn process_received(
&self,
received: FramedNymPacket,
) -> Result<MixProcessingResult, MixProcessingError> {
// explicit packet size will help to correctly parse final hop
nanos!("process_received", {
measure!({
let packet_size = received.packet_size();
let packet_type = received.packet_type();
+22 -27
View File
@@ -79,13 +79,7 @@ impl NymNetworkDetails {
pub fn new_from_env() -> Self {
fn get_optional_env<K: AsRef<OsStr>>(env: K) -> Option<String> {
match var(env) {
Ok(var) => {
if var.is_empty() {
None
} else {
Some(var)
}
}
Ok(var) => Some(var),
Err(VarError::NotPresent) => None,
err => panic!("Unable to set: {:?}", err),
}
@@ -119,15 +113,28 @@ impl NymNetworkDetails {
Some(var(var_names::NYM_API).expect("nym api not set")),
get_optional_env(var_names::NYXD_WEBSOCKET),
))
.with_mixnet_contract(get_optional_env(var_names::MIXNET_CONTRACT_ADDRESS))
.with_vesting_contract(get_optional_env(var_names::VESTING_CONTRACT_ADDRESS))
.with_coconut_bandwidth_contract(get_optional_env(
var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
.with_mixnet_contract(Some(
var(var_names::MIXNET_CONTRACT_ADDRESS).expect("mixnet contract not set"),
))
.with_vesting_contract(Some(
var(var_names::VESTING_CONTRACT_ADDRESS).expect("vesting contract not set"),
))
.with_coconut_bandwidth_contract(Some(
var(var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS)
.expect("coconut bandwidth contract not set"),
))
.with_group_contract(Some(
var(var_names::GROUP_CONTRACT_ADDRESS).expect("group contract not set"),
))
.with_multisig_contract(Some(
var(var_names::MULTISIG_CONTRACT_ADDRESS).expect("multisig contract not set"),
))
.with_coconut_dkg_contract(Some(
var(var_names::COCONUT_DKG_CONTRACT_ADDRESS).expect("coconut dkg contract not set"),
))
.with_ephemera_contract(Some(
var(var_names::EPHEMERA_CONTRACT_ADDRESS).expect("ephemera contract not set"),
))
.with_group_contract(get_optional_env(var_names::GROUP_CONTRACT_ADDRESS))
.with_multisig_contract(get_optional_env(var_names::MULTISIG_CONTRACT_ADDRESS))
.with_coconut_dkg_contract(get_optional_env(var_names::COCONUT_DKG_CONTRACT_ADDRESS))
.with_ephemera_contract(get_optional_env(var_names::EPHEMERA_CONTRACT_ADDRESS))
.with_service_provider_directory_contract(get_optional_env(
var_names::SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS,
))
@@ -178,12 +185,6 @@ impl NymNetworkDetails {
self
}
#[must_use]
pub fn with_chain_details(mut self, chain_details: ChainDetails) -> Self {
self.chain_details = chain_details;
self
}
#[must_use]
pub fn with_bech32_account_prefix<S: Into<String>>(mut self, prefix: S) -> Self {
self.chain_details.bech32_account_prefix = prefix.into();
@@ -226,12 +227,6 @@ impl NymNetworkDetails {
self
}
#[must_use]
pub fn with_contracts(mut self, contracts: NymContracts) -> Self {
self.contracts = contracts;
self
}
#[must_use]
pub fn with_mixnet_contract<S: Into<String>>(mut self, contract: Option<S>) -> Self {
self.contracts.mixnet_contract_address = contract.map(Into::into);
+5 -7
View File
@@ -16,13 +16,11 @@ pub const MIXNET_CONTRACT_ADDRESS: &str =
"n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr";
pub const VESTING_CONTRACT_ADDRESS: &str =
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
pub const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "";
pub const GROUP_CONTRACT_ADDRESS: &str = "";
pub const MULTISIG_CONTRACT_ADDRESS: &str = "";
pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "";
pub const EPHEMERA_CONTRACT_ADDRESS: &str = "";
pub const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const GROUP_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const MULTISIG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const EPHEMERA_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
-17
View File
@@ -1,17 +0,0 @@
[package]
name = "nym-metrics"
version = "0.1.0"
authors.workspace = true
repository.workspace = true
homepage.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
prometheus = { workspace = true }
log = { workspace = true }
dashmap = { workspace = true }
lazy_static = "1.4"
-278
View File
@@ -1,278 +0,0 @@
use dashmap::DashMap;
pub use log::error;
use log::{debug, warn};
use std::fmt;
pub use std::time::Instant;
use prometheus::{core::Collector, Counter, Encoder as _, Gauge, Registry, TextEncoder};
#[macro_export]
macro_rules! prepend_package_name {
($name: literal) => {
&format!(
"{}_{}",
std::module_path!()
.split("::")
.next()
.unwrap_or("x")
.to_string(),
$name
)
};
}
#[macro_export]
macro_rules! inc_by {
($name:literal, $x:expr) => {
$crate::REGISTRY.inc_by($crate::prepend_package_name!($name), $x as f64);
};
}
#[macro_export]
macro_rules! inc {
($name:literal) => {
$crate::REGISTRY.inc($crate::prepend_package_name!($name));
};
}
#[macro_export]
macro_rules! metrics {
() => {
$crate::REGISTRY.to_string();
};
}
#[macro_export]
macro_rules! nanos {
( $name:literal, $x:expr ) => {{
let start = $crate::Instant::now();
// if the block needs to return something, we can return it
let r = $x;
let duration = start.elapsed().as_nanos() as f64;
let name = $crate::prepend_package_name!($name);
$crate::REGISTRY.inc_by(&format!("{}_nanos", $name), duration);
r
}};
}
lazy_static::lazy_static! {
pub static ref REGISTRY: MetricsController = MetricsController::default();
}
#[derive(Default)]
pub struct MetricsController {
registry: Registry,
registry_index: DashMap<String, Metric>,
}
enum Metric {
C(Box<Counter>),
G(Box<Gauge>),
}
fn fq_name(c: &dyn Collector) -> String {
c.desc()
.first()
.map(|d| d.fq_name.clone())
.unwrap_or_default()
}
impl Metric {
#[inline(always)]
fn fq_name(&self) -> String {
match self {
Metric::C(c) => fq_name(c.as_ref()),
Metric::G(g) => fq_name(g.as_ref()),
}
}
#[inline(always)]
fn inc(&self) {
match self {
Metric::C(c) => c.inc(),
Metric::G(g) => g.inc(),
}
}
#[inline(always)]
fn inc_by(&self, value: f64) {
match self {
Metric::C(c) => c.inc_by(value),
Metric::G(g) => g.add(value),
}
}
#[inline(always)]
fn set(&self, value: f64) {
match self {
Metric::C(_c) => {
warn!("Cannot set value for counter {:?}", self.fq_name());
}
Metric::G(g) => g.set(value),
}
}
}
impl fmt::Display for MetricsController {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let metrics = self.gather();
let output = match String::from_utf8(metrics) {
Ok(output) => output,
Err(e) => return write!(f, "Error decoding metrics to String: {}", e),
};
write!(f, "{}", output)
}
}
impl MetricsController {
#[inline(always)]
pub fn gather(&self) -> Vec<u8> {
let mut buffer = vec![];
let encoder = TextEncoder::new();
let metrics = self.registry.gather();
match encoder.encode(&metrics, &mut buffer) {
Ok(_) => {}
Err(e) => error!("Error encoding metrics to buffer: {}", e),
}
buffer
}
pub fn to_writer(&self, writer: &mut dyn std::io::Write) {
let metrics = self.gather();
match writer.write_all(&metrics) {
Ok(_) => {}
Err(e) => error!("Error writing metrics to writer: {}", e),
}
}
pub fn set(&self, name: &str, value: f64) {
if let Some(metric) = self.registry_index.get(name) {
metric.set(value);
} else {
let gauge = match Gauge::new(sanitize_metric_name(name), name) {
Ok(g) => g,
Err(e) => {
debug!("Failed to create gauge {:?}:\n{}", name, e);
return;
}
};
self.register_gauge(Box::new(gauge));
self.set(name, value)
}
}
pub fn inc(&self, name: &str) {
if let Some(metric) = self.registry_index.get(name) {
metric.inc();
} else {
let counter = match Counter::new(sanitize_metric_name(name), name) {
Ok(c) => c,
Err(e) => {
debug!("Failed to create counter {:?}:\n{}", name, e);
return;
}
};
self.register_counter(Box::new(counter));
self.inc(name)
}
}
pub fn inc_by(&self, name: &str, value: f64) {
if let Some(metric) = self.registry_index.get(name) {
metric.inc_by(value);
} else {
let counter = match Counter::new(sanitize_metric_name(name), name) {
Ok(c) => c,
Err(e) => {
debug!("Failed to create counter {:?}:\n{}", name, e);
return;
}
};
self.register_counter(Box::new(counter));
self.inc_by(name, value)
}
}
fn register_gauge(&self, metric: Box<Gauge>) {
let fq_name = metric
.desc()
.first()
.map(|d| d.fq_name.clone())
.unwrap_or_default();
if self.registry_index.contains_key(&fq_name) {
return;
}
match self.registry.register(metric.clone()) {
Ok(_) => {
self.registry_index
.insert(fq_name, Metric::G(metric.clone()));
}
Err(e) => {
debug!("Failed to register {:?}:\n{}", fq_name, e)
}
}
}
fn register_counter(&self, metric: Box<Counter>) {
let fq_name = metric
.desc()
.first()
.map(|d| d.fq_name.clone())
.unwrap_or_default();
if self.registry_index.contains_key(&fq_name) {
return;
}
match self.registry.register(metric.clone()) {
Ok(_) => {
self.registry_index
.insert(fq_name, Metric::C(metric.clone()));
}
Err(e) => {
debug!("Failed to register {:?}:\n{}", fq_name, e)
}
}
}
}
fn sanitize_metric_name(name: &str) -> String {
// The first character must be [a-zA-Z_:], and all subsequent characters must be [a-zA-Z0-9_:].
let mut out = String::with_capacity(name.len());
let mut is_invalid: fn(char) -> bool = invalid_metric_name_start_character;
for c in name.chars() {
if is_invalid(c) {
out.push('_');
} else {
out.push(c);
}
is_invalid = invalid_metric_name_character;
}
out
}
#[inline]
fn invalid_metric_name_start_character(c: char) -> bool {
// Essentially, needs to match the regex pattern of [a-zA-Z_:].
!(c.is_ascii_alphabetic() || c == '_' || c == ':')
}
#[inline]
fn invalid_metric_name_character(c: char) -> bool {
// Essentially, needs to match the regex pattern of [a-zA-Z0-9_:].
!(c.is_ascii_alphanumeric() || c == '_' || c == ':')
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sanitization() {
assert_eq!(
sanitize_metric_name("packets_sent_34.242.65.133:1789"),
"packets_sent_34_242_65_133:1789"
)
}
}
+22
View File
@@ -0,0 +1,22 @@
[package]
name = "nym-noise"
version = "0.1.0"
authors = ["Simon Wicky <simon@nymtech.net>"]
edition = "2021"
license.workspace = true
[dependencies]
snow = "0.9.2"
futures = "0.3"
tokio = { version = "1.24.1", features = ["net","io-util","time"] }
tokio-util = { workspace = true, features = ["codec"] }
pin-project = "1"
log = "0.4.19"
sha2 = "0.10.7"
bytes = "1.0"
thiserror = "1.0.44"
semver = "0.11"
# internal
nym-topology = { path = "../topology"}
nym-crypto = { path = "../crypto" }
+72
View File
@@ -0,0 +1,72 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use std::io;
use pin_project::pin_project;
use tokio::{
io::{AsyncRead, AsyncWrite},
net::TcpStream,
};
use crate::stream::NoiseStream;
#[pin_project(project = ConnectionProj)]
pub enum Connection {
Tcp(#[pin] TcpStream),
Noise(#[pin] NoiseStream),
}
impl Connection {
pub fn peer_addr(&self) -> Result<std::net::SocketAddr, io::Error> {
match self {
Self::Noise(stream) => stream.peer_addr(),
Self::Tcp(stream) => stream.peer_addr(),
}
}
}
impl AsyncRead for Connection {
fn poll_read(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> std::task::Poll<io::Result<()>> {
match self.project() {
ConnectionProj::Noise(stream) => stream.poll_read(cx, buf),
ConnectionProj::Tcp(stream) => stream.poll_read(cx, buf),
}
}
}
impl AsyncWrite for Connection {
fn poll_write(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> std::task::Poll<Result<usize, io::Error>> {
match self.project() {
ConnectionProj::Noise(stream) => stream.poll_write(cx, buf),
ConnectionProj::Tcp(stream) => stream.poll_write(cx, buf),
}
}
fn poll_flush(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), io::Error>> {
match self.project() {
ConnectionProj::Noise(stream) => stream.poll_flush(cx),
ConnectionProj::Tcp(stream) => stream.poll_flush(cx),
}
}
fn poll_shutdown(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), io::Error>> {
match self.project() {
ConnectionProj::Noise(stream) => stream.poll_shutdown(cx),
ConnectionProj::Tcp(stream) => stream.poll_shutdown(cx),
}
}
}
+43
View File
@@ -0,0 +1,43 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use snow::Error;
use std::io;
use std::num::TryFromIntError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum NoiseError {
#[error("encountered a Noise decryption error")]
DecryptionError,
#[error("encountered a Noise Protocol error - {0}")]
ProtocolError(Error),
#[error("encountered an IO error - {0}")]
IoError(#[from] io::Error),
#[error("Incorrect state")]
IncorrectStateError,
#[error("Handshake timeout")]
HandshakeTimeoutError(#[from] tokio::time::error::Elapsed),
#[error("Handshake did not complete")]
HandshakeError,
#[error(transparent)]
IntConversionError(#[from] TryFromIntError),
#[error("unable to extract public key - {0}")]
EncryptionKeyConversionError(#[from] nym_crypto::asymmetric::encryption::KeyRecoveryError),
}
impl From<Error> for NoiseError {
fn from(err: Error) -> Self {
match err {
Error::Decrypt => NoiseError::DecryptionError,
err => NoiseError::ProtocolError(err),
}
}
}
+148
View File
@@ -0,0 +1,148 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::connection::Connection;
use crate::error::NoiseError;
use crate::stream::{NoisePattern, NoiseStream};
use log::*;
use nym_crypto::asymmetric::encryption;
use nym_topology::NymTopology;
use sha2::{Digest, Sha256};
use snow::{error::Prerequisite, Builder, Error};
use tokio::net::TcpStream;
pub mod connection;
pub mod error;
pub mod stream;
const NOISE_PSK_PREFIX: &[u8] = b"NYMTECH_NOISE_dQw4w9WgXcQ";
pub async fn upgrade_noise_initiator(
conn: TcpStream,
pattern: NoisePattern,
local_private_key: &encryption::PrivateKey,
remote_pub_key: &encryption::PublicKey,
epoch: u32,
) -> Result<Connection, NoiseError> {
trace!("Perform Noise Handshake, initiator side");
let secret = [
NOISE_PSK_PREFIX.to_vec(),
remote_pub_key.to_bytes().to_vec(),
epoch.to_be_bytes().to_vec(),
]
.concat();
let secret_hash = Sha256::digest(secret);
let handshake = Builder::new(pattern.as_str().parse()?)
.local_private_key(&local_private_key.to_bytes())
.remote_public_key(&remote_pub_key.to_bytes())
.psk(pattern.psk_position(), &secret_hash)
.build_initiator()?;
let noise_stream = NoiseStream::new(conn, handshake);
Ok(Connection::Noise(noise_stream.perform_handshake().await?))
}
pub async fn upgrade_noise_initiator_with_topology(
conn: TcpStream,
pattern: NoisePattern,
topology: &NymTopology,
epoch: u32,
local_private_key: &encryption::PrivateKey,
) -> Result<Connection, NoiseError> {
//Get init material
let responder_addr = conn.peer_addr().map_err(|err| {
error!("Unable to extract peer address from connection - {err}");
Error::Prereq(Prerequisite::RemotePublicKey)
})?;
let remote_pub_key = match topology.find_node_key_by_mix_host(responder_addr, true) {
Ok(Some(key)) => encryption::PublicKey::from_base58_string(key)?,
Ok(None) => {
warn!(
"{:?} can't speak Noise yet, falling back to TCP",
responder_addr
);
return Ok(Connection::Tcp(conn));
}
Err(_) => {
error!(
"Cannot find public key for node with address {:?}",
responder_addr
); //Do we still pursue a TCP connection or not?
return Err(Error::Prereq(Prerequisite::RemotePublicKey).into());
}
};
upgrade_noise_initiator(conn, pattern, local_private_key, &remote_pub_key, epoch).await
}
pub async fn upgrade_noise_responder(
conn: TcpStream,
pattern: NoisePattern,
local_public_key: &encryption::PublicKey,
local_private_key: &encryption::PrivateKey,
epoch: u32,
) -> Result<Connection, NoiseError> {
trace!("Perform Noise Handshake, responder side");
let secret = [
NOISE_PSK_PREFIX.to_vec(),
local_public_key.to_bytes().to_vec(),
epoch.to_be_bytes().to_vec(),
]
.concat();
let secret_hash = Sha256::digest(secret);
let handshake = Builder::new(pattern.as_str().parse()?)
.local_private_key(&local_private_key.to_bytes())
.psk(pattern.psk_position(), &secret_hash)
.build_responder()?;
let noise_stream = NoiseStream::new(conn, handshake);
Ok(Connection::Noise(noise_stream.perform_handshake().await?))
}
pub async fn upgrade_noise_responder_with_topology(
conn: TcpStream,
pattern: NoisePattern,
topology: &NymTopology,
epoch: u32,
local_public_key: &encryption::PublicKey,
local_private_key: &encryption::PrivateKey,
) -> Result<Connection, NoiseError> {
//Get init material
let initiator_addr = match conn.peer_addr() {
Ok(addr) => addr,
Err(err) => {
error!("Unable to extract peer address from connection - {err}");
return Err(Error::Prereq(Prerequisite::RemotePublicKey).into());
}
};
match topology.find_node_key_by_mix_host(initiator_addr, false) {
Ok(Some(_)) => {
//Existing node supporting Noise
upgrade_noise_responder(conn, pattern, local_public_key, local_private_key, epoch).await
}
Ok(None) => {
//Existing node not supporting Noise yet
warn!(
"{:?} can't speak Noise yet, falling back to TCP",
initiator_addr
);
Ok(Connection::Tcp(conn))
}
Err(_) => {
//Non existing node
error!(
"Cannot find public key for node with address {:?}",
initiator_addr
); //Do we still pursue a TCP connection with that node or not?
Err(Error::Prereq(Prerequisite::RemotePublicKey).into())
}
}
}
+220
View File
@@ -0,0 +1,220 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::error::NoiseError;
use bytes::BytesMut;
use futures::{Sink, SinkExt, Stream, StreamExt};
use pin_project::pin_project;
use snow::{HandshakeState, TransportState};
use std::cmp::min;
use std::collections::VecDeque;
use std::io;
use std::pin::Pin;
use std::task::Poll;
use tokio::{
io::{AsyncRead, AsyncWrite, ReadBuf},
net::TcpStream,
};
use tokio_util::codec::{Framed, LengthDelimitedCodec};
const MAXMSGLEN: usize = 65535;
const TAGLEN: usize = 16;
#[derive(Default)]
pub enum NoisePattern {
#[default]
XKpsk3,
IKpsk2,
}
impl NoisePattern {
pub(crate) fn as_str(&self) -> &'static str {
match self {
Self::XKpsk3 => "Noise_XKpsk3_25519_AESGCM_SHA256",
Self::IKpsk2 => "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s", //Wireguard handshake (not exactly though)
}
}
pub(crate) fn psk_position(&self) -> u8 {
//automatic parsing, works for correct pattern, more convenient
match self.as_str().find("psk") {
Some(n) => {
let psk_index = n + 3;
let psk_char = self.as_str().chars().nth(psk_index).unwrap();
psk_char.to_string().parse().unwrap()
//if this fails, it means hardcoded pattern are wrong
}
None => 0,
}
}
}
/// Wrapper around a TcpStream
#[pin_project]
pub struct NoiseStream {
#[pin]
inner_stream: Framed<TcpStream, LengthDelimitedCodec>,
handshake: Option<HandshakeState>,
noise: Option<TransportState>,
dec_buffer: VecDeque<u8>,
}
impl NoiseStream {
pub(crate) fn new(inner_stream: TcpStream, handshake: HandshakeState) -> NoiseStream {
NoiseStream {
inner_stream: LengthDelimitedCodec::builder()
.length_field_type::<u16>()
.new_framed(inner_stream),
handshake: Some(handshake),
noise: None,
dec_buffer: VecDeque::with_capacity(MAXMSGLEN),
}
}
pub(crate) async fn perform_handshake(mut self) -> Result<Self, NoiseError> {
//Check if we are in the correct state
let Some(mut handshake) = self.handshake else {
return Err(NoiseError::IncorrectStateError);
};
self.handshake = None;
while !handshake.is_handshake_finished() {
if handshake.is_my_turn() {
self.send_handshake_msg(&mut handshake).await?;
} else {
self.recv_handshake_msg(&mut handshake).await?;
}
}
self.noise = Some(handshake.into_transport_mode()?);
Ok(self)
}
async fn send_handshake_msg(
&mut self,
handshake: &mut HandshakeState,
) -> Result<(), NoiseError> {
let mut buf = BytesMut::zeroed(MAXMSGLEN + TAGLEN);
let len = handshake.write_message(&[], &mut buf)?;
buf.truncate(len);
self.inner_stream.send(buf.into()).await?;
Ok(())
}
async fn recv_handshake_msg(
&mut self,
handshake: &mut HandshakeState,
) -> Result<(), NoiseError> {
match self.inner_stream.next().await {
Some(Ok(msg)) => {
let mut buf = vec![0u8; MAXMSGLEN];
handshake.read_message(&msg, &mut buf)?;
Ok(())
}
Some(Err(err)) => Err(NoiseError::IoError(err)),
None => Err(NoiseError::HandshakeError),
}
}
pub fn peer_addr(&self) -> Result<std::net::SocketAddr, io::Error> {
self.inner_stream.get_ref().peer_addr()
}
}
impl AsyncRead for NoiseStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
let projected_self = self.project();
match projected_self.inner_stream.poll_next(cx) {
Poll::Pending => {
//no new data, waking is already scheduled.
//Nothing new to decrypt, only check if we can return something from dec_storage, happens after
}
Poll::Ready(Some(Ok(noise_msg))) => {
//We have a new moise msg
let mut dec_msg = vec![0u8; MAXMSGLEN];
let len = match projected_self.noise {
Some(transport_state) => {
match transport_state.read_message(&noise_msg, &mut dec_msg) {
Ok(len) => len,
Err(_) => return Poll::Ready(Err(io::ErrorKind::InvalidInput.into())),
}
}
None => return Poll::Ready(Err(io::ErrorKind::Other.into())),
};
projected_self.dec_buffer.extend(&dec_msg[..len]);
}
Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err)),
//Stream is done, return Ok with nothing in buf
Poll::Ready(None) => return Poll::Ready(Ok(())),
}
//check and return what we can
let read_len = min(buf.remaining(), projected_self.dec_buffer.len());
if read_len > 0 {
buf.put_slice(
&projected_self
.dec_buffer
.drain(..read_len)
.collect::<Vec<u8>>(),
);
return Poll::Ready(Ok(()));
}
//If we end up here, it must mean the previous poll_next was pending as well, otherwise something was returned. Hence waking is already scheduled
Poll::Pending
}
}
impl AsyncWrite for NoiseStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, std::io::Error>> {
let mut projected_self = self.project();
match projected_self.inner_stream.as_mut().poll_ready(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
Poll::Ready(Ok(())) => {
let mut noise_buf = BytesMut::zeroed(MAXMSGLEN + TAGLEN);
let Ok(len) = (match projected_self.noise {
Some(transport_state) => transport_state.write_message(buf, &mut noise_buf),
None => return Poll::Ready(Err(io::ErrorKind::Other.into())),
}) else {
return Poll::Ready(Err(io::ErrorKind::InvalidInput.into()));
};
noise_buf.truncate(len);
match projected_self.inner_stream.start_send(noise_buf.into()) {
Ok(()) => Poll::Ready(Ok(buf.len())),
Err(e) => Poll::Ready(Err(e)),
}
}
}
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
self.project().inner_stream.poll_flush(cx)
}
fn poll_shutdown(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
self.project().inner_stream.poll_close(cx)
}
}
+4 -4
View File
@@ -237,7 +237,7 @@ mod message_receiver {
mix_id: 123,
owner: "foomp1".to_string(),
host: "10.20.30.40".parse().unwrap(),
mix_host: "10.20.30.40:1789".parse().unwrap(),
mix_hosts: vec!["10.20.30.40:1789".parse().unwrap()],
identity_key: identity::PublicKey::from_base58_string(
"3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7",
)
@@ -257,7 +257,7 @@ mod message_receiver {
mix_id: 234,
owner: "foomp2".to_string(),
host: "11.21.31.41".parse().unwrap(),
mix_host: "11.21.31.41:1789".parse().unwrap(),
mix_hosts: vec!["11.21.31.41:1789".parse().unwrap()],
identity_key: identity::PublicKey::from_base58_string(
"D6YaMzLSY7mANtSQRKXsmMZpqgqiVkeiagKM4V4oFPFr",
)
@@ -277,7 +277,7 @@ mod message_receiver {
mix_id: 456,
owner: "foomp3".to_string(),
host: "12.22.32.42".parse().unwrap(),
mix_host: "12.22.32.42:1789".parse().unwrap(),
mix_hosts: vec!["12.22.32.42:1789".parse().unwrap()],
identity_key: identity::PublicKey::from_base58_string(
"GkWDysw4AjESv1KiAiVn7JzzCMJeksxNSXVfr1PpX8wD",
)
@@ -297,7 +297,7 @@ mod message_receiver {
vec![gateway::Node {
owner: "foomp4".to_string(),
host: "1.2.3.4".parse().unwrap(),
mix_host: "1.2.3.4:1789".parse().unwrap(),
mix_hosts: vec!["1.2.3.4:1789".parse().unwrap()],
clients_ws_port: 9000,
clients_wss_port: None,
identity_key: identity::PublicKey::from_base58_string(
+1 -4
View File
@@ -23,7 +23,6 @@ use nym_client_core::init::types::GatewaySetup;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType;
use nym_task::manager::TaskStatus;
use nym_task::{TaskClient, TaskHandle};
use anyhow::anyhow;
@@ -178,9 +177,7 @@ where
))?;
// Listen to status messages from task, that we forward back to the caller
shutdown
.start_status_listener(sender, TaskStatus::Ready)
.await;
shutdown.start_status_listener(sender).await;
let res = tokio::select! {
biased;
+2 -8
View File
@@ -46,8 +46,6 @@ enum TaskError {
pub enum TaskStatus {
#[error("Ready")]
Ready,
#[error("Ready and connected to gateway: {0}")]
ReadyWithGateway(String),
}
/// Listens to status and error messages from tasks, as well as notifying them to gracefully
@@ -163,14 +161,10 @@ impl TaskManager {
self.notify_tx.send(())
}
pub async fn start_status_listener(
&mut self,
mut sender: StatusSender,
start_status: TaskStatus,
) {
pub async fn start_status_listener(&mut self, mut sender: StatusSender) {
// Announce that we are operational. This means that in the application where this is used,
// everything is up and running and ready to go.
if let Err(msg) = sender.send(Box::new(start_status)).await {
if let Err(msg) = sender.send(Box::new(TaskStatus::Ready)).await {
log::error!("Error sending status message: {}", msg);
};
+42
View File
@@ -0,0 +1,42 @@
[package]
name = "nym-topology-control"
version = "0.1.0"
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-trait = { workspace = true }
futures = { workspace = true }
log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { workspace = true, features = ["derive"] }
tap = "1.0.1"
url = { workspace = true, features = ["serde"] }
tokio = { workspace = true, features = ["macros"]}
# internal
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
nym-sphinx = { path = "../nymsphinx" }
nym-topology = { path = "../topology", features = ["serializable"] }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
nym-task = { path = "../task" }
nym-network-defaults = { path = "../network-defaults" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.11"
features = ["time"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
workspace = true
features = ["tokio"]
[target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers]
version = "0.2.4"
features = ["futures"]
@@ -49,7 +49,7 @@ impl<'a> Deref for TopologyReadPermit<'a> {
impl<'a> TopologyReadPermit<'a> {
/// Using provided topology read permit, tries to get an immutable reference to the underlying
/// topology. For obvious reasons the lifetime of the topology reference is bound to the permit.
pub(crate) fn try_get_valid_topology_ref(
pub fn try_get_valid_topology_ref(
&'a self,
ack_recipient: &Recipient,
packet_recipient: Option<&Recipient>,
@@ -83,6 +83,16 @@ impl<'a> TopologyReadPermit<'a> {
Ok(topology)
}
pub fn try_get_raw_topology_ref(&'a self) -> Result<&'a NymTopology, NymTopologyError> {
// 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through?
let topology = self
.permit
.as_ref()
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
Ok(topology)
}
}
impl<'a> From<RwLockReadGuard<'a, Option<NymTopology>>> for TopologyReadPermit<'a> {
@@ -3,6 +3,7 @@ use std::{collections::HashMap, fmt};
use log::{debug, error, info};
use nym_explorer_client::{ExplorerClient, PrettyDetailedMixNodeBond};
use nym_network_defaults::var_names::EXPLORER_API;
use nym_sphinx::addressing::clients::Recipient;
use nym_topology::{
nym_topology_from_detailed,
provider_trait::{async_trait, TopologyProvider},
@@ -14,8 +15,6 @@ use serde::{Deserialize, Serialize};
use tap::TapOptional;
use url::Url;
use crate::config::GroupBy;
const MIN_NODES_PER_LAYER: usize = 1;
fn create_explorer_client() -> Option<ExplorerClient> {
@@ -38,6 +37,22 @@ fn create_explorer_client() -> Option<ExplorerClient> {
Some(client)
}
#[allow(clippy::large_enum_variant)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum GroupBy {
CountryGroup(CountryGroup),
NymAddress(Recipient),
}
impl std::fmt::Display for GroupBy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupBy::CountryGroup(group) => write!(f, "group: {}", group),
GroupBy::NymAddress(address) => write!(f, "address: {}", address),
}
}
}
#[derive(Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum CountryGroup {
Europe,
@@ -279,6 +294,14 @@ impl GeoAwareTopologyProvider {
Ok(gateways) => gateways,
};
let nodes_described = match self.validator_client.get_cached_described_nodes().await {
Err(err) => {
error!("failed to get described nodes - {err}");
return None;
}
Ok(epoch) => epoch,
};
// Also fetch mixnodes cached by explorer-api, with the purpose of getting their
// geolocation.
debug!("Fetching mixnodes from explorer-api...");
@@ -338,7 +361,7 @@ impl GeoAwareTopologyProvider {
.filter(|m| filtered_mixnode_ids.contains(&m.mix_id()))
.collect::<Vec<_>>();
let topology = nym_topology_from_detailed(mixnodes, gateways)
let topology = nym_topology_from_detailed(mixnodes, gateways, nodes_described)
.filter_system_version(&self.client_version);
// TODO: return real error type
@@ -1,11 +1,11 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::spawn_future;
pub(crate) use accessor::{TopologyAccessor, TopologyReadPermit};
pub use accessor::{TopologyAccessor, TopologyReadPermit};
use futures::StreamExt;
use log::*;
use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_task::spawn;
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::NymTopologyError;
use std::time::Duration;
@@ -16,9 +16,9 @@ use tokio::time::sleep;
#[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::sleep;
mod accessor;
pub mod accessor;
pub mod geo_aware_provider;
pub(crate) mod nym_api_provider;
pub mod nym_api_provider;
// TODO: move it to config later
const MAX_FAILURE_COUNT: usize = 10;
@@ -142,7 +142,7 @@ impl TopologyRefresher {
}
pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
spawn_future(async move {
spawn(async move {
debug!("Started TopologyRefresher with graceful shutdown support");
#[cfg(not(target_arch = "wasm32"))]
@@ -9,7 +9,7 @@ use rand::prelude::SliceRandom;
use rand::thread_rng;
use url::Url;
pub(crate) struct NymApiTopologyProvider {
pub struct NymApiTopologyProvider {
validator_client: nym_validator_client::client::NymApiClient,
nym_api_urls: Vec<Url>,
@@ -18,7 +18,7 @@ pub(crate) struct NymApiTopologyProvider {
}
impl NymApiTopologyProvider {
pub(crate) fn new(mut nym_api_urls: Vec<Url>, client_version: String) -> Self {
pub fn new(mut nym_api_urls: Vec<Url>, client_version: String) -> Self {
nym_api_urls.shuffle(&mut thread_rng());
NymApiTopologyProvider {
@@ -77,13 +77,22 @@ impl NymApiTopologyProvider {
Ok(gateways) => gateways,
};
let topology = nym_topology_from_detailed(mixnodes, gateways)
let nodes_described = match self.validator_client.get_cached_described_nodes().await {
Err(err) => {
error!("failed to get described nodes - {err}");
return None;
}
Ok(epoch) => epoch,
};
let topology = nym_topology_from_detailed(mixnodes, gateways, nodes_described.clone())
.filter_system_version(&self.client_version);
if let Err(err) = self.check_layer_distribution(&topology) {
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used: {err}");
self.use_next_nym_api();
None
let empty_topology = NymTopology::empty().with_described_nodes(nodes_described);
Some(empty_topology)
} else {
Some(topology)
}
+18 -16
View File
@@ -46,9 +46,9 @@ pub enum GatewayConversionError {
pub struct Node {
pub owner: String,
pub host: NetworkAddress,
// we're keeping this as separate resolved field since we do not want to be resolving the potential
// hostname every time we want to construct a path via this node
pub mix_host: SocketAddr,
// we're keeping all resolved IPs as a separate field since we do not want to be resolving the potential
// hostname every time we want to construct a path via this node. When we need one, we default to the first one
pub mix_hosts: Vec<SocketAddr>,
// #[serde(alias = "clients_port")]
pub clients_ws_port: u16,
@@ -66,7 +66,7 @@ impl std::fmt::Debug for Node {
f.debug_struct("gateway::Node")
.field("host", &self.host)
.field("owner", &self.owner)
.field("mix_host", &self.mix_host)
.field("mix_hosts", &self.mix_hosts)
.field("clients_ws_port", &self.clients_ws_port)
.field("clients_wss_port", &self.clients_wss_port)
.field("identity_key", &self.identity_key.to_base58_string())
@@ -88,13 +88,12 @@ impl Node {
pub fn extract_mix_host(
host: &NetworkAddress,
mix_port: u16,
) -> Result<SocketAddr, GatewayConversionError> {
Ok(host.to_socket_addrs(mix_port).map_err(|err| {
GatewayConversionError::InvalidAddress {
) -> Result<Vec<SocketAddr>, GatewayConversionError> {
host.to_socket_addrs(mix_port)
.map_err(|err| GatewayConversionError::InvalidAddress {
value: host.to_string(),
source: err,
}
})?[0])
})
}
pub fn identity(&self) -> &NodeIdentity {
@@ -135,7 +134,7 @@ impl filter::Versioned for Node {
impl<'a> From<&'a Node> for SphinxNode {
fn from(node: &'a Node) -> Self {
let node_address_bytes = NymNodeRoutingAddress::from(node.mix_host)
let node_address_bytes = NymNodeRoutingAddress::from(node.mix_hosts[0])
.try_into()
.unwrap();
@@ -151,12 +150,12 @@ impl<'a> TryFrom<&'a GatewayBond> for Node {
// try to completely resolve the host in the mix situation to avoid doing it every
// single time we want to construct a path
let mix_host = Self::extract_mix_host(&host, bond.gateway.mix_port)?;
let mix_hosts = Self::extract_mix_host(&host, bond.gateway.mix_port)?;
Ok(Node {
owner: bond.owner.as_str().to_owned(),
host,
mix_host,
mix_hosts,
clients_ws_port: bond.gateway.clients_port,
clients_wss_port: None,
identity_key: identity::PublicKey::from_base58_string(&bond.gateway.identity_key)?,
@@ -196,14 +195,17 @@ impl<'a> TryFrom<&'a DescribedGateway> for Node {
// get ip from the self-reported values so we wouldn't need to do any hostname resolution
// (which doesn't really work in wasm)
let mix_host = SocketAddr::new(ips[0], value.bond.gateway.mix_port);
let mix_hosts = ips
.iter()
.map(|ip| SocketAddr::new(*ip, value.bond.gateway.mix_port))
.collect();
Ok(Node {
owner: value.bond.owner.as_str().to_owned(),
host,
mix_host,
clients_ws_port: self_described.mixnet_websockets.ws_port,
clients_wss_port: self_described.mixnet_websockets.wss_port,
mix_hosts,
clients_ws_port: self_described.mixnet_websockets.unwrap().ws_port, //SW gateway have that field
clients_wss_port: self_described.mixnet_websockets.unwrap().wss_port, //SW gateway have that field
identity_key: identity::PublicKey::from_base58_string(
&self_described.host_information.keys.ed25519,
)?,
+86 -10
View File
@@ -19,7 +19,7 @@ use std::str::FromStr;
#[cfg(feature = "serializable")]
use ::serde::{Deserialize, Deserializer, Serialize, Serializer};
use nym_api_requests::models::DescribedGateway;
use nym_api_requests::models::{DescribedGateway, DescribedNymNode};
pub mod error;
pub mod filter;
@@ -115,14 +115,40 @@ pub type MixLayer = u8;
pub struct NymTopology {
mixes: BTreeMap<MixLayer, Vec<mix::Node>>,
gateways: Vec<gateway::Node>,
described_nodes: Vec<DescribedNymNode>,
}
impl NymTopology {
pub fn new(mixes: BTreeMap<MixLayer, Vec<mix::Node>>, gateways: Vec<gateway::Node>) -> Self {
NymTopology { mixes, gateways }
pub fn new(
mixes: BTreeMap<MixLayer, Vec<mix::Node>>,
gateways: Vec<gateway::Node>,
described_nodes: Vec<DescribedNymNode>,
) -> Self {
NymTopology {
mixes: mixes.clone(),
gateways: gateways.clone(),
described_nodes: described_nodes.clone(),
}
}
pub fn new_unordered(unordered_mixes: Vec<mix::Node>, gateways: Vec<gateway::Node>) -> Self {
pub fn empty() -> Self {
NymTopology {
mixes: BTreeMap::new(),
gateways: Vec::new(),
described_nodes: Vec::new(),
}
}
pub fn with_described_nodes(mut self, described_nodes: Vec<DescribedNymNode>) -> Self {
self.described_nodes = described_nodes;
self
}
pub fn new_unordered(
unordered_mixes: Vec<mix::Node>,
gateways: Vec<gateway::Node>,
described_nodes: Vec<DescribedNymNode>,
) -> Self {
let mut mixes = BTreeMap::new();
for node in unordered_mixes.into_iter() {
let layer = node.layer as MixLayer;
@@ -130,7 +156,7 @@ impl NymTopology {
layer_entry.push(node)
}
NymTopology { mixes, gateways }
NymTopology::new(mixes, gateways, described_nodes)
}
#[cfg(feature = "serializable")]
@@ -142,8 +168,9 @@ impl NymTopology {
pub fn from_detailed(
mix_details: Vec<MixNodeDetails>,
gateway_bonds: Vec<GatewayBond>,
described_nodes: Vec<DescribedNymNode>,
) -> Self {
nym_topology_from_detailed(mix_details, gateway_bonds)
nym_topology_from_detailed(mix_details, gateway_bonds, described_nodes)
}
pub fn find_mix(&self, mix_id: MixId) -> Option<&mix::Node> {
@@ -168,6 +195,53 @@ impl NymTopology {
None
}
pub fn find_node_key_by_mix_host(
&self,
mix_host: SocketAddr,
check_port: bool,
) -> Result<Option<String>, NymTopologyError> {
for node in self.described_nodes.iter() {
let (sphinx_key, socket_addresses, description) = match node {
DescribedNymNode::Gateway(g) => {
let sphinx_key = &g.bond.gateway.sphinx_key;
let gateway_node: Option<gateway::Node> = (&g.bond).try_into().ok();
let mix_hosts = gateway_node.map(|node| node.mix_hosts);
let description = &g.self_described;
(sphinx_key, mix_hosts, description)
}
DescribedNymNode::Mixnode(m) => {
let sphinx_key = &m.bond.mix_node.sphinx_key;
let mix_node: Option<mix::Node> = (&m.bond).try_into().ok();
let mix_hosts = mix_node.map(|node| node.mix_hosts);
let description = &m.self_described;
(sphinx_key, mix_hosts, description)
}
};
if let Some(sock_addr) = socket_addresses {
let existing_node = if check_port {
//Initiator side, we know the port should be correct as well
sock_addr.contains(&mix_host)
} else {
//responder side, we don't know the port.
//SW This can lead to some troubles if two nodes shares the same IP and one support Noise but not the other. This in only for the progressive update though
let ip_addresses = sock_addr.iter().map(|addr| addr.ip()).collect::<Vec<_>>();
ip_addresses.contains(&mix_host.ip())
};
if existing_node {
//we have our node
if let Some(d) = description {
if d.noise_information.supported {
return Ok(Some(sphinx_key.to_string()));
}
}
return Ok(None);
}
}
}
//didn't find that node
Err(NymTopologyError::NoMixnodesAvailable)
}
pub fn find_gateway(&self, gateway_identity: IdentityKeyRef) -> Option<&gateway::Node> {
self.gateways
.iter()
@@ -379,6 +453,7 @@ impl NymTopology {
NymTopology {
mixes: self.mixes.filter_by_version(expected_mix_version),
gateways: self.gateways.clone(),
described_nodes: self.described_nodes.clone(),
}
}
}
@@ -426,6 +501,7 @@ impl IntoGatewayNode for DescribedGateway {
pub fn nym_topology_from_detailed<G>(
mix_details: Vec<MixNodeDetails>,
gateway_bonds: Vec<G>,
described_nodes: Vec<DescribedNymNode>,
) -> NymTopology
where
G: IntoGatewayNode,
@@ -469,7 +545,7 @@ where
}
}
NymTopology::new(mixes, gateways)
NymTopology::new(mixes, gateways, described_nodes)
}
#[cfg(test)]
@@ -489,7 +565,7 @@ mod converting_mixes_to_vec {
mix_id: 42,
owner: "N/A".to_string(),
host: "3.3.3.3".parse().unwrap(),
mix_host: "3.3.3.3:1789".parse().unwrap(),
mix_hosts: vec!["3.3.3.3:1789".parse().unwrap()],
identity_key: identity::PublicKey::from_base58_string(
"3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7",
)
@@ -516,7 +592,7 @@ mod converting_mixes_to_vec {
mixes.insert(1, vec![node1, node2]);
mixes.insert(2, vec![node3]);
let topology = NymTopology::new(mixes, vec![]);
let topology = NymTopology::new(mixes, vec![], vec![]);
let mixvec = topology.mixes_as_vec();
assert!(mixvec.iter().any(|node| node.owner == "N/A"));
}
@@ -528,7 +604,7 @@ mod converting_mixes_to_vec {
#[test]
fn returns_an_empty_vec() {
let topology = NymTopology::new(BTreeMap::new(), vec![]);
let topology = NymTopology::new(BTreeMap::new(), vec![], vec![]);
let mixvec = topology.mixes_as_vec();
assert!(mixvec.is_empty());
}
+11 -12
View File
@@ -34,9 +34,9 @@ pub struct Node {
pub mix_id: MixId,
pub owner: String,
pub host: NetworkAddress,
// we're keeping this as separate resolved field since we do not want to be resolving the potential
// hostname every time we want to construct a path via this node
pub mix_host: SocketAddr,
// we're keeping all resolved IPs as a separate field since we do not want to be resolving the potential
// hostname every time we want to construct a path via this node. When we need one, we default to the first one
pub mix_hosts: Vec<SocketAddr>,
pub identity_key: identity::PublicKey,
pub sphinx_key: encryption::PublicKey, // TODO: or nymsphinx::PublicKey? both are x25519
pub layer: Layer,
@@ -49,7 +49,7 @@ impl std::fmt::Debug for Node {
.field("mix_id", &self.mix_id)
.field("owner", &self.owner)
.field("host", &self.host)
.field("mix_host", &self.mix_host)
.field("mix_hosts", &self.mix_hosts)
.field("identity_key", &self.identity_key.to_base58_string())
.field("sphinx_key", &self.sphinx_key.to_base58_string())
.field("layer", &self.layer)
@@ -70,13 +70,12 @@ impl Node {
pub fn extract_mix_host(
host: &NetworkAddress,
mix_port: u16,
) -> Result<SocketAddr, MixnodeConversionError> {
Ok(host.to_socket_addrs(mix_port).map_err(|err| {
MixnodeConversionError::InvalidAddress {
) -> Result<Vec<SocketAddr>, MixnodeConversionError> {
host.to_socket_addrs(mix_port)
.map_err(|err| MixnodeConversionError::InvalidAddress {
value: host.to_string(),
source: err,
}
})?[0])
})
}
}
@@ -89,7 +88,7 @@ impl filter::Versioned for Node {
impl<'a> From<&'a Node> for SphinxNode {
fn from(node: &'a Node) -> Self {
let node_address_bytes = NymNodeRoutingAddress::from(node.mix_host)
let node_address_bytes = NymNodeRoutingAddress::from(node.mix_hosts[0])
.try_into()
.unwrap();
@@ -105,13 +104,13 @@ impl<'a> TryFrom<&'a MixNodeBond> for Node {
// try to completely resolve the host in the mix situation to avoid doing it every
// single time we want to construct a path
let mix_host = Self::extract_mix_host(&host, bond.mix_node.mix_port)?;
let mix_hosts = Self::extract_mix_host(&host, bond.mix_node.mix_port)?;
Ok(Node {
mix_id: bond.mix_id,
owner: bond.owner.as_str().to_owned(),
host,
mix_host,
mix_hosts,
identity_key: identity::PublicKey::from_base58_string(&bond.mix_node.identity_key)?,
sphinx_key: encryption::PublicKey::from_base58_string(&bond.mix_node.sphinx_key)?,
layer: bond.layer,
+26 -11
View File
@@ -4,6 +4,7 @@
use crate::gateway::GatewayConversionError;
use crate::mix::MixnodeConversionError;
use crate::{gateway, mix, MixLayer, NymTopology};
use nym_api_requests::models::DescribedNymNode;
use nym_config::defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use nym_crypto::asymmetric::{encryption, identity};
use serde::{Deserialize, Serialize};
@@ -53,6 +54,12 @@ impl From<SerializableTopologyError> for JsValue {
pub struct SerializableNymTopology {
pub mixnodes: BTreeMap<MixLayer, Vec<SerializableMixNode>>,
pub gateways: Vec<SerializableGateway>,
//SW NOTE : make this an option to keep backwards compatibility. Noise with fallback needs that to work though
// Once fallback is removed, we only need a list of unfiltered nodes that can be constructed from mixnodes and gateways
// depending on the usecase of this struct
#[serde(alias = "described_nodes")]
pub described_nodes: Option<Vec<DescribedNymNode>>, //DescribedNymNode is already Serialize and Deserialize
}
impl TryFrom<SerializableNymTopology> for NymTopology {
@@ -76,7 +83,11 @@ impl TryFrom<SerializableNymTopology> for NymTopology {
.map(TryInto::try_into)
.collect::<Result<_, _>>()?;
Ok(NymTopology::new(converted_mixes, gateways))
Ok(NymTopology::new(
converted_mixes,
gateways,
value.described_nodes.unwrap_or_default(),
))
}
}
@@ -89,6 +100,7 @@ impl From<NymTopology> for SerializableNymTopology {
.map(|(&l, nodes)| (l, nodes.iter().map(Into::into).collect()))
.collect(),
gateways: value.gateways().iter().map(Into::into).collect(),
described_nodes: Some(value.described_nodes),
}
}
}
@@ -135,13 +147,13 @@ impl TryFrom<SerializableMixNode> for mix::Node {
// try to completely resolve the host in the mix situation to avoid doing it every
// single time we want to construct a path
let mix_host = mix::Node::extract_mix_host(&host, mix_port)?;
let mix_hosts = mix::Node::extract_mix_host(&host, mix_port)?;
Ok(mix::Node {
mix_id: value.mix_id,
owner: value.owner,
host,
mix_host,
mix_hosts,
identity_key: identity::PublicKey::from_base58_string(&value.identity_key)
.map_err(MixnodeConversionError::from)?,
sphinx_key: encryption::PublicKey::from_base58_string(&value.sphinx_key)
@@ -159,7 +171,7 @@ impl<'a> From<&'a mix::Node> for SerializableMixNode {
mix_id: value.mix_id,
owner: value.owner.clone(),
host: value.host.to_string(),
mix_port: Some(value.mix_host.port()),
mix_port: Some(value.mix_hosts[0].port()),
identity_key: value.identity_key.to_base58_string(),
sphinx_key: value.sphinx_key.to_base58_string(),
layer: value.layer.into(),
@@ -181,8 +193,8 @@ pub struct SerializableGateway {
// optional ip address in the case of host being a hostname that can't be resolved
// (thank you wasm)
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "explicit_ip")]
pub explicit_ip: Option<IpAddr>,
#[serde(alias = "explicit_ips")]
pub explicit_ips: Option<Vec<IpAddr>>,
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
#[serde(alias = "mix_port")]
@@ -221,8 +233,11 @@ impl TryFrom<SerializableGateway> for gateway::Node {
// try to completely resolve the host in the mix situation to avoid doing it every
// single time we want to construct a path
let mix_host = if let Some(explicit_ip) = value.explicit_ip {
SocketAddr::new(explicit_ip, mix_port)
let mix_hosts = if let Some(explicit_ips) = value.explicit_ips {
explicit_ips
.iter()
.map(|explicit_ip| SocketAddr::new(*explicit_ip, mix_port))
.collect()
} else {
gateway::Node::extract_mix_host(&host, mix_port)?
};
@@ -230,7 +245,7 @@ impl TryFrom<SerializableGateway> for gateway::Node {
Ok(gateway::Node {
owner: value.owner,
host,
mix_host,
mix_hosts,
clients_ws_port,
clients_wss_port: value.clients_wss_port,
identity_key: identity::PublicKey::from_base58_string(&value.identity_key)
@@ -247,8 +262,8 @@ impl<'a> From<&'a gateway::Node> for SerializableGateway {
SerializableGateway {
owner: value.owner.clone(),
host: value.host.to_string(),
explicit_ip: Some(value.mix_host.ip()),
mix_port: Some(value.mix_host.port()),
explicit_ips: Some(value.mix_hosts.iter().map(|addr| addr.ip()).collect()),
mix_port: Some(value.mix_hosts[0].port()),
clients_ws_port: Some(value.clients_ws_port),
clients_wss_port: value.clients_wss_port,
identity_key: value.identity_key.to_base58_string(),
+13 -16
View File
@@ -18,7 +18,7 @@
},
"devDependencies": {
"@tsconfig/recommended": "^1.0.1",
"prettier": "^2.8.7",
"prettier": "^2.2.1",
"typescript": "^4.1.3"
}
},
@@ -570,9 +570,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
"funding": [
{
"type": "individual",
@@ -875,18 +875,15 @@
}
},
"node_modules/prettier": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
"integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/protobufjs": {
@@ -1667,9 +1664,9 @@
}
},
"follow-redirects": {
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
"version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
},
"fsevents": {
"version": "2.3.2",
@@ -1888,9 +1885,9 @@
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw=="
},
"prettier": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz",
"integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==",
"dev": true
},
"protobufjs": {
+1 -5
View File
@@ -31,13 +31,9 @@ assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
minimum_rust_version = "1.66"
wallet_release_version = "1.2.8"
# nym-vpn related variables
nym_vpn_releases = "https://github.com/nymtech/nym-vpn-client/releases"
nym_vpn_latest_binary_url = "https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.4"
nym_vpn_form_url = "https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko-2"
# versions are pulled by cmdrun now
# nym_vpn_gui_version = "0.0.6"
# nym_vpn_cli_version = "0.0.4"
[preprocessor.last-changed]
command = "mdbook-last-changed"
renderer = ["html"]
+1
View File
@@ -25,6 +25,7 @@
- [CLI](nymvpn/cli.md)
- [Linux](nymvpn/cli-linux.md)
- [MacOS](nymvpn/cli-mac.md)
- [Testing](nymvpn/testing.md)
- [Troubleshooting](nymvpn/troubleshooting.md)
- [NymVPN FAQ](nymvpn/faq.md)
- [NymConnect X Monero](tutorials/monero.md)
@@ -8,29 +8,25 @@ NymVPN is an experimental software and it's for [testing](./testing.md) purposes
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
1. Open Github [releases page]({{nym_vpn_releases}}) and download the binary for Debian based Linux
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_releases}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
1. Open Github [releases page]({{nym_vpn_latest_binary_url}}) and download the binary for Debian based Linux
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_latest_binary_url}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
```sh
echo "<SHA_STRING>" | shasum -a 256 -c
# choose a correct one according to your binary, this is just an example
# echo "0e4abb461e86b2c168577e0294112a3bacd3a24bf8565b49783bfebd9b530e23 nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_amd64.tar.gz" | shasum -a 256 -c
# echo "0e4abb461e86b2c168577e0294112a3bacd3a24bf8565b49783bfebd9b530e23 nym-vpn-cli_0.1.0_ubuntu-22.04_amd64.zip" | shasum -a 256 -c
```
3. Extract files:
1. Extract files:
```sh
tar -xvf <BINARY>.tar.gz
tar -xvf <BINARY>
# for example
# tar -xvf nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_x86_64.tar.gz
# tar -xvf nym-vpn-cli_0.0.2_ubuntu-22.04_x86_64.tar.gz
```
4. Make executable by running:
2. Make executable by running:
```sh
# make sure you are in the right sub-directory
# possibly you may have to cd into a sub-directory
chmod u+x ./nym-vpn-cli
```
5. Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries by running:
```sh
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
@@ -8,19 +8,19 @@ NymVPN is an experimental software and it's for [testing](./testing.md) purposes
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
1. Open Github [releases page]({{nym_vpn_releases}}) and download the binary for MacOS
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_releases}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
1. Open Github [releases page]({{nym_vpn_latest_binary_url}}) and download the binary for MacOS
2. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_latest_binary_url}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
```sh
echo "<SHA_STRING>" | shasum -a 256 -c
# choose a correct one according to your binary, this is just an example
# echo "96623ccc69bc4cc0e4e3e18528b6dae6be69f645d0a592d926a3158ce2d0c269 nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_macos_x86_64.zip" | shasum -a 256 -c
# echo "96623ccc69bc4cc0e4e3e18528b6dae6be69f645d0a592d926a3158ce2d0c269 nym-vpn-cli_0.1.0_macos_x86_64.zip" | shasum -a 256 -c
```
3. Extract files:
```sh
tar -xvf <BINARY>
# for example
# tar -xvf nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_macos_aarch64.tar.gz
# tar -xvf nym-vpn-cli_0.0.2_macos_aarch64.tar.gz
```
4. Make executable by running:
```sh
+16 -5
View File
@@ -5,9 +5,10 @@ Our alpha testing round is done with participants at live workshop events. This
**If you commit to test NymVPN alpha, please start with the [user research form]({{nym_vpn_form_url}}) where all the steps will be provided**. If you disagree with any of the conditions listed, please leave this page.
```
Follow the simple [automated script](#automated-script-for-cli-installation) below to install and run NymVPN CLI. If you prefer to do a manual setup follow the steps in the guide for [Linux](cli-linux.md) or [MacOS](cli-mac.md).
Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums or download the binaries directly.
NymVPN CLI is a fundamental way to run the client for different purposes, currently it is a must for users who want to run the [testing scripts](testing.md).
Follow the simple [automated script](#automated-script-for-cli-installation) below to install and run NymVPN CLI. If you prefer to do a manual setup follow the steps in the guide for [Linux](cli-linux.md) or [MacOS](cli-mac.md).
## Automated Script for CLI Installation
@@ -15,12 +16,22 @@ We wrote a [script](https://gist.github.com/serinko/d65450653d6bbafacbcee71c9cb8
1. Open a terminal window in a directory where you want the script and NymVPN CLI binary be downloaded and run
```sh
curl -o execute-nym-vpn-cli-binary.sh -L https://gist.githubusercontent.com/serinko/d65450653d6bbafacbcee71c9cb8fb31/raw/4b70371fb000fd08910c0f778e78566d002e1319/execute-nym-vpn-cli-binary.sh && chmod u+x execute-nym-vpn-cli-binary.sh && sudo -E ./execute-nym-vpn-cli-binary.sh
curl -o execute-nym-vpn-cli-binary.sh -L https://gist.githubusercontent.com/serinko/d65450653d6bbafacbcee71c9cb8fb31/raw/0cbcdd18f7ee94f559692b936061248ebbbf2773/execute-nym-vpn-cli-binary.sh
```
2. Follow the prompts in the program
2. Make the script executable
```sh
chmod u+x execute-nym-vpn-cli-binary.sh
```
3. The script will automatically start the client. Make sure to **turn off any other VPNs** and follow the prompts:
3. Start the script, turn off any VPN and run
```sh
sudo -E ./execute-nym-vpn-cli-binary.sh
```
4. Follow the prompts in the program
5. The script will automatically start the client. Make sure to **turn off any other VPNs** and follow the prompts:
* It prints a JSON view of existing Gateways and prompt you to:
- *Make sure to use two different Gateways for entry and exit!*
@@ -12,35 +12,33 @@ NymVPN is an experimental software and it's for [testing](./testing.md) purposes
### Installation
1. Open Github [releases page]({{nym_vpn_releases}}) and download the binary for Debian based Linux
2. Required (if you don't want to check shasum, skip this point): Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_releases}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
1. Open Github [releases page]({{nym_vpn_latest_binary_url}}) and download the binary for Debian based Linux
2. Required (if you don't want to check shasum, skip this point): Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_latest_binary_url}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
```sh
echo "<SHA_STRING>" | shasum -a 256 -c
# choose a correct one according to your binary, this is just an example
# echo "a5f91f20d587975e30b6a75d3a9e195234cf1269eac278139a5b9c39b039e807 nym-vpn-desktop_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_ubuntu-22.04_x86_64.tar.gz" | shasum -a 256 -c
# echo "a5f91f20d587975e30b6a75d3a9e195234cf1269eac278139a5b9c39b039e807 nym-vpn-desktop_0.0.3_ubuntu-22.04_x86_64.zip" | shasum -a 256 -c
```
3. Extract files:
```sh
tar -xvf <BINARY>.tar.gz
tar -xvf <BINARY>
# for example
# tar -xvf nym-vpn-desktop_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_ubuntu-22.04_x86_64.tar.gz
# tar -xvf nym-vpn-desktop_0.0.4_ubuntu-22.04_x86_64.tar.gz
```
4. If you prefer to run `.AppImage` make executable by running:
```sh
# make sure you cd into the right sub-directory after extraction
chmod u+x ./nym-vpn_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_amd64.AppImage
chmod u+x ./appimage/nym-vpn_0.0.4_amd64.AppImage
```
5. If you prefer to use the `.deb` version for installation (works on Debian based Linux only), open terminal in the same directory and run:
```sh
# make sure you cd into the right sub-directory after extraction
sudo dpkg -i ./nym-vpn_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_amd64.deb
cd deb
sudo dpkg -i ./nym-vpn_0.0.4_amd64.deb
# or
sudo apt-get install -f ./nym-vpn_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_amd64.deb
sudo apt-get install -f ./nym-vpn_0.0.4_amd64.deb
```
NymVPN alpha version runs over Nym testnet (called sandbox), a little extra configuration is needed for the application to work.
@@ -74,7 +72,7 @@ Open terminal and run:
```sh
# .AppImage must be run from the same directory as the binary
sudo -E ./nym-vpn_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_amd64.AppImage
sudo -E ./nym-vpn_0.0.4_amd64.AppImage
# .deb installation shall be executable from anywhere as
sudo -E nym-vpn
@@ -17,21 +17,21 @@ mkdir -p "$HOME/nym-vpn-latest"
```
-->
1. Open Github [releases page]({{nym_vpn_releases}}) and download the binary for your version of MacOS
1. Open Github [releases page]({{nym_vpn_latest_binary_url}}) and download the binary for your version of MacOS
2. Recommended (skip this point if you don't want to verify): Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_releases}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
2. Recommended (skip this point if you don't want to verify): Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_latest_binary_url}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
```sh
echo "<SHA_STRING>" | shasum -a 256 -c
# choose a correct one according to your binary, this is just an example
# echo "da4c0bf8e8b52658312d341fa3581954cfcb6efd516d9a448c76d042a454b5df nym-vpn-desktop_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_macos_x86_64.zip" | shasum -a 256 -c
# echo "da4c0bf8e8b52658312d341fa3581954cfcb6efd516d9a448c76d042a454b5df nym-vpn-desktop_0.0.3_macos_x86_64.zip" | shasum -a 256 -c
```
3. Extract the downloaded file manually or by a command:
```sh
tar -xvf <BINARY>.tar.gz
tar -xvf <BINARY>
# for example
# tar -xvf nym-vpn-desktop_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_macos_aarch64.tar.gz
# tar -xvf nym-vpn-desktop_0.0.4_macos_aarch64.tar.gz
```
<!-- seems redundant
5. Move to the application content directory:
@@ -71,7 +71,7 @@ env_config_file = "sandbox.env"
```
Alternatively do it by using this command:
```sh
echo "env_config_file = sandbox.env" > /Applications/nym-vpn.app/Contents/MacOS/config.toml
echo "env_config_file = sandbox.env" > /Applications/nym-vpn.app/Contents/MacOS//config.toml
```
## Run NymVPN
+5 -8
View File
@@ -10,26 +10,23 @@ This is the alpha version of NymVPN desktop application (GUI). A demo of how the
Follow the simple [automated script](#automated-script-for-gui-installation) below to install and run NymVPN GUI. If the script didn't work for your distribution or you prefer to do a manual setup follow the steps in the guide for [Linux](gui-linux.md) or [MacOS](gui-mac.md) .
Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums or download the binaries directly.
Visit NymVPN alpha latest [release page]({{nym_vpn_latest_binary_url}}) to check sha sums or download the binaries directly.
## Automated Script for GUI Installation
We wrote a [script](https://gist.github.com/tommyv1987/7d210d4daa8f7abc61f9a696d0321f19) which does download of dependencies and the application, sha256 verification, extraction, installation and configuration for Linux and MacOS users automatically. Turn off all VPNs and follow the steps below.
We wrote a [script](https://gist.github.com/serinko/e0a9f7ff3d79e974ec6f6783caa1137e) which does download of dependencies and the application, sha256 verification, extraction, installation and configuration for Linux and MacOS users automatically. Turn off all VPNs and follow the steps below.
1. Open a terminal window in a directory where you want the script to be downloaded and run
```sh
curl -o nym-vpn-desktop-install-run.sh -L https://gist.githubusercontent.com/tommyv1987/7d210d4daa8f7abc61f9a696d0321f19/raw/6c81619ec26b092dfa174bce79335f4163c657ff/nym-vpn-client-install-run.sh && chmod u+x nym-vpn-desktop-install-run.sh && sudo -E ./nym-vpn-desktop-install-run.sh
curl -o nym-vpn-desktop-install-run.sh -L https://gist.githubusercontent.com/serinko/e0a9f7ff3d79e974ec6f6783caa1137e/raw/227c8c348a1e58f68cb500e4504b22412177c680/nym-vpn-desktop-install-run.sh && chmod u+x nym-vpn-desktop-install-run.sh && sudo -E ./nym-vpn-desktop-install-run.sh
```
2. Follow the prompts in the program
To start the application again, reconnect your wifi and run
```sh
# Linux .AppImage
sudo -E ~/nym-vpn-latest/nym-vpn-desktop_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_ubuntu-22.04_x86_64/nym-vpn_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_amd64.AppImage
# Linux .deb
sudo -E nym-vpn
# Linux
sudo -E ~/nym-vpn-latest/nym-vpn_0.0.4_amd64.AppImage
# MacOS
sudo -E $HOME/nym-vpn-latest/nym-vpn
+7 -5
View File
@@ -5,7 +5,7 @@
**Nym proudly presents NymVPN alpha** - a client that uses [Nym Mixnet](https://nymtech.net) to anonymise all of a user's internet traffic through either a 5-hop mixnet (for a full network privacy) or the faster 2-hop decentralised VPN (with some extra features).
**You are invited to take part in the alpha testing** of this new application. The following pages provide a how-to guide, explaining steps to install and run NymVPN [CLI](cli.md) and [GUI](gui.md) on the Sandbox testnet environment.
**You are invited to take part in the alpha testing** of this new application. The following pages provide a how-to guide, explaining steps to install and run NymVPN [CLI](cli.md) and [GUI](gui.md) on the Sandbox testnet environment as well as provide some scripts for [qualitative testing](testing.md).
**Here is how**
@@ -13,12 +13,13 @@
2. Please consent to the GDPR so we can use the results
3. To test the GUI, [go here](gui.md)
4. To test the CLI, [go here](cli.md)
5. Fill and submit the [form!]({{nym_vpn_form_url}})
6. Join the [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat) if you have any questions, comments or blockers
5. Run [qualitative testing script](testing.md)
6. Fill and submit the [form!]({{nym_vpn_form_url}})
7. Join the [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat) if you have any questions, comments or blockers
***NymVPN alpha testing will last from 15th of January - 15th of February.***
*NOTE: NymVPN alpha is experimental software for testing purposes only.*
*NOTE: NymVPN alpha is experimental software for [testing purposes](testing.md) only.*
## NymVPN Overview
@@ -44,9 +45,10 @@ The client can optionally do the first hop (local client to Entry Gateway) using
## NymVPN Resources & Guides
* [NymVPN webpage](https://nymvpn.com)
* [Alpha release page]({{nym_vpn_releases}})
* [Alpha release page]({{nym_vpn_latest_binary_url}})
* [NymVPN application (GUI) guide](gui.md)
* [NymVPN Command Line Interface (CLI) guide](cli.md)
* [Testing scripts](testing.md)
* [Troubleshooting](troubleshooting.md)
* [NymVPN FAQ](faq.md)
* [NymVPN matrix channel](https://matrix.to/#/#NymVPN:nymtech.chat)
@@ -0,0 +1,61 @@
# NymVPN alpha - Desktop: Guide for Mac OS
```admonish info
NymVPN is an experimental software and it's for [testing](./testing.md) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the workshop) so we use their results to improve the client, and submit the form [*NymVPN User research*]({{nym_vpn_form_url}}) with the testing results.
```
## Preparation
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
### Installation
1. Create a directory `~/nym-vpn-latest`
```sh
mkdir -p "$HOME/nym-vpn-latest"
```
2. Open Github [releases page]({{nym_vpn_latest_binary_url}}) and download the binary for MacOS
3. Verify sha hash of your downloaded binary with the one listed on the [releases page]({{nym_vpn_latest_binary_url}}). You can use a simple `shasum` command and compare strings (ie with Python) or run in the same directory the following command, exchanging `<SHA_STRING>` with the one of your binary, like in the example:
```sh
echo "<SHA_STRING>" | shasum -a 256 -c
# choose a correct one according to your binary, this is just an example
# echo "da4c0bf8e8b52658312d341fa3581954cfcb6efd516d9a448c76d042a454b5df nym-vpn-desktop_0.0.3_macos_x86_64.zip" | shasum -a 256 -c
```
4. Extract files:
```sh
tar -xvf <BINARY>
# for example
# tar -xvf nym-vpn-desktop_0.0.4_macos_aarch64.tar.gz
```
5. Move to the application directory and make executable
```sh
cd "macos/nym-vpn.app/Contents/MacOS"
chmod u+x nym-vpn
```
6. Move `nym-vpn` to your `~/nym-vpn-latest` directory
```sh
mv nym-vpn "$HOME/nym-vpn-latest"
```
### Configuration
7. Create the configuration file by opening a text editor and saving the lines below as `config.toml` in the same directory `~/nym-vpn-latest`
```toml
env_config_file = ".env"
```
8. Create testnet configuration file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `.env` in the same directory `~/nym-vpn-latest`
```sh
curl -L "https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env" -o "$HOME/nym-vpn-latest/.env"
```
## Run NymVPN
**For NymVPN to work, all other VPNs must be switched off!** At this alpha stage of NymVPN, the network connection (wifi) must be reconnected after or in between the testing rounds.
Run:
```sh
sudo -E $HOME/nym-vpn-latest/nym-vpn
```
In case of errors check out the [troubleshooting](troubleshooting.html#installing-gui-on-macos-not-working) section.
@@ -1,6 +0,0 @@
#!/bin/bash
release_url="https://api.github.com/repos/nymtech/nym-vpn-client/releases"
current_cli_version=$(curl -s $release_url | jq -r '.[].tag_name' | grep '^nym-vpn-cli-v' | sort -Vr | head -n 1 | awk -F'-v' '{print $NF}')
echo "${current_cli_version}"
@@ -1,6 +0,0 @@
#!/bin/bash
release_url="https://api.github.com/repos/nymtech/nym-vpn-client/releases"
version=$(curl -s $release_url | jq -r '.[].tag_name' | grep '^nym-vpn-desktop-v' | sort -Vr | head -n 1 | awk -F'-v' '{print $NF}')
echo "${version}"
@@ -14,7 +14,7 @@ One of the main aims of NymVPN alpha release is testing; your results will help
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
1. Create a directory called `nym-vpn-tests` and copy your `nym-vpn-cli` binary ([download here]({{nym_vpn_releases}}))
1. Create a directory called `nym-vpn-tests` and copy your `nym-vpn-cli` binary ([download here]({{nym_vpn_latest_binary_url}}))
2. Copy or download [`sandbox.env`](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) testnet config file to the same directory
```sh
curl -o sandbox.env -L https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env
@@ -46,8 +46,6 @@ If you are running NymVPN on mac OS for the first time, you may see this alert m
3. Possibly you may have to confirm again upon running the application
<!--
#### Missing `jq` error
In case of missing `jq` on Linux (Debian) install it with:
@@ -86,4 +84,3 @@ NEW_ENDPOINT="http://localhost:8000/data.json"
python3 -m http.server 8000
```
6. Continue with the steps listed in [testing section](testing.md)
-->
@@ -682,8 +682,6 @@ Which should return:
Proxying various full node services through port 80 can then be done by creating a file with the following at `/etc/nginx/sites-enabled/nyxd-webrequests.conf`:
Setting up a reverse proxy using a webserver such as Nginx allows you to easily configure SSL certificates for the endpoints. When running on mainnet, it is recommended to encrypt all web traffic to your node.
```sh
### To expose RPC server
server {
@@ -697,14 +695,6 @@ server {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /websocket {
proxy_pass http://127.0.0.1:26657;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
}
### To expose Cosmos API server
@@ -2,6 +2,10 @@
[//]: # (> The nym-api binary was built in the [building nym]&#40;../binaries/building-nym.md&#41; section. If you haven't yet built Nym and want to run the code, go there first. You can build just the API with `cargo build --release --bin nym-api`.)
> The `nym-api` binary will be released in the immediate future - we're releasing this document beforehand so that validators have information as soon as possible and get an idea of what to expect. This doc will be expanded over time as we release the API binary itself as well as start enabling functionality.
>
> You can build the API with `cargo build --release --bin nym-api`.
> Any syntax in `<>` brackets is a user's unique variable. Exchange with a corresponding name without the `<>` brackets.
## What is the Nym API?
+2
View File
@@ -22,3 +22,5 @@ REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
NYXD="http://127.0.0.1:26657"
NYM_API="http://127.0.0.1:8000"
DKG_TIME_CONFIGURATION="600,300,300,60,60,1209600"
+7 -2
View File
@@ -11,13 +11,18 @@ MIX_DENOM_DISPLAY=nym
STAKE_DENOM=unyx
STAKE_DENOM_DISPLAY=nyx
DENOMS_EXPONENT=6
MIXNET_CONTRACT_ADDRESS=n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr
VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
GROUP_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m
MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
COCONUT_DKG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
EPHEMERA_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090"
NYXD="https://rpc.nymtech.net"
NYM_API="https://validator.nymtech.net/api/"
NYXD_WS="wss://rpc.nymtech.net/websocket"
EXPLORER_API="https://explorer.nymtech.net/api/"
DKG_TIME_CONFIGURATION="259200,300,300,60,60,1209600"
+1
View File
@@ -25,4 +25,5 @@ EXPLORER_API=https://qa-network-explorer.qa.nymte.ch/api
NYXD="https://qa-validator.qa.nymte.ch"
NYM_API="https://qa-nym-api.qa.nymte.ch/api"
DKG_TIME_CONFIGURATION="600,300,300,60,60,1209600"
EXIT_POLICY="https://nymtech.net/.wellknown/network-requester/exit-policy.txt"
+6 -6
View File
@@ -112,12 +112,12 @@ pub(crate) struct NodeStats {
)]
previous_update_time: SystemTime,
packets_received_since_startup: f64,
packets_sent_since_startup: f64,
packets_explicitly_dropped_since_startup: f64,
packets_received_since_last_update: f64,
packets_sent_since_last_update: f64,
packets_explicitly_dropped_since_last_update: f64,
packets_received_since_startup: u64,
packets_sent_since_startup: u64,
packets_explicitly_dropped_since_startup: u64,
packets_received_since_last_update: u64,
packets_sent_since_last_update: u64,
packets_explicitly_dropped_since_last_update: u64,
}
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
+3
View File
@@ -78,6 +78,9 @@ nym-task = { path = "../common/task" }
nym-types = { path = "../common/types" }
nym-validator-client = { path = "../common/client-libs/validator-client" }
nym-ip-packet-router = { path = "../service-providers/ip-packet-router" }
nym-topology-control = { path = "../common/topology-control" }
nym-topology = { path = "../common/topology" }
nym-noise = { path = "../common/nymnoise" }
nym-wireguard = { path = "../common/wireguard", optional = true }
defguard_wireguard_rs = { git = "https://github.com/neacsu/wireguard-rs.git", rev = "c2cd0c1119f699f4bc43f5e6ffd6fc242caa42ed", optional = true }
-8
View File
@@ -28,14 +28,6 @@ nym-sphinx = { path = "../../common/nymsphinx" }
nym-credentials = { path = "../../common/credentials" }
nym-credentials-interface = { path = "../../common/credentials-interface" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
workspace = true
features = ["time"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
workspace = true
features = ["tokio"]
[dependencies.tungstenite]
workspace = true
default-features = false
+2 -2
View File
@@ -13,7 +13,7 @@ pub mod models;
pub mod registration;
pub mod types;
pub const CURRENT_PROTOCOL_VERSION: u8 = CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION;
pub const CURRENT_PROTOCOL_VERSION: u8 = CREDENTIAL_UPDATE_V1_PROTOCOL_VERSION;
/// Defines the current version of the communication protocol between gateway and clients.
/// It has to be incremented for any breaking change.
@@ -21,7 +21,7 @@ pub const CURRENT_PROTOCOL_VERSION: u8 = CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION;
// 1 - initial release
// 2 - changes to client credentials structure
pub const INITIAL_PROTOCOL_VERSION: u8 = 1;
pub const CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION: u8 = 2;
pub const CREDENTIAL_UPDATE_V1_PROTOCOL_VERSION: u8 = 2;
pub type GatewayMac = HmacOutput<GatewayIntegrityHmacAlgorithm>;
@@ -24,18 +24,11 @@ impl<'a> ClientHandshake<'a> {
ws_stream: &'a mut S,
identity: &'a nym_crypto::asymmetric::identity::KeyPair,
gateway_pubkey: identity::PublicKey,
expects_credential_usage: bool,
) -> Self
where
S: Stream<Item = WsItem> + Sink<WsMessage> + Unpin + Send + 'a,
{
let mut state = State::new(
rng,
ws_stream,
identity,
Some(gateway_pubkey),
expects_credential_usage,
);
let mut state = State::new(rng, ws_stream, identity, Some(gateway_pubkey));
ClientHandshake {
handshake_future: Box::pin(async move {
@@ -25,7 +25,4 @@ pub enum HandshakeError {
MalformedRequest,
#[error("sent request was malformed")]
HandshakeFailure,
#[error("timed out waiting for a handshake message")]
Timeout,
}
@@ -26,7 +26,7 @@ impl<'a> GatewayHandshake<'a> {
where
S: Stream<Item = WsItem> + Sink<WsMessage> + Unpin + Send + 'a,
{
let mut state = State::new(rng, ws_stream, identity, None, true);
let mut state = State::new(rng, ws_stream, identity, None);
GatewayHandshake {
handshake_future: Box::pin(async move {
// If any step along the way failed (that are non-network related),
@@ -30,19 +30,11 @@ pub async fn client_handshake<'a, S>(
ws_stream: &'a mut S,
identity: &'a identity::KeyPair,
gateway_pubkey: identity::PublicKey,
expects_credential_usage: bool,
) -> Result<SharedKeys, HandshakeError>
where
S: Stream<Item = WsItem> + Sink<WsMessage> + Unpin + Send + 'a,
{
ClientHandshake::new(
rng,
ws_stream,
identity,
gateway_pubkey,
expects_credential_usage,
)
.await
ClientHandshake::new(rng, ws_stream, identity, gateway_pubkey).await
}
#[cfg(not(target_arch = "wasm32"))]
@@ -1,4 +1,4 @@
// Copyright 2020-2024 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::registration::handshake::error::HandshakeError;
@@ -15,17 +15,9 @@ use nym_crypto::{
};
use nym_sphinx::params::{GatewayEncryptionAlgorithm, GatewaySharedKeyHkdfAlgorithm};
use rand::{CryptoRng, RngCore};
use std::convert::TryInto;
use std::str::FromStr;
use std::time::Duration;
use std::convert::{TryFrom, TryInto};
use tungstenite::Message as WsMessage;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::timeout;
#[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::timeout;
/// Handshake state.
pub(crate) struct State<'a, S> {
/// The underlying WebSocket stream.
@@ -44,10 +36,6 @@ pub(crate) struct State<'a, S> {
/// The known or received public identity key of the remote.
/// Ideally it would always be known before the handshake was initiated.
remote_pubkey: Option<identity::PublicKey>,
// this field is really out of place here, however, we need to propagate this information somehow
// in order to establish correct protocol for backwards compatibility reasons
expects_credential_usage: bool,
}
impl<'a, S> State<'a, S> {
@@ -56,7 +44,6 @@ impl<'a, S> State<'a, S> {
ws_stream: &'a mut S,
identity: &'a identity::KeyPair,
remote_pubkey: Option<identity::PublicKey>,
expects_credential_usage: bool,
) -> Self {
let ephemeral_keypair = encryption::KeyPair::new(rng);
State {
@@ -65,7 +52,6 @@ impl<'a, S> State<'a, S> {
identity,
remote_pubkey,
derived_shared_keys: None,
expects_credential_usage,
}
}
@@ -81,8 +67,15 @@ impl<'a, S> State<'a, S> {
self.identity
.public_key()
.to_bytes()
.into_iter()
.chain(self.ephemeral_keypair.public_key().to_bytes())
.iter()
.cloned()
.chain(
self.ephemeral_keypair
.public_key()
.to_bytes()
.iter()
.cloned(),
)
.collect()
}
@@ -139,8 +132,9 @@ impl<'a, S> State<'a, S> {
.ephemeral_keypair
.public_key()
.to_bytes()
.into_iter()
.chain(remote_ephemeral_key.to_bytes())
.iter()
.cloned()
.chain(remote_ephemeral_key.to_bytes().iter().cloned())
.collect();
let signature = self.identity.private_key().sign(message);
@@ -183,8 +177,15 @@ impl<'a, S> State<'a, S> {
// g^y || g^x, if y is remote and x is local
let signed_payload: Vec<_> = remote_ephemeral_key
.to_bytes()
.into_iter()
.chain(self.ephemeral_keypair.public_key().to_bytes())
.iter()
.cloned()
.chain(
self.ephemeral_keypair
.public_key()
.to_bytes()
.iter()
.cloned(),
)
.collect();
self.remote_pubkey
@@ -199,54 +200,33 @@ impl<'a, S> State<'a, S> {
self.remote_pubkey = Some(remote_pubkey)
}
async fn _receive_handshake_message(&mut self) -> Result<Vec<u8>, HandshakeError>
where
S: Stream<Item = WsItem> + Unpin,
{
loop {
let Some(msg) = self.ws_stream.next().await else {
return Err(HandshakeError::ClosedStream);
};
let Ok(msg) = msg else {
return Err(HandshakeError::NetworkError);
};
match msg {
WsMessage::Text(ref ws_msg) => {
match types::RegistrationHandshake::from_str(ws_msg) {
Ok(reg_handshake_msg) => {
return match reg_handshake_msg {
// hehe, that's a bit disgusting that the type system requires we explicitly ignore the
// protocol_version field that we actually never attach at this point
// yet another reason for the overdue refactor
types::RegistrationHandshake::HandshakePayload { data, .. } => {
Ok(data)
}
types::RegistrationHandshake::HandshakeError { message } => {
Err(HandshakeError::RemoteError(message))
}
};
}
Err(_) => {
error!("Received a non-handshake message during the registration handshake! It's getting dropped. The received content was: '{msg}'");
continue;
}
}
}
_ => error!("Received non-text message during registration handshake"),
}
}
}
pub(crate) async fn receive_handshake_message(&mut self) -> Result<Vec<u8>, HandshakeError>
where
S: Stream<Item = WsItem> + Unpin,
{
// TODO: make timeout duration configurable
timeout(Duration::from_secs(5), self._receive_handshake_message())
.await
.map_err(|_| HandshakeError::Timeout)?
loop {
if let Some(msg) = self.ws_stream.next().await {
if let Ok(msg) = msg {
match msg {
WsMessage::Text(ws_msg) => match types::RegistrationHandshake::try_from(ws_msg) {
Ok(reg_handshake_msg) => return match reg_handshake_msg {
// hehe, that's a bit disgusting that the type system requires we explicitly ignore the
// protocol_version field that we actually never attach at this point
// yet another reason for the overdue refactor
types::RegistrationHandshake::HandshakePayload { data, .. } => Ok(data),
types::RegistrationHandshake::HandshakeError { message } => Err(HandshakeError::RemoteError(message)),
},
Err(_) => error!("Received a non-handshake message during the registration handshake! It's getting dropped."),
},
_ => error!("Received non-text message during registration handshake"),
}
} else {
return Err(HandshakeError::NetworkError);
}
} else {
return Err(HandshakeError::ClosedStream);
}
}
}
// upon receiving this, the receiver should terminate the handshake
@@ -271,8 +251,7 @@ impl<'a, S> State<'a, S> {
where
S: Sink<WsMessage> + Unpin,
{
let handshake_message =
types::RegistrationHandshake::new_payload(payload, self.expects_credential_usage);
let handshake_message = types::RegistrationHandshake::new_payload(payload);
self.ws_stream
.send(WsMessage::Text(handshake_message.try_into().unwrap()))
.await

Some files were not shown because too many files have changed in this diff Show More