Compare commits

..

1 Commits

Author SHA1 Message Date
Jon Häggblad 0b5d38094f wip 2024-03-08 21:40:05 +01:00
146 changed files with 1045 additions and 40987 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
+70 -167
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,7 +5209,6 @@ dependencies = [
"nym-explorer-client",
"nym-gateway-client",
"nym-gateway-requests",
"nym-metrics",
"nym-network-defaults",
"nym-nonexhaustive-delayqueue",
"nym-pemstore",
@@ -5587,7 +5522,7 @@ dependencies = [
"dotenvy",
"futures",
"humantime-serde",
"hyper 0.14.27",
"hyper",
"ipnetwork 0.16.0",
"log",
"nym-api-requests",
@@ -5678,9 +5613,7 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
"tokio",
"tungstenite",
"wasmtimer",
"zeroize",
]
@@ -5784,16 +5717,6 @@ 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"
@@ -5834,19 +5757,19 @@ 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",
@@ -5859,6 +5782,7 @@ dependencies = [
"nym-topology",
"nym-types",
"nym-validator-client",
"opentelemetry",
"rand 0.7.3",
"serde",
"serde_json",
@@ -5867,6 +5791,7 @@ dependencies = [
"tokio",
"tokio-util",
"toml 0.5.11",
"tracing",
"url",
]
@@ -5875,12 +5800,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 +5821,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-util",
"tracing",
"url",
]
@@ -6022,7 +5949,7 @@ dependencies = [
"dashmap",
"fastrand 2.0.1",
"hmac 0.12.1",
"hyper 0.14.27",
"hyper",
"ipnetwork 0.16.0",
"mime",
"nym-config",
@@ -6175,7 +6102,7 @@ dependencies = [
"dotenvy",
"futures",
"hex",
"http 0.2.9",
"http",
"httpcodec",
"libp2p",
"log",
@@ -6915,7 +6842,7 @@ checksum = "a819b71d6530c4297b49b3cae2939ab3a8cc1b9f382826a1bc29dd0ca3864906"
dependencies = [
"async-trait",
"bytes",
"http 0.2.9",
"http",
"isahc",
"opentelemetry_api",
]
@@ -6929,7 +6856,7 @@ dependencies = [
"async-trait",
"futures",
"futures-executor",
"http 0.2.9",
"http",
"isahc",
"once_cell",
"opentelemetry",
@@ -7514,21 +7441,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 +7562,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.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc74a6e6a56708be1cf5c4c4d1a0dc21d33b2dcaa24e731b7fa9c287ce4f916f"
checksum = "383703acfc34f7a00724846c14dc5ea4407c59e5aedcbbb18a1c0c1a23fe5013"
dependencies = [
"psl-types",
]
@@ -8056,13 +7962,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 +7983,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 +8016,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 +8177,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 +8197,8 @@ dependencies = [
"cookie",
"either",
"futures",
"http 0.2.9",
"hyper 0.14.27",
"http",
"hyper",
"indexmap 2.0.2",
"log",
"memchr",
@@ -8902,9 +8808,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.6.0"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b0ed1662c5a68664f45b76d18deb0e234aff37207086803165c961eb695e981"
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
dependencies = [
"base64 0.21.4",
"chrono",
@@ -8919,11 +8825,11 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.6.0"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "568577ff0ef47b879f736cd66740e022f3672788cdf002a05a4e609ea5a6fb15"
checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
dependencies = [
"darling 0.20.5",
"darling 0.20.3",
"proc-macro2",
"quote",
"syn 2.0.38",
@@ -9096,9 +9002,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 +9853,7 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
dependencies = [
"futures-util",
"log",
"rustls 0.21.10",
"rustls-native-certs",
"tokio",
"tokio-rustls 0.24.1",
"tungstenite",
]
@@ -10053,9 +9956,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 +10001,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 +10280,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"
+3 -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,7 +38,6 @@ 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" }
@@ -49,19 +48,6 @@ 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 +58,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 +91,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"]
@@ -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 {
@@ -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!()))))
}
+12
View File
@@ -4,6 +4,7 @@
use crate::client::mix_traffic::transceiver::ErasedGatewayError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_gateway_client::error::GatewayClientError;
use nym_task::manager::TaskStatusTrait;
use nym_topology::gateway::GatewayConversionError;
use nym_topology::NymTopologyError;
use nym_validator_client::ValidatorClientError;
@@ -165,3 +166,14 @@ pub enum ClientCoreStatusMessage {
#[error("The connected gateway is very slow, or the connection to it is very slow")]
GatewayIsVerySlow,
}
// impl TaskStatusTrait for ClientCoreStatusMessage {}
// impl<T: std::fmt::Debug + std::fmt::Display + Send + Sync + Any + 'static> TaskStatusTrait for T {
// fn as_any(&self) -> &dyn Any {
// self
// }
//
// fn as_any_mut(&mut self) -> &mut dyn Any {
// self
// }
// }
@@ -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))
}
+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 -13
View File
@@ -2,22 +2,13 @@ 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 = 4;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair {
@@ -33,7 +24,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),
@@ -116,59 +116,24 @@ impl IpPacketResponse {
) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::Info(InfoResponse {
data: IpPacketResponseData::Error(ErrorResponse {
request_id,
reply_to,
reply: InfoResponseReply::VersionMismatch {
reply: ErrorResponseReply::VersionMismatch {
request_version,
response_version: our_version,
},
level: InfoLevel::Error,
}),
}
}
pub fn new_data_info_response(
reply_to: Recipient,
reply: InfoResponseReply,
level: InfoLevel,
) -> Self {
pub fn new_data_error_response(reply_to: Recipient, reply: ErrorResponseReply) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::Info(InfoResponse {
data: IpPacketResponseData::Error(ErrorResponse {
request_id: 0,
reply_to,
reply,
level,
}),
}
}
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,
},
}),
}
}
@@ -182,7 +147,7 @@ impl IpPacketResponse {
IpPacketResponseData::Data(_) => None,
IpPacketResponseData::Pong(response) => Some(response.request_id),
IpPacketResponseData::Health(response) => Some(response.request_id),
IpPacketResponseData::Info(response) => Some(response.request_id),
IpPacketResponseData::Error(response) => Some(response.request_id),
}
}
@@ -195,7 +160,7 @@ impl IpPacketResponse {
IpPacketResponseData::Data(_) => None,
IpPacketResponseData::Pong(response) => Some(&response.reply_to),
IpPacketResponseData::Health(response) => Some(&response.reply_to),
IpPacketResponseData::Info(response) => Some(&response.reply_to),
IpPacketResponseData::Error(response) => Some(&response.reply_to),
}
}
@@ -236,8 +201,8 @@ pub enum IpPacketResponseData {
// Response for a health request
Health(HealthResponse),
// Info response. This can be anything from informative messages to errors
Info(InfoResponse),
// Error response
Error(ErrorResponse),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -373,15 +338,14 @@ pub struct HealthResponseReply {
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct InfoResponse {
pub struct ErrorResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: InfoResponseReply,
pub level: InfoLevel,
pub reply: ErrorResponseReply,
}
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
pub enum InfoResponseReply {
pub enum ErrorResponseReply {
#[error("{msg}")]
Generic { msg: String },
#[error(
@@ -394,10 +358,3 @@ pub enum InfoResponseReply {
#[error("destination failed exit policy filter check: {dst}")]
ExitPolicyFilterCheckFailed { dst: String },
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum InfoLevel {
Info,
Warn,
Error,
}
-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"
)
}
}
+9
View File
@@ -1,6 +1,7 @@
use crate::socks::types::SocksProxyError;
use nym_client_core::error::ClientCoreError;
use nym_socks5_requests::{ConnectionError, ConnectionId};
use nym_task::manager::TaskStatusTrait;
#[derive(thiserror::Error, Debug)]
pub enum Socks5ClientCoreError {
@@ -20,6 +21,14 @@ pub enum Socks5ClientCoreError {
},
}
#[derive(thiserror::Error, Debug)]
pub enum Socks5ClientCoreStatusMessage {
#[error(transparent)]
Socks5Error(#[from] Socks5ClientCoreError),
}
// impl TaskStatusTrait for Socks5ClientCoreStatusMessage {}
impl From<ConnectionError> for Socks5ClientCoreError {
fn from(value: ConnectionError) -> Self {
Socks5ClientCoreError::NetworkRequesterError {
+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;
@@ -1,7 +1,7 @@
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::Socks5ClientCoreError;
use crate::error::{Socks5ClientCoreError, Socks5ClientCoreStatusMessage};
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
@@ -136,7 +136,8 @@ impl MixnetResponseListener {
if let Some(received_responses) = received_responses {
for reconstructed_message in received_responses {
if let Err(err) = self.on_message(reconstructed_message) {
self.shutdown.send_status_msg(Box::new(err));
let msg = Socks5ClientCoreStatusMessage::from(err);
self.shutdown.send_status_msg(Box::new(msg));
}
}
} else {
+28 -9
View File
@@ -3,6 +3,7 @@
use futures::{future::pending, FutureExt, SinkExt, StreamExt};
use log::{log, Level};
use std::any::Any;
use std::future::Future;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{error::Error, time::Duration};
@@ -23,7 +24,7 @@ pub(crate) type SentError = Box<dyn Error + Send + Sync>;
type ErrorSender = mpsc::UnboundedSender<SentError>;
type ErrorReceiver = mpsc::UnboundedReceiver<SentError>;
pub type SentStatus = Box<dyn Error + Send + Sync>;
pub type SentStatus = Box<dyn TaskStatusTrait + Send + Sync>;
pub type StatusSender = futures::channel::mpsc::Sender<SentStatus>;
pub type StatusReceiver = futures::channel::mpsc::Receiver<SentStatus>;
@@ -46,10 +47,32 @@ enum TaskError {
pub enum TaskStatus {
#[error("Ready")]
Ready,
#[error("Ready and connected to gateway: {0}")]
ReadyWithGateway(String),
}
pub trait TaskStatusTrait:
std::fmt::Debug + std::fmt::Display + Send + Sync + 'static + Any
{
// As Any requires 'static, it implicitly enforces the 'static lifetime here as well.
// This method tries to cast the trait object back to a reference of its concrete type.
fn as_any(&self) -> &dyn Any;
// Optionally, for downcasting to mutable references.
fn as_any_mut(&mut self) -> &mut dyn Any;
}
impl<T: std::fmt::Debug + std::fmt::Display + Send + Sync + Any + 'static> TaskStatusTrait for T {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
// impl TaskStatusTrait for TaskStatus {}
/// Listens to status and error messages from tasks, as well as notifying them to gracefully
/// shutdown. Keeps track of if task stop unexpectedly, such as in a panic.
#[derive(Debug)]
@@ -163,14 +186,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);
};
+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)]
-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
+5 -31
View File
@@ -5,7 +5,7 @@ use crate::authentication::encrypted_address::EncryptedAddressBytes;
use crate::iv::IV;
use crate::models::{CredentialSpendingRequest, OldV1Credential};
use crate::registration::handshake::SharedKeys;
use crate::{GatewayMacSize, CURRENT_PROTOCOL_VERSION, INITIAL_PROTOCOL_VERSION};
use crate::{GatewayMacSize, CURRENT_PROTOCOL_VERSION};
use log::error;
use nym_credentials::coconut::bandwidth::CredentialSpendingData;
use nym_credentials_interface::{CoconutError, UnknownCredentialType};
@@ -19,7 +19,6 @@ use nym_sphinx::params::{GatewayEncryptionAlgorithm, GatewayIntegrityHmacAlgorit
use nym_sphinx::DestinationAddressBytes;
use serde::{Deserialize, Serialize};
use std::convert::{TryFrom, TryInto};
use std::str::FromStr;
use std::string::FromUtf8Error;
use thiserror::Error;
use tungstenite::protocol::Message;
@@ -38,17 +37,9 @@ pub enum RegistrationHandshake {
}
impl RegistrationHandshake {
pub fn new_payload(data: Vec<u8>, will_use_credentials: bool) -> Self {
// if we're not going to be using credentials, advertise lower protocol version to allow connection
// to wider range of gateways
let protocol_version = if will_use_credentials {
Some(CURRENT_PROTOCOL_VERSION)
} else {
Some(INITIAL_PROTOCOL_VERSION)
};
pub fn new_payload(data: Vec<u8>) -> Self {
RegistrationHandshake::HandshakePayload {
protocol_version,
protocol_version: Some(CURRENT_PROTOCOL_VERSION),
data,
}
}
@@ -60,19 +51,11 @@ impl RegistrationHandshake {
}
}
impl FromStr for RegistrationHandshake {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_str(s)
}
}
impl TryFrom<String> for RegistrationHandshake {
type Error = serde_json::Error;
fn try_from(msg: String) -> Result<Self, serde_json::Error> {
msg.parse()
serde_json::from_str(&msg)
}
}
@@ -172,18 +155,9 @@ impl ClientControlRequest {
address: DestinationAddressBytes,
enc_address: EncryptedAddressBytes,
iv: IV,
uses_credentials: bool,
) -> Self {
// if we're not going to be using credentials, advertise lower protocol version to allow connection
// to wider range of gateways
let protocol_version = if uses_credentials {
Some(CURRENT_PROTOCOL_VERSION)
} else {
Some(INITIAL_PROTOCOL_VERSION)
};
ClientControlRequest::Authenticate {
protocol_version,
protocol_version: Some(CURRENT_PROTOCOL_VERSION),
address: address.as_base58_string(),
enc_address: enc_address.to_base58_string(),
iv: iv.to_base58_string(),
+8 -9
View File
@@ -88,19 +88,18 @@ impl OverrideConfig {
if config.network_requester.enabled
&& config.storage_paths.network_requester_config.is_none()
{
config = config.with_default_network_requester_config_path();
}
if config.ip_packet_router.enabled && config.storage_paths.ip_packet_router_config.is_none()
Ok(config.with_default_network_requester_config_path())
} else if config.ip_packet_router.enabled
&& config.storage_paths.ip_packet_router_config.is_none()
{
config = config.with_default_ip_packet_router_config_path();
Ok(config.with_default_ip_packet_router_config_path())
} else {
Ok(config)
}
Ok(config)
}
}
#[derive(Default, Debug)]
#[derive(Default)]
pub(crate) struct OverrideNetworkRequesterConfig {
pub(crate) fastmode: bool,
pub(crate) no_cover: bool,
@@ -113,7 +112,7 @@ pub(crate) struct OverrideNetworkRequesterConfig {
pub(crate) statistics_recipient: Option<String>,
}
#[derive(Default, Debug)]
#[derive(Default)]
pub(crate) struct OverrideIpPacketRouterConfig {
// TODO
}
+4 -6
View File
@@ -17,7 +17,7 @@ use std::{fs, io};
use super::helpers::OverrideIpPacketRouterConfig;
#[derive(Args, Clone, Debug)]
#[derive(Args, Clone)]
pub struct Init {
/// Id of the gateway we want to create config for
#[clap(long)]
@@ -82,11 +82,11 @@ pub struct Init {
statistics_service_url: Option<url::Url>,
/// Allows this gateway to run an embedded network requester for minimal network overhead
#[clap(long)]
#[clap(long, conflicts_with = "with_ip_packet_router")]
with_network_requester: bool,
/// Allows this gateway to run an embedded network requester for minimal network overhead
#[clap(long, hide = true)]
#[clap(long, hide = true, conflicts_with = "with_network_requester")]
with_ip_packet_router: bool,
// ##### NETWORK REQUESTER FLAGS #####
@@ -243,9 +243,7 @@ pub async fn execute(args: Init) -> anyhow::Result<()> {
if config.network_requester.enabled {
initialise_local_network_requester(&config, nr_opts, *identity_keys.public_key())
.await?;
}
if config.ip_packet_router.enabled {
} else if config.ip_packet_router.enabled {
initialise_local_ip_packet_router(&config, ip_opts, *identity_keys.public_key())
.await?;
}
+2 -1
View File
@@ -5,6 +5,7 @@ use crate::Cli;
use clap::CommandFactory;
use clap::Subcommand;
use nym_bin_common::completions::{fig_generate, ArgShell};
use std::error::Error;
pub(crate) mod build_info;
pub(crate) mod helpers;
@@ -49,7 +50,7 @@ pub(crate) enum Commands {
GenerateFigSpec,
}
pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> {
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
let bin_name = "nym-gateway";
match args.command {
+2 -2
View File
@@ -87,11 +87,11 @@ pub struct Run {
statistics_service_url: Option<url::Url>,
/// Allows this gateway to run an embedded network requester for minimal network overhead
#[arg(long)]
#[arg(long, conflicts_with = "with_ip_packet_router")]
with_network_requester: Option<bool>,
/// Allows this gateway to run an embedded network requester for minimal network overhead
#[arg(long, hide = true)]
#[arg(long, hide = true, conflicts_with = "with_network_requester")]
with_ip_packet_router: Option<bool>,
// ##### NETWORK REQUESTER FLAGS #####
+2 -1
View File
@@ -11,6 +11,7 @@ use nym_bin_common::bin_info;
use nym_bin_common::logging::{maybe_print_banner, setup_logging};
use nym_bin_common::output_format::OutputFormat;
use nym_network_defaults::setup_env;
use std::error::Error;
use std::sync::OnceLock;
mod commands;
@@ -41,7 +42,7 @@ struct Cli {
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
setup_logging();
let args = Cli::parse();
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use super::websocket::message_receiver::{IsActiveRequestSender, MixMessageSender};
use crate::node::client_handling::embedded_clients::LocalEmbeddedClientHandle;
use crate::node::client_handling::embedded_network_requester::LocalNetworkRequesterHandle;
use dashmap::DashMap;
use log::warn;
use nym_sphinx::DestinationAddressBytes;
@@ -12,8 +12,8 @@ enum ActiveClient {
/// Handle to a remote client connected via a network socket.
Remote(ClientIncomingChannels),
/// Handle to a locally (inside the same process) running client.
Embedded(LocalEmbeddedClientHandle),
/// Handle to a locally (inside the same process) running network requester client.
Embedded(LocalNetworkRequesterHandle),
}
impl ActiveClient {
@@ -149,14 +149,13 @@ impl ActiveClientsStore {
}
}
/// Inserts a handle to the embedded client
pub(crate) fn insert_embedded(&self, local_client_handle: LocalEmbeddedClientHandle) {
let key = local_client_handle.client_destination();
let entry = ActiveClient::Embedded(local_client_handle);
/// Inserts a handle to the embedded network requester
pub(crate) fn insert_embedded(&self, local_nr_handle: LocalNetworkRequesterHandle) {
let key = local_nr_handle.client_destination();
let entry = ActiveClient::Embedded(local_nr_handle);
if self.inner.insert(key, entry).is_some() {
// this is literally impossible since we're starting the local embedded client before
// even spawning the websocket listener task
panic!("somehow we already had a client with the same address as our local embedded client!")
// this is literally impossible since we're starting local NR before even spawning the websocket listener task
panic!("somehow we already had a client with the same address as our local NR!")
}
}
@@ -12,15 +12,15 @@ use nym_sphinx::DestinationAddressBytes;
use nym_task::TaskClient;
#[derive(Debug)]
pub(crate) struct LocalEmbeddedClientHandle {
/// Nym address of the embedded client.
pub(crate) struct LocalNetworkRequesterHandle {
/// Nym address of the embedded network requester.
pub(crate) address: Recipient,
/// Message channel used internally to forward any received mix packets to the client.
/// Message channel used internally to forward any received mix packets to the network requester.
pub(crate) mix_message_sender: MixMessageSender,
}
impl LocalEmbeddedClientHandle {
impl LocalNetworkRequesterHandle {
pub(crate) fn new(address: Recipient, mix_message_sender: MixMessageSender) -> Self {
Self {
address,
@@ -28,6 +28,17 @@ impl LocalEmbeddedClientHandle {
}
}
// TODO: generalize this whole thing to be general. And change the name(s).
pub(crate) fn new_ip(
start_data: nym_ip_packet_router::OnStartData,
mix_message_sender: MixMessageSender,
) -> Self {
Self {
address: start_data.address,
mix_message_sender,
}
}
pub(crate) fn client_destination(&self) -> DestinationAddressBytes {
self.address.identity().derive_destination_address()
}
@@ -37,8 +48,8 @@ impl LocalEmbeddedClientHandle {
// calling the method. however, this would have caused slightly more complexity and more overhead
// (due to more data being copied to every [mix] connection)
//
/// task responsible for receiving messages for locally embedded clients from multiple mix
/// connections and forwarding them via the router. kinda equivalent of a client socket handler
/// task responsible for receiving messages for locally NR requester from multiple mix connections
/// and forwarding them via the router. kinda equivalent of a client socket handler
pub(crate) struct MessageRouter {
mix_receiver: MixMessageReceiver,
packet_router: PacketRouter,
@@ -60,29 +71,29 @@ impl MessageRouter {
if let Err(err) = self.packet_router.route_received(messages) {
// TODO: what should we do here? I don't think this could/should ever fail.
// is panicking the appropriate thing to do then?
error!("failed to route packets to local embedded client: {err}")
error!("failed to route packets to local NR: {err}")
}
}
pub(crate) async fn run_with_shutdown(mut self, mut shutdown: TaskClient) {
debug!("Started embedded client message router with graceful shutdown support");
debug!("Started embedded network requester message router with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
messages = self.mix_receiver.next() => match messages {
Some(messages) => self.handle_received_messages(messages),
None => {
log::trace!("embedded_clients::MessageRouter: Stopping since channel closed");
log::trace!("embedded_network_requester::MessageRouter: Stopping since channel closed");
break;
}
},
_ = shutdown.recv_with_delay() => {
log::trace!("embedded_clients::MessageRouter: Received shutdown");
log::trace!("embedded_network_requester::MessageRouter: Received shutdown");
debug_assert!(shutdown.is_shutdown());
break
}
}
}
debug!("embedded_network_clients::MessageRouter: Exiting")
debug!("embedded_network_requester::MessageRouter: Exiting")
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ use crate::node::client_handling::bandwidth::Bandwidth;
pub(crate) mod active_clients;
mod bandwidth;
pub(crate) mod embedded_clients;
pub(crate) mod embedded_network_requester;
pub(crate) mod websocket;
pub(crate) const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new(64 * 1024 * 1024 * 1024); // 64GB
@@ -92,9 +92,6 @@ pub(crate) enum RequestHandlingError {
#[error("the provided credential did not have a bandwidth attribute")]
MissingBandwidthAttribute,
#[error("the DKG contract is unavailable")]
UnavailableDkgContract,
}
impl RequestHandlingError {
@@ -603,7 +600,7 @@ where
None => break,
Some(Ok(socket_msg)) => socket_msg,
Some(Err(err)) => {
debug!("failed to obtain message from websocket stream! stopping connection handler: {err}");
error!("failed to obtain message from websocket stream! stopping connection handler: {err}");
break;
}
};
@@ -614,7 +611,7 @@ where
if let Some(response) = self.handle_request(socket_msg).await {
if let Err(err) = self.inner.send_websocket_message(response).await {
debug!(
warn!(
"Failed to send message over websocket: {err}. Assuming the connection is dead.",
);
break;
@@ -624,13 +621,13 @@ where
mix_messages = self.mix_receiver.next() => {
let mix_messages = match mix_messages {
None => {
debug!("mix receiver was closed! Assuming the connection is dead.");
warn!("mix receiver was closed! Assuming the connection is dead.");
break;
}
Some(mix_messages) => mix_messages,
};
if let Err(err) = self.inner.push_packets_to_client(&self.client.shared_keys, mix_messages).await {
debug!("failed to send the unwrapped sphinx packets back to the client - {err}, assuming the connection is dead");
warn!("failed to send the unwrapped sphinx packets back to the client - {err}, assuming the connection is dead");
break;
}
}
@@ -36,7 +36,6 @@ pub(crate) struct CoconutVerifier {
impl CoconutVerifier {
pub async fn new(
nyxd_client: DirectSigningHttpRpcNyxdClient,
only_coconut_credentials: bool,
) -> Result<Self, RequestHandlingError> {
let mix_denom_base = nyxd_client.current_chain_details().mix_denom.base.clone();
let address = nyxd_client.address();
@@ -46,17 +45,9 @@ impl CoconutVerifier {
// don't make it a hard failure in case we're running on mainnet (where DKG hasn't been deployed yet)
if nyxd_client.dkg_contract_address().is_none() {
if !only_coconut_credentials {
warn!(
"the DKG contract address is not available - \
no coconut credentials will be redeemable \
(if the DKG ceremony hasn't been run yet this warning is expected)"
);
} else {
// if we require coconut credentials, we MUST have DKG contract available
return Err(RequestHandlingError::UnavailableDkgContract);
}
error!(
"DKG contract address is not available - no coconut credentials will be redeemable"
);
return Ok(CoconutVerifier {
address,
nyxd_client: RwLock::new(nyxd_client),
@@ -69,10 +60,6 @@ impl CoconutVerifier {
let Ok(current_epoch) = nyxd_client.get_current_epoch().await else {
// another case of somebody putting a placeholder address that doesn't exist
error!("the specified DKG contract address is invalid - no coconut credentials will be redeemable");
if only_coconut_credentials {
// if we require coconut credentials, we MUST have DKG contract available
return Err(RequestHandlingError::UnavailableDkgContract);
}
return Ok(CoconutVerifier {
address,
nyxd_client: RwLock::new(nyxd_client),
@@ -667,7 +667,7 @@ where
let msg = match msg {
Ok(msg) => msg,
Err(err) => {
debug!("failed to obtain message from websocket stream! stopping connection handler: {err}");
error!("failed to obtain message from websocket stream! stopping connection handler: {err}");
break;
}
};
@@ -723,7 +723,7 @@ where
Message::Binary(_) => {
// perhaps logging level should be reduced here, let's leave it for now and see what happens
// if client is working correctly, this should have never happened
debug!("possibly received a sphinx packet without prior authentication. Request is going to be ignored");
warn!("possibly received a sphinx packet without prior authentication. Request is going to be ignored");
if let Err(err) = self
.send_websocket_message(
ServerResponse::new_error(
+22 -11
View File
@@ -11,7 +11,9 @@ use crate::config::Config;
use crate::error::GatewayError;
use crate::http::HttpApiBuilder;
use crate::node::client_handling::active_clients::ActiveClientsStore;
use crate::node::client_handling::embedded_clients::{LocalEmbeddedClientHandle, MessageRouter};
use crate::node::client_handling::embedded_network_requester::{
LocalNetworkRequesterHandle, MessageRouter,
};
use crate::node::client_handling::websocket;
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
use crate::node::helpers::{initialise_main_storage, load_network_requester_config};
@@ -49,7 +51,7 @@ struct StartedNetworkRequester {
used_request_filter: RequestFilter,
/// Handle to interact with the local network requester
handle: LocalEmbeddedClientHandle,
handle: LocalNetworkRequesterHandle,
}
/// Wire up and create Gateway instance
@@ -78,7 +80,7 @@ pub(crate) async fn create_gateway(
let cfg = load_ip_packet_router_config(&config.gateway.id, path)?;
Some(override_ip_packet_router_config(cfg, ip_config_override))
} else {
// if IPR is enabled, the config path must be specified
// if NR is enabled, the config path must be specified
return Err(GatewayError::UnspecifiedIpPacketRouterConfig);
}
} else {
@@ -317,7 +319,7 @@ impl<St> Gateway<St> {
info!("the local network requester is running on {address}",);
Ok(StartedNetworkRequester {
used_request_filter: start_data.request_filter,
handle: LocalEmbeddedClientHandle::new(address, nr_mix_sender),
handle: LocalNetworkRequesterHandle::new(address, nr_mix_sender),
})
}
@@ -325,16 +327,17 @@ impl<St> Gateway<St> {
&self,
forwarding_channel: MixForwardingSender,
shutdown: TaskClient,
) -> Result<LocalEmbeddedClientHandle, GatewayError> {
) -> Result<LocalNetworkRequesterHandle, GatewayError> {
info!("Starting IP packet provider...");
// if network requester is enabled, configuration file must be provided!
let Some(ip_opts) = &self.ip_packet_router_opts else {
log::error!("IP packet router is enabled but no configuration file was provided!");
return Err(GatewayError::UnspecifiedIpPacketRouterConfig);
};
// this gateway, whenever it has anything to send to its local NR will use fake_client_tx
let (ipr_mix_sender, ipr_mix_receiver) = mpsc::unbounded();
let (nr_mix_sender, nr_mix_receiver) = mpsc::unbounded();
let router_shutdown = shutdown.fork("message_router");
let (router_tx, mut router_rx) = oneshot::channel();
@@ -345,6 +348,7 @@ impl<St> Gateway<St> {
router_tx,
);
// TODO: well, wire it up internally to gateway traffic, shutdowns, etc.
let (on_start_tx, on_start_rx) = oneshot::channel();
let mut ip_packet_router =
nym_ip_packet_router::IpPacketRouter::new(ip_opts.config.clone())
@@ -375,11 +379,16 @@ impl<St> Gateway<St> {
return Err(GatewayError::IpPacketRouterStartupFailure);
};
MessageRouter::new(ipr_mix_receiver, packet_router).start_with_shutdown(router_shutdown);
let address = start_data.address;
MessageRouter::new(nr_mix_receiver, packet_router).start_with_shutdown(router_shutdown);
info!(
"the local ip packet router is running on {}",
start_data.address
);
info!("the local ip packet router is running on {address}");
Ok(LocalEmbeddedClientHandle::new(address, ipr_mix_sender))
Ok(LocalNetworkRequesterHandle::new_ip(
start_data,
nr_mix_sender,
))
}
async fn wait_for_interrupt(
@@ -447,7 +456,7 @@ impl<St> Gateway<St> {
let coconut_verifier = {
let nyxd_client = self.random_nyxd_client()?;
CoconutVerifier::new(nyxd_client, self.config.gateway.only_coconut_credentials).await
CoconutVerifier::new(nyxd_client).await
}?;
let mix_forwarding_channel =
@@ -495,6 +504,8 @@ impl<St> Gateway<St> {
None
};
// NOTE: this is mutually exclusive with the network requester (for now). This is reflected
// in the command line arguments as well.
if self.config.ip_packet_router.enabled {
let embedded_ip_sp = self
.start_ip_packet_router(
+15 -2
View File
@@ -26,7 +26,6 @@ cupid = "0.6.1"
dirs = "4.0"
futures = { workspace = true }
humantime-serde = "1.0"
lazy_static = "1.4"
log = { workspace = true }
rand = "0.7.3"
serde = { workspace = true, features = ["derive"] }
@@ -36,8 +35,14 @@ tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] }
tokio-util = { workspace = true, features = ["codec"] }
toml = "0.5.8"
url = { workspace = true, features = ["serde"] }
cfg-if = "1.0.0"
thiserror = { workspace = true }
## tracing
tracing = { workspace = true, optional = true }
opentelemetry = { version = "0.19.0", optional = true }
# internal
nym-node = { path = "../nym-node" }
@@ -46,7 +51,6 @@ nym-crypto = { path = "../common/crypto" }
nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" }
nym-mixnet-client = { path = "../common/client-libs/mixnet-client" }
nym-mixnode-common = { path = "../common/mixnode-common" }
nym-metrics = { path = "../common/nym-metrics" }
nym-nonexhaustive-delayqueue = { path = "../common/nonexhaustive-delayqueue" }
nym-sphinx = { path = "../common/nymsphinx" }
nym-sphinx-params = { path = "../common/nymsphinx/params" }
@@ -56,6 +60,7 @@ nym-types = { path = "../common/types" }
nym-topology = { path = "../common/topology" }
nym-validator-client = { path = "../common/client-libs/validator-client" }
nym-bin-common = { path = "../common/bin-common", features = ["output_format"] }
cpu-cycles = { path = "../cpu-cycles", optional = true }
[dev-dependencies]
tokio = { workspace = true, features = [
@@ -68,6 +73,14 @@ tokio = { workspace = true, features = [
nym-sphinx-types = { path = "../common/nymsphinx/types" }
nym-sphinx-params = { path = "../common/nymsphinx/params" }
[features]
cpucycles = [
"nym-mixnode-common/cpucycles",
"tracing",
"opentelemetry",
"nym-bin-common/tracing",
]
[package.metadata.deb]
name = "nym-mixnode"
maintainer-scripts = "debian"
-4
View File
@@ -42,9 +42,6 @@ pub(crate) struct Init {
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
#[clap(long)]
metrics_key: Option<String>,
}
impl From<Init> for OverrideConfig {
@@ -56,7 +53,6 @@ impl From<Init> for OverrideConfig {
verloc_port: init_config.verloc_port,
http_api_port: init_config.http_api_port,
nym_apis: init_config.nym_apis,
metrics_key: init_config.metrics_key,
}
}
}
-2
View File
@@ -59,7 +59,6 @@ struct OverrideConfig {
verloc_port: Option<u16>,
http_api_port: Option<u16>,
nym_apis: Option<Vec<url::Url>>,
metrics_key: Option<String>,
}
pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> {
@@ -84,7 +83,6 @@ fn override_config(config: Config, args: OverrideConfig) -> Config {
.with_optional(Config::with_mix_port, args.mix_port)
.with_optional(Config::with_verloc_port, args.verloc_port)
.with_optional(Config::with_http_api_port, args.http_api_port)
.with_optional(Config::with_metrics_key, args.metrics_key)
.with_optional_custom_env(
Config::with_custom_nym_apis,
args.nym_apis,
-4
View File
@@ -44,9 +44,6 @@ pub(crate) struct Run {
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
#[clap(long)]
metrics_key: Option<String>,
}
impl From<Run> for OverrideConfig {
@@ -58,7 +55,6 @@ impl From<Run> for OverrideConfig {
verloc_port: run_config.verloc_port,
http_api_port: run_config.http_api_port,
nym_apis: run_config.nym_apis,
metrics_key: run_config.metrics_key,
}
}
}
-10
View File
@@ -80,7 +80,6 @@ fn default_mixnode_http_config() -> config::Http {
DEFAULT_HTTP_API_LISTENING_PORT,
),
landing_page_assets_path: None,
metrics_key: None,
}
}
@@ -209,15 +208,6 @@ impl Config {
pub fn get_nym_api_endpoints(&self) -> Vec<Url> {
self.mixnode.nym_api_urls.clone()
}
pub fn with_metrics_key(mut self, metrics_key: String) -> Self {
self.http.metrics_key = Some(metrics_key);
self
}
pub fn metrics_key(&self) -> Option<&String> {
self.http.metrics_key.as_ref()
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
-1
View File
@@ -95,7 +95,6 @@ impl From<ConfigV1_1_32> for Config {
value.mixnode.http_api_port,
),
landing_page_assets_path: None,
metrics_key: None,
},
// /\ ADDED
mixnode: MixNode {
-2
View File
@@ -57,8 +57,6 @@ bind_address = '{{ http.bind_address }}'
# Path to assets directory of custom landing page of this node
landing_page_assets_path = '{{ http.landing_page_assets_path }}'
metrics_key = '{{ http.metrics_key }}'
[storage_paths]
# Path to file containing private identity key.
+27 -1
View File
@@ -3,11 +3,18 @@
use ::nym_config::defaults::setup_env;
use clap::{crate_name, crate_version, Parser};
use log::info;
use nym_bin_common::bin_info;
use std::sync::OnceLock;
#[allow(unused_imports)]
use nym_bin_common::logging::{maybe_print_banner, setup_logging};
#[cfg(feature = "cpucycles")]
use nym_bin_common::setup_tracing;
#[cfg(feature = "cpucycles")]
use nym_mixnode_common::measure;
#[cfg(feature = "cpucycles")]
use tracing::instrument;
mod commands;
mod config;
@@ -34,6 +41,12 @@ struct Cli {
command: commands::Commands,
}
#[cfg(feature = "cpucycles")]
#[instrument(fields(cpucycles))]
fn test_function() {
measure!({})
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let args = Cli::parse();
@@ -43,10 +56,23 @@ async fn main() -> anyhow::Result<()> {
maybe_print_banner(crate_name!(), crate_version!());
}
setup_logging();
cfg_if::cfg_if! {
if #[cfg(feature = "cpucycles")] {
setup_tracing!("mixnode");
info!("CPU cycles measurement is ON")
} else {
setup_logging();
info!("CPU cycles measurement is OFF")
}
}
commands::execute(args).await?;
cfg_if::cfg_if! {
if #[cfg(feature = "cpucycles")] {
opentelemetry::global::shutdown_tracer_provider();
}}
Ok(())
}
-3
View File
@@ -4,7 +4,6 @@
use crate::node::http::legacy::description::description;
use crate::node::http::legacy::hardware::hardware;
use crate::node::http::legacy::state::MixnodeAppState;
use crate::node::http::legacy::stats::metrics;
use crate::node::http::legacy::stats::stats;
use crate::node::http::legacy::verloc::verloc;
use crate::node::node_description::NodeDescription;
@@ -30,7 +29,6 @@ pub(crate) mod api_routes {
pub(crate) const VERLOC: &str = "/verloc";
pub(crate) const DESCRIPTION: &str = "/description";
pub(crate) const STATS: &str = "/stats";
pub(crate) const METRICS: &str = "/metrics";
pub(crate) const HARDWARE: &str = "/hardware";
}
@@ -46,7 +44,6 @@ pub(crate) fn routes<S: Send + Sync + 'static + Clone>(
)
.route(api_routes::STATS, get(stats))
.route(api_routes::HARDWARE, get(hardware))
.route(api_routes::METRICS, get(metrics))
.fallback(not_found)
.with_state(state)
}
-1
View File
@@ -10,7 +10,6 @@ use axum::extract::FromRef;
pub(crate) struct MixnodeAppState {
pub(crate) verloc: VerlocState,
pub(crate) stats: SharedNodeStats,
pub(crate) metrics_key: Option<String>,
}
impl FromRef<MixnodeAppState> for VerlocState {
+9 -35
View File
@@ -1,59 +1,33 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::node_statistics::{NodeStatsSimple, SharedNodeStats};
use axum::{
extract::{Query, State},
http::HeaderMap,
};
use nym_metrics::metrics;
use crate::node::node_statistics::{NodeStats, NodeStatsSimple, SharedNodeStats};
use axum::extract::{Query, State};
use nym_node::http::api::{FormattedResponse, Output};
use serde::{Deserialize, Serialize};
use super::state::MixnodeAppState;
#[derive(Serialize)]
#[serde(untagged)]
pub enum NodeStatsResponse {
Full(String),
Full(NodeStats),
Simple(NodeStatsSimple),
}
pub(crate) async fn metrics(State(state): State<MixnodeAppState>, headers: HeaderMap) -> String {
if let Some(metrics_key) = state.metrics_key {
if let Some(auth) = headers.get("Authorization") {
if auth.to_str().unwrap_or_default() == format!("Bearer {}", metrics_key) {
metrics!()
} else {
"Unauthorized".to_string()
}
} else {
"Unauthorized".to_string()
}
} else {
"Set metrics_key in config to enable Prometheus metrics".to_string()
}
}
pub(crate) async fn stats(
Query(params): Query<StatsQueryParams>,
State(stats): State<SharedNodeStats>,
) -> MixnodeStatsResponse {
let output = params.output.unwrap_or_default();
// there's no point in returning the entire hashmap of sending destinations in regular mode
let response = generate_stats(params.debug, stats).await;
output.to_response(response)
}
async fn generate_stats(full: bool, stats: SharedNodeStats) -> NodeStatsResponse {
let snapshot_data = stats.clone_data().await;
if full {
NodeStatsResponse::Full(metrics!())
// there's no point in returning the entire hashmap of sending destinations in regular mode
let response = if params.debug {
NodeStatsResponse::Full(snapshot_data)
} else {
NodeStatsResponse::Simple(snapshot_data.simplify())
}
};
output.to_response(response)
}
pub type MixnodeStatsResponse = FormattedResponse<NodeStatsResponse>;
-6
View File
@@ -64,12 +64,6 @@ impl<'a> HttpApiBuilder<'a> {
}
}
#[must_use]
pub(crate) fn with_metrics_key(mut self, metrics_key: Option<&String>) -> Self {
self.legacy_mixnode.metrics_key = metrics_key.map(|k| k.to_string());
self
}
#[must_use]
pub(crate) fn with_verloc(mut self, verloc: VerlocState) -> Self {
self.legacy_mixnode.verloc = verloc;
@@ -9,7 +9,7 @@ use crate::node::TaskClient;
use futures::StreamExt;
use log::debug;
use log::{error, info, warn};
use nym_metrics::nanos;
use nym_mixnode_common::measure;
use nym_sphinx::forwarding::packet::MixPacket;
use nym_sphinx::framing::codec::NymCodec;
use nym_sphinx::framing::packet::FramedNymPacket;
@@ -19,6 +19,9 @@ use tokio::net::TcpStream;
use tokio::time::Instant;
use tokio_util::codec::Framed;
#[cfg(feature = "cpucycles")]
use tracing::instrument;
pub(crate) mod packet_processing;
#[derive(Clone)]
@@ -50,6 +53,10 @@ impl ConnectionHandler {
.expect("the delay-forwarder has died!");
}
#[cfg_attr(
feature = "cpucycles",
instrument(skip(self, framed_sphinx_packet), fields(cpucycles))
)]
fn handle_received_packet(&self, framed_sphinx_packet: FramedNymPacket) {
//
// TODO: here be replay attack detection - it will require similar key cache to the one in
@@ -59,7 +66,7 @@ impl ConnectionHandler {
// all processing such, key caching, etc. was done.
// however, if it was a forward hop, we still need to delay it
nanos!("handle_received_packet", {
measure!({
match self.packet_processor.process_received(framed_sphinx_packet) {
Err(err) => debug!("We failed to process received sphinx packet - {err}"),
Ok(res) => match res {
-3
View File
@@ -72,13 +72,11 @@ impl MixNode {
&self,
atomic_verloc_result: AtomicVerlocResult,
node_stats_pointer: SharedNodeStats,
metrics_key: Option<&String>,
task_client: TaskClient,
) -> Result<(), MixnodeError> {
HttpApiBuilder::new(&self.config, &self.identity_keypair, &self.sphinx_keypair)
.with_verloc(VerlocState::new(atomic_verloc_result))
.with_mixing_stats(node_stats_pointer)
.with_metrics_key(metrics_key)
.with_descriptor(self.descriptor.clone())
.start(task_client)
}
@@ -251,7 +249,6 @@ impl MixNode {
self.start_http_api(
atomic_verloc_results,
node_stats_pointer,
self.config.metrics_key(),
shutdown.subscribe().named("http-api"),
)?;
+75 -64
View File
@@ -1,8 +1,6 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use nym_metrics::inc_by;
use super::TaskClient;
use futures::channel::mpsc;
use futures::lock::Mutex;
@@ -17,7 +15,7 @@ use std::time::{Duration, SystemTime};
use tokio::sync::{RwLock, RwLockReadGuard};
// convenience aliases
type PacketsMap = HashMap<String, f64>;
type PacketsMap = HashMap<String, u64>;
type PacketDataReceiver = mpsc::UnboundedReceiver<PacketEvent>;
type PacketDataSender = mpsc::UnboundedSender<PacketEvent>;
@@ -29,15 +27,14 @@ pub(crate) struct SharedNodeStats {
impl SharedNodeStats {
pub(crate) fn new() -> Self {
let now = SystemTime::now();
SharedNodeStats {
inner: Arc::new(RwLock::new(NodeStats {
update_time: now,
previous_update_time: now,
packets_received_since_startup: 0.,
packets_sent_since_startup_all: 0.,
packets_dropped_since_startup_all: 0.,
packets_received_since_last_update: 0.,
packets_received_since_startup: 0,
packets_sent_since_startup: HashMap::new(),
packets_explicitly_dropped_since_startup: HashMap::new(),
packets_received_since_last_update: 0,
packets_sent_since_last_update: HashMap::new(),
packets_explicitly_dropped_since_last_update: HashMap::new(),
})),
@@ -46,7 +43,7 @@ impl SharedNodeStats {
pub(crate) async fn update(
&self,
new_received: f64,
new_received: u64,
new_sent: PacketsMap,
new_dropped: PacketsMap,
) {
@@ -57,24 +54,20 @@ impl SharedNodeStats {
guard.update_time = snapshot_time;
guard.packets_received_since_startup += new_received;
for count in new_sent.values() {
guard.packets_sent_since_startup_all += count;
for (mix, count) in &new_sent {
*guard
.packets_sent_since_startup
.entry(mix.clone())
.or_insert(0) += *count;
}
for count in new_dropped.values() {
guard.packets_dropped_since_startup_all += count;
for (mix, count) in &new_dropped {
*guard
.packets_explicitly_dropped_since_last_update
.entry(mix.clone())
.or_insert(0) += *count;
}
inc_by!("packets_received_since_startup", new_received);
inc_by!(
"packets_sent_since_startup_all",
new_sent.values().sum::<f64>()
);
inc_by!(
"packets_dropped_since_startup_all",
new_dropped.values().sum::<f64>()
);
guard.packets_received_since_last_update = new_received;
guard.packets_sent_since_last_update = new_sent;
guard.packets_explicitly_dropped_since_last_update = new_dropped;
@@ -89,18 +82,27 @@ impl SharedNodeStats {
}
}
#[derive(Clone)]
#[derive(Serialize, Clone)]
pub struct NodeStats {
#[serde(serialize_with = "humantime_serde::serialize")]
update_time: SystemTime,
#[serde(serialize_with = "humantime_serde::serialize")]
previous_update_time: SystemTime,
packets_received_since_startup: f64,
packets_sent_since_startup_all: f64,
packets_dropped_since_startup_all: f64,
packets_received_since_last_update: f64,
packets_received_since_startup: u64,
// note: sent does not imply forwarded. We don't know if it was delivered successfully
packets_sent_since_startup: PacketsMap,
// we know for sure we dropped packets to those destinations
packets_explicitly_dropped_since_startup: PacketsMap,
packets_received_since_last_update: u64,
// note: sent does not imply forwarded. We don't know if it was delivered successfully
packets_sent_since_last_update: PacketsMap,
// we know for sure we dropped packets to those destinations
packets_explicitly_dropped_since_last_update: PacketsMap,
}
@@ -110,10 +112,10 @@ impl Default for NodeStats {
NodeStats {
update_time: SystemTime::UNIX_EPOCH,
previous_update_time: SystemTime::UNIX_EPOCH,
packets_received_since_startup: 0.,
packets_sent_since_startup_all: 0.,
packets_dropped_since_startup_all: 0.,
packets_received_since_last_update: 0.,
packets_received_since_startup: 0,
packets_sent_since_startup: Default::default(),
packets_explicitly_dropped_since_startup: Default::default(),
packets_received_since_last_update: 0,
packets_sent_since_last_update: Default::default(),
packets_explicitly_dropped_since_last_update: Default::default(),
}
@@ -126,8 +128,11 @@ impl NodeStats {
update_time: self.update_time,
previous_update_time: self.previous_update_time,
packets_received_since_startup: self.packets_received_since_startup,
packets_sent_since_startup: self.packets_sent_since_startup_all,
packets_explicitly_dropped_since_startup: self.packets_dropped_since_startup_all,
packets_sent_since_startup: self.packets_sent_since_startup.values().sum(),
packets_explicitly_dropped_since_startup: self
.packets_explicitly_dropped_since_startup
.values()
.sum(),
packets_received_since_last_update: self.packets_received_since_last_update,
packets_sent_since_last_update: self.packets_sent_since_last_update.values().sum(),
packets_explicitly_dropped_since_last_update: self
@@ -146,21 +151,21 @@ pub struct NodeStatsSimple {
#[serde(serialize_with = "humantime_serde::serialize")]
previous_update_time: SystemTime,
packets_received_since_startup: f64,
packets_received_since_startup: u64,
// note: sent does not imply forwarded. We don't know if it was delivered successfully
packets_sent_since_startup: f64,
packets_sent_since_startup: u64,
// we know for sure we dropped those packets
packets_explicitly_dropped_since_startup: f64,
packets_explicitly_dropped_since_startup: u64,
packets_received_since_last_update: f64,
packets_received_since_last_update: u64,
// note: sent does not imply forwarded. We don't know if it was delivered successfully
packets_sent_since_last_update: f64,
packets_sent_since_last_update: u64,
// we know for sure we dropped those packets
packets_explicitly_dropped_since_last_update: f64,
packets_explicitly_dropped_since_last_update: u64,
}
pub(crate) enum PacketEvent {
@@ -198,14 +203,14 @@ impl CurrentPacketData {
async fn increment_sent(&self, destination: String) {
let mut unlocked = self.inner.sent.lock().await;
let receiver_count = unlocked.entry(destination).or_insert(0.);
*receiver_count += 1.;
let receiver_count = unlocked.entry(destination).or_insert(0);
*receiver_count += 1;
}
async fn increment_dropped(&self, destination: String) {
let mut unlocked = self.inner.dropped.lock().await;
let dropped_count = unlocked.entry(destination).or_insert(0.);
*dropped_count += 1.;
let dropped_count = unlocked.entry(destination).or_insert(0);
*dropped_count += 1;
}
async fn acquire_and_reset(&self) -> (u64, PacketsMap, PacketsMap) {
@@ -327,9 +332,7 @@ impl StatsUpdater {
async fn update_stats(&self) {
// grab new data since last update
let (received, sent, dropped) = self.current_packet_data.acquire_and_reset().await;
self.current_stats
.update(received as f64, sent, dropped)
.await;
self.current_stats.update(received, sent, dropped).await;
}
async fn run(&mut self) {
@@ -373,18 +376,21 @@ impl PacketStatsConsoleLogger {
info!(
"Since startup mixed {} packets! ({} in last {} seconds)",
stats.packets_sent_since_startup_all,
stats.packets_sent_since_last_update.values().sum::<f64>(),
stats.packets_sent_since_startup.values().sum::<u64>(),
stats.packets_sent_since_last_update.values().sum::<u64>(),
difference_secs,
);
if stats.packets_dropped_since_startup_all > 0. {
if !stats.packets_explicitly_dropped_since_startup.is_empty() {
info!(
"Since startup dropped {} packets! ({} in last {} seconds)",
stats.packets_dropped_since_startup_all,
stats
.packets_explicitly_dropped_since_startup
.values()
.sum::<u64>(),
stats
.packets_explicitly_dropped_since_last_update
.values()
.sum::<f64>(),
.sum::<u64>(),
difference_secs,
);
}
@@ -397,19 +403,22 @@ impl PacketStatsConsoleLogger {
);
trace!(
"Since startup sent packets to the following: \n{:#?} \n And in last {} seconds: {:#?})",
stats.packets_sent_since_startup_all,
stats.packets_sent_since_startup,
difference_secs,
stats.packets_sent_since_last_update
);
} else {
info!(
"Since startup mixed {} packets!",
stats.packets_sent_since_startup_all,
stats.packets_sent_since_startup.values().sum::<u64>(),
);
if stats.packets_dropped_since_startup_all > 0. {
if !stats.packets_explicitly_dropped_since_startup.is_empty() {
info!(
"Since startup dropped {} packets!",
stats.packets_dropped_since_startup_all,
stats
.packets_explicitly_dropped_since_startup
.values()
.sum::<u64>(),
);
}
@@ -418,8 +427,8 @@ impl PacketStatsConsoleLogger {
stats.packets_received_since_startup
);
trace!(
"Since startup sent packets {}",
stats.packets_sent_since_startup_all
"Since startup sent packets to the following: \n{:#?}",
stats.packets_sent_since_startup
);
}
}
@@ -512,7 +521,6 @@ impl Controller {
#[cfg(test)]
mod tests {
use super::*;
use nym_metrics::metrics;
use nym_task::TaskManager;
#[tokio::test]
@@ -537,11 +545,14 @@ mod tests {
// Get output (stats)
let stats = node_stats_pointer.read().await;
assert_eq!(&stats.packets_sent_since_startup_all, &2.);
assert_eq!(&stats.packets_sent_since_last_update.get("foo"), &Some(&2.));
assert_eq!(&stats.packets_sent_since_startup.get("foo"), &Some(&2u64));
assert_eq!(&stats.packets_sent_since_startup.len(), &1);
assert_eq!(
&stats.packets_sent_since_last_update.get("foo"),
&Some(&2u64)
);
assert_eq!(&stats.packets_sent_since_last_update.len(), &1);
assert_eq!(&stats.packets_received_since_startup, &0.);
assert_eq!(&stats.packets_dropped_since_startup_all, &0.);
assert_eq!(metrics!(), "# HELP nym_mixnode_packets_dropped_since_startup_all nym_mixnode_packets_dropped_since_startup_all\n# TYPE nym_mixnode_packets_dropped_since_startup_all counter\nnym_mixnode_packets_dropped_since_startup_all 0\n# HELP nym_mixnode_packets_received_since_startup nym_mixnode_packets_received_since_startup\n# TYPE nym_mixnode_packets_received_since_startup counter\nnym_mixnode_packets_received_since_startup 0\n# HELP nym_mixnode_packets_sent_since_startup_all nym_mixnode_packets_sent_since_startup_all\n# TYPE nym_mixnode_packets_sent_since_startup_all counter\nnym_mixnode_packets_sent_since_startup_all 2\n")
assert_eq!(&stats.packets_received_since_startup, &0u64);
assert!(&stats.packets_explicitly_dropped_since_startup.is_empty());
}
}
+6 -6
View File
@@ -2555,9 +2555,9 @@
"integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ=="
},
"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==",
"funding": [
{
"type": "individual",
@@ -6845,9 +6845,9 @@
"integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ=="
},
"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=="
},
"form-data": {
"version": "4.0.0",
+3 -3
View File
@@ -1568,9 +1568,9 @@ flatted@^3.1.0:
integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==
follow-redirects@^1.15.0:
version "1.15.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
version "1.15.4"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf"
integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==
form-data@4.0.0, form-data@^4.0.0:
version "4.0.0"

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