Merge remote-tracking branch 'origin/develop' into jon/update-some-nym-prefixes

This commit is contained in:
Jon Häggblad
2024-03-20 10:51:58 +01:00
116 changed files with 40276 additions and 926 deletions
+10 -2
View File
@@ -14,12 +14,20 @@ 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: 'node16'
main: 'index.js'
using: 'node20'
main: 'dist/index.js'
branding:
icon: 'hash'
color: 'green'
@@ -0,0 +1,450 @@
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
@@ -0,0 +1,57 @@
'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});
}
});
@@ -1,15 +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");
// 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);
}
+2 -13
View File
@@ -2,17 +2,6 @@
"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"
},
"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"
}
"main": "dist/index.js",
"type": "module"
}
@@ -0,0 +1,14 @@
# 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
```
@@ -142,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 }) {
export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrId, algorithm = 'sha256', filename = 'hashes.json', cache = false, upload = true, owner = 'nymtech', repo = 'nym' }) {
console.log("🚀🚀🚀 Getting releases");
let auth;
@@ -157,8 +157,6 @@ export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrI
auth: process.env.GITHUB_TOKEN,
request: { fetch }
});
const owner = "nymtech";
const repo = "nym";
let releases;
if(cache) {
@@ -212,7 +210,13 @@ export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrI
releasesToProcess.forEach(release => {
const {tag_name, name} = release;
const tagComponents = tag_name.split('-v');
const matches = tag_name.match(/(\S+)-v([0-9]+\.[0-9]+\.\S+)/);
if(!matches || matches.length < 2) {
return;
}
const tagComponents = matches.slice(1);
const componentName = tagComponents[0];
const componentVersion = 'v' + tagComponents[1];
@@ -0,0 +1,21 @@
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,26 +1,28 @@
{
"name": "ghaction-generate-release-hashes",
"name": "nym-hash-release",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ghaction-generate-release-hashes",
"name": "nym-hash-release",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/core": "^1.10.1",
"@actions/github": "^5.1.1",
"@octokit/auth-action": "^4.0.0",
"@octokit/rest": "^20.0.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"
}
},
"node_modules/@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz",
"integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
@@ -46,12 +48,12 @@
}
},
"node_modules/@octokit/auth-action": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-action/-/auth-action-4.0.0.tgz",
"integrity": "sha512-sMm9lWZdiX6e89YFaLrgE9EFs94k58BwIkvjOtozNWUqyTmsrnWFr/M5LolaRzZ7Kmb5FbhF9hi7FEeE274SoQ==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@octokit/auth-action/-/auth-action-4.0.1.tgz",
"integrity": "sha512-mJLOcFFafIivLZ7BEkGDCTFoHPJv7BeL5Zwy7j5qMDU0b/DKshhi6GCU9tw3vmKhOxTNquYfvwqsEfPpemaaxg==",
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/types": "^11.0.0"
"@octokit/types": "^12.0.0"
},
"engines": {
"node": ">= 18"
@@ -66,16 +68,16 @@
}
},
"node_modules/@octokit/auth-action/node_modules/@octokit/openapi-types": {
"version": "18.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz",
"integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw=="
"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/auth-action/node_modules/@octokit/types": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-11.1.0.tgz",
"integrity": "sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==",
"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": "^18.0.0"
"@octokit/openapi-types": "^20.0.0"
}
},
"node_modules/@octokit/auth-token": {
@@ -191,14 +193,14 @@
}
},
"node_modules/@octokit/rest": {
"version": "20.0.1",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.1.tgz",
"integrity": "sha512-wROV21RwHQIMNb2Dgd4+pY+dVy1Dwmp85pBrgr6YRRDYRBu9Gb+D73f4Bl2EukZSj5hInq2Tui9o7gAQpc2k2Q==",
"version": "20.0.2",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz",
"integrity": "sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ==",
"dependencies": {
"@octokit/core": "^5.0.0",
"@octokit/plugin-paginate-rest": "^8.0.0",
"@octokit/plugin-paginate-rest": "^9.0.0",
"@octokit/plugin-request-log": "^4.0.0",
"@octokit/plugin-rest-endpoint-methods": "^9.0.0"
"@octokit/plugin-rest-endpoint-methods": "^10.0.0"
},
"engines": {
"node": ">= 18"
@@ -261,17 +263,30 @@
"integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw=="
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest": {
"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==",
"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==",
"dependencies": {
"@octokit/types": "^11.0.0"
"@octokit/types": "^12.6.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": ">=5"
"@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"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-request-log": {
@@ -286,17 +301,30 @@
}
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods": {
"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==",
"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==",
"dependencies": {
"@octokit/types": "^11.0.0"
"@octokit/types": "^12.6.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": ">=5"
"@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"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/request": {
@@ -343,6 +371,15 @@
"@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",
@@ -0,0 +1,23 @@
{
"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"
}
}
Generated
+59 -37
View File
@@ -1419,14 +1419,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "cpu-cycles"
version = "0.1.0"
dependencies = [
"cfg-if",
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.2.9"
@@ -1906,12 +1898,12 @@ dependencies = [
[[package]]
name = "darling"
version = "0.20.3"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8"
dependencies = [
"darling_core 0.20.3",
"darling_macro 0.20.3",
"darling_core 0.20.5",
"darling_macro 0.20.5",
]
[[package]]
@@ -1944,9 +1936,9 @@ dependencies = [
[[package]]
name = "darling_core"
version = "0.20.3"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3"
dependencies = [
"fnv",
"ident_case",
@@ -1980,11 +1972,11 @@ dependencies = [
[[package]]
name = "darling_macro"
version = "0.20.3"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77"
dependencies = [
"darling_core 0.20.3",
"darling_core 0.20.5",
"quote",
"syn 2.0.38",
]
@@ -5588,7 +5580,9 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
"tokio",
"tungstenite",
"wasmtimer",
"zeroize",
]
@@ -5717,6 +5711,17 @@ dependencies = [
"thiserror",
]
[[package]]
name = "nym-metrics"
version = "0.1.0"
dependencies = [
"dashmap",
"lazy_static",
"log",
"prometheus",
"regex",
]
[[package]]
name = "nym-mixnet-client"
version = "0.1.0"
@@ -5757,19 +5762,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",
@@ -5782,7 +5787,6 @@ dependencies = [
"nym-topology",
"nym-types",
"nym-validator-client",
"opentelemetry",
"rand 0.7.3",
"serde",
"serde_json",
@@ -5791,7 +5795,6 @@ dependencies = [
"tokio",
"tokio-util",
"toml 0.5.11",
"tracing",
"url",
]
@@ -5800,13 +5803,12 @@ 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",
@@ -5821,7 +5823,6 @@ dependencies = [
"thiserror",
"tokio",
"tokio-util",
"tracing",
"url",
]
@@ -7441,6 +7442,21 @@ 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"
@@ -7563,10 +7579,16 @@ dependencies = [
]
[[package]]
name = "psl"
version = "2.1.14"
name = "protobuf"
version = "2.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "383703acfc34f7a00724846c14dc5ea4407c59e5aedcbbb18a1c0c1a23fe5013"
checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
[[package]]
name = "psl"
version = "2.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc74a6e6a56708be1cf5c4c4d1a0dc21d33b2dcaa24e731b7fa9c287ce4f916f"
dependencies = [
"psl-types",
]
@@ -7962,13 +7984,13 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.2"
version = "1.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.3",
"regex-automata 0.4.6",
"regex-syntax 0.8.2",
]
@@ -7983,9 +8005,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.3"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
@@ -8808,9 +8830,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.4.0"
version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
checksum = "1b0ed1662c5a68664f45b76d18deb0e234aff37207086803165c961eb695e981"
dependencies = [
"base64 0.21.4",
"chrono",
@@ -8825,11 +8847,11 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.4.0"
version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
checksum = "568577ff0ef47b879f736cd66740e022f3672788cdf002a05a4e609ea5a6fb15"
dependencies = [
"darling 0.20.3",
"darling 0.20.5",
"proc-macro2",
"quote",
"syn 2.0.38",
+19 -6
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,6 +57,7 @@ members = [
"common/nonexhaustive-delayqueue",
"common/nymcoconut",
"common/nym-id",
"common/nym-metrics",
"common/nymsphinx",
"common/nymsphinx/acknowledgements",
"common/nymsphinx/addressing",
@@ -112,9 +113,10 @@ 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 = [
@@ -130,7 +132,16 @@ 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"]
@@ -180,10 +191,12 @@ 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"
@@ -208,9 +221,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"
@@ -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_V1_PROTOCOL_VERSION,
BinaryRequest, ClientControlRequest, ServerResponse, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION,
CURRENT_PROTOCOL_VERSION,
};
use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
@@ -438,6 +438,7 @@ 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),
@@ -494,8 +495,13 @@ 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).into();
let msg = ClientControlRequest::new_authenticate(
self_address,
encrypted_address,
iv,
!self.disabled_credentials_mode,
)
.into();
match self.send_websocket_message(msg).await? {
ServerResponse::Authenticate {
@@ -599,7 +605,7 @@ impl<C, St> GatewayClient<C, St> {
});
};
if gateway_protocol < CREDENTIAL_UPDATE_V1_PROTOCOL_VERSION {
if gateway_protocol < CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION {
return Err(GatewayClientError::OutdatedGatewayCredentialVersion {
negotiated_protocol: Some(gateway_protocol),
});
+3 -2
View File
@@ -8,7 +8,8 @@ pub mod response;
// version 3: initial version
// version 4: IPv6 support
pub const CURRENT_VERSION: u8 = 4;
// version 5: Add severity level to info response
pub const CURRENT_VERSION: u8 = 5;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IpPair {
@@ -24,7 +25,7 @@ impl IpPair {
impl Display for IpPair {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "IPv4: {}, IPV6: {}", self.ipv4, self.ipv6)
write!(f, "IPv4: {}, IPv6: {}", self.ipv4, self.ipv6)
}
}
+28
View File
@@ -83,6 +83,34 @@ 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),
+54 -11
View File
@@ -116,24 +116,59 @@ impl IpPacketResponse {
) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::Error(ErrorResponse {
data: IpPacketResponseData::Info(InfoResponse {
request_id,
reply_to,
reply: ErrorResponseReply::VersionMismatch {
reply: InfoResponseReply::VersionMismatch {
request_version,
response_version: our_version,
},
level: InfoLevel::Error,
}),
}
}
pub fn new_data_error_response(reply_to: Recipient, reply: ErrorResponseReply) -> Self {
pub fn new_data_info_response(
reply_to: Recipient,
reply: InfoResponseReply,
level: InfoLevel,
) -> Self {
Self {
version: CURRENT_VERSION,
data: IpPacketResponseData::Error(ErrorResponse {
data: IpPacketResponseData::Info(InfoResponse {
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,
},
}),
}
}
@@ -147,7 +182,7 @@ impl IpPacketResponse {
IpPacketResponseData::Data(_) => None,
IpPacketResponseData::Pong(response) => Some(response.request_id),
IpPacketResponseData::Health(response) => Some(response.request_id),
IpPacketResponseData::Error(response) => Some(response.request_id),
IpPacketResponseData::Info(response) => Some(response.request_id),
}
}
@@ -160,7 +195,7 @@ impl IpPacketResponse {
IpPacketResponseData::Data(_) => None,
IpPacketResponseData::Pong(response) => Some(&response.reply_to),
IpPacketResponseData::Health(response) => Some(&response.reply_to),
IpPacketResponseData::Error(response) => Some(&response.reply_to),
IpPacketResponseData::Info(response) => Some(&response.reply_to),
}
}
@@ -201,8 +236,8 @@ pub enum IpPacketResponseData {
// Response for a health request
Health(HealthResponse),
// Error response
Error(ErrorResponse),
// Info response. This can be anything from informative messages to errors
Info(InfoResponse),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -338,14 +373,15 @@ pub struct HealthResponseReply {
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ErrorResponse {
pub struct InfoResponse {
pub request_id: u64,
pub reply_to: Recipient,
pub reply: ErrorResponseReply,
pub reply: InfoResponseReply,
pub level: InfoLevel,
}
#[derive(Clone, Debug, Serialize, Deserialize, thiserror::Error)]
pub enum ErrorResponseReply {
pub enum InfoResponseReply {
#[error("{msg}")]
Generic { msg: String },
#[error(
@@ -358,3 +394,10 @@ pub enum ErrorResponseReply {
#[error("destination failed exit policy filter check: {dst}")]
ExitPolicyFilterCheckFailed { dst: String },
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum InfoLevel {
Info,
Warn,
Error,
}
+1 -9
View File
@@ -25,9 +25,6 @@ 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" }
@@ -39,9 +36,4 @@ nym-sphinx-types = { path = "../nymsphinx/types" }
nym-task = { path = "../task" }
nym-validator-client = { path = "../client-libs/validator-client" }
nym-bin-common = { path = "../bin-common" }
cfg-if = "1.0.0"
cpu-cycles = { path = "../../cpu-cycles", optional = true }
[features]
cpucycles = ["cpu-cycles", "tracing"]
nym-metrics = { path = "../nym-metrics" }
-37
View File
@@ -2,40 +2,3 @@
// 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,8 +15,6 @@ use nym_sphinx_types::{
};
use std::convert::TryFrom;
use std::sync::Arc;
#[cfg(feature = "cpucycles")]
use tracing::instrument;
type ForwardAck = MixPacket;
@@ -51,15 +49,11 @@ 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> {
measure!({
nanos!("perform_initial_packet_processing", {
packet.process(&self.sphinx_key).map_err(|err| {
debug!("Failed to unwrap NymPacket packet: {err}");
MixProcessingError::NymPacketProcessingError(err)
@@ -68,17 +62,12 @@ 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> {
measure!({
nanos!("perform_initial_unwrapping", {
let packet = received.into_inner();
self.perform_initial_packet_processing(packet)
})
}
@@ -223,16 +212,12 @@ 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
measure!({
nanos!("process_received", {
let packet_size = received.packet_size();
let packet_type = received.packet_type();
+12
View File
@@ -185,6 +185,12 @@ 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();
@@ -227,6 +233,12 @@ 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);
+18
View File
@@ -0,0 +1,18 @@
[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 }
regex = "1.1"
lazy_static = "1.4"
+180
View File
@@ -0,0 +1,180 @@
use dashmap::DashMap;
pub use log::error;
use log::{debug, warn};
use regex::Regex;
use std::fmt;
pub use std::time::Instant;
use prometheus::{core::Collector, Counter, Encoder as _, Gauge, Registry, TextEncoder};
#[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;
$crate::REGISTRY.inc_by(&format!("{}_nanos", $name), duration);
r
}};
}
lazy_static::lazy_static! {
pub static ref RE: Regex = Regex::new(r"[^a-zA-Z0-9_]").unwrap();
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 {
fn fq_name(&self) -> String {
match self {
Metric::C(c) => fq_name(c.as_ref()),
Metric::G(g) => fq_name(g.as_ref()),
}
}
fn inc_by(&self, value: f64) {
match self {
Metric::C(c) => c.inc_by(value),
Metric::G(g) => g.add(value),
}
}
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 mut buffer = vec![];
let encoder = TextEncoder::new();
let metrics = self.registry.gather();
match encoder.encode(&metrics, &mut buffer) {
Ok(_) => {}
Err(e) => return write!(f, "Error encoding metrics to buffer: {}", e),
}
let output = match String::from_utf8(buffer) {
Ok(output) => output,
Err(e) => return write!(f, "Error decoding metrics to String: {}", e),
};
write!(f, "{}", output)
}
}
impl MetricsController {
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_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)
}
}
}
}
#[inline(always)]
fn sanitize_metric_name(name: &str) -> String {
RE.replace_all(name, "_").to_string()
}
#[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"
)
}
}
+4 -1
View File
@@ -23,6 +23,7 @@ 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;
@@ -177,7 +178,9 @@ where
))?;
// Listen to status messages from task, that we forward back to the caller
shutdown.start_status_listener(sender).await;
shutdown
.start_status_listener(sender, TaskStatus::Ready)
.await;
let res = tokio::select! {
biased;
+8 -2
View File
@@ -46,6 +46,8 @@ enum TaskError {
pub enum TaskStatus {
#[error("Ready")]
Ready,
#[error("Ready and connected to gateway: {0}")]
ReadyWithGateway(String),
}
/// Listens to status and error messages from tasks, as well as notifying them to gracefully
@@ -161,10 +163,14 @@ impl TaskManager {
self.notify_tx.send(())
}
pub async fn start_status_listener(&mut self, mut sender: StatusSender) {
pub async fn start_status_listener(
&mut self,
mut sender: StatusSender,
start_status: TaskStatus,
) {
// 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(TaskStatus::Ready)).await {
if let Err(msg) = sender.send(Box::new(start_status)).await {
log::error!("Error sending status message: {}", msg);
};
+5 -1
View File
@@ -31,9 +31,13 @@ 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_latest_binary_url = "https://github.com/nymtech/nym/releases/tag/nym-vpn-alpha-0.0.4"
nym_vpn_releases = "https://github.com/nymtech/nym-vpn-client/releases"
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,7 +25,6 @@
- [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,25 +8,29 @@ 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_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:
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:
```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_0.1.0_ubuntu-22.04_amd64.zip" | shasum -a 256 -c
# echo "0e4abb461e86b2c168577e0294112a3bacd3a24bf8565b49783bfebd9b530e23 nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_amd64.tar.gz" | shasum -a 256 -c
```
1. Extract files:
3. Extract files:
```sh
tar -xvf <BINARY>
tar -xvf <BINARY>.tar.gz
# for example
# tar -xvf nym-vpn-cli_0.0.2_ubuntu-22.04_x86_64.tar.gz
# tar -xvf nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_ubuntu-22.04_x86_64.tar.gz
```
2. Make executable by running:
4. Make executable by running:
```sh
# possibly you may have to cd into a sub-directory
# make sure you are in the right 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_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:
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:
```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_0.1.0_macos_x86_64.zip" | shasum -a 256 -c
# echo "96623ccc69bc4cc0e4e3e18528b6dae6be69f645d0a592d926a3158ce2d0c269 nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_macos_x86_64.zip" | shasum -a 256 -c
```
3. Extract files:
```sh
tar -xvf <BINARY>
# for example
# tar -xvf nym-vpn-cli_0.0.2_macos_aarch64.tar.gz
# tar -xvf nym-vpn-cli_<!-- cmdrun scripts/nym_vpn_cli_version.sh -->_macos_aarch64.tar.gz
```
4. Make executable by running:
```sh
+5 -16
View File
@@ -5,33 +5,22 @@ 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.
```
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).
Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums or download the binaries directly.
## Automated Script for CLI Installation
We wrote a [script](https://gist.github.com/serinko/d65450653d6bbafacbcee71c9cb8fb31) which does download of the CLI, sha256 verification, extraction, installation and configuration for Linux and MacOS users automatically following the steps below:
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/0cbcdd18f7ee94f559692b936061248ebbbf2773/execute-nym-vpn-cli-binary.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
```
2. Make the script executable
```sh
chmod u+x execute-nym-vpn-cli-binary.sh
```
2. Follow the prompts in the program
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:
3. 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,33 +12,35 @@ NymVPN is an experimental software and it's for [testing](./testing.md) purposes
### Installation
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:
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:
```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_0.0.3_ubuntu-22.04_x86_64.zip" | shasum -a 256 -c
# echo "a5f91f20d587975e30b6a75d3a9e195234cf1269eac278139a5b9c39b039e807 nym-vpn-desktop_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_ubuntu-22.04_x86_64.tar.gz" | shasum -a 256 -c
```
3. Extract files:
```sh
tar -xvf <BINARY>
tar -xvf <BINARY>.tar.gz
# for example
# tar -xvf nym-vpn-desktop_0.0.4_ubuntu-22.04_x86_64.tar.gz
# tar -xvf nym-vpn-desktop_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_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 ./appimage/nym-vpn_0.0.4_amd64.AppImage
chmod u+x ./nym-vpn_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_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
cd deb
sudo dpkg -i ./nym-vpn_0.0.4_amd64.deb
# 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
# or
sudo apt-get install -f ./nym-vpn_0.0.4_amd64.deb
sudo apt-get install -f ./nym-vpn_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_amd64.deb
```
NymVPN alpha version runs over Nym testnet (called sandbox), a little extra configuration is needed for the application to work.
@@ -72,7 +74,7 @@ Open terminal and run:
```sh
# .AppImage must be run from the same directory as the binary
sudo -E ./nym-vpn_0.0.4_amd64.AppImage
sudo -E ./nym-vpn_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_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_latest_binary_url}}) and download the binary for your version of MacOS
1. Open Github [releases page]({{nym_vpn_releases}}) 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_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:
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:
```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
# echo "da4c0bf8e8b52658312d341fa3581954cfcb6efd516d9a448c76d042a454b5df nym-vpn-desktop_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_macos_x86_64.zip" | shasum -a 256 -c
```
3. Extract the downloaded file manually or by a command:
```sh
tar -xvf <BINARY>
tar -xvf <BINARY>.tar.gz
# for example
# tar -xvf nym-vpn-desktop_0.0.4_macos_aarch64.tar.gz
# tar -xvf nym-vpn-desktop_<!-- cmdrun scripts/nym_vpn_desktop_version.sh -->_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
+8 -5
View File
@@ -10,23 +10,26 @@ 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_latest_binary_url}}) to check sha sums or download the binaries directly.
Visit NymVPN alpha latest [release page]({{nym_vpn_releases}}) to check sha sums or download the binaries directly.
## Automated Script for GUI Installation
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.
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.
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/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
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
```
2. Follow the prompts in the program
To start the application again, reconnect your wifi and run
```sh
# Linux
sudo -E ~/nym-vpn-latest/nym-vpn_0.0.4_amd64.AppImage
# 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
# MacOS
sudo -E $HOME/nym-vpn-latest/nym-vpn
+5 -7
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 as well as provide some scripts for [qualitative testing](testing.md).
**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.
**Here is how**
@@ -13,13 +13,12 @@
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. 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
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
***NymVPN alpha testing will last from 15th of January - 15th of February.***
*NOTE: NymVPN alpha is experimental software for [testing purposes](testing.md) only.*
*NOTE: NymVPN alpha is experimental software for testing purposes only.*
## NymVPN Overview
@@ -45,10 +44,9 @@ 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_latest_binary_url}})
* [Alpha release page]({{nym_vpn_releases}})
* [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)
@@ -1,61 +0,0 @@
# 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.
@@ -0,0 +1,6 @@
#!/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}"
@@ -0,0 +1,6 @@
#!/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_latest_binary_url}}))
1. Create a directory called `nym-vpn-tests` and copy your `nym-vpn-cli` binary ([download here]({{nym_vpn_releases}}))
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,6 +46,8 @@ 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:
@@ -84,3 +86,4 @@ NEW_ENDPOINT="http://localhost:8000/data.json"
python3 -m http.server 8000
```
6. Continue with the steps listed in [testing section](testing.md)
-->
+3 -1
View File
@@ -6,7 +6,9 @@ use thiserror::Error;
use url::Url;
// Re-export request types
pub use nym_explorer_api_requests::{PrettyDetailedGatewayBond, PrettyDetailedMixNodeBond};
pub use nym_explorer_api_requests::{
Location, PrettyDetailedGatewayBond, PrettyDetailedMixNodeBond,
};
// Paths
const API_VERSION: &str = "v1";
+8
View File
@@ -28,6 +28,14 @@ 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_V1_PROTOCOL_VERSION;
pub const CURRENT_PROTOCOL_VERSION: u8 = CREDENTIAL_UPDATE_V2_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_V1_PROTOCOL_VERSION;
// 1 - initial release
// 2 - changes to client credentials structure
pub const INITIAL_PROTOCOL_VERSION: u8 = 1;
pub const CREDENTIAL_UPDATE_V1_PROTOCOL_VERSION: u8 = 2;
pub const CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION: u8 = 2;
pub type GatewayMac = HmacOutput<GatewayIntegrityHmacAlgorithm>;
@@ -24,11 +24,18 @@ 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));
let mut state = State::new(
rng,
ws_stream,
identity,
Some(gateway_pubkey),
expects_credential_usage,
);
ClientHandshake {
handshake_future: Box::pin(async move {
@@ -25,4 +25,7 @@ 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);
let mut state = State::new(rng, ws_stream, identity, None, true);
GatewayHandshake {
handshake_future: Box::pin(async move {
// If any step along the way failed (that are non-network related),
@@ -30,11 +30,19 @@ 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).await
ClientHandshake::new(
rng,
ws_stream,
identity,
gateway_pubkey,
expects_credential_usage,
)
.await
}
#[cfg(not(target_arch = "wasm32"))]
@@ -1,4 +1,4 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// Copyright 2020-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::registration::handshake::error::HandshakeError;
@@ -15,9 +15,17 @@ use nym_crypto::{
};
use nym_sphinx::params::{GatewayEncryptionAlgorithm, GatewaySharedKeyHkdfAlgorithm};
use rand::{CryptoRng, RngCore};
use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::str::FromStr;
use std::time::Duration;
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.
@@ -36,6 +44,10 @@ 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> {
@@ -44,6 +56,7 @@ 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 {
@@ -52,6 +65,7 @@ impl<'a, S> State<'a, S> {
identity,
remote_pubkey,
derived_shared_keys: None,
expects_credential_usage,
}
}
@@ -67,15 +81,8 @@ impl<'a, S> State<'a, S> {
self.identity
.public_key()
.to_bytes()
.iter()
.cloned()
.chain(
self.ephemeral_keypair
.public_key()
.to_bytes()
.iter()
.cloned(),
)
.into_iter()
.chain(self.ephemeral_keypair.public_key().to_bytes())
.collect()
}
@@ -132,9 +139,8 @@ impl<'a, S> State<'a, S> {
.ephemeral_keypair
.public_key()
.to_bytes()
.iter()
.cloned()
.chain(remote_ephemeral_key.to_bytes().iter().cloned())
.into_iter()
.chain(remote_ephemeral_key.to_bytes())
.collect();
let signature = self.identity.private_key().sign(message);
@@ -177,15 +183,8 @@ 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()
.iter()
.cloned()
.chain(
self.ephemeral_keypair
.public_key()
.to_bytes()
.iter()
.cloned(),
)
.into_iter()
.chain(self.ephemeral_keypair.public_key().to_bytes())
.collect();
self.remote_pubkey
@@ -200,35 +199,56 @@ impl<'a, S> State<'a, S> {
self.remote_pubkey = Some(remote_pubkey)
}
pub(crate) async fn receive_handshake_message(&mut self) -> Result<Vec<u8>, HandshakeError>
async fn _receive_handshake_message(&mut self) -> Result<Vec<u8>, HandshakeError>
where
S: Stream<Item = WsItem> + Unpin,
{
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 {
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."),
},
_ => error!("Received non-text message during registration handshake"),
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;
}
}
} else {
return Err(HandshakeError::NetworkError);
}
} else {
return Err(HandshakeError::ClosedStream);
_ => 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)?
}
// upon receiving this, the receiver should terminate the handshake
pub(crate) async fn send_handshake_error<M: Into<String>>(
&mut self,
@@ -251,7 +271,8 @@ impl<'a, S> State<'a, S> {
where
S: Sink<WsMessage> + Unpin,
{
let handshake_message = types::RegistrationHandshake::new_payload(payload);
let handshake_message =
types::RegistrationHandshake::new_payload(payload, self.expects_credential_usage);
self.ws_stream
.send(WsMessage::Text(handshake_message.try_into().unwrap()))
.await
+31 -5
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};
use crate::{GatewayMacSize, CURRENT_PROTOCOL_VERSION, INITIAL_PROTOCOL_VERSION};
use log::error;
use nym_credentials::coconut::bandwidth::CredentialSpendingData;
use nym_credentials_interface::{CoconutError, UnknownCredentialType};
@@ -19,6 +19,7 @@ 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;
@@ -37,9 +38,17 @@ pub enum RegistrationHandshake {
}
impl RegistrationHandshake {
pub fn new_payload(data: Vec<u8>) -> Self {
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)
};
RegistrationHandshake::HandshakePayload {
protocol_version: Some(CURRENT_PROTOCOL_VERSION),
protocol_version,
data,
}
}
@@ -51,11 +60,19 @@ 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> {
serde_json::from_str(&msg)
msg.parse()
}
}
@@ -155,9 +172,18 @@ 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: Some(CURRENT_PROTOCOL_VERSION),
protocol_version,
address: address.as_base58_string(),
enc_address: enc_address.to_base58_string(),
iv: iv.to_base58_string(),
+10 -9
View File
@@ -88,18 +88,19 @@ impl OverrideConfig {
if config.network_requester.enabled
&& config.storage_paths.network_requester_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()
{
Ok(config.with_default_ip_packet_router_config_path())
} else {
Ok(config)
config = config.with_default_network_requester_config_path();
}
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)
}
}
#[derive(Default)]
#[derive(Default, Debug)]
pub(crate) struct OverrideNetworkRequesterConfig {
pub(crate) fastmode: bool,
pub(crate) no_cover: bool,
@@ -112,7 +113,7 @@ pub(crate) struct OverrideNetworkRequesterConfig {
pub(crate) statistics_recipient: Option<String>,
}
#[derive(Default)]
#[derive(Default, Debug)]
pub(crate) struct OverrideIpPacketRouterConfig {
// TODO
}
+6 -4
View File
@@ -17,7 +17,7 @@ use std::{fs, io};
use super::helpers::OverrideIpPacketRouterConfig;
#[derive(Args, Clone)]
#[derive(Args, Clone, Debug)]
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, conflicts_with = "with_ip_packet_router")]
#[clap(long)]
with_network_requester: bool,
/// Allows this gateway to run an embedded network requester for minimal network overhead
#[clap(long, hide = true, conflicts_with = "with_network_requester")]
#[clap(long, hide = true)]
with_ip_packet_router: bool,
// ##### NETWORK REQUESTER FLAGS #####
@@ -243,7 +243,9 @@ 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?;
} else if config.ip_packet_router.enabled {
}
if config.ip_packet_router.enabled {
initialise_local_ip_packet_router(&config, ip_opts, *identity_keys.public_key())
.await?;
}
+1 -2
View File
@@ -5,7 +5,6 @@ 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;
@@ -50,7 +49,7 @@ pub(crate) enum Commands {
GenerateFigSpec,
}
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> {
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, conflicts_with = "with_ip_packet_router")]
#[arg(long)]
with_network_requester: Option<bool>,
/// Allows this gateway to run an embedded network requester for minimal network overhead
#[arg(long, hide = true, conflicts_with = "with_network_requester")]
#[arg(long, hide = true)]
with_ip_packet_router: Option<bool>,
// ##### NETWORK REQUESTER FLAGS #####
+1 -2
View File
@@ -11,7 +11,6 @@ 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;
@@ -42,7 +41,7 @@ struct Cli {
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
async fn main() -> anyhow::Result<()> {
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_network_requester::LocalNetworkRequesterHandle;
use crate::node::client_handling::embedded_clients::LocalEmbeddedClientHandle;
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 network requester client.
Embedded(LocalNetworkRequesterHandle),
/// Handle to a locally (inside the same process) running client.
Embedded(LocalEmbeddedClientHandle),
}
impl ActiveClient {
@@ -149,13 +149,14 @@ impl ActiveClientsStore {
}
}
/// 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);
/// 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);
if self.inner.insert(key, entry).is_some() {
// 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!")
// 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!")
}
}
@@ -12,15 +12,15 @@ use nym_sphinx::DestinationAddressBytes;
use nym_task::TaskClient;
#[derive(Debug)]
pub(crate) struct LocalNetworkRequesterHandle {
/// Nym address of the embedded network requester.
pub(crate) struct LocalEmbeddedClientHandle {
/// Nym address of the embedded client.
pub(crate) address: Recipient,
/// Message channel used internally to forward any received mix packets to the network requester.
/// Message channel used internally to forward any received mix packets to the client.
pub(crate) mix_message_sender: MixMessageSender,
}
impl LocalNetworkRequesterHandle {
impl LocalEmbeddedClientHandle {
pub(crate) fn new(address: Recipient, mix_message_sender: MixMessageSender) -> Self {
Self {
address,
@@ -28,17 +28,6 @@ impl LocalNetworkRequesterHandle {
}
}
// 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()
}
@@ -48,8 +37,8 @@ impl LocalNetworkRequesterHandle {
// 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 NR requester from multiple mix connections
/// and forwarding them via the router. kinda equivalent of a client socket handler
/// 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
pub(crate) struct MessageRouter {
mix_receiver: MixMessageReceiver,
packet_router: PacketRouter,
@@ -71,29 +60,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 NR: {err}")
error!("failed to route packets to local embedded client: {err}")
}
}
pub(crate) async fn run_with_shutdown(mut self, mut shutdown: TaskClient) {
debug!("Started embedded network requester message router with graceful shutdown support");
debug!("Started embedded client 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_network_requester::MessageRouter: Stopping since channel closed");
log::trace!("embedded_clients::MessageRouter: Stopping since channel closed");
break;
}
},
_ = shutdown.recv_with_delay() => {
log::trace!("embedded_network_requester::MessageRouter: Received shutdown");
log::trace!("embedded_clients::MessageRouter: Received shutdown");
debug_assert!(shutdown.is_shutdown());
break
}
}
}
debug!("embedded_network_requester::MessageRouter: Exiting")
debug!("embedded_network_clients::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_network_requester;
pub(crate) mod embedded_clients;
pub(crate) mod websocket;
pub(crate) const FREE_TESTNET_BANDWIDTH_VALUE: Bandwidth = Bandwidth::new(64 * 1024 * 1024 * 1024); // 64GB
+10 -21
View File
@@ -11,9 +11,7 @@ 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_network_requester::{
LocalNetworkRequesterHandle, MessageRouter,
};
use crate::node::client_handling::embedded_clients::{LocalEmbeddedClientHandle, 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};
@@ -51,7 +49,7 @@ struct StartedNetworkRequester {
used_request_filter: RequestFilter,
/// Handle to interact with the local network requester
handle: LocalNetworkRequesterHandle,
handle: LocalEmbeddedClientHandle,
}
/// Wire up and create Gateway instance
@@ -80,7 +78,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 NR is enabled, the config path must be specified
// if IPR is enabled, the config path must be specified
return Err(GatewayError::UnspecifiedIpPacketRouterConfig);
}
} else {
@@ -319,7 +317,7 @@ impl<St> Gateway<St> {
info!("the local network requester is running on {address}",);
Ok(StartedNetworkRequester {
used_request_filter: start_data.request_filter,
handle: LocalNetworkRequesterHandle::new(address, nr_mix_sender),
handle: LocalEmbeddedClientHandle::new(address, nr_mix_sender),
})
}
@@ -327,17 +325,16 @@ impl<St> Gateway<St> {
&self,
forwarding_channel: MixForwardingSender,
shutdown: TaskClient,
) -> Result<LocalNetworkRequesterHandle, GatewayError> {
) -> Result<LocalEmbeddedClientHandle, 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 (nr_mix_sender, nr_mix_receiver) = mpsc::unbounded();
let (ipr_mix_sender, ipr_mix_receiver) = mpsc::unbounded();
let router_shutdown = shutdown.fork("message_router");
let (router_tx, mut router_rx) = oneshot::channel();
@@ -348,7 +345,6 @@ 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())
@@ -379,16 +375,11 @@ impl<St> Gateway<St> {
return Err(GatewayError::IpPacketRouterStartupFailure);
};
MessageRouter::new(nr_mix_receiver, packet_router).start_with_shutdown(router_shutdown);
info!(
"the local ip packet router is running on {}",
start_data.address
);
MessageRouter::new(ipr_mix_receiver, packet_router).start_with_shutdown(router_shutdown);
let address = start_data.address;
Ok(LocalNetworkRequesterHandle::new_ip(
start_data,
nr_mix_sender,
))
info!("the local ip packet router is running on {address}");
Ok(LocalEmbeddedClientHandle::new(address, ipr_mix_sender))
}
async fn wait_for_interrupt(
@@ -504,8 +495,6 @@ 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(
+2 -15
View File
@@ -26,6 +26,7 @@ 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"] }
@@ -35,14 +36,8 @@ 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" }
@@ -51,6 +46,7 @@ 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" }
@@ -60,7 +56,6 @@ 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 = [
@@ -73,14 +68,6 @@ 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,6 +42,9 @@ 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 {
@@ -53,6 +56,7 @@ 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,6 +59,7 @@ 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<()> {
@@ -83,6 +84,7 @@ 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,6 +44,9 @@ 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 {
@@ -55,6 +58,7 @@ 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,6 +80,7 @@ fn default_mixnode_http_config() -> config::Http {
DEFAULT_HTTP_API_LISTENING_PORT,
),
landing_page_assets_path: None,
metrics_key: None,
}
}
@@ -208,6 +209,15 @@ 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,6 +95,7 @@ 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,6 +57,8 @@ 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.
+1 -27
View File
@@ -3,18 +3,11 @@
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;
@@ -41,12 +34,6 @@ 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();
@@ -56,23 +43,10 @@ async fn main() -> anyhow::Result<()> {
maybe_print_banner(crate_name!(), crate_version!());
}
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")
}
}
setup_logging();
commands::execute(args).await?;
cfg_if::cfg_if! {
if #[cfg(feature = "cpucycles")] {
opentelemetry::global::shutdown_tracer_provider();
}}
Ok(())
}
+3
View File
@@ -4,6 +4,7 @@
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;
@@ -29,6 +30,7 @@ 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";
}
@@ -44,6 +46,7 @@ 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,6 +10,7 @@ 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 {
+35 -9
View File
@@ -1,33 +1,59 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use crate::node::node_statistics::{NodeStats, NodeStatsSimple, SharedNodeStats};
use axum::extract::{Query, State};
use crate::node::node_statistics::{NodeStatsSimple, SharedNodeStats};
use axum::{
extract::{Query, State},
http::HeaderMap,
};
use nym_metrics::REGISTRY;
use nym_node::http::api::{FormattedResponse, Output};
use serde::{Deserialize, Serialize};
use super::state::MixnodeAppState;
#[derive(Serialize)]
#[serde(untagged)]
pub enum NodeStatsResponse {
Full(NodeStats),
Full(String),
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) {
REGISTRY.to_string()
} 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();
let snapshot_data = stats.clone_data().await;
// there's no point in returning the entire hashmap of sending destinations in regular mode
let response = if params.debug {
NodeStatsResponse::Full(snapshot_data)
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(REGISTRY.to_string())
} else {
NodeStatsResponse::Simple(snapshot_data.simplify())
};
output.to_response(response)
}
}
pub type MixnodeStatsResponse = FormattedResponse<NodeStatsResponse>;
+6
View File
@@ -64,6 +64,12 @@ 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_mixnode_common::measure;
use nym_metrics::nanos;
use nym_sphinx::forwarding::packet::MixPacket;
use nym_sphinx::framing::codec::NymCodec;
use nym_sphinx::framing::packet::FramedNymPacket;
@@ -19,9 +19,6 @@ 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)]
@@ -53,10 +50,6 @@ 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
@@ -66,7 +59,7 @@ impl ConnectionHandler {
// all processing such, key caching, etc. was done.
// however, if it was a forward hop, we still need to delay it
measure!({
nanos!("handle_received_packet", {
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,11 +72,13 @@ 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)
}
@@ -249,6 +251,7 @@ impl MixNode {
self.start_http_api(
atomic_verloc_results,
node_stats_pointer,
self.config.metrics_key(),
shutdown.subscribe().named("http-api"),
)?;
+60 -75
View File
@@ -1,6 +1,8 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: GPL-3.0-only
use nym_metrics::REGISTRY;
use super::TaskClient;
use futures::channel::mpsc;
use futures::lock::Mutex;
@@ -15,7 +17,7 @@ use std::time::{Duration, SystemTime};
use tokio::sync::{RwLock, RwLockReadGuard};
// convenience aliases
type PacketsMap = HashMap<String, u64>;
type PacketsMap = HashMap<String, f64>;
type PacketDataReceiver = mpsc::UnboundedReceiver<PacketEvent>;
type PacketDataSender = mpsc::UnboundedSender<PacketEvent>;
@@ -27,14 +29,15 @@ 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: HashMap::new(),
packets_explicitly_dropped_since_startup: HashMap::new(),
packets_received_since_last_update: 0,
packets_received_since_startup: 0.,
packets_sent_since_startup_all: 0.,
packets_dropped_since_startup_all: 0.,
packets_received_since_last_update: 0.,
packets_sent_since_last_update: HashMap::new(),
packets_explicitly_dropped_since_last_update: HashMap::new(),
})),
@@ -43,7 +46,7 @@ impl SharedNodeStats {
pub(crate) async fn update(
&self,
new_received: u64,
new_received: f64,
new_sent: PacketsMap,
new_dropped: PacketsMap,
) {
@@ -54,20 +57,21 @@ impl SharedNodeStats {
guard.update_time = snapshot_time;
guard.packets_received_since_startup += new_received;
for (mix, count) in &new_sent {
*guard
.packets_sent_since_startup
.entry(mix.clone())
.or_insert(0) += *count;
for count in new_sent.values() {
guard.packets_sent_since_startup_all += count;
}
for (mix, count) in &new_dropped {
*guard
.packets_explicitly_dropped_since_last_update
.entry(mix.clone())
.or_insert(0) += *count;
for count in new_dropped.values() {
guard.packets_dropped_since_startup_all += count;
}
REGISTRY.inc_by("packets_received_since_startup", new_received);
REGISTRY.inc_by("packets_sent_since_startup_all", new_sent.values().sum());
REGISTRY.inc_by(
"packets_dropped_since_startup_all",
new_dropped.values().sum(),
);
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;
@@ -82,27 +86,18 @@ impl SharedNodeStats {
}
}
#[derive(Serialize, Clone)]
#[derive(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: 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,
packets_received_since_startup: f64,
packets_sent_since_startup_all: f64,
packets_dropped_since_startup_all: f64,
packets_received_since_last_update: f64,
// 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,
}
@@ -112,10 +107,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: Default::default(),
packets_explicitly_dropped_since_startup: Default::default(),
packets_received_since_last_update: 0,
packets_received_since_startup: 0.,
packets_sent_since_startup_all: 0.,
packets_dropped_since_startup_all: 0.,
packets_received_since_last_update: 0.,
packets_sent_since_last_update: Default::default(),
packets_explicitly_dropped_since_last_update: Default::default(),
}
@@ -128,11 +123,8 @@ 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.values().sum(),
packets_explicitly_dropped_since_startup: self
.packets_explicitly_dropped_since_startup
.values()
.sum(),
packets_sent_since_startup: self.packets_sent_since_startup_all,
packets_explicitly_dropped_since_startup: self.packets_dropped_since_startup_all,
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
@@ -151,21 +143,21 @@ pub struct NodeStatsSimple {
#[serde(serialize_with = "humantime_serde::serialize")]
previous_update_time: SystemTime,
packets_received_since_startup: u64,
packets_received_since_startup: f64,
// note: sent does not imply forwarded. We don't know if it was delivered successfully
packets_sent_since_startup: u64,
packets_sent_since_startup: f64,
// we know for sure we dropped those packets
packets_explicitly_dropped_since_startup: u64,
packets_explicitly_dropped_since_startup: f64,
packets_received_since_last_update: u64,
packets_received_since_last_update: f64,
// note: sent does not imply forwarded. We don't know if it was delivered successfully
packets_sent_since_last_update: u64,
packets_sent_since_last_update: f64,
// we know for sure we dropped those packets
packets_explicitly_dropped_since_last_update: u64,
packets_explicitly_dropped_since_last_update: f64,
}
pub(crate) enum PacketEvent {
@@ -203,14 +195,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) {
@@ -332,7 +324,9 @@ 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, sent, dropped).await;
self.current_stats
.update(received as f64, sent, dropped)
.await;
}
async fn run(&mut self) {
@@ -376,21 +370,18 @@ impl PacketStatsConsoleLogger {
info!(
"Since startup mixed {} packets! ({} in last {} seconds)",
stats.packets_sent_since_startup.values().sum::<u64>(),
stats.packets_sent_since_last_update.values().sum::<u64>(),
stats.packets_sent_since_startup_all,
stats.packets_sent_since_last_update.values().sum::<f64>(),
difference_secs,
);
if !stats.packets_explicitly_dropped_since_startup.is_empty() {
if stats.packets_dropped_since_startup_all > 0. {
info!(
"Since startup dropped {} packets! ({} in last {} seconds)",
stats
.packets_explicitly_dropped_since_startup
.values()
.sum::<u64>(),
stats.packets_dropped_since_startup_all,
stats
.packets_explicitly_dropped_since_last_update
.values()
.sum::<u64>(),
.sum::<f64>(),
difference_secs,
);
}
@@ -403,22 +394,19 @@ impl PacketStatsConsoleLogger {
);
trace!(
"Since startup sent packets to the following: \n{:#?} \n And in last {} seconds: {:#?})",
stats.packets_sent_since_startup,
stats.packets_sent_since_startup_all,
difference_secs,
stats.packets_sent_since_last_update
);
} else {
info!(
"Since startup mixed {} packets!",
stats.packets_sent_since_startup.values().sum::<u64>(),
stats.packets_sent_since_startup_all,
);
if !stats.packets_explicitly_dropped_since_startup.is_empty() {
if stats.packets_dropped_since_startup_all > 0. {
info!(
"Since startup dropped {} packets!",
stats
.packets_explicitly_dropped_since_startup
.values()
.sum::<u64>(),
stats.packets_dropped_since_startup_all,
);
}
@@ -427,8 +415,8 @@ impl PacketStatsConsoleLogger {
stats.packets_received_since_startup
);
trace!(
"Since startup sent packets to the following: \n{:#?}",
stats.packets_sent_since_startup
"Since startup sent packets {}",
stats.packets_sent_since_startup_all
);
}
}
@@ -545,14 +533,11 @@ mod tests {
// Get output (stats)
let stats = node_stats_pointer.read().await;
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_startup_all, &2.);
assert_eq!(&stats.packets_sent_since_last_update.get("foo"), &Some(&2.));
assert_eq!(&stats.packets_sent_since_last_update.len(), &1);
assert_eq!(&stats.packets_received_since_startup, &0u64);
assert!(&stats.packets_explicitly_dropped_since_startup.is_empty());
assert_eq!(&stats.packets_received_since_startup, &0.);
assert_eq!(&stats.packets_dropped_since_startup_all, &0.);
assert_eq!(REGISTRY.to_string(), "# HELP packets_dropped_since_startup_all packets_dropped_since_startup_all\n# TYPE packets_dropped_since_startup_all counter\npackets_dropped_since_startup_all 0\n# HELP packets_received_since_startup packets_received_since_startup\n# TYPE packets_received_since_startup counter\npackets_received_since_startup 0\n# HELP packets_sent_since_startup_all packets_sent_since_startup_all\n# TYPE packets_sent_since_startup_all counter\npackets_sent_since_startup_all 2\n")
}
}
+2
View File
@@ -4087,7 +4087,9 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
"tokio",
"tungstenite",
"wasmtimer",
"zeroize",
]
+1 -1
View File
@@ -39,7 +39,7 @@ pub async fn handle_task_status(
window: &tauri::Window,
) {
match task_status {
TaskStatus::Ready => {
TaskStatus::Ready | TaskStatus::ReadyWithGateway(_) => {
{
let mut state_w = state.write().await;
state_w.mark_connected(window);
+4
View File
@@ -49,6 +49,9 @@ pub struct Http {
/// Path to assets directory of custom landing page of this node.
#[serde(deserialize_with = "de_maybe_path")]
pub landing_page_assets_path: Option<PathBuf>,
#[serde(default)]
pub metrics_key: Option<String>,
}
impl Default for Http {
@@ -56,6 +59,7 @@ impl Default for Http {
Http {
bind_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), DEFAULT_HTTP_PORT),
landing_page_assets_path: None,
metrics_key: None,
}
}
}
+3 -3
View File
@@ -20,13 +20,13 @@ echo "Using $localnetdir for the localnet"
# initialise mixnet
echo "initialising mixnode1..."
cargo run --release --bin nym-mixnode -- init --id "mix1-$suffix" --host 127.0.0.1 --mix-port 10001 --verloc-port 20001 --http-api-port 30001 --output=json >>"$localnetdir/mix1.json"
cargo run --release --bin nym-mixnode -- init --id "mix1-$suffix" --host 127.0.0.1 --mix-port 10001 --verloc-port 20001 --http-api-port 30001 --metrics-key=lala --output=json >>"$localnetdir/mix1.json"
echo "initialising mixnode2..."
cargo run --release --bin nym-mixnode -- init --id "mix2-$suffix" --host 127.0.0.1 --mix-port 10002 --verloc-port 20002 --http-api-port 30002 --output=json >>"$localnetdir/mix2.json"
cargo run --release --bin nym-mixnode -- init --id "mix2-$suffix" --host 127.0.0.1 --mix-port 10002 --verloc-port 20002 --http-api-port 30002 --metrics-key=lala --output=json >>"$localnetdir/mix2.json"
echo "initialising mixnode3..."
cargo run --release --bin nym-mixnode -- init --id "mix3-$suffix" --host 127.0.0.1 --mix-port 10003 --verloc-port 20003 --http-api-port 30003 --output=json >>"$localnetdir/mix3.json"
cargo run --release --bin nym-mixnode -- init --id "mix3-$suffix" --host 127.0.0.1 --mix-port 10003 --verloc-port 20003 --http-api-port 30003 --metrics-key=lala --output=json >>"$localnetdir/mix3.json"
echo "initialising gateway..."
cargo run --release --bin nym-gateway -- init --id "gateway-$suffix" --host 127.0.0.1 --mix-port 10004 --clients-port 9000 --output=json >>"$localnetdir/gateway.json"
+83
View File
@@ -0,0 +1,83 @@
#!/bin/bash
# Check if the script is run as root
if [ "$(id -u)" -ne 0 ]; then
echo "This script must be run as root. Please use sudo or log in as the root user."
exit 1
fi
# Hardcoded node_exporter version
node_exporter_version="1.7.0"
# Create a user for node_exporter without a home directory
useradd --no-create-home --shell /bin/false node_exporter
# Download node_exporter
echo "Downloading node_exporter version $node_exporter_version..."
wget "https://github.com/prometheus/node_exporter/releases/download/v$node_exporter_version/node_exporter-$node_exporter_version.linux-amd64.tar.gz" -O /tmp/node_exporter-$node_exporter_version.linux-amd64.tar.gz
if [ $? -ne 0 ]; then
echo "Failed to download node_exporter."
exit 1
fi
# Unarchive node_exporter
echo "Unarchiving node_exporter..."
tar xvfz /tmp/node_exporter-$node_exporter_version.linux-amd64.tar.gz -C /tmp
if [ $? -ne 0 ]; then
echo "Failed to unarchive node_exporter."
exit 1
fi
# Move node_exporter to /usr/local/bin
echo "Moving node_exporter to /usr/local/bin..."
mv /tmp/node_exporter-$node_exporter_version.linux-amd64/node_exporter /usr/local/bin/node_exporter
if [ $? -ne 0 ]; then
echo "Failed to move node_exporter."
exit 1
fi
# Set ownership and permissions
chown node_exporter:node_exporter /usr/local/bin/node_exporter
chmod 0755 /usr/local/bin/node_exporter
# Create node_exporter service file
echo "Creating node_exporter service file..."
cat <<EOF > /etc/systemd/system/node_exporter.service
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter --web.config.file /etc/prometheus_node_exporter/configuration.yml
[Install]
WantedBy=multi-user.target
EOF
mkdir -p /etc/prometheus_node_exporter/
sudo cat << EOF > /etc/prometheus_node_exporter/configuration.yml
basic_auth_users:
prometheus: "\$2y\$10\$aB1RMr6ZGg2psbMOezmfluVzGcH/VHIqP4Lksx0DWuw/QSr9Iccwu"
EOF
ufw allow 9100
# Reload systemd, enable and start node_exporter service
echo "Configuring systemd for node_exporter..."
systemctl daemon-reload
systemctl enable node_exporter.service
systemctl start node_exporter.service
# Cleanup
echo "Cleaning up..."
rm -rf /tmp/node_exporter-${node_exporter_version}.linux-amd64.tar.gz
rm -rf /tmp/node_exporter-${node_exporter_version}.linux-amd64
echo "node_exporter installation and configuration complete."
+117
View File
@@ -0,0 +1,117 @@
import argparse
import os
import requests
import json
from collections import namedtuple
Config = namedtuple("Config", ["port", "outfile", "outlink", "env"])
def gateway_targets(entry):
targets = [
# Config(
# None,
# "/tmp/temp_targets_gateway.json",
# f"/tmp/prom_targets_gateway_{entry['env']}.json",
# entry["env"],
# ),
Config(
9100,
"/tmp/temp_targets_gateway_node.json",
f"/tmp/prom_targets_gateway_node_{entry['env']}.json",
entry["env"],
),
]
gateways = requests.get(f"{entry['nym_api']}/api/v1/gateways").json()
for config in targets:
config_to_targets(config, gateways, {"kind": "gateway"})
def mixnode_targets(entry):
targets = [
Config(
None,
"/tmp/temp_targets_mix.json",
f"/tmp/prom_targets_mix_{entry['env']}.json",
entry["env"],
),
Config(
9100,
"/tmp/temp_targets_node.json",
f"/tmp/prom_targets_node_{entry['env']}.json",
entry["env"],
),
]
mixnodes = requests.get(f"{entry['nym_api']}/api/v1/mixnodes").json()
for config in targets:
config_to_targets(config, mixnodes, {"kind": "mixnode"})
def validate_config_entry(entry):
return entry.get("nym_api") and entry.get("env")
def config_to_targets(config, mixnodes, labels=None):
prom_targets = [
make_prom_target(config.env, mixnode, config.port, labels)
for mixnode in mixnodes
]
with open(config.outfile, "w") as f:
json.dump(prom_targets, f)
os.chmod(config.outfile, 0o777)
os.rename(config.outfile, config.outlink)
os.chmod(config.outlink, 0o777)
print(f"Prometheus -> {len(prom_targets)} targets written to {config.outlink}")
def make_prom_target(env, mixnode, port=None, labels=None):
bond_info = mixnode.get("bond_information", {})
mix_node = bond_info.get("mix_node", {})
host = mix_node.get("host", None)
port = port if port else mix_node.get("http_api_port", None)
if host is None or port is None:
return None
target = {
"targets": [f"{host}:{port}"],
"labels": {
"mix_node_host": host,
"identity_key": mix_node.get("identity_key", None),
"sphinx_key": mix_node.get("sphinx_key", None),
"mix_node_version": mix_node.get("version", None),
"mixnet_env": env,
},
}
for k, v in labels.items():
target["labels"][k] = v
return target
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Create prometheus targets for rewarded set mixnodes."
)
parser.add_argument(
"config",
type=str,
help="Config file, see scripts/prom_targets_config.json",
)
args = parser.parse_args()
config_file = args.config
with open(config_file, "r") as f:
config = json.load(f)
for entry in config:
mixnode_targets(entry)
gateway_targets(entry)
+4
View File
@@ -0,0 +1,4 @@
[
{ "nym_api": "https://sandbox-nym-api1.nymtech.net", "env": "sandbox" },
{ "nym_api": "https://api.performance.nymte.ch", "env": "performance" }
]
+1
View File
@@ -0,0 +1 @@
requests
+7
View File
@@ -0,0 +1,7 @@
# FFI
This repo contains bindings for C/C++ and Go in the respectively named directories.
`shared/` contains shared 'internal' functions which are imported by bindings. Primarily these functions rely on managing:
* that the client is not mutated by multiple threads simultaneously
* that client actions happen in blocking threads
+346 -15
View File
@@ -155,6 +155,47 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "askama"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28"
dependencies = [
"askama_derive",
"askama_escape",
]
[[package]]
name = "askama_derive"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83"
dependencies = [
"askama_parser",
"basic-toml",
"mime",
"mime_guess",
"proc-macro2",
"quote",
"serde",
"syn 2.0.48",
]
[[package]]
name = "askama_escape"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
[[package]]
name = "askama_parser"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0"
dependencies = [
"nom",
]
[[package]]
name = "async-trait"
version = "0.1.77"
@@ -240,6 +281,15 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "basic-toml"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5"
dependencies = [
"serde",
]
[[package]]
name = "bincode"
version = "1.3.3"
@@ -474,6 +524,38 @@ dependencies = [
"serde",
]
[[package]]
name = "camino"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
dependencies = [
"serde",
]
[[package]]
name = "cargo-platform"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d"
dependencies = [
"serde",
]
[[package]]
name = "cargo_metadata"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a"
dependencies = [
"camino",
"cargo-platform",
"semver 1.0.21",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "cc"
version = "1.0.83"
@@ -1466,6 +1548,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e"
[[package]]
name = "fs-err"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
dependencies = [
"autocfg",
]
[[package]]
name = "funty"
version = "2.0.0"
@@ -1650,6 +1741,12 @@ dependencies = [
"url",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "gloo-net"
version = "0.3.1"
@@ -1696,6 +1793,17 @@ dependencies = [
"web-sys",
]
[[package]]
name = "goblin"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d6b4de4a8eb6c46a8c77e1d3be942cb9a8bf073c22374578e5ba4b08ed0ff68"
dependencies = [
"log",
"plain",
"scroll",
]
[[package]]
name = "group"
version = "0.10.0"
@@ -2283,6 +2391,16 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -2300,9 +2418,9 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.10"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
@@ -2561,6 +2679,20 @@ dependencies = [
"thiserror",
]
[[package]]
name = "nym-cpp-ffi"
version = "0.1.1"
dependencies = [
"anyhow",
"bs58 0.5.0",
"lazy_static",
"nym-bin-common",
"nym-ffi-shared",
"nym-sdk",
"nym-sphinx-anonymous-replies",
"tokio",
]
[[package]]
name = "nym-credential-storage"
version = "0.1.0"
@@ -2696,6 +2828,21 @@ dependencies = [
"url",
]
[[package]]
name = "nym-ffi-shared"
version = "0.1.0"
dependencies = [
"anyhow",
"bs58 0.5.0",
"lazy_static",
"nym-bin-common",
"nym-sdk",
"nym-sphinx-anonymous-replies",
"tokio",
"uniffi",
"uniffi_build",
]
[[package]]
name = "nym-gateway-client"
version = "0.1.0"
@@ -3292,19 +3439,6 @@ dependencies = [
"x25519-dalek 2.0.0",
]
[[package]]
name = "nym_cpp_ffi"
version = "0.1.0"
dependencies = [
"anyhow",
"bs58 0.5.0",
"lazy_static",
"nym-bin-common",
"nym-sdk",
"nym-sphinx-anonymous-replies",
"tokio",
]
[[package]]
name = "object"
version = "0.32.2"
@@ -3320,6 +3454,12 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "oneshot-uniffi"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c548d5c78976f6955d72d0ced18c48ca07030f7a1d4024529fedd7c1c01b29c"
[[package]]
name = "opaque-debug"
version = "0.2.3"
@@ -3611,6 +3751,12 @@ version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "platforms"
version = "3.3.0"
@@ -4149,6 +4295,26 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "scroll"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
dependencies = [
"scroll_derive",
]
[[package]]
name = "scroll_derive"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "sct"
version = "0.6.1"
@@ -4220,6 +4386,9 @@ name = "semver"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
dependencies = [
"serde",
]
[[package]]
name = "semver-parser"
@@ -4382,6 +4551,12 @@ dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "slab"
version = "0.4.9"
@@ -4649,6 +4824,12 @@ dependencies = [
"tokio-rustls 0.23.4",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stringprep"
version = "0.1.4"
@@ -5184,6 +5365,15 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]]
name = "unicase"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.15"
@@ -5217,6 +5407,138 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
name = "uniffi"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21345172d31092fd48c47fd56c53d4ae9e41c4b1f559fb8c38c1ab1685fd919f"
dependencies = [
"anyhow",
"camino",
"clap",
"uniffi_bindgen",
"uniffi_build",
"uniffi_core",
"uniffi_macros",
]
[[package]]
name = "uniffi_bindgen"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd992f2929a053829d5875af1eff2ee3d7a7001cb3b9a46cc7895f2caede6940"
dependencies = [
"anyhow",
"askama",
"camino",
"cargo_metadata",
"clap",
"fs-err",
"glob",
"goblin",
"heck",
"once_cell",
"paste",
"serde",
"toml 0.5.11",
"uniffi_meta",
"uniffi_testing",
"uniffi_udl",
]
[[package]]
name = "uniffi_build"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "001964dd3682d600084b3aaf75acf9c3426699bc27b65e96bb32d175a31c74e9"
dependencies = [
"anyhow",
"camino",
"uniffi_bindgen",
]
[[package]]
name = "uniffi_checksum_derive"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55137c122f712d9330fd985d66fa61bdc381752e89c35708c13ce63049a3002c"
dependencies = [
"quote",
"syn 2.0.48",
]
[[package]]
name = "uniffi_core"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6121a127a3af1665cd90d12dd2b3683c2643c5103281d0fed5838324ca1fad5b"
dependencies = [
"anyhow",
"bytes",
"camino",
"log",
"once_cell",
"oneshot-uniffi",
"paste",
"static_assertions",
]
[[package]]
name = "uniffi_macros"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11cf7a58f101fcedafa5b77ea037999b88748607f0ef3a33eaa0efc5392e92e4"
dependencies = [
"bincode",
"camino",
"fs-err",
"once_cell",
"proc-macro2",
"quote",
"serde",
"syn 2.0.48",
"toml 0.5.11",
"uniffi_build",
"uniffi_meta",
]
[[package]]
name = "uniffi_meta"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71dc8573a7b1ac4b71643d6da34888273ebfc03440c525121f1b3634ad3417a2"
dependencies = [
"anyhow",
"bytes",
"siphasher",
"uniffi_checksum_derive",
]
[[package]]
name = "uniffi_testing"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "118448debffcb676ddbe8c5305fb933ab7e0123753e659a71dc4a693f8d9f23c"
dependencies = [
"anyhow",
"camino",
"cargo_metadata",
"fs-err",
"once_cell",
]
[[package]]
name = "uniffi_udl"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "889edb7109c6078abe0e53e9b4070cf74a6b3468d141bdf5ef1bd4d1dc24a1c3"
dependencies = [
"anyhow",
"uniffi_meta",
"uniffi_testing",
"weedle2",
]
[[package]]
name = "universal-hash"
version = "0.5.1"
@@ -5473,6 +5795,15 @@ dependencies = [
"webpki 0.22.4",
]
[[package]]
name = "weedle2"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e79c5206e1f43a2306fd64bdb95025ee4228960f2e6c5a8b173f3caaf807741"
dependencies = [
"nom",
]
[[package]]
name = "winapi"
version = "0.3.9"
+4 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "nym_cpp_ffi"
version = "0.1.0"
name = "nym-cpp-ffi"
version = "0.1.1"
edition = "2021"
[lib]
@@ -10,11 +10,11 @@ crate-type = ["cdylib"]
[dependencies]
# Async runtime
tokio = { version = "1", features = ["full"] }
# Nym clients, addressing, packet format, common tools (logging)
# Nym clients, addressing, packet format, common tools (logging), ffi shared
nym-sdk = { git = "https://github.com/nymtech/nym", branch = "master" }
nym-bin-common = { git = "https://github.com/nymtech/nym", branch = "master" }
nym-sphinx-anonymous-replies = { git = "https://github.com/nymtech/nym", branch = "master" }
# static var macro
nym-ffi-shared = { path = "../shared" }
lazy_static = "1.4.0"
# error handling
anyhow = "1.0.75"
+2 -2
View File
@@ -1,8 +1,8 @@
# C++ FFI
> ⚠️ This is an initial version of this library in order to give developers something to experiment with. If you use this code to begin testing out Mixnet integration and run into issues, errors, or have feedback, please feel free to open an issue; feedback from developers trying to use it will help us improve it. If you have questions feel free to reach out via our [Matrix channel]().
> ⚠️ This is an initial version of this library in order to give developers something to experiment with. If you use this code to begin testing out Mixnet integration and run into issues, errors, or have feedback, please feel free to open an issue; feedback from developers trying to use it will help us improve it. If you have questions feel free to reach out via our [Matrix channel](https://matrix.to/#/#dev:nymtech.chat).
This repo contains:
* `lib.rs`: an initial version of bindings for interacting with the Mixnet via the Rust SDK from C++.
* `lib.rs`: an initial version of bindings for interacting with the Mixnet via the Rust SDK from C++. These are essentially match statements wrapping imported functions from the `nym-ffi-shared` lib allowing for nicer [error handling](#error-handling-).
* `main.cpp`: an example of using this library, relying on `Boost` for threads.
The example `.cpp` file is a simple example flow of:
+1 -1
View File
@@ -44,7 +44,7 @@ else
build_artifacts_and_link;
./main;
else
echo "unknown optional argument - the only available optional argument is 'clean'"
printf "unknown optional argument - the only available optional argument is 'clean'"
exit 1
fi
fi
+45 -171
View File
@@ -1,49 +1,13 @@
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use anyhow::{anyhow, bail};
use lazy_static::lazy_static;
use nym_sdk::mixnet::{MixnetClient, MixnetMessageSender, ReconstructedMessage};
use nym_sphinx_anonymous_replies::requests::AnonymousSenderTag;
use nym_ffi_shared;
use std::ffi::{c_char, c_int, CStr, CString};
use nym_sphinx_anonymous_replies::requests::AnonymousSenderTag;
use std::mem::forget;
use std::sync::{Arc, Mutex};
use tokio::runtime::Runtime;
/*
NYM_CLIENT: Static reference (only init-ed once) to:
- Arc: share ownership
- Mutex: thread-safe way to share data between threads
- Option: init-ed or not
RUNTIME: Tokio runtime: no need to pass back to C and deal with raw pointers as it was previously
*/
lazy_static! {
static ref NYM_CLIENT: Arc<Mutex<Option<MixnetClient>>> = Arc::new(Mutex::new(None));
static ref RUNTIME: Runtime = Runtime::new().unwrap();
}
#[derive(Debug)]
pub enum StatusCode {
NoError = 0,
ClientInitError = -1,
ClientUninitialisedError = -2,
SelfAddrError = -3,
SendMsgError = -4,
ReplyError = -5,
ListenError = -6,
}
// pub type CIntCallback = extern "C" fn(i32);
pub type CStringCallback = extern "C" fn(*const c_char);
pub type CMessageCallback = extern "C" fn(ReceivedMessage);
// FFI-sanitised way of sending back a ReconstructedMessage to C
#[repr(C)]
pub struct ReceivedMessage {
message: *const u8,
size: usize,
sender_tag: *const c_char,
}
mod types;
use crate::types::types::{CMessageCallback, CStringCallback, ReceivedMessage, StatusCode};
#[no_mangle]
pub extern "C" fn init_logging() {
@@ -52,76 +16,31 @@ pub extern "C" fn init_logging() {
#[no_mangle]
pub extern "C" fn init_ephemeral() -> c_int {
match init_ephemeral_internal() {
match nym_ffi_shared::init_ephemeral_internal() {
Ok(_) => StatusCode::NoError as c_int,
Err(_) => StatusCode::ClientInitError as c_int,
}
}
fn init_ephemeral_internal() -> anyhow::Result<(), anyhow::Error> {
if NYM_CLIENT.lock().unwrap().as_ref().is_some() {
bail!("client already exists");
} else {
RUNTIME.block_on(async move {
let init_client = MixnetClient::connect_new().await?;
let mut client = NYM_CLIENT.try_lock();
if let Ok(ref mut client) = client {
**client = Some(init_client);
} else {
anyhow!("couldnt lock NYM_CLIENT");
}
Ok::<(), anyhow::Error>(())
})?;
}
Ok(())
}
#[no_mangle]
pub extern "C" fn get_self_address(callback: CStringCallback) -> c_int {
match get_self_address_internal(callback) {
Ok(_) => StatusCode::NoError as c_int,
match nym_ffi_shared::get_self_address_internal(/*callback*/) {
Ok(addr) => {
let c_ptr = CString::new(addr).expect("could not convert Nym address to CString");
let call = CStringCallback::new(callback.callback);
// as_ptr() keeps ownership in rust unlike into_raw() so no need to free it
call.trigger(c_ptr.as_ptr());
StatusCode::NoError as c_int
}
Err(_) => StatusCode::SelfAddrError as c_int,
}
}
fn get_self_address_internal(callback: CStringCallback) -> anyhow::Result<(), anyhow::Error> {
let client = NYM_CLIENT.lock().expect("could not lock NYM_CLIENT");
if client.is_none() {
bail!("Client is not yet initialised");
}
let nym_client = client
.as_ref()
.ok_or_else(|| anyhow!("could not get client as_ref()"))?;
// get address as cstring
let c_string = CString::new(nym_client.nym_address().to_string())?;
// as_ptr() keeps ownership in rust unlike into_raw() so no need to free it
callback(c_string.as_ptr());
Ok(())
}
#[no_mangle]
pub extern "C" fn send_message(recipient: *const c_char, message: *const c_char) -> c_int {
match send_message_internal(recipient, message) {
Ok(_) => StatusCode::NoError as c_int,
Err(_) => StatusCode::SendMsgError as c_int,
}
}
fn send_message_internal(
recipient: *const c_char,
message: *const c_char,
) -> anyhow::Result<(), anyhow::Error> {
let client = NYM_CLIENT.lock().expect("could not lock NYM_CLIENT");
if client.is_none() {
bail!("Client is not yet initialised");
}
let nym_client = client
.as_ref()
.ok_or_else(|| anyhow!("could not get client as_ref()"))?;
let c_str = unsafe {
if recipient.is_null() {
bail!("recipient is null");
return StatusCode::RecipientNullError as c_int;
}
let c_str = CStr::from_ptr(recipient);
c_str
@@ -130,44 +49,24 @@ fn send_message_internal(
let recipient = r_str.parse().unwrap();
let c_str = unsafe {
if message.is_null() {
bail!("message is null");
return StatusCode::MessageNullError as c_int;
}
let c_str = CStr::from_ptr(message);
c_str
};
let message = c_str.to_str().unwrap();
// send message
RUNTIME.block_on(async move {
nym_client.send_plain_message(recipient, message).await?;
Ok::<(), anyhow::Error>(())
})?;
Ok(())
match nym_ffi_shared::send_message_internal(recipient, message) {
Ok(_) => StatusCode::NoError as c_int,
Err(_) => StatusCode::SendMsgError as c_int,
}
}
#[no_mangle]
pub extern "C" fn reply(recipient: *const c_char, message: *const c_char) -> c_int {
match reply_internal(recipient, message) {
Ok(_) => StatusCode::NoError as c_int,
Err(_) => StatusCode::ReplyError as c_int,
}
}
fn reply_internal(
recipient: *const c_char,
message: *const c_char,
) -> anyhow::Result<(), anyhow::Error> {
let client = NYM_CLIENT.lock().expect("could not lock NYM_CLIENT");
if client.is_none() {
bail!("Client is not yet initialised");
}
let nym_client = client
.as_ref()
.ok_or_else(|| anyhow!("could not get client as_ref()"))?;
let recipient = unsafe {
if recipient.is_null() {
bail!("recipient is null");
return StatusCode::RecipientNullError as c_int;
}
let r_str = CStr::from_ptr(recipient).to_string_lossy().into_owned();
AnonymousSenderTag::try_from_base58_string(r_str)
@@ -175,64 +74,39 @@ fn reply_internal(
};
let message = unsafe {
if message.is_null() {
bail!("message is null");
return StatusCode::MessageNullError as c_int;
}
let c_str = CStr::from_ptr(message);
let r_str = c_str.to_str().unwrap();
r_str
};
RUNTIME.block_on(async move {
nym_client.send_reply(recipient, message).await?;
Ok::<(), anyhow::Error>(())
})?;
Ok(())
match nym_ffi_shared::reply_internal(recipient, message) {
Ok(_) => StatusCode::NoError as c_int,
Err(_) => StatusCode::ReplyError as c_int,
}
}
#[no_mangle]
pub extern "C" fn listen_for_incoming(callback: CMessageCallback) -> c_int {
match listen_for_incoming_internal(callback) {
Ok(_) => StatusCode::NoError as c_int,
match nym_ffi_shared::listen_for_incoming_internal() {
Ok(received) => {
let message_ptr = received.message.as_ptr();
let message_length = received.message.len();
let c_string = CString::new(received.sender_tag.unwrap().to_string()).unwrap();
let sender_ptr = c_string.as_ptr();
// stop deallocation when out of scope as passing raw ptr to it elsewhere
forget(received);
let rec_for_c = ReceivedMessage {
message: message_ptr,
size: message_length,
sender_tag: sender_ptr,
};
let call = CMessageCallback::new(callback.callback);
call.trigger(rec_for_c);
StatusCode::NoError as c_int
}
Err(_) => StatusCode::ListenError as c_int,
}
}
fn listen_for_incoming_internal(callback: CMessageCallback) -> anyhow::Result<(), anyhow::Error> {
let mut binding = NYM_CLIENT.lock().expect("could not lock NYM_CLIENT");
if binding.is_none() {
bail!("recipient is null");
}
let client = binding
.as_mut()
.ok_or_else(|| anyhow!("could not get client as_ref()"))?;
RUNTIME.block_on(async move {
let received = wait_for_non_empty_message(client).await?;
let message_ptr = received.message.as_ptr();
let message_length = received.message.len();
let c_string = CString::new(received.sender_tag.unwrap().to_string())?;
let sender_ptr = c_string.as_ptr();
// stop deallocation when out of scope as passing raw ptr to it elsewhere
forget(received);
let rec_for_c = ReceivedMessage {
message: message_ptr,
size: message_length,
sender_tag: sender_ptr,
};
callback(rec_for_c);
Ok::<(), anyhow::Error>(())
})?;
Ok(())
}
pub async fn wait_for_non_empty_message(
client: &mut MixnetClient,
) -> anyhow::Result<ReconstructedMessage> {
while let Some(mut new_message) = client.wait_for_messages().await {
if !new_message.is_empty() {
return new_message
.pop()
.ok_or_else(|| anyhow!("could not get client as_ref()"));
}
}
bail!("(Rust) did not receive any non-empty message")
}
+7 -5
View File
@@ -67,6 +67,7 @@ int main() {
// - execute
// - get() returned val
// - handle val
// initialise an ephemeral client - aka one without specified keystore
boost::packaged_task<char> init(boost::bind(init_ephemeral));
boost::unique_future<char> init_future = init.get_future();
init();
@@ -77,7 +78,7 @@ int main() {
return_code = get_self_address(string_callback_function);
handle(return_code);
// send a message through the mixnet - in this case to ourselves
// send a message through the mixnet - in this case to ourselves using the value from get_self_address
std::cout << "(c++) message to send through mixnet: " << message << std::endl;
boost::packaged_task<char> send(boost::bind(send_message, addr, message));
boost::unique_future<char> send_future = send.get_future();
@@ -94,16 +95,17 @@ int main() {
return_code = listen_future.get();
handle(return_code);
// replying to incoming message (from ourselves) with SURBs- note that sending a message to a recipient and
// replying to an incoming are different functions
// replying to incoming message (from ourselves) with SURBs - note that sending a message to a recipient and
// replying to an incoming are different functions: replying relies on parsing the incoming sender_tag on the Rust
// side and creating an AnonymousSenderTag type, instead of the Recipient type which relies on a nym address
boost::packaged_task<char> reply_fn(boost::bind(reply, sender_tag, reply_message));
boost::unique_future<char> reply_future = reply_fn.get_future();
reply_fn();
return_code = reply_future.get();
handle(return_code);
// sleep so that the nym side logging can catch up - in reality you'd have another process running to keep logging
// going, so this is only necessary for this reference implementation
// sleep so that the client processes can catch up - in reality you'd have another process running to keep logging
// going, so this is only necessary for this reference
std::this_thread::sleep_for(std::chrono::seconds(40));
return 0;
+52
View File
@@ -0,0 +1,52 @@
pub mod types {
use std::ffi::c_char;
#[derive(Debug)]
pub enum StatusCode {
NoError = 0,
ClientInitError = -1,
ClientUninitialisedError = -2,
SelfAddrError = -3,
SendMsgError = -4,
ReplyError = -5,
ListenError = -6,
RecipientNullError = -7,
MessageNullError = -8,
}
#[repr(C)]
pub struct CStringCallback {
pub callback: extern "C" fn(*const c_char),
}
impl CStringCallback {
pub fn new(callback: extern "C" fn(*const c_char)) -> Self {
CStringCallback { callback }
}
pub fn trigger(&self, char: *const c_char) {
(self.callback)(char);
}
}
#[repr(C)]
pub struct CMessageCallback {
pub callback: extern "C" fn(ReceivedMessage),
}
impl CMessageCallback {
pub fn new(callback: extern "C" fn(ReceivedMessage)) -> Self {
CMessageCallback { callback }
}
pub fn trigger(&self, message: ReceivedMessage) {
(self.callback)(message)
}
}
#[repr(C)]
pub struct ReceivedMessage {
pub message: *const u8,
pub size: usize,
pub sender_tag: *const c_char,
}
}
+6061
View File
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
[package]
name = "nym-go-ffi" #"goffitest"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
name = "nym_go_ffi" #"go_ffi"
[dependencies]
# Bindgen
uniffi = { version = "0.25.2", features = ["cli"] }
# Nym clients, addressing, packet format, common tools (logging), ffi shared
nym-bin-common = { git = "https://github.com/nymtech/nym", branch = "master" }
nym-sdk = { git = "https://github.com/nymtech/nym", branch = "master" }
nym-sphinx-anonymous-replies = { git = "https://github.com/nymtech/nym", branch = "master" }
nym-ffi-shared = { path = "../shared" }
# Async runtime
tokio = { version = "1", features = ["full"] }
lazy_static = "1.4.0"
# error handling
anyhow = "1.0.79"
thiserror = "1.0.56"
[build-dependencies]
uniffi = { version = "0.25.2", features = ["build" ] }
uniffi_build = { version = "0.25.2", features=["builtin-bindgen"] }
[[bin]]
name = "uniffi-bindgen"
path = "uniffi-bindgen.rs"
+47
View File
@@ -0,0 +1,47 @@
# Go FFI
> ⚠️ This is an initial version of this library in order to give developers something to experiment with. If you use this code to begin testing out Mixnet integration and run into issues, errors, or have feedback, please feel free to open an issue; feedback from developers trying to use it will help us improve it. If you have questions feel free to reach out via our [Matrix channel](https://matrix.to/#/#dev:nymtech.chat).
This repo contains:
* `lib.rs`: an initial version of bindings for interacting with the Mixnet via the Rust SDK from Go. These are essentially match statemtns wrapping imported functions from the `nym-ffi-shared` lib.
* `ffi/`: a directory containing:
* the `bindings/` files generated using [`uniffi-bindgen-go`](https://github.com/NordSecurity/uniffi-bindgen-go)
* [`example.go`](./example.go): an example of using this library.
The `example.go` file is an example flow of:
* setting up Nym client logging
* creating an ephemeral Nym client (no key storage / persistent address - this will come in a future iteration)
* getting its [Nym address](https://nymtech.net/docs/clients/addressing-system.html)
* using that address to send a message to yourself via the Mixnet
* listen for and parse the incoming message for the `sender_tag` used for [anonymous replies with SURBs](https://nymtech.net/docs/architecture/traffic-flow.html#private-replies-using-surbs)
* send a reply to yourself using SURBs
## Useage - Consuming the Library
You can import the bindings as normal and interact with them as shown in the [example file](./example.go). This example imports the bindings from the this repository (hence the `go.mod` and `go.sum` in the crate root) but you can import them remotely as usual.
## Useage - Developing on the Library
If you want to fork and add new features/functions to this library use the following instructions to rebuild the Go bindings.
Rust functions exposed to the Go binding library are in `./src/lib.rs`.
The `build.sh` script in the root of the repository speeds up the task of building and linking the Rust and Go code.
* if want to quickly recompile your code run it as-is with `./build.sh`
* if you want to clean build both the Rust and Go code after removing existing compiled binaries run it with the optional `clean` argument: `./build.sh clean`.
> Make sure to run the script from the root of the project directory, and that your LD PATH is set first!
> ```
> RUST_BINARIES=target/release
> echo 'export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:'${RUST_BINARIES} >> ~/.zshrc
> source ~/.zshrc
> ```
This script will:
* (optionally if called with `clean` argument) remove existing Rust and Go artifacts
* build `lib.rs` with the `--release` flag
* compile the Go bindings
**WIP** you need to manually add the following `cgo` flags to the generated bindings immediately underneath LN3 (`// #include <bindings.h`). In the future this will be automated in `build.sh`:
```
// #cgo LDFLAGS: -L../../target/release -lnym_go_ffi
```
+4
View File
@@ -0,0 +1,4 @@
fn main() {
uniffi::generate_scaffolding("src/bindings.udl").unwrap();
}
+53
View File
@@ -0,0 +1,53 @@
#!/bin/bash
PROJECT_NAME="go"
set -eu
MODE="--release"
GO_DIR="./go-nym"
UDL_PATH="./src/bindings.udl"
build_artifacts() {
# build rust
cargo build $MODE
# build go bindings
printf "building go bindings \n"
uniffi-bindgen-go $UDL_PATH --out-dir $GO_DIR
printf "bindings built \n\n"
# something not right with these - having to add it manually to bindings.go for the moment
# pushd $GO_DIR/bindings
# echo $(pwd)
# LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}:../../target/release" \
# CGO_LDFLAGS="-L../target/release -lnym_go_ffi -lm -ldl" \
# CGO_ENABLED=1 \
# go run ../main.go
}
clean_artifacts() {
# clean up existing things
rm -rf $GO_DIR
cargo clean
}
if [ $(pwd | awk -F/ '{print $NF}') != ${PROJECT_NAME} ]
then
printf "please run from root dir of project"
exit 1
fi
if [ $# -eq 0 ];
then
build_artifacts;
else
arg=$1
if [ "$arg" == "clean" ]; then
clean_artifacts;
build_artifacts;
else
printf "unknown optional argument - the only available optional argument is 'clean'"
exit 1
fi
fi
+74
View File
@@ -0,0 +1,74 @@
package main
import (
"fmt"
"nymffi/go-nym/bindings"
"time"
)
func main() {
// initialise Nym client logging - this is quite verbose but very informative
bindings.InitLogging()
// initialise an ephemeral client - aka one without specified keystore
err := bindings.InitEphemeral()
if err != nil {
fmt.Println(err)
return
}
// get our client's address
str, err2 := bindings.GetSelfAddress()
if err2 != nil {
fmt.Println("(Go) Error:", err2)
return
}
fmt.Println("(Go) response:")
fmt.Println(str)
// send a message through the mixnet - in this case to ourselves using the value from GetSelfAddress
err3 := bindings.SendMessage(str, "helloworld")
if err3 != nil {
fmt.Println("(Go) Error:", err3)
return
}
// listen out for incoming messages: in the future the client can be split into a listening and a sending client,
// allowing for this to run as a persistent process in its own thread and not have to block but instead be running
// concurrently
//
// assuming a data type like so:
// type IncomingMessage struct {
// Message string
// SenderTag vec<u8>
// }
incomingMessage, err4 := bindings.ListenForIncoming()
if err4 != nil {
fmt.Println("(Go) Error:", err4)
return
}
fmt.Println("(Go) incoming message: ", incomingMessage.Message, " from: ", incomingMessage.Sender)
// we can just use the byte array we parsed from the incoming message to reply with: this is a
// byte representation of the sender_tag used for Single Use Reply Blocks (SURBs)
//
// replying to incoming message (from ourselves) with SURBs - note that sending a message to a recipient and
// replying to an incoming are different functions: replying relies on parsing the incoming sender_tag on the Rust
// side and creating an AnonymousSenderTag type, instead of the Recipient type which relies on a nym address
//
// you will see in the client logs that there are requests for more SURBs that we send to ourselves to
// be able to fit the full reply message in there. In a future iteration of this code we can also expose
// a send() which allows for developers to dictate the number of SURBs to send along with their outgoing message
fmt.Println("(Go) replying to received message")
err5 := bindings.Reply(incomingMessage.Sender, "replyworld")
if err5 != nil {
fmt.Println("(Go) Error:", err5)
return
}
// sleep so that the nym client processes can catch up - in reality you'd have another process
// running to keep logging going, so this is only necessary for this reference
time.Sleep(30 * time.Second)
fmt.Println("(Go) end go example")
}
+8
View File
@@ -0,0 +1,8 @@
#include <bindings.h>
// This file exists beacause of
// https://github.com/golang/go/issues/11263
void cgo_rust_task_callback_bridge_bindings(RustTaskCallback cb, const void * taskData, int8_t status) {
cb(taskData, status);
}
+773
View File
@@ -0,0 +1,773 @@
package bindings
// #include <bindings.h>
// #cgo LDFLAGS: -L../../target/release -lnym_go_ffi
import "C"
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"math"
"unsafe"
)
type RustBuffer = C.RustBuffer
type RustBufferI interface {
AsReader() *bytes.Reader
Free()
ToGoBytes() []byte
Data() unsafe.Pointer
Len() int
Capacity() int
}
func RustBufferFromExternal(b RustBufferI) RustBuffer {
return RustBuffer{
capacity: C.int(b.Capacity()),
len: C.int(b.Len()),
data: (*C.uchar)(b.Data()),
}
}
func (cb RustBuffer) Capacity() int {
return int(cb.capacity)
}
func (cb RustBuffer) Len() int {
return int(cb.len)
}
func (cb RustBuffer) Data() unsafe.Pointer {
return unsafe.Pointer(cb.data)
}
func (cb RustBuffer) AsReader() *bytes.Reader {
b := unsafe.Slice((*byte)(cb.data), C.int(cb.len))
return bytes.NewReader(b)
}
func (cb RustBuffer) Free() {
rustCall(func(status *C.RustCallStatus) bool {
C.ffi_nym_go_ffi_rustbuffer_free(cb, status)
return false
})
}
func (cb RustBuffer) ToGoBytes() []byte {
return C.GoBytes(unsafe.Pointer(cb.data), C.int(cb.len))
}
func stringToRustBuffer(str string) RustBuffer {
return bytesToRustBuffer([]byte(str))
}
func bytesToRustBuffer(b []byte) RustBuffer {
if len(b) == 0 {
return RustBuffer{}
}
// We can pass the pointer along here, as it is pinned
// for the duration of this call
foreign := C.ForeignBytes{
len: C.int(len(b)),
data: (*C.uchar)(unsafe.Pointer(&b[0])),
}
return rustCall(func(status *C.RustCallStatus) RustBuffer {
return C.ffi_nym_go_ffi_rustbuffer_from_bytes(foreign, status)
})
}
type BufLifter[GoType any] interface {
Lift(value RustBufferI) GoType
}
type BufLowerer[GoType any] interface {
Lower(value GoType) RustBuffer
}
type FfiConverter[GoType any, FfiType any] interface {
Lift(value FfiType) GoType
Lower(value GoType) FfiType
}
type BufReader[GoType any] interface {
Read(reader io.Reader) GoType
}
type BufWriter[GoType any] interface {
Write(writer io.Writer, value GoType)
}
type FfiRustBufConverter[GoType any, FfiType any] interface {
FfiConverter[GoType, FfiType]
BufReader[GoType]
}
func LowerIntoRustBuffer[GoType any](bufWriter BufWriter[GoType], value GoType) RustBuffer {
// This might be not the most efficient way but it does not require knowing allocation size
// beforehand
var buffer bytes.Buffer
bufWriter.Write(&buffer, value)
bytes, err := io.ReadAll(&buffer)
if err != nil {
panic(fmt.Errorf("reading written data: %w", err))
}
return bytesToRustBuffer(bytes)
}
func LiftFromRustBuffer[GoType any](bufReader BufReader[GoType], rbuf RustBufferI) GoType {
defer rbuf.Free()
reader := rbuf.AsReader()
item := bufReader.Read(reader)
if reader.Len() > 0 {
// TODO: Remove this
leftover, _ := io.ReadAll(reader)
panic(fmt.Errorf("Junk remaining in buffer after lifting: %s", string(leftover)))
}
return item
}
func rustCallWithError[U any](converter BufLifter[error], callback func(*C.RustCallStatus) U) (U, error) {
var status C.RustCallStatus
returnValue := callback(&status)
err := checkCallStatus(converter, status)
return returnValue, err
}
func checkCallStatus(converter BufLifter[error], status C.RustCallStatus) error {
switch status.code {
case 0:
return nil
case 1:
return converter.Lift(status.errorBuf)
case 2:
// when the rust code sees a panic, it tries to construct a rustbuffer
// with the message. but if that code panics, then it just sends back
// an empty buffer.
if status.errorBuf.len > 0 {
panic(fmt.Errorf("%s", FfiConverterStringINSTANCE.Lift(status.errorBuf)))
} else {
panic(fmt.Errorf("Rust panicked while handling Rust panic"))
}
default:
return fmt.Errorf("unknown status code: %d", status.code)
}
}
func checkCallStatusUnknown(status C.RustCallStatus) error {
switch status.code {
case 0:
return nil
case 1:
panic(fmt.Errorf("function not returning an error returned an error"))
case 2:
// when the rust code sees a panic, it tries to construct a rustbuffer
// with the message. but if that code panics, then it just sends back
// an empty buffer.
if status.errorBuf.len > 0 {
panic(fmt.Errorf("%s", FfiConverterStringINSTANCE.Lift(status.errorBuf)))
} else {
panic(fmt.Errorf("Rust panicked while handling Rust panic"))
}
default:
return fmt.Errorf("unknown status code: %d", status.code)
}
}
func rustCall[U any](callback func(*C.RustCallStatus) U) U {
returnValue, err := rustCallWithError(nil, callback)
if err != nil {
panic(err)
}
return returnValue
}
func writeInt8(writer io.Writer, value int8) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeUint8(writer io.Writer, value uint8) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeInt16(writer io.Writer, value int16) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeUint16(writer io.Writer, value uint16) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeInt32(writer io.Writer, value int32) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeUint32(writer io.Writer, value uint32) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeInt64(writer io.Writer, value int64) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeUint64(writer io.Writer, value uint64) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeFloat32(writer io.Writer, value float32) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func writeFloat64(writer io.Writer, value float64) {
if err := binary.Write(writer, binary.BigEndian, value); err != nil {
panic(err)
}
}
func readInt8(reader io.Reader) int8 {
var result int8
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readUint8(reader io.Reader) uint8 {
var result uint8
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readInt16(reader io.Reader) int16 {
var result int16
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readUint16(reader io.Reader) uint16 {
var result uint16
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readInt32(reader io.Reader) int32 {
var result int32
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readUint32(reader io.Reader) uint32 {
var result uint32
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readInt64(reader io.Reader) int64 {
var result int64
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readUint64(reader io.Reader) uint64 {
var result uint64
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readFloat32(reader io.Reader) float32 {
var result float32
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func readFloat64(reader io.Reader) float64 {
var result float64
if err := binary.Read(reader, binary.BigEndian, &result); err != nil {
panic(err)
}
return result
}
func init() {
uniffiCheckChecksums()
}
func uniffiCheckChecksums() {
// Get the bindings contract version from our ComponentInterface
bindingsContractVersion := 24
// Get the scaffolding contract version by calling the into the dylib
scaffoldingContractVersion := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint32_t {
return C.ffi_nym_go_ffi_uniffi_contract_version(uniffiStatus)
})
if bindingsContractVersion != int(scaffoldingContractVersion) {
// If this happens try cleaning and rebuilding your project
panic("bindings: UniFFI contract version mismatch")
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_nym_go_ffi_checksum_func_get_self_address(uniffiStatus)
})
if checksum != 51546 {
// If this happens try cleaning and rebuilding your project
panic("bindings: uniffi_nym_go_ffi_checksum_func_get_self_address: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_nym_go_ffi_checksum_func_init_ephemeral(uniffiStatus)
})
if checksum != 28391 {
// If this happens try cleaning and rebuilding your project
panic("bindings: uniffi_nym_go_ffi_checksum_func_init_ephemeral: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_nym_go_ffi_checksum_func_init_logging(uniffiStatus)
})
if checksum != 1547 {
// If this happens try cleaning and rebuilding your project
panic("bindings: uniffi_nym_go_ffi_checksum_func_init_logging: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_nym_go_ffi_checksum_func_listen_for_incoming(uniffiStatus)
})
if checksum != 52894 {
// If this happens try cleaning and rebuilding your project
panic("bindings: uniffi_nym_go_ffi_checksum_func_listen_for_incoming: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_nym_go_ffi_checksum_func_reply(uniffiStatus)
})
if checksum != 50524 {
// If this happens try cleaning and rebuilding your project
panic("bindings: uniffi_nym_go_ffi_checksum_func_reply: UniFFI API checksum mismatch")
}
}
{
checksum := rustCall(func(uniffiStatus *C.RustCallStatus) C.uint16_t {
return C.uniffi_nym_go_ffi_checksum_func_send_message(uniffiStatus)
})
if checksum != 33425 {
// If this happens try cleaning and rebuilding your project
panic("bindings: uniffi_nym_go_ffi_checksum_func_send_message: UniFFI API checksum mismatch")
}
}
}
type FfiConverterString struct{}
var FfiConverterStringINSTANCE = FfiConverterString{}
func (FfiConverterString) Lift(rb RustBufferI) string {
defer rb.Free()
reader := rb.AsReader()
b, err := io.ReadAll(reader)
if err != nil {
panic(fmt.Errorf("reading reader: %w", err))
}
return string(b)
}
func (FfiConverterString) Read(reader io.Reader) string {
length := readInt32(reader)
buffer := make([]byte, length)
read_length, err := reader.Read(buffer)
if err != nil {
panic(err)
}
if read_length != int(length) {
panic(fmt.Errorf("bad read length when reading string, expected %d, read %d", length, read_length))
}
return string(buffer)
}
func (FfiConverterString) Lower(value string) RustBuffer {
return stringToRustBuffer(value)
}
func (FfiConverterString) Write(writer io.Writer, value string) {
if len(value) > math.MaxInt32 {
panic("String is too large to fit into Int32")
}
writeInt32(writer, int32(len(value)))
write_length, err := io.WriteString(writer, value)
if err != nil {
panic(err)
}
if write_length != len(value) {
panic(fmt.Errorf("bad write length when writing string, expected %d, written %d", len(value), write_length))
}
}
type FfiDestroyerString struct{}
func (FfiDestroyerString) Destroy(_ string) {}
type FfiConverterBytes struct{}
var FfiConverterBytesINSTANCE = FfiConverterBytes{}
func (c FfiConverterBytes) Lower(value []byte) RustBuffer {
return LowerIntoRustBuffer[[]byte](c, value)
}
func (c FfiConverterBytes) Write(writer io.Writer, value []byte) {
if len(value) > math.MaxInt32 {
panic("[]byte is too large to fit into Int32")
}
writeInt32(writer, int32(len(value)))
write_length, err := writer.Write(value)
if err != nil {
panic(err)
}
if write_length != len(value) {
panic(fmt.Errorf("bad write length when writing []byte, expected %d, written %d", len(value), write_length))
}
}
func (c FfiConverterBytes) Lift(rb RustBufferI) []byte {
return LiftFromRustBuffer[[]byte](c, rb)
}
func (c FfiConverterBytes) Read(reader io.Reader) []byte {
length := readInt32(reader)
buffer := make([]byte, length)
read_length, err := reader.Read(buffer)
if err != nil {
panic(err)
}
if read_length != int(length) {
panic(fmt.Errorf("bad read length when reading []byte, expected %d, read %d", length, read_length))
}
return buffer
}
type FfiDestroyerBytes struct{}
func (FfiDestroyerBytes) Destroy(_ []byte) {}
type IncomingMessage struct {
Message string
Sender []byte
}
func (r *IncomingMessage) Destroy() {
FfiDestroyerString{}.Destroy(r.Message)
FfiDestroyerBytes{}.Destroy(r.Sender)
}
type FfiConverterTypeIncomingMessage struct{}
var FfiConverterTypeIncomingMessageINSTANCE = FfiConverterTypeIncomingMessage{}
func (c FfiConverterTypeIncomingMessage) Lift(rb RustBufferI) IncomingMessage {
return LiftFromRustBuffer[IncomingMessage](c, rb)
}
func (c FfiConverterTypeIncomingMessage) Read(reader io.Reader) IncomingMessage {
return IncomingMessage{
FfiConverterStringINSTANCE.Read(reader),
FfiConverterBytesINSTANCE.Read(reader),
}
}
func (c FfiConverterTypeIncomingMessage) Lower(value IncomingMessage) RustBuffer {
return LowerIntoRustBuffer[IncomingMessage](c, value)
}
func (c FfiConverterTypeIncomingMessage) Write(writer io.Writer, value IncomingMessage) {
FfiConverterStringINSTANCE.Write(writer, value.Message)
FfiConverterBytesINSTANCE.Write(writer, value.Sender)
}
type FfiDestroyerTypeIncomingMessage struct{}
func (_ FfiDestroyerTypeIncomingMessage) Destroy(value IncomingMessage) {
value.Destroy()
}
type GoWrapError struct {
err error
}
func (err GoWrapError) Error() string {
return fmt.Sprintf("GoWrapError: %s", err.err.Error())
}
func (err GoWrapError) Unwrap() error {
return err.err
}
// Err* are used for checking error type with `errors.Is`
var ErrGoWrapErrorClientInitError = fmt.Errorf("GoWrapErrorClientInitError")
var ErrGoWrapErrorClientUninitialisedError = fmt.Errorf("GoWrapErrorClientUninitialisedError")
var ErrGoWrapErrorSelfAddrError = fmt.Errorf("GoWrapErrorSelfAddrError")
var ErrGoWrapErrorSendMsgError = fmt.Errorf("GoWrapErrorSendMsgError")
var ErrGoWrapErrorReplyError = fmt.Errorf("GoWrapErrorReplyError")
var ErrGoWrapErrorListenError = fmt.Errorf("GoWrapErrorListenError")
// Variant structs
type GoWrapErrorClientInitError struct {
message string
}
func NewGoWrapErrorClientInitError() *GoWrapError {
return &GoWrapError{
err: &GoWrapErrorClientInitError{},
}
}
func (err GoWrapErrorClientInitError) Error() string {
return fmt.Sprintf("ClientInitError: %s", err.message)
}
func (self GoWrapErrorClientInitError) Is(target error) bool {
return target == ErrGoWrapErrorClientInitError
}
type GoWrapErrorClientUninitialisedError struct {
message string
}
func NewGoWrapErrorClientUninitialisedError() *GoWrapError {
return &GoWrapError{
err: &GoWrapErrorClientUninitialisedError{},
}
}
func (err GoWrapErrorClientUninitialisedError) Error() string {
return fmt.Sprintf("ClientUninitialisedError: %s", err.message)
}
func (self GoWrapErrorClientUninitialisedError) Is(target error) bool {
return target == ErrGoWrapErrorClientUninitialisedError
}
type GoWrapErrorSelfAddrError struct {
message string
}
func NewGoWrapErrorSelfAddrError() *GoWrapError {
return &GoWrapError{
err: &GoWrapErrorSelfAddrError{},
}
}
func (err GoWrapErrorSelfAddrError) Error() string {
return fmt.Sprintf("SelfAddrError: %s", err.message)
}
func (self GoWrapErrorSelfAddrError) Is(target error) bool {
return target == ErrGoWrapErrorSelfAddrError
}
type GoWrapErrorSendMsgError struct {
message string
}
func NewGoWrapErrorSendMsgError() *GoWrapError {
return &GoWrapError{
err: &GoWrapErrorSendMsgError{},
}
}
func (err GoWrapErrorSendMsgError) Error() string {
return fmt.Sprintf("SendMsgError: %s", err.message)
}
func (self GoWrapErrorSendMsgError) Is(target error) bool {
return target == ErrGoWrapErrorSendMsgError
}
type GoWrapErrorReplyError struct {
message string
}
func NewGoWrapErrorReplyError() *GoWrapError {
return &GoWrapError{
err: &GoWrapErrorReplyError{},
}
}
func (err GoWrapErrorReplyError) Error() string {
return fmt.Sprintf("ReplyError: %s", err.message)
}
func (self GoWrapErrorReplyError) Is(target error) bool {
return target == ErrGoWrapErrorReplyError
}
type GoWrapErrorListenError struct {
message string
}
func NewGoWrapErrorListenError() *GoWrapError {
return &GoWrapError{
err: &GoWrapErrorListenError{},
}
}
func (err GoWrapErrorListenError) Error() string {
return fmt.Sprintf("ListenError: %s", err.message)
}
func (self GoWrapErrorListenError) Is(target error) bool {
return target == ErrGoWrapErrorListenError
}
type FfiConverterTypeGoWrapError struct{}
var FfiConverterTypeGoWrapErrorINSTANCE = FfiConverterTypeGoWrapError{}
func (c FfiConverterTypeGoWrapError) Lift(eb RustBufferI) error {
return LiftFromRustBuffer[error](c, eb)
}
func (c FfiConverterTypeGoWrapError) Lower(value *GoWrapError) RustBuffer {
return LowerIntoRustBuffer[*GoWrapError](c, value)
}
func (c FfiConverterTypeGoWrapError) Read(reader io.Reader) error {
errorID := readUint32(reader)
message := FfiConverterStringINSTANCE.Read(reader)
switch errorID {
case 1:
return &GoWrapError{&GoWrapErrorClientInitError{message}}
case 2:
return &GoWrapError{&GoWrapErrorClientUninitialisedError{message}}
case 3:
return &GoWrapError{&GoWrapErrorSelfAddrError{message}}
case 4:
return &GoWrapError{&GoWrapErrorSendMsgError{message}}
case 5:
return &GoWrapError{&GoWrapErrorReplyError{message}}
case 6:
return &GoWrapError{&GoWrapErrorListenError{message}}
default:
panic(fmt.Sprintf("Unknown error code %d in FfiConverterTypeGoWrapError.Read()", errorID))
}
}
func (c FfiConverterTypeGoWrapError) Write(writer io.Writer, value *GoWrapError) {
switch variantValue := value.err.(type) {
case *GoWrapErrorClientInitError:
writeInt32(writer, 1)
case *GoWrapErrorClientUninitialisedError:
writeInt32(writer, 2)
case *GoWrapErrorSelfAddrError:
writeInt32(writer, 3)
case *GoWrapErrorSendMsgError:
writeInt32(writer, 4)
case *GoWrapErrorReplyError:
writeInt32(writer, 5)
case *GoWrapErrorListenError:
writeInt32(writer, 6)
default:
_ = variantValue
panic(fmt.Sprintf("invalid error value `%v` in FfiConverterTypeGoWrapError.Write", value))
}
}
func GetSelfAddress() (string, error) {
_uniffiRV, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_nym_go_ffi_fn_func_get_self_address(_uniffiStatus)
})
if _uniffiErr != nil {
var _uniffiDefaultValue string
return _uniffiDefaultValue, _uniffiErr
} else {
return FfiConverterStringINSTANCE.Lift(_uniffiRV), _uniffiErr
}
}
func InitEphemeral() error {
_, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) bool {
C.uniffi_nym_go_ffi_fn_func_init_ephemeral(_uniffiStatus)
return false
})
return _uniffiErr
}
func InitLogging() {
rustCall(func(_uniffiStatus *C.RustCallStatus) bool {
C.uniffi_nym_go_ffi_fn_func_init_logging(_uniffiStatus)
return false
})
}
func ListenForIncoming() (IncomingMessage, error) {
_uniffiRV, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI {
return C.uniffi_nym_go_ffi_fn_func_listen_for_incoming(_uniffiStatus)
})
if _uniffiErr != nil {
var _uniffiDefaultValue IncomingMessage
return _uniffiDefaultValue, _uniffiErr
} else {
return FfiConverterTypeIncomingMessageINSTANCE.Lift(_uniffiRV), _uniffiErr
}
}
func Reply(recipient []byte, message string) error {
_, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) bool {
C.uniffi_nym_go_ffi_fn_func_reply(FfiConverterBytesINSTANCE.Lower(recipient), FfiConverterStringINSTANCE.Lower(message), _uniffiStatus)
return false
})
return _uniffiErr
}
func SendMessage(recipient string, message string) error {
_, _uniffiErr := rustCallWithError(FfiConverterTypeGoWrapError{}, func(_uniffiStatus *C.RustCallStatus) bool {
C.uniffi_nym_go_ffi_fn_func_send_message(FfiConverterStringINSTANCE.Lower(recipient), FfiConverterStringINSTANCE.Lower(message), _uniffiStatus)
return false
})
return _uniffiErr
}
+427
View File
@@ -0,0 +1,427 @@
// This file was autogenerated by some hot garbage in the `uniffi` crate.
// Trust me, you don't want to mess with it!
#include <stdbool.h>
#include <stdint.h>
// The following structs are used to implement the lowest level
// of the FFI, and thus useful to multiple uniffied crates.
// We ensure they are declared exactly once, with a header guard, UNIFFI_SHARED_H.
#ifdef UNIFFI_SHARED_H
// We also try to prevent mixing versions of shared uniffi header structs.
// If you add anything to the #else block, you must increment the version suffix in UNIFFI_SHARED_HEADER_V6
#ifndef UNIFFI_SHARED_HEADER_V6
#error Combining helper code from multiple versions of uniffi is not supported
#endif // ndef UNIFFI_SHARED_HEADER_V6
#else
#define UNIFFI_SHARED_H
#define UNIFFI_SHARED_HEADER_V6
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V6 in this file. ⚠️
typedef struct RustBuffer {
int32_t capacity;
int32_t len;
uint8_t *data;
} RustBuffer;
typedef int32_t (*ForeignCallback)(uint64_t, int32_t, uint8_t *, int32_t, RustBuffer *);
// Task defined in Rust that Go executes
typedef void (*RustTaskCallback)(const void *, int8_t);
// Callback to execute Rust tasks using a Go routine
//
// Args:
// executor: ForeignExecutor lowered into a uint64_t value
// delay: Delay in MS
// task: RustTaskCallback to call
// task_data: data to pass the task callback
typedef int8_t (*ForeignExecutorCallback)(uint64_t, uint32_t, RustTaskCallback, void *);
typedef struct ForeignBytes {
int32_t len;
const uint8_t *data;
} ForeignBytes;
// Error definitions
typedef struct RustCallStatus {
int8_t code;
RustBuffer errorBuf;
} RustCallStatus;
// Continuation callback for UniFFI Futures
typedef void (*RustFutureContinuation)(void * , int8_t);
// ⚠️ Attention: If you change this #else block (ending in `#endif // def UNIFFI_SHARED_H`) you *must* ⚠️
// ⚠️ increment the version suffix in all instances of UNIFFI_SHARED_HEADER_V6 in this file. ⚠️
#endif // def UNIFFI_SHARED_H
// Needed because we can't execute the callback directly from go.
void cgo_rust_task_callback_bridge_bindings(RustTaskCallback, const void *, int8_t);
int8_t uniffiForeignExecutorCallbackbindings(uint64_t, uint32_t, RustTaskCallback, void*);
void uniffiFutureContinuationCallbackbindings(void*, int8_t);
RustBuffer uniffi_nym_go_ffi_fn_func_get_self_address(
RustCallStatus* out_status
);
void uniffi_nym_go_ffi_fn_func_init_ephemeral(
RustCallStatus* out_status
);
void uniffi_nym_go_ffi_fn_func_init_logging(
RustCallStatus* out_status
);
RustBuffer uniffi_nym_go_ffi_fn_func_listen_for_incoming(
RustCallStatus* out_status
);
void uniffi_nym_go_ffi_fn_func_reply(
RustBuffer recipient,
RustBuffer message,
RustCallStatus* out_status
);
void uniffi_nym_go_ffi_fn_func_send_message(
RustBuffer recipient,
RustBuffer message,
RustCallStatus* out_status
);
RustBuffer ffi_nym_go_ffi_rustbuffer_alloc(
int32_t size,
RustCallStatus* out_status
);
RustBuffer ffi_nym_go_ffi_rustbuffer_from_bytes(
ForeignBytes bytes,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rustbuffer_free(
RustBuffer buf,
RustCallStatus* out_status
);
RustBuffer ffi_nym_go_ffi_rustbuffer_reserve(
RustBuffer buf,
int32_t additional,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_continuation_callback_set(
RustFutureContinuation callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_u8(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_u8(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_u8(
void* handle,
RustCallStatus* out_status
);
uint8_t ffi_nym_go_ffi_rust_future_complete_u8(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_i8(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_i8(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_i8(
void* handle,
RustCallStatus* out_status
);
int8_t ffi_nym_go_ffi_rust_future_complete_i8(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_u16(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_u16(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_u16(
void* handle,
RustCallStatus* out_status
);
uint16_t ffi_nym_go_ffi_rust_future_complete_u16(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_i16(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_i16(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_i16(
void* handle,
RustCallStatus* out_status
);
int16_t ffi_nym_go_ffi_rust_future_complete_i16(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_u32(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_u32(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_u32(
void* handle,
RustCallStatus* out_status
);
uint32_t ffi_nym_go_ffi_rust_future_complete_u32(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_i32(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_i32(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_i32(
void* handle,
RustCallStatus* out_status
);
int32_t ffi_nym_go_ffi_rust_future_complete_i32(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_u64(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_u64(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_u64(
void* handle,
RustCallStatus* out_status
);
uint64_t ffi_nym_go_ffi_rust_future_complete_u64(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_i64(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_i64(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_i64(
void* handle,
RustCallStatus* out_status
);
int64_t ffi_nym_go_ffi_rust_future_complete_i64(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_f32(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_f32(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_f32(
void* handle,
RustCallStatus* out_status
);
float ffi_nym_go_ffi_rust_future_complete_f32(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_f64(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_f64(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_f64(
void* handle,
RustCallStatus* out_status
);
double ffi_nym_go_ffi_rust_future_complete_f64(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_pointer(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_pointer(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_pointer(
void* handle,
RustCallStatus* out_status
);
void* ffi_nym_go_ffi_rust_future_complete_pointer(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_rust_buffer(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_rust_buffer(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_rust_buffer(
void* handle,
RustCallStatus* out_status
);
RustBuffer ffi_nym_go_ffi_rust_future_complete_rust_buffer(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_poll_void(
void* handle,
void* uniffi_callback,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_cancel_void(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_free_void(
void* handle,
RustCallStatus* out_status
);
void ffi_nym_go_ffi_rust_future_complete_void(
void* handle,
RustCallStatus* out_status
);
uint16_t uniffi_nym_go_ffi_checksum_func_get_self_address(
RustCallStatus* out_status
);
uint16_t uniffi_nym_go_ffi_checksum_func_init_ephemeral(
RustCallStatus* out_status
);
uint16_t uniffi_nym_go_ffi_checksum_func_init_logging(
RustCallStatus* out_status
);
uint16_t uniffi_nym_go_ffi_checksum_func_listen_for_incoming(
RustCallStatus* out_status
);
uint16_t uniffi_nym_go_ffi_checksum_func_reply(
RustCallStatus* out_status
);
uint16_t uniffi_nym_go_ffi_checksum_func_send_message(
RustCallStatus* out_status
);
uint32_t ffi_nym_go_ffi_uniffi_contract_version(
RustCallStatus* out_status
);
+3
View File
@@ -0,0 +1,3 @@
module nymffi
go 1.20
View File

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