Compare commits

..

2 Commits

Author SHA1 Message Date
mfahampshire 9a22f3dbba started on rendezvous example 2023-08-23 21:52:52 +02:00
mfahampshire faf558053a working on rendzv example 2023-08-23 20:47:24 +02:00
355 changed files with 2374 additions and 11623 deletions
@@ -1,2 +0,0 @@
.tmp
hashes.json
@@ -1,25 +0,0 @@
name: 'Nym Hash Release'
author: 'Nym Technologies SA'
description: 'Generate hashes and signatures for assets in Nym releases'
inputs:
hash-type:
description: 'Type of hash to generate (md5, sha1, sha256, sha512)'
required: false
default: 'sha256'
file-name:
description: 'File name to save as if desired'
required: false
default: 'hashes.json'
release-tag-or-name-or-id:
description: 'The tag/release to process. Uses the release id when trigger from a release.'
required: false
default: ''
outputs:
hashes:
description: 'A string containing JSON with the release asset hashes and signatures'
runs:
using: 'node16'
main: 'index.js'
branding:
icon: 'hash'
color: 'green'
@@ -1,259 +0,0 @@
import hasha from "hasha";
import fetch from "node-fetch";
import { Octokit } from "@octokit/rest";
import fs from "fs";
import path from "path";
async function run(assets, algorithm, filename, cache) {
try {
fs.mkdirSync('.tmp');
} catch(e) {
// ignore
}
const hashes = {};
let numAwaiting = 0;
for (const asset of assets) {
if (filename === "" || asset.name !== filename) { // don't hash the hash file (if the file has the same name)
numAwaiting++;
let buffer = null;
let sig = null;
if(cache) {
// cache in `${WORKING_DIR}/.tmp/`
const cacheFilename = path.resolve(`.tmp/${asset.name}`);
if(!fs.existsSync(cacheFilename)) {
console.log(`Downloading ${asset.browser_download_url}... to ${cacheFilename}`);
buffer = Buffer.from(await fetch(asset.browser_download_url).then(res => res.arrayBuffer()));
fs.writeFileSync(cacheFilename, buffer);
} else {
console.log(`Loading from ${cacheFilename}`);
buffer = Buffer.from(fs.readFileSync(cacheFilename));
// console.log('Reading signature from content');
// if(asset.name.endsWith('.sig')) {
// sig = fs.readFileSync(cacheFilename).toString();
// }
}
} else {
// fetch always
buffer = Buffer.from(await fetch(asset.browser_download_url).then(res => res.arrayBuffer()));
}
if(!hashes[asset.name]) {
hashes[asset.name] = {};
}
if(asset.name.endsWith('.sig')) {
sig = buffer.toString();
}
hashes[asset.name][algorithm] = hasha(new Uint8Array(buffer), {algorithm: algorithm});
let platform;
let kind;
if(asset.name.endsWith('.sig')) {
kind = 'signature';
}
if(asset.name.endsWith('.app.tar.gz')) {
platform = 'MacOS';
kind = 'auto-updater';
}
if(asset.name.endsWith('.app.tar.gz.sig')) {
platform = 'MacOS';
kind = 'auto-updater-signature';
}
if(asset.name.endsWith('.dmg')) {
platform = 'MacOS';
kind = 'installer';
}
if(asset.name.endsWith('.msi.zip')) {
platform = 'Windows';
kind = 'auto-updater';
}
if(asset.name.endsWith('.msi.zip.sig')) {
platform = 'Windows';
kind = 'auto-updater-signature';
}
if(asset.name.endsWith('.msi')) {
platform = 'Windows';
kind = 'installer';
}
if(asset.name.endsWith('.AppImage.tar.gz')) {
platform = 'Linux';
kind = 'auto-updater';
}
if(asset.name.endsWith('.AppImage.tar.gz.sig')) {
platform = 'Linux';
kind = 'auto-updater-signature';
}
if(asset.name.endsWith('.AppImage')) {
platform = 'Linux';
kind = 'installer';
}
hashes[asset.name].downloadUrl = asset.browser_download_url;
if(platform) {
hashes[asset.name].platform = platform;
}
if(kind) {
hashes[asset.name].kind = kind;
}
// process Tauri signature files
if(asset.name.endsWith('.sig')) {
const otherFilename = asset.name.replace('.sig', '');
if(!hashes[otherFilename]) {
hashes[otherFilename] = {};
}
hashes[otherFilename].signature = sig;
}
}
}
return hashes;
}
export async function createHashes({ assets, algorithm, filename, cache }) {
const output = await run(assets, algorithm, filename, cache);
if(filename?.length) {
fs.writeFileSync(filename, JSON.stringify(output, null, 2));
}
return output;
}
export async function createHashesFromReleaseTagOrNameOrId({ releaseTagOrNameOrId, algorithm = 'sha256', filename = 'hashes.json', cache = false, upload = true }) {
console.log("🚀🚀🚀 Getting releases");
let auth;
let authStrategy;
if(process.env.GITHUB_TOKEN) {
console.log('Using GITHUB_TOKEN for auth');
// authStrategy = createActionAuth();
// auth = await authStrategy();
}
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
request: { fetch }
});
const owner = "nymtech";
const repo = "nym";
let releases;
if(cache) {
const cacheFilename = path.resolve(`.tmp/releases.json`);
if(!fs.existsSync(cacheFilename)) {
releases = await octokit.paginate(
octokit.rest.repos.listReleases,
{
owner,
repo,
per_page: 100,
},
(response) => response.data
);
fs.writeFileSync(cacheFilename, JSON.stringify(releases, null, 2));
} else {
console.log('Loading releases from cache...');
releases = JSON.parse(fs.readFileSync(cacheFilename));
}
} else {
releases = await octokit.paginate(
octokit.rest.repos.listReleases,
{
owner,
repo,
per_page: 100,
},
(response) => response.data
)
}
// process all releases by default
let releasesToProcess = releases;
// process a single release
if(releaseTagOrNameOrId) {
releasesToProcess = releases.filter(r => {
if (r.tag_name === releaseTagOrNameOrId) {
return true;
}
if (`${r.id}` === `${releaseTagOrNameOrId}`) {
return true;
}
if (r.name === releaseTagOrNameOrId) {
return true;
}
return false;
});
}
releasesToProcess.forEach(release => {
const {tag_name, name} = release;
const tagComponents = tag_name.split('-v');
const componentName = tagComponents[0];
const componentVersion = 'v' + tagComponents[1];
if(!tagComponents[1] || !name) {
return;
}
release.componentName = componentName;
release.componentVersion = componentVersion;
})
releasesToProcess = releasesToProcess.filter(release =>
!!release.name && !!release.componentVersion
);
console.log('Releases to process:');
console.table(releasesToProcess.map(r => {
const { id, name, tag_name, componentName, componentVersion, assets } = r;
return { id, name, tag_name, componentName, componentVersion, assetCount: assets.length };
}));
for(const release of releasesToProcess) {
const {id, name, tag_name, html_url, componentName, componentVersion} = release;
const hashes = await createHashes({ assets: release.assets, algorithm, filename, cache });
const output = {
id, name, tag_name, html_url,
componentName,
componentVersion,
assets: hashes,
};
if(upload) {
console.log(`🚚 Uploading ${filename} to release name="${release.name}" id=${release.id} (${release.upload_url})...`);
const exists = (await octokit.repos.listReleaseAssets({ owner, repo, release_id: release.id })).data.find(a => a.name === filename)
if (exists) {
console.log(`Deleting existing asset ${filename}...`);
await octokit.repos.deleteReleaseAsset({ owner, repo, asset_id: exists.id })
console.log('Deleted existing asset');
}
try {
const data = JSON.stringify(output, null, 2);
await octokit.rest.repos.uploadReleaseAsset({
owner,
repo,
release_id: release.id,
headers: {
'X-GitHub-Api-Version': '2022-11-28'
},
name: filename,
data,
});
console.log('✅ Upload to release is complete.');
} catch(e) {
console.log('❌ failed to upload:', e.message, e.status, e.response.data);
console.log(e);
process.exit(-1);
}
}
}
}
@@ -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);
}
-536
View File
@@ -1,536 +0,0 @@
{
"name": "ghaction-generate-release-hashes",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ghaction-generate-release-hashes",
"version": "1.0.0",
"license": "MIT",
"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"
}
},
"node_modules/@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"node_modules/@actions/github": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"@octokit/core": "^3.6.0",
"@octokit/plugin-paginate-rest": "^2.17.0",
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
}
},
"node_modules/@actions/http-client": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.1.tgz",
"integrity": "sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==",
"dependencies": {
"tunnel": "^0.0.6"
}
},
"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==",
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/types": "^11.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/auth-action/node_modules/@octokit/auth-token": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
"integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
"engines": {
"node": ">= 18"
}
},
"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=="
},
"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==",
"dependencies": {
"@octokit/openapi-types": "^18.0.0"
}
},
"node_modules/@octokit/auth-token": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
"dependencies": {
"@octokit/types": "^6.0.3"
}
},
"node_modules/@octokit/core": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
"dependencies": {
"@octokit/auth-token": "^2.4.4",
"@octokit/graphql": "^4.5.8",
"@octokit/request": "^5.6.3",
"@octokit/request-error": "^2.0.5",
"@octokit/types": "^6.0.3",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/endpoint": {
"version": "6.0.12",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
"dependencies": {
"@octokit/types": "^6.0.3",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/graphql": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
"dependencies": {
"@octokit/request": "^5.6.0",
"@octokit/types": "^6.0.3",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/openapi-types": {
"version": "12.11.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
"integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="
},
"node_modules/@octokit/plugin-paginate-rest": {
"version": "2.21.3",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz",
"integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==",
"dependencies": {
"@octokit/types": "^6.40.0"
},
"peerDependencies": {
"@octokit/core": ">=2"
}
},
"node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "5.16.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
"integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==",
"dependencies": {
"@octokit/types": "^6.39.0",
"deprecation": "^2.3.1"
},
"peerDependencies": {
"@octokit/core": ">=3"
}
},
"node_modules/@octokit/request": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
"dependencies": {
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.1.0",
"@octokit/types": "^6.16.1",
"is-plain-object": "^5.0.0",
"node-fetch": "^2.6.7",
"universal-user-agent": "^6.0.0"
}
},
"node_modules/@octokit/request-error": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
"dependencies": {
"@octokit/types": "^6.0.3",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
},
"node_modules/@octokit/request/node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"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==",
"dependencies": {
"@octokit/core": "^5.0.0",
"@octokit/plugin-paginate-rest": "^8.0.0",
"@octokit/plugin-request-log": "^4.0.0",
"@octokit/plugin-rest-endpoint-methods": "^9.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/auth-token": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
"integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/core": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.0.tgz",
"integrity": "sha512-YbAtMWIrbZ9FCXbLwT9wWB8TyLjq9mxpKdgB3dUNxQcIVTf9hJ70gRPwAcqGZdY6WdJPZ0I7jLaaNDCiloGN2A==",
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.0.0",
"@octokit/request": "^8.0.2",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^11.0.0",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/endpoint": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.0.tgz",
"integrity": "sha512-szrQhiqJ88gghWY2Htt8MqUDO6++E/EIXqJ2ZEp5ma3uGS46o7LZAzSLt49myB7rT+Hfw5Y6gO3LmOxGzHijAQ==",
"dependencies": {
"@octokit/types": "^11.0.0",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/graphql": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.1.tgz",
"integrity": "sha512-T5S3oZ1JOE58gom6MIcrgwZXzTaxRnxBso58xhozxHpOqSTgDS6YNeEUvZ/kRvXgPrRz/KHnZhtb7jUMRi9E6w==",
"dependencies": {
"@octokit/request": "^8.0.1",
"@octokit/types": "^11.0.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/rest/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=="
},
"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==",
"dependencies": {
"@octokit/types": "^11.0.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": ">=5"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/plugin-request-log": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.0.tgz",
"integrity": "sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA==",
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": ">=5"
}
},
"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==",
"dependencies": {
"@octokit/types": "^11.0.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": ">=5"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/request": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.1.tgz",
"integrity": "sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ==",
"dependencies": {
"@octokit/endpoint": "^9.0.0",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^11.1.0",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/rest/node_modules/@octokit/request-error": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.0.tgz",
"integrity": "sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ==",
"dependencies": {
"@octokit/types": "^11.0.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/rest/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==",
"dependencies": {
"@octokit/openapi-types": "^18.0.0"
}
},
"node_modules/@octokit/types": {
"version": "6.41.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
"integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
"dependencies": {
"@octokit/openapi-types": "^12.11.0"
}
},
"node_modules/before-after-hook": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"engines": {
"node": ">= 12"
}
},
"node_modules/deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/hasha": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz",
"integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==",
"dependencies": {
"is-stream": "^2.0.0",
"type-fest": "^0.8.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"engines": {
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
},
"node_modules/type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"engines": {
"node": ">=8"
}
},
"node_modules/universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
"engines": {
"node": ">= 8"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
}
}
@@ -1,18 +0,0 @@
{
"name": "nym-hash-release",
"version": "1.0.0",
"description": "Generate hashes and signatures for assets in Nym releases",
"main": "index.js",
"type": "module",
"scripts": {
"local": "node run-local.mjs"
},
"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"
}
}
@@ -1,6 +0,0 @@
import {createHashesFromReleaseTagOrNameOrId} from './create-hashes.mjs';
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 119065724, cache: true, upload: false});
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: '119065724', cache: true, upload: false});
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'nym-connect-v1.1.19-snickers', cache: true, upload: false});
await createHashesFromReleaseTagOrNameOrId({releaseTagOrNameOrId: 'Nym Connect v1.1.19-snickers', cache: true, upload: false});
@@ -98,6 +98,7 @@ jobs:
cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR
cp target/release/nym-credential-client $OUTPUT_DIR
cp target/release/explorer-api $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
-4
View File
@@ -6,7 +6,6 @@ on:
- 'clients/**'
- 'common/**'
- 'explorer-api/**'
- 'ephemera/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
@@ -16,7 +15,6 @@ on:
- 'nym-api/**'
- 'nym-outfox/**'
- 'tools/nym-cli/**'
- 'tools/nym-nr-query/**'
- 'tools/ts-rs-cli/**'
- 'Cargo.toml'
pull_request:
@@ -24,7 +22,6 @@ on:
- 'clients/**'
- 'common/**'
- 'explorer-api/**'
- 'ephemera/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
@@ -34,7 +31,6 @@ on:
- 'nym-api/**'
- 'nym-outfox/**'
- 'tools/nym-cli/**'
- 'tools/nym-nr-query/**'
- 'tools/ts-rs-cli/**'
- 'Cargo.toml'
-40
View File
@@ -38,7 +38,6 @@ jobs:
- name: Build all projects in documentation/ & move to ~/dist/docs/
run: cd documentation && ./build_all_to_dist.sh
continue-on-error: false
- name: Deploy branch master to dev
continue-on-error: true
uses: easingthemes/ssh-deploy@main
@@ -50,7 +49,6 @@ jobs:
REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/
EXCLUDE: "/node_modules/"
- name: Deploy branch master to prod
if: github.ref == 'refs/heads/master'
uses: easingthemes/ssh-deploy@main
@@ -62,44 +60,6 @@ jobs:
REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/
EXCLUDE: "/node_modules/"
- name: Post process
run: cd documentation && ./post_process.sh
continue-on-error: false
- name: Create Vercel project file
uses: mobiledevops/secret-to-file-action@v1
with:
base64-encoded-secret: ${{ secrets.VERCEL_PROJECT_JSON_BASE64 }}
filename: "project.json"
is-executable: true
working-directory: "./dist/docs/.vercel"
- name: Install Vercel CLI
run: npm install --global vercel@latest
- name: Pull Vercel Environment Information (preview)
if: github.ref != 'refs/heads/master'
run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
working-directory: dist/docs
- name: Pull Vercel Environment Information (production)
if: github.ref == 'refs/heads/master'
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
working-directory: dist/docs
- name: Build Project Artifacts
run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
working-directory: dist/docs
- name: Deploy Project Artifacts to Vercel (preview)
if: github.ref != 'refs/heads/master'
run: vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }}
working-directory: dist/docs
- name: Deploy Project Artifacts to Vercel (master)
if: github.ref == 'refs/heads/master'
run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
working-directory: dist/docs
- name: Matrix - Node Install
run: npm install
working-directory: .github/workflows/support-files
@@ -112,11 +112,31 @@ jobs:
files: |
nym-connect/desktop/target/release/bundle/dmg/*.dmg
nym-connect/desktop/target/release/bundle/macos/*.app.tar.gz*
- id: release-info
name: Prepare release info
run: |
ref=${{ github.ref_name }}
semver="${ref##nym-connect-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_${semver}_x64.dmg " >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/dmg/nym-connect_*_x64.dmg') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-connect/desktop/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect.app.tar.gz
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect.app.tar.gz.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: NymConnect
category: connect
platform: MacOS
secrets: inherit
@@ -79,11 +79,31 @@ jobs:
files: |
nym-connect/desktop/target/release/bundle/appimage/*.AppImage
nym-connect/desktop/target/release/bundle/appimage/*.AppImage.tar.gz*
- id: release-info
name: Prepare release info
run: |
ref=${{ github.ref_name }}
semver="${ref##nym-connect-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_${semver}_amd64.AppImage" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/appimage/nym-connect_*_amd64.AppImage') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-connect/desktop/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect_${{ needs.publish-tauri.outputs.version }}_amd64.AppImage.tar.gz
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect_${{ needs.publish-tauri.outputs.version }}_amd64.AppImage.tar.gz.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: NymConnect
category: connect
platform: Ubuntu
secrets: inherit
@@ -98,11 +98,32 @@ jobs:
files: |
nym-connect/desktop/target/release/bundle/msi/*.msi
nym-connect/desktop/target/release/bundle/msi/*.msi.zip*
- id: release-info
name: Prepare release info
shell: bash
run: |
ref=${{ github.ref_name }}
semver="${ref##nym-connect-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_${semver}_x64_en-US.msi" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/msi/nym-connect_*_x64_en-US.msi') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-connect/desktop/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect_${{ needs.publish-tauri.outputs.version }}_x64_en-US.msi.zip
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-connect_${{ needs.publish-tauri.outputs.version }}_x64_en-US.msi.zip.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: NymConnect
category: connect
platform: Windows
secrets: inherit
+140 -1
View File
@@ -96,10 +96,149 @@ jobs:
target/release/nym-network-statistics
target/release/nym-cli
- id: release-info
name: Prepare release info
run: |
semver="${${{ github.ref_name }}##nym-binaries-}" && semver="${semver##v}"
echo "version=$semver" >> "$GITHUB_OUTPUT"
- id: binary-hashes
name: Generate binary hashes
run: |
echo "client_hash=${{ hashFiles('target/release/nym-client') }}" >> "$GITHUB_OUTPUT"
echo "mixnode_hash=${{ hashFiles('target/release/nym-mixnode') }}" >> "$GITHUB_OUTPUT"
echo "gateway_hash=${{ hashFiles('target/release/nym-gateway') }}" >> "$GITHUB_OUTPUT"
echo "socks5_hash=${{ hashFiles('target/release/nym-socks5-client') }}" >> "$GITHUB_OUTPUT"
echo "netreq_hash=${{ hashFiles('target/release/nym-network-requester') }}" >> "$GITHUB_OUTPUT"
echo "cli_hash=${{ hashFiles('target/release/nym-cli') }}" >> "$GITHUB_OUTPUT"
echo "netstat_hash=${{ hashFiles('target/release/nym-network-statistics') }}" >> "$GITHUB_OUTPUT"
- id: binary-versions
name: Get binary versions
run: |
v=$(rg '^version = "(.*)"' -or '$1' clients/native/Cargo.toml) && echo "client_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' mixnode/Cargo.toml) && echo "mixnode_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' gateway/Cargo.toml) && echo "gateway_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' clients/socks5/Cargo.toml) && echo "socks5_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' service-providers/network-requester/Cargo.toml) && echo "netreq_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' tools/nym-cli/Cargo.toml) && echo "cli_version=$v" >> "$GITHUB_OUTPUT"
v=$(rg '^version = "(.*)"' -or '$1' service-providers/network-statistics/Cargo.toml) && echo "netstat_version=$v" >> "$GITHUB_OUTPUT"
push-release-data-client:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.client_version }}
filename: nym-client
file_hash: ${{ needs.publish-nym.outputs.client_hash }}
name: Client
category: binaries
secrets: inherit
push-release-data-mixnode:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.mixnode_version }}
filename: nym-mixnode
file_hash: ${{ needs.publish-nym.outputs.mixnode_hash }}
name: Mixnode
category: binaries
secrets: inherit
push-release-data-gateway:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.gateway_version }}
filename: nym-gateway
file_hash: ${{ needs.publish-nym.outputs.gateway_hash }}
name: Gateway
category: binaries
secrets: inherit
push-release-data-socks5:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.socks5_version }}
filename: nym-socks5-client
file_hash: ${{ needs.publish-nym.outputs.socks5_hash }}
name: Socks5 Client
category: binaries
secrets: inherit
push-release-data-network-requester:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.netreq_version }}
filename: nym-network-requester
file_hash: ${{ needs.publish-nym.outputs.netreq_hash }}
name: Network Requester
category: binaries
secrets: inherit
push-release-data-cli:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.cli_version }}
filename: nym-cli
file_hash: ${{ needs.publish-nym.outputs.cli_hash }}
name: Cli
category: binaries
secrets: inherit
push-release-data-network-stat:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
needs: publish-nym
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-nym.outputs.release_id }}
release_date: ${{ needs.publish-nym.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/CHANGELOG.md
version: ${{ needs.publish-nym.outputs.netstat_version }}
filename: nym-network-statistics
file_hash: ${{ needs.publish-nym.outputs.netstat_hash }}
name: Network Statistics
category: binaries
secrets: inherit
+22 -1
View File
@@ -100,10 +100,31 @@ jobs:
nym-wallet/target/release/bundle/dmg/*.dmg
nym-wallet/target/release/bundle/macos/*.app.tar.gz*
- id: release-info
name: Prepare release info
run: |
ref=${{ github.ref_name }}
semver="${ref##nym-wallet-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_${semver}_x64.dmg" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/dmg/nym-wallet_*_x64.dmg') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-wallet/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet.app.tar.gz
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet.app.tar.gz.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: Wallet
category: wallet
platform: MacOS
secrets: inherit
@@ -77,10 +77,31 @@ jobs:
nym-wallet/target/release/bundle/appimage/*.AppImage
nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz*
- id: release-info
name: Prepare release info
run: |
ref=${{ github.ref_name }}
semver="${ref##nym-wallet-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_${semver}_amd64.AppImage" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/appimage/nym-wallet_*_amd64.AppImage') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-wallet/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet_${{ needs.publish-tauri.outputs.version }}_amd64.AppImage.tar.gz
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet_${{ needs.publish-tauri.outputs.version }}_amd64.AppImage.tar.gz.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: Wallet
category: wallet
platform: Ubuntu
secrets: inherit
@@ -97,10 +97,31 @@ jobs:
nym-wallet/target/release/bundle/msi/*.msi
nym-wallet/target/release/bundle/msi/*.msi.zip*
- id: release-info
name: Prepare release info
run: |
ref=${{ github.ref_name }}
semver="${ref##nym-wallet-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_${semver}_x64_en-US.msi" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/msi/nym-wallet_*_x64_en-US.msi') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/release-calculate-hash.yml
uses: ./.github/workflows/push-release-data.yml
needs: publish-tauri
with:
release_tag: ${{ github.ref_name }}
release_id: ${{ needs.publish-tauri.outputs.release_id }}
release_date: ${{ needs.publish-tauri.outputs.release_date }}
download_base_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}
changelog_url: https://github.com/nymtech/nym/blob/${{ github.ref_name }}/nym-wallet/CHANGELOG.md
archive_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet_${{ needs.publish-tauri.outputs.version }}_x64_en-US.msi.zip
sig_url: https://github.com/nymtech/nym/releases/download/${{ github.ref_name }}/nym-wallet_${{ needs.publish-tauri.outputs.version }}_x64_en-US.msi.zip.sig
version: ${{ needs.publish-tauri.outputs.version }}
filename: ${{ needs.publish-tauri.outputs.filename }}
file_hash: ${{ needs.publish-tauri.outputs.file_hash }}
name: Wallet
category: wallet
platform: Windows
secrets: inherit
+212
View File
@@ -0,0 +1,212 @@
name: Push release data
env:
strapi_download_url: 'https://strapi.feat-nym-update-nym-web.websites.dev.nymte.ch/api/downloaders'
strapi_updater_url: 'https://strapi.feat-nym-update-nym-web.websites.dev.nymte.ch/api/updaters'
on:
workflow_call:
inputs:
release_tag:
required: true
description: Release tag
type: string
release_id:
required: true
description: Release ID
type: string
release_date:
required: true
description: Release date
type: string
download_base_url:
required: true
description: Download base URL
type: string
changelog_url:
required: true
description: Changelog URL
type: string
archive_url:
required: false
description: Binary archive URL
type: string
sig_url:
required: false
description: Archive signature URL
type: string
version:
required: true
description: Release version (semver)
type: string
filename:
required: true
description: Binary file name
type: string
file_hash:
required: true
description: Binary hash (sha256)
type: string
name:
required: true
description: Name
type: string
category:
required: true
description: Category
type: string
platform:
required: false
description: Platform
type: string
workflow_dispatch:
inputs:
# ⚠ since inputs are limited to 10 max for workflow_dispatch
# some properties were omitted
version:
required: true
description: Release version (semver)
type: string
default: '1.0.0'
release_id:
required: true
description: Release ID
type: string
default: '1234'
release_date:
required: true
description: Release date
type: string
default: '2023-06-26T10:09:16Z'
download_base_url:
required: true
description: Download base URL
type: string
default: 'https://github.com/nymtech/nym/releases/download/nym-wallet-v1.0.0'
changelog_url:
required: true
description: Changelog URL
type: string
default: 'https://github.com/nymtech/nym/blob/nym-wallet-v1.0.0/nym-wallet/CHANGELOG.md'
filename:
required: true
description: Binary file name
type: string
default: 'nym-wallet_1.0.0_amd64.AppImage'
file_hash:
required: true
description: Binary hash (sha256)
type: string
default: 'xxx'
name:
required: true
description: Name
type: string
default: 'Wallet'
category:
required: true
description: Category
default: 'wallet'
type: choice
options:
- wallet
- connect
- binaries
platform:
required: false
description: Platform
default: 'Ubuntu'
type: choice
options:
- Ubuntu
- Windows
- MacOS
jobs:
push-download-data:
name: Push download data to Strapi
runs-on: custom-runner-linux
steps:
- name: Release info
run: |
echo "version: ${{ inputs.version }}"
echo "tag: ${{ inputs.release_tag }}"
- id: get_sig
name: Get sig
if: ${{ inputs.sig_url != null }}
run: |
output=$(curl -LsSf ${{ inputs.sig_url }})
echo "sig=$output" >> "$GITHUB_OUTPUT"
- id: strapi-request
name: Strapi request
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.strapi_download_url }}
method: 'POST'
bearerToken: ${{ secrets.STRAPI_API_TOKEN_RELEASES }}
customHeaders: '{"Content-Type": "application/json"}'
data: |
{
"data": {
"releaseId": "${{ inputs.release_id }}",
"releaseDate": "${{ inputs.release_date }}",
"downloadBaseUrl": "${{ inputs.download_base_url }}",
"changelogUrl": "${{ inputs.changelog_url }}",
"version": "${{ inputs.version }}",
"filename": "${{ inputs.filename }}",
"name": "${{ inputs.name }}",
"category": "${{ inputs.category }}",
"platform": "${{ inputs.platform }}",
"sha256": "${{ inputs.file_hash }}",
"sig": "${{ steps.get_sig.outputs.sig }}"
}
}
- name: Strapi Response
run: |
echo ${{ steps.strapi-request.outputs.response }}
push-update-data:
name: Push update data to Strapi
runs-on: custom-runner-linux
# only push update data for tauri apps (desktop wallet and NC)
if: ${{ inputs.category == 'wallet' || inputs.category == 'connect' }}
steps:
- name: Release info
run: |
echo "version: ${{ inputs.version }}"
echo "tag: ${{ inputs.release_tag }}"
- id: get_sig
name: Get sig
if: ${{ inputs.sig_url != null }}
run: |
output=$(curl -LsSf ${{ inputs.sig_url }})
echo "sig=$output" >> "$GITHUB_OUTPUT"
- id: strapi-request
name: Strapi request
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.strapi_updater_url }}
method: 'POST'
bearerToken: ${{ secrets.STRAPI_API_TOKEN_RELEASES }}
customHeaders: '{"Content-Type": "application/json"}'
data: |
{
"data": {
"releaseId": "${{ inputs.release_id }}",
"releaseDate": "${{ inputs.release_date }}",
"downloadUrl": "${{ inputs.archive_url }}",
"changelog": "See ${{ inputs.changelog_url }} for the changelog",
"version": "${{ inputs.version }}",
"filename": "${{ inputs.filename }}",
"category": "${{ inputs.category }}",
"platform": "${{ inputs.platform }}",
"sha256": "${{ inputs.file_hash }}",
"sig": "${{ steps.get_sig.outputs.sig }}"
}
}
- name: Strapi Response
run: |
echo ${{ steps.strapi-request.outputs.response }}
@@ -1,39 +0,0 @@
name: Releases - calculate file hashes
on:
workflow_call:
inputs:
release_tag:
description: 'Release tag'
required: true
type: string
workflow_dispatch:
release_tag:
tag:
description: 'Release tag'
required: true
type: string
jobs:
build:
name: Calculate hash for assets in release
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install packages
run: cd ./.github/actions/nym-hash-releases && npm i
- uses: ./.github/actions/nym-hash-releases
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
release-tag-or-name-or-id: ${{ inputs.release_tag }}
- uses: actions/upload-artifact@v2
with:
name: Asset Hashes
path: hashes.json
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: 1.71.0
toolchain: stable
override: true
components: rustfmt, clippy
+2 -36
View File
@@ -4,41 +4,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [v1.1.30-twix] (2023-09-05)
- geo_aware_provider: fix too much filtering of gateways ([#3826])
- network-requester: add description to config ([#3799])
- Speedy mode - selects gateway based on latency in medium / speedy mode ([#3770])
- Chore/enable versioning ([#3768])
- Create explorer-client and use in geo aware provider ([#3824])
[#3826]: https://github.com/nymtech/nym/pull/3826
[#3799]: https://github.com/nymtech/nym/pull/3799
[#3770]: https://github.com/nymtech/nym/issues/3770
[#3768]: https://github.com/nymtech/nym/pull/3768
[#3824]: https://github.com/nymtech/nym/pull/3824
## [v1.1.29-snickers] (2023-08-29)
- Add EXPLORER_API configurable url ([#3810])
- Bugfix/use correct tendermint dialect ([#3802])
- Explorer - look up gateways based on geo-location ([#3776])
- Speedy mode - select the mixnodes based on the location of the NR ([#3775])
- NR - reduce response time by removing poisson delay ([#3774])
- [demo] libp2p example with nym-sdk ([#3763])
- introduced /network/details endpoint to nym-api to return used network information ([#3758])
- Feature/issue credentials ([#3691])
[#3810]: https://github.com/nymtech/nym/pull/3810
[#3802]: https://github.com/nymtech/nym/pull/3802
[#3776]: https://github.com/nymtech/nym/issues/3776
[#3775]: https://github.com/nymtech/nym/issues/3775
[#3774]: https://github.com/nymtech/nym/issues/3774
[#3763]: https://github.com/nymtech/nym/pull/3763
[#3758]: https://github.com/nymtech/nym/pull/3758
[#3691]: https://github.com/nymtech/nym/pull/3691
## [v1.1.28] (2023-08-22)
## [1.1.28] (2023-08-22)
- [final step3]: add [rust] support to nyxd client in wasm ([#3743])
- Feature/ephemera upgrade ([#3791])
@@ -53,7 +19,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#3767]: https://github.com/nymtech/nym/pull/3767
## [v1.1.27] (2023-08-16)
## [1.1.27] (2023-08-16)
- fix serialisation of contract types ([#3752])
- Investigate spending credentials from the main API (coconut enabled to a gateway) from feature/ephemera branch ([#3741])
Generated
+44 -74
View File
@@ -846,9 +846,9 @@ dependencies = [
[[package]]
name = "bindgen"
version = "0.65.1"
version = "0.64.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5"
checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
dependencies = [
"bitflags 1.3.2",
"cexpr",
@@ -856,13 +856,12 @@ dependencies = [
"lazy_static",
"lazycell",
"peeking_take_while",
"prettyplease 0.2.12",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 2.0.28",
"syn 1.0.109",
]
[[package]]
@@ -2768,7 +2767,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "explorer-api"
version = "1.1.28"
version = "1.1.26"
dependencies = [
"chrono",
"clap 4.3.21",
@@ -5016,9 +5015,9 @@ dependencies = [
[[package]]
name = "librocksdb-sys"
version = "0.11.0+8.1.1"
version = "0.10.0+7.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e"
checksum = "0fe4d5874f5ff2bc616e55e8c6086d478fcda13faf9495768a4aa1c22042d30b"
dependencies = [
"bindgen",
"bzip2-sys",
@@ -5638,7 +5637,7 @@ dependencies = [
[[package]]
name = "nym-api"
version = "1.1.29"
version = "1.1.27"
dependencies = [
"actix-web",
"anyhow",
@@ -5785,7 +5784,7 @@ dependencies = [
[[package]]
name = "nym-cli"
version = "1.1.28"
version = "1.1.26"
dependencies = [
"anyhow",
"base64 0.13.1",
@@ -5811,7 +5810,6 @@ dependencies = [
name = "nym-cli-commands"
version = "1.0.0"
dependencies = [
"anyhow",
"base64 0.13.1",
"bip39",
"bs58 0.4.0",
@@ -5825,16 +5823,10 @@ dependencies = [
"humantime-serde",
"k256 0.13.1",
"log",
"nym-bandwidth-controller",
"nym-bin-common",
"nym-client-core",
"nym-coconut-bandwidth-contract-common",
"nym-coconut-dkg-common",
"nym-config",
"nym-contracts-common",
"nym-credential-storage",
"nym-credential-utils",
"nym-credentials",
"nym-crypto",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
@@ -5858,7 +5850,7 @@ dependencies = [
[[package]]
name = "nym-client"
version = "1.1.28"
version = "1.1.26"
dependencies = [
"clap 4.3.21",
"dirs 4.0.0",
@@ -5898,7 +5890,6 @@ version = "1.1.15"
dependencies = [
"async-trait",
"base64 0.21.2",
"cfg-if",
"dashmap 5.5.0",
"dirs 4.0.0",
"futures",
@@ -5909,7 +5900,7 @@ dependencies = [
"nym-config",
"nym-credential-storage",
"nym-crypto",
"nym-explorer-client",
"nym-explorer-api-requests",
"nym-gateway-client",
"nym-gateway-requests",
"nym-network-defaults",
@@ -6031,6 +6022,26 @@ dependencies = [
"thiserror",
]
[[package]]
name = "nym-credential-client"
version = "0.1.0"
dependencies = [
"clap 4.3.21",
"log",
"nym-bandwidth-controller",
"nym-bin-common",
"nym-client-core",
"nym-config",
"nym-credential-storage",
"nym-credentials",
"nym-network-defaults",
"nym-pemstore",
"nym-validator-client",
"serde",
"thiserror",
"tokio",
]
[[package]]
name = "nym-credential-storage"
version = "0.1.0"
@@ -6042,28 +6053,12 @@ dependencies = [
"tokio",
]
[[package]]
name = "nym-credential-utils"
version = "0.1.0"
dependencies = [
"log",
"nym-bandwidth-controller",
"nym-client-core",
"nym-config",
"nym-credential-storage",
"nym-credentials",
"nym-validator-client",
"thiserror",
"tokio",
]
[[package]]
name = "nym-credentials"
version = "0.1.0"
dependencies = [
"bls12_381 0.5.0",
"cosmrs",
"log",
"nym-api-requests",
"nym-coconut-interface",
"nym-crypto",
@@ -6151,22 +6146,9 @@ dependencies = [
"ts-rs",
]
[[package]]
name = "nym-explorer-client"
version = "0.1.0"
dependencies = [
"log",
"nym-explorer-api-requests",
"reqwest",
"serde",
"thiserror",
"tokio",
"url",
]
[[package]]
name = "nym-gateway"
version = "1.1.28"
version = "1.1.26"
dependencies = [
"anyhow",
"async-trait",
@@ -6321,7 +6303,7 @@ dependencies = [
[[package]]
name = "nym-mixnode"
version = "1.1.29"
version = "1.1.27"
dependencies = [
"anyhow",
"bs58 0.4.0",
@@ -6439,7 +6421,7 @@ dependencies = [
[[package]]
name = "nym-network-requester"
version = "1.1.28"
version = "1.1.26"
dependencies = [
"anyhow",
"async-file-watcher",
@@ -6486,7 +6468,7 @@ dependencies = [
[[package]]
name = "nym-network-statistics"
version = "1.1.28"
version = "1.1.26"
dependencies = [
"dirs 4.0.0",
"log",
@@ -6584,7 +6566,6 @@ dependencies = [
name = "nym-sdk"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"bip39",
"dotenvy",
@@ -6596,7 +6577,6 @@ dependencies = [
"nym-bin-common",
"nym-client-core",
"nym-credential-storage",
"nym-credential-utils",
"nym-credentials",
"nym-crypto",
"nym-gateway-requests",
@@ -6651,7 +6631,7 @@ dependencies = [
[[package]]
name = "nym-socks5-client"
version = "1.1.28"
version = "1.1.26"
dependencies = [
"clap 4.3.21",
"lazy_static",
@@ -7779,16 +7759,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "prettyplease"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62"
dependencies = [
"proc-macro2",
"syn 2.0.28",
]
[[package]]
name = "proc-macro-crate"
version = "1.1.3"
@@ -7891,7 +7861,7 @@ dependencies = [
"log",
"multimap",
"petgraph",
"prettyplease 0.1.25",
"prettyplease",
"prost",
"prost-types",
"regex",
@@ -8642,9 +8612,9 @@ dependencies = [
[[package]]
name = "rocksdb"
version = "0.21.0"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe"
checksum = "015439787fce1e75d55f279078d33ff14b4af5d93d995e8838ee4631301c8a99"
dependencies = [
"libc",
"librocksdb-sys",
@@ -8909,7 +8879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b08f58cf71a58bda5734758eb20051cdb66c06c9243badbc45092ced1be834df"
dependencies = [
"macro_rules_attribute",
"prettyplease 0.1.25",
"prettyplease",
"proc-macro2",
"quote",
"syn 1.0.109",
@@ -10581,9 +10551,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "ts-rs"
version = "7.0.0"
version = "6.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1ff1f8c90369bc172200013ac17ae86e7b5def580687df4e6127883454ff2b0"
checksum = "4added4070a4fdf9df03457206cd2e4b12417c8560a2954d91ffcbe60177a56a"
dependencies = [
"thiserror",
"ts-rs-macros",
@@ -10606,14 +10576,14 @@ dependencies = [
[[package]]
name = "ts-rs-macros"
version = "7.0.0"
version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6f41cc0aeb7a4a55730188e147d3795a7349b501f8334697fd37629b896cdc2"
checksum = "9f807fdb3151fee75df7485b901a89624358cd07a67a8fb1a5831bf5a07681ff"
dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 1.0.109",
"termcolor",
]
+1 -3
View File
@@ -17,6 +17,7 @@ opt-level = 3
resolver = "2"
members = [
"clients/credential",
"clients/native",
"clients/native/websocket-requests",
"clients/socks5",
@@ -42,7 +43,6 @@ members = [
"common/cosmwasm-smart-contracts/vesting-contract",
"common/credential-storage",
"common/credentials",
"common/credential-utils",
"common/crypto",
"common/dkg",
"common/execute",
@@ -76,7 +76,6 @@ members = [
"common/wasm-utils",
"explorer-api",
"explorer-api/explorer-api-requests",
"explorer-api/explorer-client",
"gateway",
"gateway/gateway-requests",
"integrations/bity",
@@ -151,7 +150,6 @@ tap = "1.0.1"
tendermint-rpc = "0.32" # same version as used by cosmrs
thiserror = "1.0.38"
tokio = "1.24.1"
ts-rs = "7.0.0"
url = "2.4"
zeroize = "1.6.0"
+3
View File
@@ -77,6 +77,9 @@ $(eval $(call add_cargo_workspace,contracts,contracts,--lib --target wasm32-unkn
$(eval $(call add_cargo_workspace,wasm-client,clients/webassembly,--target wasm32-unknown-unknown))
$(eval $(call add_cargo_workspace,wallet,nym-wallet,))
$(eval $(call add_cargo_workspace,connect,nym-connect/desktop))
ifdef NYM_MOBILE
$(eval $(call add_cargo_workspace,connect-mobile,nym-connect/mobile/src-tauri))
endif
# -----------------------------------------------------------------------------
# Convenience targets for crates that are already part of the main workspace
@@ -1,18 +1,25 @@
[package]
name = "nym-credential-utils"
name = "nym-credential-client"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
log = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
clap = { version = "4.0", features = ["cargo", "derive"] }
log = "0.4"
serde = { workspace = true, features = ["derive"] }
thiserror = "1.0"
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" }
nym-credentials = { path = "../../common/credentials" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-bin-common = { path = "../../common/bin-common"}
nym-network-defaults = { path = "../../common/network-defaults" }
nym-pemstore = { path = "../../common/pemstore" }
nym-validator-client = { path = "../../common/client-libs/validator-client" }
nym-config = { path = "../../common/config" }
nym-client-core = { path = "../../common/client-core" }
+32
View File
@@ -0,0 +1,32 @@
<!--
Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
SPDX-License-Identifier: Apache-2.0
-->
## Credential binary
The credential binary is used to acquire coconut bandwidth credentials in exchange for nym tokens. Those credentials are stored in the client's `data` directory, so that they can be used as the client sees fit.
### Warning
The credential binary is still experimental software. The infrastructure for using it is not yet deployed to mainnet and it's still in the process of being deployed to sandbox.
### Building
From the project's root directory, run:
```
cargo build -p nym-credential-client
```
which generates the `nym-credential-client` binary in `target/debug/nym-credential-client`.
### Running
For example, you can get a credential worth 3 nym (3000000 unym) in a socks5 client that was already initialized like so:
```
./target/debug/nym-credential-client --config-env-file envs/sandbox.env --client-home-directory ~/.nym/socks5-clients/cred_client --nyxd-url https://sandbox-validator1.nymtech.net --mnemonic $MNEMONIC --recovery-dir /tmp/recovery --amount 3000000
```
More information regarding how to run the binary can be found by running it with the `--help` argument.
+55
View File
@@ -0,0 +1,55 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::error::Result;
use bip39::Mnemonic;
use nym_network_defaults::{NymNetworkDetails, VOUCHER_INFO};
use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient;
use nym_validator_client::nyxd::{self, DirectSigningHttpRpcNyxdClient};
use nym_validator_client::nyxd::{Coin, Fee, NyxdClient};
use std::str::FromStr;
use url::Url;
pub(crate) struct Client {
nyxd_client: DirectSigningHttpRpcNyxdClient,
mix_denom_base: String,
}
impl Client {
pub fn new(nyxd_url: &str, mnemonic: &str) -> Self {
let nyxd_url = Url::from_str(nyxd_url).unwrap();
let mnemonic = Mnemonic::from_str(mnemonic).unwrap();
let network_details = NymNetworkDetails::new_from_env();
let config = nyxd::Config::try_from_nym_network_details(&network_details)
.expect("failed to construct valid validator client config with the provided network");
let nyxd_client =
NyxdClient::connect_with_mnemonic(config, nyxd_url.as_ref(), mnemonic, None).unwrap();
Client {
nyxd_client,
mix_denom_base: network_details.chain_details.mix_denom.base,
}
}
pub async fn deposit(
&self,
amount: u64,
verification_key: String,
encryption_key: String,
fee: Option<Fee>,
) -> Result<String> {
let amount = Coin::new(amount as u128, self.mix_denom_base.clone());
Ok(self
.nyxd_client
.deposit(
amount,
String::from(VOUCHER_INFO),
verification_key,
encryption_key,
fee,
)
.await?
.transaction_hash
.to_string())
}
}
+82
View File
@@ -0,0 +1,82 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{ArgGroup, Args, Subcommand};
use log::*;
use nym_bandwidth_controller::acquire::state::State;
use nym_bin_common::completions::ArgShell;
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use crate::error::Result;
use crate::recovery_storage::RecoveryStorage;
#[derive(Subcommand)]
pub(crate) enum Command {
/// Run the binary to obtain a credential
Run(Run),
/// Generate shell completions
Completions(ArgShell),
/// Generate Fig specification
GenerateFigSpec,
}
#[derive(Args)]
#[clap(group(
ArgGroup::new("recov")
.required(true)
.args(&["amount", "recovery_mode"]),
))]
pub(crate) struct Run {
/// Home directory of the client that is supposed to use the credential.
#[clap(long)]
pub(crate) client_home_directory: std::path::PathBuf,
/// A mnemonic for the account that buys the credential
#[clap(long)]
pub(crate) mnemonic: String,
/// The amount of utokens the credential will hold. If recovery mode is enabled, this value
/// is not needed
#[clap(long, default_value = "0")]
pub(crate) amount: u64,
/// Path to a directory used to store recovery files for unconsumed deposits
#[clap(long)]
pub(crate) recovery_dir: std::path::PathBuf,
/// Recovery mode, when enabled, tries to recover any deposit data dumped in recovery_dir
#[clap(long)]
pub(crate) recovery_mode: bool,
}
pub(crate) async fn recover_credentials<C: DkgQueryClient + Send + Sync>(
client: &C,
recovery_storage: &RecoveryStorage,
shared_storage: &PersistentStorage,
) -> Result<()> {
for voucher in recovery_storage.unconsumed_vouchers()? {
let state = State::new(voucher);
if let Err(e) =
nym_bandwidth_controller::acquire::get_credential(&state, client, shared_storage).await
{
error!(
"Could not recover deposit {} due to {:?}, try again later",
state.voucher.tx_hash(),
e
)
} else {
info!(
"Converted deposit {} to a credential, removing recovery data for it",
state.voucher.tx_hash()
);
if let Err(e) = recovery_storage.remove_voucher(state.voucher.tx_hash().to_string()) {
warn!("Could not remove recovery data - {:?}", e);
}
}
}
Ok(())
}
+36
View File
@@ -0,0 +1,36 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::time::SystemTimeError;
use thiserror::Error;
use nym_credential_storage::error::StorageError;
use nym_credentials::error::Error as CredentialError;
use nym_validator_client::nyxd::error::NyxdError;
use nym_validator_client::ValidatorClientError;
pub type Result<T> = std::result::Result<T, CredentialClientError>;
#[derive(Error, Debug)]
pub enum CredentialClientError {
#[error("IO error: {0}")]
IOError(#[from] std::io::Error),
#[error("Bandwidth controller error: {0}")]
BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError),
#[error("Nyxd error: {0}")]
Nyxd(#[from] NyxdError),
#[error("Validator client error: {0}")]
ValidatorClientError(#[from] ValidatorClientError),
#[error("Credential error: {0}")]
Credential(#[from] CredentialError),
#[error("Could not use shared storage")]
SharedStorageError(#[from] StorageError),
#[error("Could not get system time")]
SysTimeError(#[from] SystemTimeError),
}
+131
View File
@@ -0,0 +1,131 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod commands;
mod error;
mod recovery_storage;
use commands::*;
use error::Result;
use log::*;
use nym_bin_common::completions::fig_generate;
use nym_config::DEFAULT_DATA_DIR;
use nym_network_defaults::{setup_env, NymNetworkDetails};
use std::process::exit;
use std::time::{Duration, SystemTime};
use clap::{CommandFactory, Parser};
use nym_bin_common::logging::setup_logging;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use nym_validator_client::nyxd::{Coin, Config};
use nym_validator_client::DirectSigningHttpRpcNyxdClient;
const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute
#[derive(Parser)]
#[clap(author = "Nymtech", version, about)]
struct Cli {
/// Path pointing to an env file that configures the client.
#[clap(short, long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
#[clap(subcommand)]
pub(crate) command: Command,
}
async fn block_until_coconut_is_available<C: DkgQueryClient + Send + Sync>(
client: &C,
) -> Result<()> {
loop {
let epoch = client.get_current_epoch().await?;
let current_timestamp_secs = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)?
.as_secs();
if epoch.state.is_final() {
if current_timestamp_secs + SAFETY_BUFFER_SECS >= epoch.finish_timestamp.seconds() {
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
exit(0);
}
break;
} else {
// Use 1 additional second to not start the next iteration immediately and spam get_current_epoch queries
let secs_until_final = epoch
.final_timestamp_secs()
.saturating_sub(current_timestamp_secs)
+ 1;
info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final);
std::thread::sleep(Duration::from_secs(secs_until_final));
}
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::parse();
setup_logging();
setup_env(args.config_env_file.as_ref());
let bin_name = "nym-credential-client";
match args.command {
Command::Run(r) => {
// we assume the structure of <home-dir>/data
let data_dir = r.client_home_directory.join(DEFAULT_DATA_DIR);
let paths = CommonClientPaths::new_default(data_dir);
let db_path = paths.credentials_database;
let shared_storage =
nym_credential_storage::initialise_persistent_storage(db_path).await;
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
let network_details = NymNetworkDetails::new_from_env();
let config = Config::try_from_nym_network_details(&network_details).expect(
"failed to construct valid validator client config with the provided network",
);
let amount = Coin::new(
r.amount as u128,
network_details.chain_details.mix_denom.base,
);
let endpoint = network_details.endpoints[0].nyxd_url.as_str();
let client = DirectSigningHttpRpcNyxdClient::connect_with_mnemonic(
config,
endpoint,
r.mnemonic.parse().unwrap(),
)?;
block_until_coconut_is_available(&client).await?;
info!("Starting depositing funds, don't kill the process");
if !r.recovery_mode {
let state = nym_bandwidth_controller::acquire::deposit(&client, amount).await?;
if nym_bandwidth_controller::acquire::get_credential(
&state,
&client,
&shared_storage,
)
.await
.is_err()
{
warn!("Failed to obtain credential. Dumping recovery data.",);
match recovery_storage.insert_voucher(&state.voucher) {
Ok(file_path) => {
warn!("Dumped recovery data to {:?}. Try using recovery mode to convert it to a credential", file_path);
}
Err(e) => {
error!("Could not dump recovery data to file system due to {:?}, the deposit will be lost!", e)
}
}
}
} else {
recover_credentials(&client, &recovery_storage, &shared_storage).await?;
}
}
Command::Completions(c) => c.generate(&mut Cli::command(), bin_name),
Command::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
}
Ok(())
}
@@ -0,0 +1,56 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
use std::fs::{create_dir_all, read_dir, File};
use std::io::{Read, Write};
use std::path::PathBuf;
pub struct RecoveryStorage {
recovery_dir: PathBuf,
}
impl RecoveryStorage {
pub fn new(recovery_dir: PathBuf) -> std::io::Result<Self> {
create_dir_all(&recovery_dir)?;
Ok(Self { recovery_dir })
}
pub fn unconsumed_vouchers(&self) -> std::io::Result<impl Iterator<Item = BandwidthVoucher>> {
Ok(read_dir(&self.recovery_dir)?
.filter_map(|entry| entry.ok())
.filter_map(|entry| {
let path = entry.path();
if path.is_file() {
Some(path)
} else {
None
}
})
.filter_map(|path| File::open(path).ok())
.filter_map(|mut f| {
let mut buff = Vec::new();
if f.read_to_end(&mut buff).is_ok() {
Some(buff)
} else {
None
}
})
.filter_map(|buff| BandwidthVoucher::try_from_bytes(&buff).ok()))
}
pub fn insert_voucher(&self, voucher: &BandwidthVoucher) -> std::io::Result<PathBuf> {
let file_name = voucher.tx_hash().to_string();
let file_path = self.recovery_dir.join(file_name);
let mut file = File::create(&file_path)?;
let buff = voucher.to_bytes();
file.write_all(&buff)?;
Ok(file_path)
}
pub fn remove_voucher(&self, file_name: String) -> std::io::Result<()> {
let file_path = self.recovery_dir.join(file_name);
std::fs::remove_file(file_path)
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-client"
version = "1.1.28"
version = "1.1.26"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021"
@@ -105,10 +105,10 @@ impl ClientRequest {
let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes();
std::iter::once(ClientRequestTag::Send as u8)
.chain(recipient.to_bytes()) // will not be length prefixed because the length is constant
.chain(conn_id_bytes)
.chain(data_len_bytes)
.chain(data)
.chain(recipient.to_bytes().into_iter()) // will not be length prefixed because the length is constant
.chain(conn_id_bytes.into_iter())
.chain(data_len_bytes.into_iter())
.chain(data.into_iter())
.collect()
}
@@ -180,11 +180,11 @@ impl ClientRequest {
let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes();
std::iter::once(ClientRequestTag::SendAnonymous as u8)
.chain(reply_surbs.to_be_bytes())
.chain(recipient.to_bytes()) // will not be length prefixed because the length is constant
.chain(conn_id_bytes)
.chain(data_len_bytes)
.chain(data)
.chain(reply_surbs.to_be_bytes().into_iter())
.chain(recipient.to_bytes().into_iter()) // will not be length prefixed because the length is constant
.chain(conn_id_bytes.into_iter())
.chain(data_len_bytes.into_iter())
.chain(data.into_iter())
.collect()
}
@@ -258,10 +258,10 @@ impl ClientRequest {
let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes();
std::iter::once(ClientRequestTag::Reply as u8)
.chain(sender_tag.to_bytes())
.chain(conn_id_bytes)
.chain(message_len_bytes)
.chain(message)
.chain(sender_tag.to_bytes().into_iter())
.chain(conn_id_bytes.into_iter())
.chain(message_len_bytes.into_iter())
.chain(message.into_iter())
.collect()
}
@@ -332,7 +332,7 @@ impl ClientRequest {
fn serialize_closed_connection(connection_id: u64) -> Vec<u8> {
let conn_id_bytes = connection_id.to_be_bytes();
std::iter::once(ClientRequestTag::ClosedConnection as u8)
.chain(conn_id_bytes)
.chain(conn_id_bytes.into_iter())
.collect()
}
@@ -359,7 +359,7 @@ impl ClientRequest {
fn serialize_get_lane_queue_lengths(connection_id: u64) -> Vec<u8> {
let conn_id_bytes = connection_id.to_be_bytes();
std::iter::once(ClientRequestTag::GetLaneQueueLength as u8)
.chain(conn_id_bytes)
.chain(conn_id_bytes.into_iter())
.collect()
}
@@ -67,15 +67,15 @@ impl ServerResponse {
if let Some(sender_tag) = reconstructed_message.sender_tag {
std::iter::once(ServerResponseTag::Received as u8)
.chain(std::iter::once(true as u8))
.chain(sender_tag.to_bytes())
.chain(sender_tag.to_bytes().into_iter())
.chain(message_len_bytes.iter().cloned())
.chain(reconstructed_message.message)
.chain(reconstructed_message.message.into_iter())
.collect()
} else {
std::iter::once(ServerResponseTag::Received as u8)
.chain(std::iter::once(false as u8))
.chain(message_len_bytes.iter().cloned())
.chain(reconstructed_message.message)
.chain(reconstructed_message.message.into_iter())
.collect()
}
}
@@ -149,7 +149,7 @@ impl ServerResponse {
// SELF_ADDRESS_RESPONSE_TAG || self_address
fn serialize_self_address(address: Recipient) -> Vec<u8> {
std::iter::once(ServerResponseTag::SelfAddress as u8)
.chain(address.to_bytes())
.chain(address.to_bytes().into_iter())
.collect()
}
@@ -211,8 +211,8 @@ impl ServerResponse {
let message_len_bytes = (error.message.len() as u64).to_be_bytes();
std::iter::once(ServerResponseTag::Error as u8)
.chain(std::iter::once(error.kind as u8))
.chain(message_len_bytes)
.chain(error.message.into_bytes())
.chain(message_len_bytes.into_iter())
.chain(error.message.into_bytes().into_iter())
.collect()
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-socks5-client"
version = "1.1.28"
version = "1.1.26"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021"
+4 -12
View File
@@ -17,7 +17,7 @@ use nym_client_core::client::base_client::storage::gateway_details::{
};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
use nym_client_core::config::{GatewayEndpointConfig, GroupBy, TopologyStructure};
use nym_client_core::config::{GatewayEndpointConfig, TopologyStructure};
use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet;
use nym_sphinx::params::{PacketSize, PacketType};
@@ -101,17 +101,9 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
let secondary_packet_size = args.medium_toggle.then_some(PacketSize::ExtendedPacket16);
let no_per_hop_delays = args.medium_toggle;
let topology_structure = if args.medium_toggle {
// Use the location of the network-requester
let address = config
.core
.socks5
.provider_mix_address
.parse()
.expect("failed to parse provider mix address");
TopologyStructure::GeoAware(GroupBy::NymAddress(address))
} else if let Some(code) = args.geo_routing {
TopologyStructure::GeoAware(GroupBy::CountryGroup(code))
let topology_structure = if args.medium_toggle || args.geo_routing.is_some() {
// TODO: rethink the default group. I just picked one for now.
TopologyStructure::GeoAware(args.geo_routing.unwrap_or(CountryGroup::Europe))
} else {
TopologyStructure::default()
};
+1 -15
View File
@@ -2503,7 +2503,6 @@ version = "1.1.15"
dependencies = [
"async-trait",
"base64 0.21.2",
"cfg-if 1.0.0",
"dashmap",
"dirs 4.0.0",
"futures",
@@ -2514,7 +2513,7 @@ dependencies = [
"nym-config",
"nym-credential-storage",
"nym-crypto",
"nym-explorer-client",
"nym-explorer-api-requests",
"nym-gateway-client",
"nym-gateway-requests",
"nym-network-defaults",
@@ -2677,7 +2676,6 @@ version = "0.1.0"
dependencies = [
"bls12_381 0.5.0",
"cosmrs",
"log",
"nym-api-requests",
"nym-coconut-interface",
"nym-crypto",
@@ -2752,18 +2750,6 @@ dependencies = [
"serde",
]
[[package]]
name = "nym-explorer-client"
version = "0.1.0"
dependencies = [
"log",
"nym-explorer-api-requests",
"reqwest",
"serde",
"thiserror",
"url",
]
[[package]]
name = "nym-gateway-client"
version = "0.1.0"
+2 -2
View File
@@ -114,7 +114,7 @@ impl WasmTopologyExt for Arc<ClientState> {
let this = Arc::clone(self);
future_to_promise(async move {
let Some(current_topology) = this.topology_accessor.current_topology().await else {
return Err(WasmClientError::UnavailableNetworkTopology.into());
return Err(WasmClientError::UnavailableNetworkTopology.into())
};
match current_topology.find_mix_by_identity(&mixnode_identity) {
@@ -135,7 +135,7 @@ impl WasmTopologyExt for Arc<ClientState> {
let this = Arc::clone(self);
future_to_promise(async move {
let Some(current_topology) = this.topology_accessor.current_topology().await else {
return Err(WasmClientError::UnavailableNetworkTopology.into());
return Err(WasmClientError::UnavailableNetworkTopology.into())
};
let Some(mix) = current_topology.find_mix_by_identity(&mixnode_identity) else {
@@ -52,7 +52,7 @@ pub fn encode_payload_with_headers(
Ok(metadata) => {
let metadata = metadata.as_bytes().to_vec();
let size = (metadata.len() as u64).to_be_bytes().to_vec();
Ok([size, metadata, payload].concat())
Ok(vec![size, metadata, payload].concat())
}
Err(e) => Err(JsValue::from(JsError::new(
format!("Could not encode message: {}", e).as_str(),
-4
View File
@@ -1,10 +1,6 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// After reading https://github.com/rust-lang/rust-clippy/issues/11382
// I suspect we *maybe* have hit a false positive, but I'm not sure.
#![allow(clippy::arc_with_non_send_sync)]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
@@ -58,7 +58,7 @@ impl<'a> EphemeralTestReceiver<'a> {
let Some(received_packet) = packet else {
// can't do anything more...
console_error!("packet receiver has stopped processing results!");
return true;
return true
};
match received_packet {
Received::Message(msg) => {
+1 -2
View File
@@ -10,7 +10,6 @@ rust-version = "1.66"
[dependencies]
async-trait = { workspace = true }
base64 = "0.21.2"
cfg-if = "1.0.0"
dashmap = "5.4.0"
dirs = "4.0"
futures = { workspace = true }
@@ -33,7 +32,7 @@ zeroize = { workspace = true }
nym-bandwidth-controller = { path = "../bandwidth-controller" }
nym-config = { path = "../config" }
nym-crypto = { path = "../crypto" }
nym-explorer-client = { path = "../../explorer-api/explorer-client" }
nym-explorer-api-requests = { path = "../../explorer-api/explorer-api-requests" }
nym-gateway-client = { path = "../client-libs/gateway-client" }
#gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm", "coconut"] }
nym-gateway-requests = { path = "../../gateway/gateway-requests" }
@@ -46,6 +46,7 @@ use nym_task::{TaskClient, TaskManager};
use nym_topology::provider_trait::TopologyProvider;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::sync::Arc;
use tap::TapFallible;
use url::Url;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
@@ -328,18 +329,13 @@ where
)
};
let gateway_id = gateway_client.gateway_identity();
gateway_client.set_disabled_credentials_mode(config.client.disabled_credentials_mode);
let shared_key = gateway_client
.authenticate_and_start()
.await
.map_err(|err| {
log::error!("Could not authenticate and start up the gateway connection - {err}");
ClientCoreError::GatewayClientError {
gateway_id: gateway_id.to_base58_string(),
source: err,
}
.tap_err(|err| {
log::error!("Could not authenticate and start up the gateway connection - {err}")
})?;
managed_keys.ensure_gateway_key(shared_key);
@@ -358,13 +354,11 @@ where
nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(),
)),
config::TopologyStructure::GeoAware(group_by) => {
Box::new(GeoAwareTopologyProvider::new(
nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(),
group_by,
))
}
config::TopologyStructure::GeoAware(group) => Box::new(GeoAwareTopologyProvider::new(
nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(),
group,
)),
})
}
@@ -224,6 +224,6 @@ impl RealMessagesController<OsRng> {
debug!("The reply controller has finished execution!");
});
// ack_control.start_with_shutdown(shutdown, packet_type);
ack_control.start_with_shutdown(shutdown, packet_type);
}
}
@@ -1,8 +1,7 @@
use std::{collections::HashMap, fmt};
use log::{debug, error, info};
use nym_explorer_client::{ExplorerClient, PrettyDetailedMixNodeBond};
use nym_network_defaults::var_names::EXPLORER_API;
use nym_explorer_api_requests::PrettyDetailedMixNodeBond;
use nym_topology::{
nym_topology_from_detailed,
provider_trait::{async_trait, TopologyProvider},
@@ -11,31 +10,19 @@ use nym_topology::{
use nym_validator_client::client::MixId;
use rand::{prelude::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
use tap::TapOptional;
use url::Url;
use crate::config::GroupBy;
const MIN_NODES_PER_LAYER: usize = 1;
const EXPLORER_API_MIXNODES_URL: &str = "https://explorer.nymtech.net/api/v1/mix-nodes";
fn create_explorer_client() -> Option<ExplorerClient> {
let Ok(explorer_api_url) = std::env::var(EXPLORER_API) else {
error!("Missing EXPLORER_API");
return None;
};
let Ok(explorer_api_url) = explorer_api_url.parse() else {
error!("Failed to parse EXPLORER_API");
return None;
};
log::debug!("Using explorer-api url: {}", explorer_api_url);
let Ok(client) = nym_explorer_client::ExplorerClient::new(explorer_api_url) else {
error!("Failed to create explorer-api client");
return None;
};
Some(client)
// TODO: create a explorer-api-client
async fn fetch_mixnodes_from_explorer_api() -> Option<Vec<PrettyDetailedMixNodeBond>> {
reqwest::get(EXPLORER_API_MIXNODES_URL)
.await
.ok()?
.json::<Vec<PrettyDetailedMixNodeBond>>()
.await
.ok()
}
#[derive(Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Debug)]
@@ -52,8 +39,6 @@ pub enum CountryGroup {
impl CountryGroup {
// We map contry codes into group, which initially are continent codes to a first approximation,
// but we do it manually to reserve the right to tweak this distribution for our purposes.
// NOTE: I did this quickly and it's not a complete list of all countries, but only those that
// were present in the network at the time. Please add more as needed.
fn new(country_code: &str) -> Self {
let country_code = country_code.to_uppercase();
use CountryGroup::*;
@@ -237,7 +222,7 @@ fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> {
pub struct GeoAwareTopologyProvider {
validator_client: nym_validator_client::client::NymApiClient,
filter_on: GroupBy,
filter_on: CountryGroup,
client_version: String,
}
@@ -245,10 +230,10 @@ impl GeoAwareTopologyProvider {
pub fn new(
mut nym_api_urls: Vec<Url>,
client_version: String,
filter_on: GroupBy,
filter_on: CountryGroup,
) -> GeoAwareTopologyProvider {
log::info!(
"Creating geo-aware topology provider with filter on {}",
"Creating geo-aware topology provider with filter on {:?}",
filter_on
);
nym_api_urls.shuffle(&mut thread_rng());
@@ -282,44 +267,11 @@ impl GeoAwareTopologyProvider {
// Also fetch mixnodes cached by explorer-api, with the purpose of getting their
// geolocation.
debug!("Fetching mixnodes from explorer-api...");
let explorer_client = create_explorer_client()?;
let Ok(mixnodes_from_explorer_api) = explorer_client.get_mixnodes().await else {
let Some(mixnodes_from_explorer_api) = fetch_mixnodes_from_explorer_api().await else {
error!("failed to get mixnodes from explorer-api");
return None;
};
debug!("Fetching gateways from explorer-api...");
let Ok(gateways_from_explorer_api) = explorer_client.get_gateways().await else {
error!("failed to get mixnodes from explorer-api");
return None;
};
// Determine what we should filter around
let filter_on = match self.filter_on {
GroupBy::CountryGroup(group) => group,
GroupBy::NymAddress(recipient) => {
// Convert recipient into a country group by extracting out the gateway part and
// using that as the country code.
let gateway = recipient.gateway().to_base58_string();
// Lookup the location of this gateway by using the location data from the
// explorer-api
let gateway_location = gateways_from_explorer_api
.iter()
.find(|g| g.gateway.identity_key == gateway)
.and_then(|g| g.location.clone())
.map(|location| location.two_letter_iso_country_code)
.tap_none(|| error!("No location found for the gateway: {}", gateway))?;
debug!(
"Filtering on nym-address: {}, with location: {}",
recipient, gateway_location
);
CountryGroup::new(&gateway_location)
}
};
debug!("Filter group: {}", filter_on);
// Partition mixnodes_from_explorer_api according to the value of
// two_letter_iso_country_code.
// NOTE: we construct the full distribution here, but only use the one we're interested in.
@@ -328,8 +280,8 @@ impl GeoAwareTopologyProvider {
let mixnode_distribution = group_mixnodes_by_country_code(mixnodes_from_explorer_api);
log_mixnode_distribution(&mixnode_distribution);
let Some(filtered_mixnode_ids) = mixnode_distribution.get(&filter_on) else {
error!("no mixnodes found for: {}", filter_on);
let Some(filtered_mixnode_ids) = mixnode_distribution.get(&self.filter_on) else {
error!("no mixnodes found for: {}", self.filter_on);
return None;
};
+2 -22
View File
@@ -3,10 +3,7 @@
use nym_config::defaults::NymNetworkDetails;
use nym_crypto::asymmetric::identity;
use nym_sphinx::{
addressing::clients::Recipient,
params::{PacketSize, PacketType},
};
use nym_sphinx::params::{PacketSize, PacketType};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use url::Url;
@@ -483,28 +480,11 @@ pub struct Topology {
pub topology_structure: TopologyStructure,
}
#[allow(clippy::large_enum_variant)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TopologyStructure {
#[default]
NymApi,
GeoAware(GroupBy),
}
#[allow(clippy::large_enum_variant)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum GroupBy {
CountryGroup(CountryGroup),
NymAddress(Recipient),
}
impl std::fmt::Display for GroupBy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GroupBy::CountryGroup(group) => write!(f, "group: {}", group),
GroupBy::NymAddress(address) => write!(f, "address: {}", address),
}
}
GeoAware(CountryGroup),
}
impl Default for Topology {
+2 -5
View File
@@ -13,11 +13,8 @@ pub enum ClientCoreError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("Gateway client error ({gateway_id}): {source}")]
GatewayClientError {
gateway_id: String,
source: GatewayClientError,
},
#[error("Gateway client error: {0}")]
GatewayClientError(#[from] GatewayClientError),
#[error("Ed25519 error: {0}")]
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
+7 -18
View File
@@ -11,6 +11,7 @@ use nym_gateway_client::GatewayClient;
use nym_topology::{filter::VersionFilterable, gateway};
use rand::{seq::SliceRandom, Rng};
use std::{sync::Arc, time::Duration};
use tap::TapFallible;
use tungstenite::Message;
use url::Url;
@@ -148,7 +149,7 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
Ok(GatewayWithLatency::new(gateway, avg))
}
pub async fn choose_gateway_by_latency<R: Rng>(
pub(super) async fn choose_gateway_by_latency<R: Rng>(
rng: &mut R,
gateways: &[gateway::Node],
) -> Result<gateway::Node, ClientCoreError> {
@@ -208,26 +209,14 @@ pub(super) async fn register_with_gateway(
our_identity.clone(),
timeout,
);
gateway_client.establish_connection().await.map_err(|err| {
log::warn!("Failed to establish connection with gateway!");
ClientCoreError::GatewayClientError {
gateway_id: gateway.gateway_id.clone(),
source: err,
}
})?;
gateway_client
.establish_connection()
.await
.tap_err(|_| log::warn!("Failed to establish connection with gateway!"))?;
let shared_keys = gateway_client
.perform_initial_authentication()
.await
.map_err(|err| {
log::warn!(
"Failed to register with the gateway {}!",
gateway.gateway_id
);
ClientCoreError::GatewayClientError {
gateway_id: gateway.gateway_id.clone(),
source: err,
}
})?;
.tap_err(|_| log::warn!("Failed to register with the gateway!"))?;
Ok(RegistrationResult {
shared_keys,
authenticated_ephemeral_client: Some(gateway_client),
@@ -102,12 +102,12 @@ impl PacketRouter {
}
}
// if !received_acks.is_empty() {
// trace!("routing acks");
// if let Err(err) = self.ack_sender.unbounded_send(received_acks) {
// error!("failed to send ack: {err}");
// };
// }
if !received_acks.is_empty() {
trace!("routing acks");
if let Err(err) = self.ack_sender.unbounded_send(received_acks) {
error!("failed to send ack: {err}");
};
}
Ok(())
}
}
@@ -65,7 +65,7 @@ features = ["tokio"]
[dev-dependencies]
bip39 = { workspace = true }
cosmrs = { workspace = true, features = ["bip32"] }
ts-rs = { workspace = true }
ts-rs = "6.1.2"
[[example]]
name = "offline_signing"
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use cosmrs::bank::MsgSend;
use cosmrs::rpc::HttpClient;
use cosmrs::tx::Msg;
use cosmrs::{tx, AccountId, Coin, Denom};
use nym_validator_client::http_client;
use nym_validator_client::nyxd::CosmWasmClient;
use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet;
use nym_validator_client::signing::tx_signer::TxSigner;
@@ -27,7 +27,7 @@ async fn main() {
// possibly remote client that doesn't do ANY signing
// (only broadcasts + queries for sequence numbers)
let broadcaster = http_client(validator).unwrap();
let broadcaster = HttpClient::new(validator).unwrap();
// get signer information
let sequence_response = broadcaster.get_sequence(&signer_address).await.unwrap();
@@ -9,7 +9,7 @@ use nym_validator_client::nyxd::contract_traits::{
#[tokio::main]
async fn main() {
setup_env(Some("../../../envs/qa-qwerty.env"));
setup_env(Some(&"../../../envs/qa-qwerty.env".parse().unwrap()));
let network_details = NymNetworkDetails::new_from_env();
let config =
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
@@ -9,7 +9,7 @@ use nym_validator_client::nyxd::contract_traits::{
#[tokio::main]
async fn main() {
setup_env(Some("../../../envs/qa-qwerty.env"));
setup_env(Some(&"../../../envs/qa-qwerty.env".parse().unwrap()));
let network_details = NymNetworkDetails::new_from_env();
let config =
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
@@ -26,8 +26,6 @@ pub use nym_mixnet_contract_common::{
// re-export the type to not break existing imports
pub use crate::coconut::CoconutApiClient;
#[cfg(feature = "http-client")]
use crate::rpc::http_client;
#[cfg(feature = "http-client")]
use crate::{DirectSigningHttpRpcValidatorClient, HttpRpcClient, QueryHttpRpcValidatorClient};
@@ -97,7 +95,7 @@ impl Client<HttpRpcClient, DirectSecp256k1HdWallet> {
config: Config,
mnemonic: bip39::Mnemonic,
) -> Result<DirectSigningHttpRpcValidatorClient, ValidatorClientError> {
let rpc_client = http_client(config.nyxd_url.as_str())?;
let rpc_client = HttpRpcClient::new(config.nyxd_url.as_str())?;
let prefix = &config.nyxd_config.chain_details.bech32_account_prefix;
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
@@ -128,7 +126,7 @@ impl Client<ReqwestRpcClient, DirectSecp256k1HdWallet> {
#[cfg(feature = "http-client")]
impl Client<HttpRpcClient> {
pub fn new_query(config: Config) -> Result<QueryHttpRpcValidatorClient, ValidatorClientError> {
let rpc_client = http_client(config.nyxd_url.as_str())?;
let rpc_client = HttpRpcClient::new(config.nyxd_url.as_str())?;
Ok(Self::new_with_rpc_client(config, rpc_client))
}
@@ -172,10 +170,6 @@ impl<C, S> Client<C, S> {
// validator-api wrappers
impl<C, S> Client<C, S> {
pub fn api_url(&self) -> &Url {
self.nym_api.current_url()
}
pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_url(new_endpoint)
}
@@ -246,10 +240,6 @@ impl NymApiClient {
NymApiClient { nym_api }
}
pub fn api_url(&self) -> &Url {
self.nym_api.current_url()
}
pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_url(new_endpoint);
}
@@ -20,8 +20,6 @@ pub use nym_api_requests::*;
#[cfg(feature = "http-client")]
pub use cosmrs::rpc::HttpClient as HttpRpcClient;
#[cfg(feature = "http-client")]
pub use rpc::http_client;
// some type aliasing
@@ -3,25 +3,18 @@
use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient};
use crate::nyxd::error::NyxdError;
use crate::nyxd::{Config, GasPrice, Hash, Height};
use crate::nyxd::{Config, GasPrice};
use crate::rpc::TendermintRpcClient;
use crate::signing::{
signer::{NoSigner, OfflineSigner},
AccountData,
};
use async_trait::async_trait;
use cosmrs::tendermint::{abci, evidence::Evidence, Genesis};
use cosmrs::tx::{Raw, SignDoc};
use serde::{de::DeserializeOwned, Serialize};
use std::fmt::Debug;
use tendermint_rpc::endpoint::*;
use tendermint_rpc::query::Query;
use tendermint_rpc::{Error as TendermintRpcError, Order, Paging, SimpleRequest};
use tendermint_rpc::{Error as TendermintRpcError, SimpleRequest};
#[cfg(feature = "http-client")]
use crate::http_client;
#[cfg(feature = "http-client")]
use cosmrs::rpc::{HttpClient, HttpClientUrl};
use cosmrs::tx::{Raw, SignDoc};
pub mod client_traits;
mod helpers;
@@ -80,7 +73,7 @@ impl<S> MaybeSigningClient<HttpClient, S> {
where
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
{
self.client = http_client(new_endpoint)?;
self.client = HttpClient::new(new_endpoint)?;
Ok(())
}
}
@@ -92,216 +85,6 @@ where
C: TendermintRpcClient + Send + Sync,
S: Send + Sync,
{
async fn abci_info(&self) -> Result<abci::response::Info, TendermintRpcError> {
self.client.abci_info().await
}
async fn abci_query<V>(
&self,
path: Option<String>,
data: V,
height: Option<Height>,
prove: bool,
) -> Result<abci_query::AbciQuery, TendermintRpcError>
where
V: Into<Vec<u8>> + Send,
{
self.client.abci_query(path, data, height, prove).await
}
async fn block<H>(&self, height: H) -> Result<block::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.block(height).await
}
async fn block_by_hash(
&self,
hash: Hash,
) -> Result<block_by_hash::Response, TendermintRpcError> {
self.client.block_by_hash(hash).await
}
async fn latest_block(&self) -> Result<block::Response, TendermintRpcError> {
self.client.latest_block().await
}
async fn header<H>(&self, height: H) -> Result<header::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.header(height).await
}
async fn header_by_hash(
&self,
hash: Hash,
) -> Result<header_by_hash::Response, TendermintRpcError> {
self.client.header_by_hash(hash).await
}
async fn block_results<H>(
&self,
height: H,
) -> Result<block_results::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.block_results(height).await
}
async fn latest_block_results(&self) -> Result<block_results::Response, TendermintRpcError> {
self.client.latest_block_results().await
}
async fn block_search(
&self,
query: Query,
page: u32,
per_page: u8,
order: Order,
) -> Result<block_search::Response, TendermintRpcError> {
self.client.block_search(query, page, per_page, order).await
}
async fn blockchain<H>(
&self,
min: H,
max: H,
) -> Result<blockchain::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.blockchain(min, max).await
}
async fn broadcast_tx_async<T>(
&self,
tx: T,
) -> Result<broadcast::tx_async::Response, TendermintRpcError>
where
T: Into<Vec<u8>> + Send,
{
self.client.broadcast_tx_async(tx).await
}
async fn broadcast_tx_sync<T>(
&self,
tx: T,
) -> Result<broadcast::tx_sync::Response, TendermintRpcError>
where
T: Into<Vec<u8>> + Send,
{
self.client.broadcast_tx_sync(tx).await
}
async fn broadcast_tx_commit<T>(
&self,
tx: T,
) -> Result<broadcast::tx_commit::Response, TendermintRpcError>
where
T: Into<Vec<u8>> + Send,
{
self.client.broadcast_tx_commit(tx).await
}
async fn commit<H>(&self, height: H) -> Result<commit::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.commit(height).await
}
async fn consensus_params<H>(
&self,
height: H,
) -> Result<consensus_params::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.consensus_params(height).await
}
async fn consensus_state(&self) -> Result<consensus_state::Response, TendermintRpcError> {
self.client.consensus_state().await
}
async fn validators<H>(
&self,
height: H,
paging: Paging,
) -> Result<validators::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.validators(height, paging).await
}
async fn latest_consensus_params(
&self,
) -> Result<consensus_params::Response, TendermintRpcError> {
self.client.latest_consensus_params().await
}
async fn latest_commit(&self) -> Result<commit::Response, TendermintRpcError> {
self.client.latest_commit().await
}
async fn health(&self) -> Result<(), TendermintRpcError> {
self.client.health().await
}
async fn genesis<AppState>(&self) -> Result<Genesis<AppState>, TendermintRpcError>
where
AppState: Debug + Serialize + DeserializeOwned + Send,
{
self.client.genesis().await
}
async fn net_info(&self) -> Result<net_info::Response, TendermintRpcError> {
self.client.net_info().await
}
async fn status(&self) -> Result<status::Response, TendermintRpcError> {
self.client.status().await
}
async fn broadcast_evidence(
&self,
e: Evidence,
) -> Result<evidence::Response, TendermintRpcError> {
self.client.broadcast_evidence(e).await
}
async fn tx(&self, hash: Hash, prove: bool) -> Result<tx::Response, TendermintRpcError> {
self.client.tx(hash, prove).await
}
async fn tx_search(
&self,
query: Query,
prove: bool,
page: u32,
per_page: u8,
order: Order,
) -> Result<tx_search::Response, TendermintRpcError> {
self.client
.tx_search(query, prove, page, per_page, order)
.await
}
#[cfg(any(
feature = "tendermint-rpc/http-client",
feature = "tendermint-rpc/websocket-client"
))]
async fn wait_until_healthy<T>(&self, timeout: T) -> Result<(), Error>
where
T: Into<core::time::Duration> + Send,
{
self.client.wait_until_healthy(timeout).await
}
async fn perform<R>(&self, request: R) -> Result<R::Output, TendermintRpcError>
where
R: SimpleRequest,
@@ -9,25 +9,19 @@ use crate::nyxd::cosmwasm_client::types::{
use crate::nyxd::cosmwasm_client::MaybeSigningClient;
use crate::nyxd::error::NyxdError;
use crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER;
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
use crate::signing::signer::NoSigner;
use crate::signing::signer::OfflineSigner;
use crate::signing::tx_signer::TxSigner;
use crate::signing::AccountData;
use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient, ReqwestRpcClient};
use async_trait::async_trait;
use cosmrs::cosmwasm;
use cosmrs::tendermint::{abci, evidence::Evidence, Genesis};
use cosmrs::tx::{Msg, Raw, SignDoc};
use cosmwasm_std::Addr;
use nym_network_defaults::{ChainDetails, NymNetworkDetails};
use serde::{de::DeserializeOwned, Serialize};
use std::fmt::Debug;
use serde::Serialize;
use std::time::SystemTime;
use tendermint_rpc::endpoint::block::Response as BlockResponse;
use tendermint_rpc::endpoint::*;
use tendermint_rpc::{Error as TendermintRpcError, Order};
use url::Url;
use tendermint_rpc::Error as TendermintRpcError;
pub use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient};
pub use crate::nyxd::fee::Fee;
@@ -51,13 +45,14 @@ pub use tendermint_rpc::{
};
pub use tendermint_rpc::{Request, Response, SimpleRequest};
#[cfg(feature = "http-client")]
use crate::http_client;
// #[cfg(feature = "http-client")]
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
#[cfg(feature = "http-client")]
use crate::{DirectSigningHttpRpcNyxdClient, QueryHttpRpcNyxdClient};
use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient, ReqwestRpcClient};
#[cfg(feature = "http-client")]
use cosmrs::rpc::{HttpClient, HttpClientUrl};
use tendermint_rpc::query::Query;
use url::Url;
pub mod coin;
pub mod contract_traits;
@@ -102,7 +97,7 @@ impl NyxdClient<HttpClient> {
where
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
{
let client = http_client(endpoint)?;
let client = HttpClient::new(endpoint)?;
Ok(NyxdClient {
client: MaybeSigningClient::new(client, (&config).into()),
@@ -145,7 +140,7 @@ impl NyxdClient<HttpClient, DirectSecp256k1HdWallet> {
where
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
{
let client = http_client(endpoint)?;
let client = HttpClient::new(endpoint)?;
let prefix = &config.chain_details.bech32_account_prefix;
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
@@ -573,216 +568,6 @@ where
C: TendermintRpcClient + Send + Sync,
S: Send + Sync,
{
async fn abci_info(&self) -> Result<abci::response::Info, TendermintRpcError> {
self.client.abci_info().await
}
async fn abci_query<V>(
&self,
path: Option<String>,
data: V,
height: Option<Height>,
prove: bool,
) -> Result<abci_query::AbciQuery, TendermintRpcError>
where
V: Into<Vec<u8>> + Send,
{
self.client.abci_query(path, data, height, prove).await
}
async fn block<H>(&self, height: H) -> Result<block::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.block(height).await
}
async fn block_by_hash(
&self,
hash: Hash,
) -> Result<block_by_hash::Response, TendermintRpcError> {
self.client.block_by_hash(hash).await
}
async fn latest_block(&self) -> Result<block::Response, TendermintRpcError> {
self.client.latest_block().await
}
async fn header<H>(&self, height: H) -> Result<header::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.header(height).await
}
async fn header_by_hash(
&self,
hash: Hash,
) -> Result<header_by_hash::Response, TendermintRpcError> {
self.client.header_by_hash(hash).await
}
async fn block_results<H>(
&self,
height: H,
) -> Result<block_results::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.block_results(height).await
}
async fn latest_block_results(&self) -> Result<block_results::Response, TendermintRpcError> {
self.client.latest_block_results().await
}
async fn block_search(
&self,
query: Query,
page: u32,
per_page: u8,
order: Order,
) -> Result<block_search::Response, TendermintRpcError> {
self.client.block_search(query, page, per_page, order).await
}
async fn blockchain<H>(
&self,
min: H,
max: H,
) -> Result<blockchain::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.blockchain(min, max).await
}
async fn broadcast_tx_async<T>(
&self,
tx: T,
) -> Result<broadcast::tx_async::Response, TendermintRpcError>
where
T: Into<Vec<u8>> + Send,
{
TendermintRpcClient::broadcast_tx_async(&self.client, tx).await
}
async fn broadcast_tx_sync<T>(
&self,
tx: T,
) -> Result<broadcast::tx_sync::Response, TendermintRpcError>
where
T: Into<Vec<u8>> + Send,
{
TendermintRpcClient::broadcast_tx_sync(&self.client, tx).await
}
async fn broadcast_tx_commit<T>(
&self,
tx: T,
) -> Result<broadcast::tx_commit::Response, TendermintRpcError>
where
T: Into<Vec<u8>> + Send,
{
TendermintRpcClient::broadcast_tx_commit(&self.client, tx).await
}
async fn commit<H>(&self, height: H) -> Result<commit::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.commit(height).await
}
async fn consensus_params<H>(
&self,
height: H,
) -> Result<consensus_params::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.consensus_params(height).await
}
async fn consensus_state(&self) -> Result<consensus_state::Response, TendermintRpcError> {
self.client.consensus_state().await
}
async fn validators<H>(
&self,
height: H,
paging: Paging,
) -> Result<validators::Response, TendermintRpcError>
where
H: Into<Height> + Send,
{
self.client.validators(height, paging).await
}
async fn latest_consensus_params(
&self,
) -> Result<consensus_params::Response, TendermintRpcError> {
self.client.latest_consensus_params().await
}
async fn latest_commit(&self) -> Result<commit::Response, TendermintRpcError> {
self.client.latest_commit().await
}
async fn health(&self) -> Result<(), TendermintRpcError> {
self.client.health().await
}
async fn genesis<AppState>(&self) -> Result<Genesis<AppState>, TendermintRpcError>
where
AppState: Debug + Serialize + DeserializeOwned + Send,
{
self.client.genesis().await
}
async fn net_info(&self) -> Result<net_info::Response, TendermintRpcError> {
self.client.net_info().await
}
async fn status(&self) -> Result<status::Response, TendermintRpcError> {
self.client.status().await
}
async fn broadcast_evidence(
&self,
e: Evidence,
) -> Result<evidence::Response, TendermintRpcError> {
self.client.broadcast_evidence(e).await
}
async fn tx(&self, hash: Hash, prove: bool) -> Result<TxResponse, TendermintRpcError> {
self.client.tx(hash, prove).await
}
async fn tx_search(
&self,
query: Query,
prove: bool,
page: u32,
per_page: u8,
order: Order,
) -> Result<tx_search::Response, TendermintRpcError> {
self.client
.tx_search(query, prove, page, per_page, order)
.await
}
#[cfg(any(
feature = "tendermint-rpc/http-client",
feature = "tendermint-rpc/websocket-client"
))]
async fn wait_until_healthy<T>(&self, timeout: T) -> Result<(), Error>
where
T: Into<core::time::Duration> + Send,
{
self.client.wait_until_healthy(timeout).await
}
async fn perform<R>(&self, request: R) -> Result<R::Output, TendermintRpcError>
where
R: SimpleRequest,
@@ -11,27 +11,8 @@ use tendermint_rpc::{
Error, Order, Paging, SimpleRequest,
};
#[cfg(feature = "http-client")]
use crate::error::TendermintRpcError;
#[cfg(feature = "http-client")]
use crate::HttpRpcClient;
#[cfg(feature = "http-client")]
use tendermint_rpc::client::CompatMode;
#[cfg(feature = "http-client")]
use tendermint_rpc::HttpClientUrl;
pub mod reqwest;
#[cfg(feature = "http-client")]
pub fn http_client<U>(url: U) -> Result<HttpRpcClient, TendermintRpcError>
where
U: TryInto<HttpClientUrl, Error = Error>,
{
HttpRpcClient::builder(url.try_into()?)
.compat_mode(CompatMode::V0_34)
.build()
}
// we have to create a sealed trait since `TendermintClient` needs T: Send (due to how async trait is created)
// which we can't do in wasm
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -3,32 +3,12 @@
use crate::rpc::TendermintRpcClient;
use async_trait::async_trait;
use cosmrs::tendermint::{block::Height, evidence::Evidence, Hash};
use reqwest::header::HeaderMap;
use reqwest::{header, RequestBuilder};
use tendermint_rpc::{
client::CompatMode,
dialect::{self, Dialect},
endpoint::{self, *},
query::Query,
Error, Order, Response, SimpleRequest,
};
use tendermint_rpc::{Error, Response, SimpleRequest};
use url::Url;
// copied macro from tendermint-rpc crate because that's exactly what we have to do here too
macro_rules! perform_with_compat {
($self:expr, $request:expr) => {{
let request = $request;
match $self.compat {
CompatMode::V0_37 => $self.perform_v0_37(request).await,
CompatMode::V0_34 => $self.perform_v0_34(request).await,
}
}};
}
pub struct ReqwestRpcClient {
compat: CompatMode,
inner: reqwest::Client,
url: Url,
}
@@ -36,21 +16,12 @@ pub struct ReqwestRpcClient {
impl ReqwestRpcClient {
pub fn new(url: Url) -> Self {
ReqwestRpcClient {
compat: CompatMode::V0_34,
inner: reqwest::Client::new(),
url,
}
}
pub fn set_compat_mode(&mut self, compat: CompatMode) {
self.compat = compat;
}
fn build_request<R, S>(&self, request: R) -> RequestBuilder
where
R: SimpleRequest<S>,
S: Dialect,
{
fn build_request<R: SimpleRequest>(&self, request: R) -> RequestBuilder {
let mut headers = HeaderMap::new();
headers.insert(header::CONTENT_TYPE, "application/json".parse().unwrap());
headers.insert(
@@ -68,38 +39,6 @@ impl ReqwestRpcClient {
.body(request.into_json().into_bytes())
.headers(headers)
}
async fn perform_request<R, S>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest<S>,
S: Dialect,
{
let request = self.build_request(request);
// that's extremely unfortunate. the trait requires returning tendermint rpc error so we have to make best effort error mapping
let response = request
.send()
.await
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
let bytes = response
.bytes()
.await
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
R::Response::from_string(bytes).map(Into::into)
}
async fn perform_v0_34<R>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest<dialect::v0_34::Dialect>,
{
self.perform_request(request).await
}
async fn perform_v0_37<R>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest<dialect::v0_37::Dialect>,
{
self.perform_request(request).await
}
}
trait TendermintRpcErrorMap {
@@ -119,81 +58,17 @@ impl TendermintRpcClient for ReqwestRpcClient {
where
R: SimpleRequest,
{
self.perform_request(request).await
}
async fn block_results<H>(&self, height: H) -> Result<block_results::Response, Error>
where
H: Into<Height> + Send,
{
perform_with_compat!(self, block_results::Request::new(height.into()))
}
async fn latest_block_results(&self) -> Result<block_results::Response, Error> {
perform_with_compat!(self, block_results::Request::default())
}
async fn header<H>(&self, height: H) -> Result<endpoint::header::Response, Error>
where
H: Into<Height> + Send,
{
let height = height.into();
match self.compat {
CompatMode::V0_37 => self.perform(endpoint::header::Request::new(height)).await,
CompatMode::V0_34 => {
// Back-fill with a request to /block endpoint and
// taking just the header from the response.
let resp = self.perform_v0_34(block::Request::new(height)).await?;
Ok(resp.into())
}
}
}
async fn header_by_hash(&self, hash: Hash) -> Result<header_by_hash::Response, Error> {
match self.compat {
CompatMode::V0_37 => self.perform(header_by_hash::Request::new(hash)).await,
CompatMode::V0_34 => {
// Back-fill with a request to /block_by_hash endpoint and
// taking just the header from the response.
let resp = self
.perform_v0_34(block_by_hash::Request::new(hash))
.await?;
Ok(resp.into())
}
}
}
/// `/broadcast_evidence`: broadcast an evidence.
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
match self.compat {
CompatMode::V0_37 => self.perform(evidence::Request::new(e)).await,
CompatMode::V0_34 => self.perform_v0_34(evidence::Request::new(e)).await,
}
}
async fn tx(&self, hash: Hash, prove: bool) -> Result<tx::Response, Error> {
perform_with_compat!(self, tx::Request::new(hash, prove))
}
async fn tx_search(
&self,
query: Query,
prove: bool,
page: u32,
per_page: u8,
order: Order,
) -> Result<tx_search::Response, Error> {
perform_with_compat!(
self,
tx_search::Request::new(query, prove, page, per_page, order)
)
}
async fn broadcast_tx_commit<T>(&self, tx: T) -> Result<broadcast::tx_commit::Response, Error>
where
T: Into<Vec<u8>> + Send,
{
perform_with_compat!(self, broadcast::tx_commit::Request::new(tx))
let request = self.build_request(request);
// that's extremely unfortunate. the trait requires returning tendermint rpc error so we have to make best effort error mapping
let response = request
.send()
.await
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
let bytes = response
.bytes()
.await
.map_err(TendermintRpcErrorMap::into_rpc_err)?;
R::Response::from_string(bytes).map(Into::into)
}
}
@@ -199,14 +199,16 @@ mod tests {
#[test]
fn generating_account_addresses() {
// test vectors produced from our js wallet
let mnemonics = ["crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove",
let mnemonics = vec![
"crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove",
"acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel",
"step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball"];
"step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball"
];
let prefix = NymNetworkDetails::new_mainnet()
.chain_details
.bech32_account_prefix;
let addrs = [
let addrs = vec![
"n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf",
"n1h5hgn94nsq4kh99rjj794hr5h5q6yfm2lr52es",
"n17n9flp6jflljg6fp05dsy07wcprf2uuu8g40rf",
+2 -11
View File
@@ -8,14 +8,7 @@ use serde::{Deserialize, Serialize};
use error::CoconutInterfaceError;
// We list these explicity instead of glob export due to shadowing warnings with the pub tests
// module.
pub use nym_coconut::{
aggregate_signature_shares, aggregate_verification_keys, blind_sign, hash_to_scalar,
prepare_blind_sign, prove_bandwidth_credential, Attribute, Base58, BlindSignRequest,
BlindedSignature, Bytable, CoconutError, KeyPair, Parameters, PrivateAttribute,
PublicAttribute, Signature, SignatureShare, Theta, VerificationKey,
};
pub use nym_coconut::*;
#[derive(Debug, Serialize, Deserialize, Getters, CopyGetters, Clone, PartialEq, Eq)]
pub struct Credential {
@@ -64,7 +57,7 @@ impl Credential {
pub fn verify(&self, verification_key: &VerificationKey) -> bool {
let params = Parameters::new(self.n_params).unwrap();
let public_attributes = [
let public_attributes = vec![
self.voucher_value.to_string().as_bytes(),
self.voucher_info.as_bytes(),
]
@@ -145,8 +138,6 @@ impl Base58 for Credential {}
#[cfg(test)]
mod tests {
use nym_coconut::{prove_bandwidth_credential, Signature};
use super::*;
#[test]
-7
View File
@@ -5,7 +5,6 @@ authors.workspace = true
edition = "2021"
[dependencies]
anyhow = { workspace = true }
base64 = "0.13.0"
bip39 = { workspace = true }
bs58 = "0.4"
@@ -34,7 +33,6 @@ nym-bin-common = { path = "../../common/bin-common", features = ["output_format"
nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] }
nym-network-defaults = { path = "../network-defaults" }
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common" }
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-contract" }
nym-coconut-bandwidth-contract-common = { path = "../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
@@ -43,11 +41,6 @@ nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-co
nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" }
nym-name-service-common = { path = "../cosmwasm-smart-contracts/name-service" }
nym-sphinx = { path = "../../common/nymsphinx" }
nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" }
nym-credentials = { path = "../../common/credentials" }
nym-credential-storage = { path = "../../common/credential-storage" }
nym-credential-utils = { path = "../../common/credential-utils" }
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
nym-types = { path = "../../common/types" }
@@ -1,51 +0,0 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_credential_utils::utils;
use nym_validator_client::nyxd::Coin;
use std::path::PathBuf;
#[derive(Debug, Parser)]
pub struct Args {
/// Config file of the client that is supposed to use the credential.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// The amount of utokens the credential will hold.
#[clap(long, default_value = "0")]
pub(crate) amount: u64,
/// Path to a directory used to store recovery files for unconsumed deposits
#[clap(long)]
pub(crate) recovery_dir: PathBuf,
}
pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let denom = &client.current_chain_details().mix_denom.base;
let coin = Coin::new(args.amount as u128, denom);
let persistent_storage = initialise_persistent_storage(credentials_store).await;
utils::issue_credential(&client, coin, &persistent_storage, args.recovery_dir).await?;
Ok(())
}
+1 -17
View File
@@ -1,20 +1,4 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod issue_credentials;
pub mod recover_credentials;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Coconut {
#[clap(subcommand)]
pub command: CoconutCommands,
}
#[derive(Debug, Subcommand)]
pub enum CoconutCommands {
IssueCredentials(issue_credentials::Args),
RecoverCredentials(recover_credentials::Args),
}
// TODO: add coconut commands here
@@ -1,48 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClient;
use crate::utils::CommonConfigsWrapper;
use anyhow::bail;
use clap::Parser;
use nym_credential_storage::initialise_persistent_storage;
use nym_credential_utils::{recovery_storage, utils};
use std::path::PathBuf;
#[derive(Debug, Parser)]
pub struct Args {
/// Config file of the client that is supposed to use the credential.
#[clap(long)]
pub(crate) client_config: PathBuf,
/// Path to a directory used to store recovery files for unconsumed deposits
#[clap(long)]
pub(crate) recovery_dir: PathBuf,
}
pub async fn execute(args: Args, client: QueryClient) -> anyhow::Result<()> {
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
if let Ok(id) = loaded.try_get_id() {
println!("loaded config file for client '{id}'");
}
let Ok(credentials_store) = loaded.try_get_credentials_store() else {
bail!("the loaded config does not have a credentials store information")
};
println!(
"using credentials store at '{}'",
credentials_store.display()
);
let persistent_storage = initialise_persistent_storage(credentials_store).await;
let recovery_storage = recovery_storage::RecoveryStorage::new(args.recovery_dir)?;
let recovered =
utils::recover_credentials(&client, &recovery_storage, &persistent_storage).await?;
// TODO: denom?
println!("recovered {recovered} worth of credentials");
Ok(())
}
+1 -152
View File
@@ -1,17 +1,13 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use anyhow::{anyhow, bail};
use cosmrs::AccountId;
use cosmwasm_std::{Addr, Coin as CosmWasmCoin, Decimal};
use log::error;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_validator_client::nyxd::Coin;
use serde::{Deserialize, Serialize};
use serde::Serialize;
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::fs;
use std::path::{Path, PathBuf};
// TODO: perhaps it should be moved to some global common crate?
pub fn account_id_to_cw_addr(account_id: &AccountId) -> Addr {
@@ -79,150 +75,3 @@ impl<T> DataWrapper<T> {
DataWrapper { data }
}
}
fn find_toml_value<'a>(root: &'a toml::Value, key: &str) -> Option<&'a toml::Value> {
if let toml::Value::Table(table) = root {
for (k, v) in table {
if k == key {
return Some(v);
}
if v.is_table() {
if let Some(res) = find_toml_value(v, key) {
return Some(res);
}
}
}
}
None
}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
pub(crate) enum CommonConfigsWrapper {
// native, socks5, NR, etc. clients
NymClients(Box<ClientConfigCommonWrapper>),
// nym-api
NymApi(NymApiConfigLight),
// anything else that might get get introduced
Unknown(UnknownConfigWrapper),
}
impl CommonConfigsWrapper {
pub(crate) fn try_load<P: AsRef<Path>>(path: P) -> anyhow::Result<CommonConfigsWrapper> {
let content = fs::read_to_string(path)?;
Ok(toml::from_str(&content)?)
}
pub(crate) fn try_get_id(&self) -> anyhow::Result<&str> {
match self {
CommonConfigsWrapper::NymClients(cfg) => cfg.try_get_id(),
CommonConfigsWrapper::NymApi(cfg) => Ok(&cfg.base.id),
CommonConfigsWrapper::Unknown(cfg) => cfg.try_get_id(),
}
}
pub(crate) fn try_get_credentials_store(&self) -> anyhow::Result<PathBuf> {
match self {
CommonConfigsWrapper::NymClients(cfg) => {
Ok(cfg.storage_paths.inner.credentials_database.clone())
}
CommonConfigsWrapper::NymApi(cfg) => Ok(cfg
.network_monitor
.storage_paths
.credentials_database_path
.clone()),
CommonConfigsWrapper::Unknown(cfg) => cfg.try_get_credentials_store(),
}
}
}
// ideally we would have just imported the full nym-api config structure, but that'd have been an overkill,
// because we'd have to import the whole crate
#[derive(Deserialize, Debug)]
pub(crate) struct NymApiConfigLight {
base: NymApiConfigBaseLight,
network_monitor: NymApiConfigNetworkMonitorLight,
}
#[derive(Deserialize, Debug)]
struct NymApiConfigBaseLight {
id: String,
}
#[derive(Deserialize, Debug)]
struct NymApiConfigNetworkMonitorLight {
storage_paths: NetworkMonitorPaths,
}
#[derive(Deserialize, Debug)]
struct NetworkMonitorPaths {
credentials_database_path: PathBuf,
}
// a hacky way of reading common data from client configs (native, socks5, etc.)
// it works because all clients follow the same structure for storage paths
// (or so I thought)
#[derive(Deserialize, Debug)]
pub(crate) struct ClientConfigCommonWrapper {
storage_paths: StoragePathsWrapper,
// ... but they have different structure for `nym_client_core::config::Client`
// native client has it on the top layer, whilsts socks5 has it under 'core' table
#[serde(flatten)]
other: toml::Value,
}
// wrapper to allow for any additional entries besides the common paths, like allow list for NR
#[derive(Deserialize, Debug)]
struct StoragePathsWrapper {
#[serde(flatten)]
inner: CommonClientPaths,
}
impl ClientConfigCommonWrapper {
pub(crate) fn try_get_id(&self) -> anyhow::Result<&str> {
let id_val = find_toml_value(&self.other, "id")
.ok_or_else(|| anyhow!("no id field present in the config"))?;
if let toml::Value::String(id) = id_val {
Ok(id)
} else {
bail!("no id field present in the config")
}
}
}
#[derive(Deserialize, Debug)]
pub(crate) struct UnknownConfigWrapper {
#[serde(flatten)]
inner: toml::Value,
}
impl UnknownConfigWrapper {
fn find_value(&self, key: &str) -> Option<&toml::Value> {
find_toml_value(&self.inner, key)
}
pub(crate) fn try_get_id(&self) -> anyhow::Result<&str> {
let id_val = self
.find_value("id")
.ok_or_else(|| anyhow!("no id field present in the config"))?;
if let toml::Value::String(id) = id_val {
Ok(id)
} else {
bail!("no id field present in the config")
}
}
pub(crate) fn try_get_credentials_store(&self) -> anyhow::Result<PathBuf> {
let id_val = self
.find_value("credentials_database_path")
.ok_or_else(|| anyhow!("no 'credentials_database_path' field present in the config"))?;
if let toml::Value::String(credentials_store) = id_val {
Ok(credentials_store.parse()?)
} else {
bail!("no 'credentials_database_path' field present in the config")
}
}
}
@@ -26,7 +26,7 @@ humantime-serde = "1.1.1"
# TO CHECK WHETHER STILL NEEDED:
log = { workspace = true }
time = { version = "0.3.6", features = ["parsing", "formatting"] }
ts-rs = { workspace = true, optional = true }
ts-rs = { version = "6.1.2", optional = true }
[dev-dependencies]
rand_chacha = "0.3"
@@ -15,7 +15,7 @@ mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-co
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
ts-rs = { workspace = true, optional = true}
ts-rs = {version = "6.1.2", optional = true}
[features]
schema = ["cw2"]
-31
View File
@@ -1,31 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use nym_credential_storage::error::StorageError;
use nym_credentials::error::Error as CredentialError;
use nym_validator_client::nyxd::error::NyxdError;
use std::num::ParseIntError;
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
IOError(#[from] std::io::Error),
#[error(transparent)]
BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError),
#[error(transparent)]
Nyxd(#[from] NyxdError),
#[error(transparent)]
Credential(#[from] CredentialError),
#[error("Could not use shared storage: {0}")]
SharedStorageError(#[from] StorageError),
#[error("failed to parse credential value: {0}")]
MalformedCredentialValue(#[from] ParseIntError),
}
-5
View File
@@ -1,5 +0,0 @@
pub mod errors;
pub mod recovery_storage;
pub mod utils;
pub use errors::{Error, Result};
@@ -1,64 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::errors::Result;
use log::error;
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
use std::fs::{create_dir_all, read_dir, File};
use std::io::{Read, Write};
use std::path::PathBuf;
pub struct RecoveryStorage {
recovery_dir: PathBuf,
}
impl RecoveryStorage {
pub fn new(recovery_dir: PathBuf) -> Result<Self> {
create_dir_all(&recovery_dir)?;
Ok(Self { recovery_dir })
}
pub fn unconsumed_vouchers(&self) -> Result<Vec<BandwidthVoucher>> {
let entries = read_dir(&self.recovery_dir)?;
let mut paths = vec![];
for entry in entries.flatten() {
let path = entry.path();
if path.is_file() {
paths.push(path)
}
}
let mut vouchers = vec![];
for path in paths {
if let Ok(mut file) = File::open(&path) {
let mut buff = Vec::new();
if file.read_to_end(&mut buff).is_ok() {
match BandwidthVoucher::try_from_bytes(&buff) {
Ok(voucher) => vouchers.push(voucher),
Err(err) => {
error!("failed to parse the voucher at {}: {err}", path.display())
}
}
}
}
}
Ok(vouchers)
}
pub fn insert_voucher(&self, voucher: &BandwidthVoucher) -> Result<PathBuf> {
let file_name = voucher.tx_hash().to_string();
let file_path = self.recovery_dir.join(file_name);
let mut file = File::create(&file_path)?;
let buff = voucher.to_bytes();
file.write_all(&buff)?;
Ok(file_path)
}
pub fn remove_voucher(&self, file_name: String) -> Result<()> {
let file_path = self.recovery_dir.join(file_name);
Ok(std::fs::remove_file(file_path)?)
}
}
-139
View File
@@ -1,139 +0,0 @@
use crate::errors::{Error, Result};
use crate::recovery_storage::RecoveryStorage;
use log::*;
use nym_bandwidth_controller::acquire::state::State;
use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_config::DEFAULT_DATA_DIR;
use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_validator_client::nyxd::contract_traits::{CoconutBandwidthSigningClient, DkgQueryClient};
use nym_validator_client::nyxd::Coin;
use std::path::PathBuf;
use std::process::exit;
use std::time::{Duration, SystemTime};
const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute
pub async fn issue_credential<C>(
client: &C,
amount: Coin,
persistent_storage: &PersistentStorage,
recovery_storage_path: PathBuf,
) -> Result<()>
where
C: DkgQueryClient + CoconutBandwidthSigningClient + Send + Sync,
{
let recovery_storage = setup_recovery_storage(recovery_storage_path).await;
block_until_coconut_is_available(client).await?;
info!("Starting to deposit funds, don't kill the process");
if let Ok(recovered_amount) =
recover_credentials(client, &recovery_storage, persistent_storage).await
{
if recovered_amount != 0 {
info!(
"Recovered credentials in the amount of {}",
recovered_amount
);
return Ok(());
}
};
let state = nym_bandwidth_controller::acquire::deposit(client, amount.clone()).await?;
if nym_bandwidth_controller::acquire::get_credential(&state, client, persistent_storage)
.await
.is_err()
{
warn!("Failed to obtain credential. Dumping recovery data.",);
match recovery_storage.insert_voucher(&state.voucher) {
Ok(file_path) => {
warn!("Dumped recovery data to {}. Try using recovery mode to convert it to a credential", file_path.to_str().unwrap());
}
Err(e) => {
error!("Could not dump recovery data to file system due to {:?}, the deposit will be lost!", e)
}
}
return Err(Error::Credential(
nym_credentials::error::Error::BandwidthCredentialError,
));
}
info!("Succeeded adding a credential with amount {amount}");
Ok(())
}
pub async fn setup_recovery_storage(recovery_dir: PathBuf) -> RecoveryStorage {
RecoveryStorage::new(recovery_dir).expect("")
}
pub async fn setup_persistent_storage(client_home_directory: PathBuf) -> PersistentStorage {
let data_dir = client_home_directory.join(DEFAULT_DATA_DIR);
let paths = CommonClientPaths::new_default(data_dir);
let db_path = paths.credentials_database;
nym_credential_storage::initialise_persistent_storage(db_path).await
}
pub async fn block_until_coconut_is_available<C>(client: &C) -> Result<()>
where
C: DkgQueryClient + Send + Sync,
{
loop {
let epoch = client.get_current_epoch().await?;
let current_timestamp_secs = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("the system clock is set to 01/01/1970 (or earlier)")
.as_secs();
if epoch.state.is_final() {
if current_timestamp_secs + SAFETY_BUFFER_SECS >= epoch.finish_timestamp.seconds() {
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
exit(0);
}
break;
} else {
// Use 1 additional second to not start the next iteration immediately and spam get_current_epoch queries
let secs_until_final = epoch
.final_timestamp_secs()
.saturating_sub(current_timestamp_secs)
+ 1;
info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final);
tokio::time::sleep(Duration::from_secs(secs_until_final)).await;
}
}
Ok(())
}
pub async fn recover_credentials<C>(
client: &C,
recovery_storage: &RecoveryStorage,
shared_storage: &PersistentStorage,
) -> Result<u128>
where
C: DkgQueryClient + Send + Sync,
{
let mut recovered_amount: u128 = 0;
for voucher in recovery_storage.unconsumed_vouchers()? {
let voucher_value = voucher.get_voucher_value();
recovered_amount += voucher_value.parse::<u128>()?;
let state = State::new(voucher);
let voucher = state.voucher.tx_hash();
if let Err(e) =
nym_bandwidth_controller::acquire::get_credential(&state, client, shared_storage).await
{
error!("Could not recover deposit {voucher} due to {e}, try again later",)
} else {
info!("Converted deposit {voucher} to a credential, removing recovery data for it",);
if let Err(e) = recovery_storage.remove_voucher(voucher.to_string()) {
warn!("Could not remove recovery data: {e}");
}
}
}
Ok(recovered_amount)
}
+1 -2
View File
@@ -8,8 +8,7 @@ edition = "2021"
[dependencies]
bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
cosmrs = { workspace = true }
thiserror = { workspace = true }
log = { workspace = true }
thiserror = "1.0"
# I guess temporarily until we get serde support in coconut up and running
nym-coconut-interface = { path = "../coconut-interface" }
+8 -21
View File
@@ -1,10 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::coconut::bandwidth::{BandwidthVoucher, PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES};
use crate::coconut::params::{NymApiCredentialEncryptionAlgorithm, NymApiCredentialHkdfAlgorithm};
use crate::error::Error;
use log::{debug, warn};
use nym_api_requests::coconut::BlindSignRequestBody;
use nym_coconut_interface::{
aggregate_signature_shares, aggregate_verification_keys, prove_bandwidth_credential, Attribute,
@@ -15,6 +11,10 @@ use nym_crypto::shared_key::recompute_shared_key;
use nym_crypto::symmetric::stream_cipher;
use nym_validator_client::client::CoconutApiClient;
use crate::coconut::bandwidth::{BandwidthVoucher, PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES};
use crate::coconut::params::{NymApiCredentialEncryptionAlgorithm, NymApiCredentialHkdfAlgorithm};
use crate::error::Error;
pub async fn obtain_aggregate_verification_key(
api_clients: &[CoconutApiClient],
) -> Result<VerificationKey, Error> {
@@ -107,12 +107,7 @@ pub async fn obtain_aggregate_signature(
aggregate_verification_keys(&validators_partial_vks, Some(indices.as_ref()))?;
for coconut_api_client in coconut_api_clients.iter() {
debug!(
"attempting to obtain partial credential from {}",
coconut_api_client.api_client.api_url()
);
match obtain_partial_credential(
if let Ok(signature) = obtain_partial_credential(
params,
attributes,
&coconut_api_client.api_client,
@@ -120,17 +115,9 @@ pub async fn obtain_aggregate_signature(
)
.await
{
Ok(signature) => {
let share = SignatureShare::new(signature, coconut_api_client.node_id);
shares.push(share)
}
Err(err) => {
warn!(
"failed to obtain partial credential from {}: {err}",
coconut_api_client.api_client.api_url()
);
}
};
let share = SignatureShare::new(signature, coconut_api_client.node_id);
shares.push(share)
}
}
if shares.len() < threshold as usize {
return Err(Error::NotEnoughShares);
+2 -12
View File
@@ -4,11 +4,11 @@
use crate::var_names::{DEPRECATED_API_VALIDATOR, DEPRECATED_NYMD_VALIDATOR, NYM_API, NYXD};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::path::Path;
use std::{
env::{var, VarError},
ffi::OsStr,
ops::Not,
path::PathBuf,
};
use url::Url;
@@ -43,7 +43,6 @@ pub struct NymNetworkDetails {
pub chain_details: ChainDetails,
pub endpoints: Vec<ValidatorDetails>,
pub contracts: NymContracts,
pub explorer_api: Option<String>,
}
// by default we assume the same defaults as mainnet, i.e. same prefixes and denoms
@@ -72,7 +71,6 @@ impl NymNetworkDetails {
},
endpoints: Default::default(),
contracts: Default::default(),
explorer_api: Default::default(),
}
}
@@ -138,7 +136,6 @@ impl NymNetworkDetails {
var_names::SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS,
))
.with_name_service_contract(get_optional_env(var_names::NAME_SERVICE_CONTRACT_ADDRESS))
.with_explorer_api(get_optional_env(var_names::EXPLORER_API))
}
pub fn new_mainnet() -> Self {
@@ -170,7 +167,6 @@ impl NymNetworkDetails {
service_provider_directory_contract_address: None,
name_service_contract_address: None,
},
explorer_api: parse_optional_str(mainnet::EXPLORER_API),
}
}
@@ -282,12 +278,6 @@ impl NymNetworkDetails {
self.contracts.name_service_contract_address = contract.map(Into::into);
self
}
#[must_use]
pub fn with_explorer_api<S: Into<String>>(mut self, endpoint: Option<S>) -> Self {
self.explorer_api = endpoint.map(Into::into);
self
}
}
#[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)]
@@ -388,7 +378,7 @@ fn fix_deprecated_environmental_variables() {
}
}
pub fn setup_env<P: AsRef<Path>>(config_env_file: Option<P>) {
pub fn setup_env(config_env_file: Option<&PathBuf>) {
match std::env::var(var_names::CONFIGURED) {
// if the configuration is not already set in the env vars
Err(std::env::VarError::NotPresent) => {
-4
View File
@@ -26,8 +26,6 @@ pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hh
pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
pub const NYXD_URL: &str = "https://rpc.nymtech.net";
pub const NYM_API: &str = "https://validator.nymtech.net/api/";
pub const EXPLORER_API: &str = "https://explorer.nymtech.net/api/";
pub(crate) fn validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new(NYXD_URL, Some(NYM_API))]
}
@@ -101,7 +99,6 @@ pub fn export_to_env() {
);
set_var_to_default(var_names::NYXD, NYXD_URL);
set_var_to_default(var_names::NYM_API, NYM_API);
set_var_to_default(var_names::EXPLORER_API, EXPLORER_API);
}
pub fn export_to_env_if_not_set() {
@@ -148,5 +145,4 @@ pub fn export_to_env_if_not_set() {
);
set_var_conditionally_to_default(var_names::NYXD, NYXD_URL);
set_var_conditionally_to_default(var_names::NYM_API, NYM_API);
set_var_conditionally_to_default(var_names::EXPLORER_API, EXPLORER_API);
}
-1
View File
@@ -26,7 +26,6 @@ pub const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str =
pub const NAME_SERVICE_CONTRACT_ADDRESS: &str = "NAME_SERVICE_CONTRACT_ADDRESS";
pub const NYXD: &str = "NYXD";
pub const NYM_API: &str = "NYM_API";
pub const EXPLORER_API: &str = "EXPLORER_API";
pub const DKG_TIME_CONFIGURATION: &str = "DKG_TIME_CONFIGURATION";
+2 -2
View File
@@ -354,7 +354,7 @@ impl ProofKappaZeta {
let witness_blinder = params.random_scalar();
let witness_serial_number = params.random_scalar();
let witness_binding_number = params.random_scalar();
let witness_attributes = [witness_serial_number, witness_binding_number];
let witness_attributes = vec![witness_serial_number, witness_binding_number];
let beta_bytes = verification_key
.beta_g2
@@ -417,7 +417,7 @@ impl ProofKappaZeta {
.map(|beta_i| beta_i.to_bytes())
.collect::<Vec<_>>();
let response_attributes = [self.response_serial_number, self.response_binding_number];
let response_attributes = vec![self.response_serial_number, self.response_binding_number];
// re-compute witnesses commitments
// Aw = (c * kappa) + (rt * g2) + ((1 - c) * alpha) + (rm[0] * beta[0]) + ... + (rm[i] * beta[i])
let commitment_kappa = kappa * self.challenge
@@ -17,7 +17,7 @@ pub fn prepare_identifier<R: RngCore + CryptoRng>(
let id_ciphertext = encrypt::<AckEncryptionAlgorithm>(key.inner(), &iv, &serialized_id);
// IV || ID_CIPHERTEXT
iv.into_iter().chain(id_ciphertext).collect()
iv.into_iter().chain(id_ciphertext.into_iter()).collect()
}
pub fn recover_identifier(
@@ -122,7 +122,7 @@ impl SurbAck {
.first_hop_address
.as_zero_padded_bytes(MAX_NODE_ADDRESS_UNPADDED_LEN)
.into_iter()
.chain(self.surb_ack_packet.to_bytes()?)
.chain(self.surb_ack_packet.to_bytes()?.into_iter())
.collect();
Ok((self.expected_total_delay, surb_bytes))
}
@@ -130,7 +130,7 @@ impl ReplySurb {
self.encryption_key
.to_bytes()
.into_iter()
.chain(self.surb.to_bytes())
.chain(self.surb.to_bytes().into_iter())
.collect()
}
@@ -275,7 +275,7 @@ impl RepliableMessageContent {
.to_be_bytes()
.into_iter()
.chain(reply_surbs.into_iter().flat_map(|s| s.to_bytes()))
.chain(message)
.chain(message.into_iter())
.collect()
}
RepliableMessageContent::AdditionalSurbs { reply_surbs } => {
@@ -465,7 +465,7 @@ impl ReplyMessageContent {
ReplyMessageContent::SurbRequest { recipient, amount } => recipient
.to_bytes()
.into_iter()
.chain(amount.to_be_bytes())
.chain(amount.to_be_bytes().into_iter())
.collect(),
}
}
+1 -1
View File
@@ -204,7 +204,7 @@ impl Fragment {
self.header
.to_bytes()
.into_iter()
.chain(self.payload)
.chain(self.payload.into_iter())
.collect()
}
+1 -1
View File
@@ -115,7 +115,7 @@ where
let packet_payload: Vec<_> = ack_bytes
.into_iter()
.chain(ephemeral_keypair.public_key().to_bytes().iter().cloned())
.chain(cover_content)
.chain(cover_content.into_iter())
.collect();
let route =
+2 -2
View File
@@ -106,8 +106,8 @@ impl MixPacket {
pub fn into_bytes(self) -> Result<Vec<u8>, MixPacketFormattingError> {
Ok(std::iter::once(self.packet_type as u8)
.chain(self.next_hop.as_bytes())
.chain(self.packet.to_bytes()?)
.chain(self.next_hop.as_bytes().into_iter())
.chain(self.packet.to_bytes()?.into_iter())
.collect())
}
}
+6 -3
View File
@@ -50,8 +50,8 @@ impl NymPayloadBuilder {
Ok(NymPayload(
surb_ack_bytes
.into_iter()
.chain(variant_data)
.chain(fragment_data)
.chain(variant_data.into_iter())
.chain(fragment_data.into_iter())
.collect(),
))
}
@@ -61,7 +61,10 @@ impl NymPayloadBuilder {
packet_encryption_key: &SurbEncryptionKey,
) -> Result<NymPayload, SurbAckRecoveryError> {
let key_digest = packet_encryption_key.compute_digest();
self.build::<ReplySurbEncryptionAlgorithm>(packet_encryption_key.inner(), key_digest)
self.build::<ReplySurbEncryptionAlgorithm>(
packet_encryption_key.inner(),
key_digest.into_iter(),
)
}
pub fn build_regular<R>(
+3 -3
View File
@@ -110,10 +110,10 @@ pub struct Socks5 {
/// The version of the 'service provider' this client is going to use in its communication with the
/// specified socks5 provider.
// if in doubt, use the legacy version as initially nobody will be using the updated binaries
#[serde(default)]
#[serde(default = "ProviderInterfaceVersion::new_legacy")]
pub provider_interface_version: ProviderInterfaceVersion,
#[serde(default)]
#[serde(default = "Socks5ProtocolVersion::new_legacy")]
pub socks5_protocol_version: Socks5ProtocolVersion,
/// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider.
@@ -147,7 +147,7 @@ impl Socks5 {
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
#[serde(deny_unknown_fields)]
pub struct Socks5Debug {
/// Number of reply SURBs attached to each `Request::Connect` message.
pub connection_start_surbs: u32,
+9 -5
View File
@@ -73,7 +73,7 @@ impl SocketDataHeader {
.to_be_bytes()
.into_iter()
.chain(std::iter::once(self.local_socket_closed as u8))
.chain(self.seq.to_be_bytes())
.chain(self.seq.to_be_bytes().into_iter())
}
pub fn try_from_response_bytes(
@@ -107,8 +107,8 @@ impl SocketDataHeader {
pub fn into_response_bytes_iter(self) -> impl Iterator<Item = u8> {
std::iter::once(self.local_socket_closed as u8)
.chain(self.connection_id.to_be_bytes())
.chain(self.seq.to_be_bytes())
.chain(self.connection_id.to_be_bytes().into_iter())
.chain(self.seq.to_be_bytes().into_iter())
}
}
@@ -170,7 +170,9 @@ impl SocketData {
}
pub fn into_request_bytes_iter(self) -> impl Iterator<Item = u8> {
self.header.into_request_bytes_iter().chain(self.data)
self.header
.into_request_bytes_iter()
.chain(self.data.into_iter())
}
pub fn try_from_response_bytes(b: &[u8]) -> Result<SocketData, InsufficientSocketDataError> {
@@ -188,7 +190,9 @@ impl SocketData {
}
pub fn into_response_bytes_iter(self) -> impl Iterator<Item = u8> {
self.header.into_response_bytes_iter().chain(self.data)
self.header
.into_response_bytes_iter()
.chain(self.data.into_iter())
}
}
+15 -9
View File
@@ -113,7 +113,7 @@ impl Serializable for Socks5Request {
fn into_bytes(self) -> Vec<u8> {
if let Some(version) = self.protocol_version.as_u8() {
std::iter::once(version)
.chain(self.content.into_bytes())
.chain(self.content.into_bytes().into_iter())
.collect()
} else {
std::iter::once(Self::LEGACY_TYPE_TAG)
@@ -335,12 +335,12 @@ impl Socks5RequestContent {
let remote_address_bytes_len = remote_address_bytes.len() as u16;
let iter = std::iter::once(RequestFlag::Connect as u8)
.chain(req.conn_id.to_be_bytes())
.chain(remote_address_bytes_len.to_be_bytes())
.chain(remote_address_bytes);
.chain(req.conn_id.to_be_bytes().into_iter())
.chain(remote_address_bytes_len.to_be_bytes().into_iter())
.chain(remote_address_bytes.into_iter());
if let Some(return_address) = req.return_address {
iter.chain(return_address.to_bytes()).collect()
iter.chain(return_address.to_bytes().into_iter()).collect()
} else {
iter.collect()
}
@@ -358,7 +358,7 @@ impl Socks5RequestContent {
})
.unwrap_or_default();
std::iter::once(RequestFlag::Query as u8)
.chain(query_bytes)
.chain(query_bytes.into_iter())
.collect()
}
}
@@ -495,7 +495,7 @@ mod request_deserialization_tests {
let request_bytes: Vec<_> = request_bytes_prefix
.iter()
.cloned()
.chain(recipient_bytes)
.chain(recipient_bytes.into_iter())
.collect();
assert!(Socks5RequestContent::try_from_bytes(&request_bytes)
.unwrap_err()
@@ -530,7 +530,10 @@ mod request_deserialization_tests {
let recipient = Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f").unwrap();
let recipient_bytes = recipient.to_bytes();
let request_bytes: Vec<_> = request_bytes.into_iter().chain(recipient_bytes).collect();
let request_bytes: Vec<_> = request_bytes
.into_iter()
.chain(recipient_bytes.into_iter())
.collect();
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
match request {
@@ -574,7 +577,10 @@ mod request_deserialization_tests {
let recipient = Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f").unwrap();
let recipient_bytes = recipient.to_bytes();
let request_bytes: Vec<_> = request_bytes.into_iter().chain(recipient_bytes).collect();
let request_bytes: Vec<_> = request_bytes
.into_iter()
.chain(recipient_bytes.into_iter())
.collect();
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
match request {
+5 -5
View File
@@ -84,7 +84,7 @@ impl Serializable for Socks5Response {
fn into_bytes(self) -> Vec<u8> {
if let Some(version) = self.protocol_version.as_u8() {
std::iter::once(version)
.chain(self.content.into_bytes())
.chain(self.content.into_bytes().into_iter())
.collect()
} else {
self.content.into_bytes()
@@ -192,7 +192,7 @@ impl Socks5ResponseContent {
}
Socks5ResponseContent::ConnectionError(res) => {
std::iter::once(ResponseFlag::ConnectionError as u8)
.chain(res.into_bytes())
.chain(res.into_bytes().into_iter())
.collect()
}
Socks5ResponseContent::Query(query) => {
@@ -204,7 +204,7 @@ impl Socks5ResponseContent {
})
.unwrap_or_default();
std::iter::once(ResponseFlag::Query as u8)
.chain(query_bytes)
.chain(query_bytes.into_iter())
.collect()
}
}
@@ -290,7 +290,7 @@ impl ConnectionError {
.to_be_bytes()
.iter()
.copied()
.chain(self.network_requester_error.into_bytes())
.chain(self.network_requester_error.into_bytes().into_iter())
.collect()
}
}
@@ -339,7 +339,7 @@ mod tests {
let bytes: Vec<u8> = 42u64
.to_be_bytes()
.into_iter()
.chain([0, 159, 146, 150])
.chain([0, 159, 146, 150].into_iter())
.collect();
let err = ConnectionError::try_from_bytes(&bytes).err().unwrap();
assert!(matches!(
+1 -1
View File
@@ -131,7 +131,7 @@ impl NymTopology {
}
pub fn mixes_in_layer(&self, layer: MixLayer) -> Vec<mix::Node> {
assert!([1, 2, 3].contains(&layer));
assert!(vec![1, 2, 3].contains(&layer));
self.mixes.get(&layer).unwrap().to_owned()
}
+1 -1
View File
@@ -17,7 +17,7 @@ serde_json = { workspace = true }
strum = { version = "0.23", features = ["derive"] }
thiserror = { workspace = true }
url = { workspace = true }
ts-rs = { workspace = true }
ts-rs = "6.1.2"
cosmwasm-std = { workspace = true }
cosmrs = { workspace = true }
+1
View File
@@ -5,6 +5,7 @@ use crate::console_log;
use crate::storage::cipher_export::StoredExportedStoreCipher;
use crate::storage::error::StorageError;
use futures::TryFutureExt;
use indexed_db_futures::IdbDatabase;
use nym_store_cipher::{
Aes256Gcm, Algorithm, EncryptedData, KdfInfo, KeySizeUser, Params, StoreCipher, Unsigned,
Version,
+1 -7
View File
@@ -1,13 +1,7 @@
# Documentation
## Doc projects
Each directory contains a readme with more information about running and contributing to the projects. Each is built with [`mdbook`](https://rust-lang.github.io/mdBook/index.html) - use `mdbook serve` to build and serve them (defaults to `localhost:3000`).
* `docs` contains technical documentation hosted at [https://nymtech.net/docs](https://nymtech.net/docs)
* `dev-portal` contains developer documentation hosted at [https://nymtech.net/developers](https://nymtech.net/developers)
* `operators` contains node setup and maintenance guides hosted at [https://nymtech.net/operators](https://nymtech.net/operators)
## Scripts
* `bump_versions.sh` allows you to update the ~~`platform_release_version` and~~ `wallet_release_version` variable~~s~~ in the `book.toml` of each mdbook project at once. You can also optionally update the `minimum_rust_version` as well. Helpful for lazy-updating when cutting a new version of the docs.
* `build_all_to_dist.sh` is used by the `ci-dev.yml` and `cd-dev.yml` scripts for building all mdbook projects and moving the rendered html to `../dist/` to be rsynced with various servers.
Each directory contains a readme with more information about running and contributing to the projects. Each is built with [`mdbook`](https://rust-lang.github.io/mdBook/index.html) - use `mdbook serve` to build and serve them (defaults to `localhost:3000`).
+32 -40
View File
@@ -1,45 +1,37 @@
#!/bin/bash
# this is a script called by the github CI and CD workflows to build all 3 docs projects
# and move them to /dist/ in the root of the monorepo. They are rsynced to various servers
# from there by subsequent workflow tasks.
# commands assume you run script from `nym/documentation/`
# array of project dirs
declare -a projects=("docs" "dev-portal" "operators")
# check you're calling from the right place
if [ $(pwd | awk -F/ '{print $NF}') != "documentation" ]
then
echo "failure: please run script from documentation/"
else
for i in "${projects[@]}"
do
# cd to project dir
cd "./$i" &&
# little sanity checks
echo $(pwd) && echo $(mdbook --version) &&
# clean old book
echo "cleaning old book"
rm -rf ./book/
# build book
mdbook build
# check for destination, if ! then mkdir & check again else echo thumbs up
if [ ! -d ../../dist/docs/$i ]; then
echo "dest doesn't exist: creating dir"
mkdir -p ../../dist/docs/$i
fi
if [ -d ../../dist/docs/$i ]; then
echo "cp destination exists, all good"
fi
# clean old dist/$i
rm -rf ../../dist/docs/$i
# move newly rendered book/ to dist
rsync -r ./book/html/ ../../dist/docs/$i
# sanity check
ls -laF ../../dist/docs/
# cd back to ../documentation/
cd ../
done
# rename for server paths
rm -rf ../dist/docs/developers
mv ../dist/docs/dev-portal ../dist/docs/developers
fi
## now loop through the above array
for i in "${projects[@]}"
do
# cd to project dir
cd "./$i" &&
# little sanity checks
echo $(pwd) && echo $(mdbook --version) &&
# clean old book
echo "cleaning old book"
rm -rf ./book/
# build book
mdbook build
# check for destination, if ! then mkdir & check again else echo thumbs up
if [ ! -d ../../dist/docs/$i ]; then
echo "dest doesn't exist: creating dir"
mkdir -p ../../dist/docs/$i
fi
if [ -d ../../dist/docs/$i ]; then
echo "cp destination exists, all good"
fi
# clean old dist/$i
rm -rf ../../dist/docs/$i
# move newly rendered book/ to dist
rsync -r ./book/html/ ../../dist/docs/$i
# sanity check
ls -laF ../../dist/docs/
# cd back to ../documentation/
cd ../
done
mv ../dist/docs/dev-portal ../dist/docs/developers
-40
View File
@@ -1,40 +0,0 @@
#!/usr/bin/env bash
# takes one manadatory arg and one optional arg: wallet release and minimum rust versions
# it then uses sed to bump them in the three book.toml files.
#
# e.g if the upcoming wallet release version was 1.2.9 you'd run this as:
# `./bump_versions.sh "1.2.9"`
#
# you can also set the minumum rust version by passing an optional additional argument:
# `./bump_versions.sh "1.2.9" "1.67"`
# array of project dirs
declare -a projects=("docs" "dev-portal" "operators")
# check number of args passed
if [ "$#" -lt 1 ] || [ "$#" -gt 2 ];
then
echo "failure: please pass at least 1 and at most 2 args: "
echo "./bump_version.sh <new wallet_release_version> [OPTIONAL]<new minimum_rust_version>"
exit 0
fi
# check you're calling from the right place
if [ $(pwd | awk -F/ '{print $NF}') != "documentation" ]
then
echo "failure: please run script from documentation/"
exit 0
else
## now loop through the above array sed-ing the variable values in the book.toml files
for i in "${projects[@]}"
do
# sed the vars in the book.toml file for each project
echo "setting wallet version in $i/"
sed -i 's/wallet_release_version =.*/wallet_release_version = "'$2'"/' "$i"/book.toml
if [ "$3" ]
then
echo "setting minimum rust version in $i/"
sed -i 's/minimum_rust_version = .*/minimum_rust_version = "'$3'"/' "$i"/book.toml
fi
done
fi
+1 -3
View File
@@ -17,6 +17,4 @@ book
theme/
theme
theme/*
.idea
theme/*
+1 -1
View File
@@ -12,7 +12,7 @@ If you have built a project with Nym or are compiling and writing resources abou
## Variables
There are some variables that are shared across this book, such as the current latest software version.
Variables are denoted in the `.md` files wrapped in `{{}}` (e.g `{{wallet_release_version}}`), and are located in the `book.toml` file under the `[preprocessor.variables.variables]` heading. If you are changing something like the software release version, minimum code versions in prerequisites, etc, **check in here first!**
Variables are denoted in the `.md` files wrapped in `{{}}` (e.g `{{platform_release_version}}` is the most recent release), and are located in the `book.toml` file under the `[preprocessor.variables.variables]` heading. If you are changing something like the software release version, minimum code versions in prerequisites, etc, **check in here first!**
## Building
When working locally, it is recommended that you use `mdbook serve` to have a local version of the docs served on `localhost:3000`, with hot reloading on any changes made to files in the `src/` directory.
+3 -3
View File
@@ -38,8 +38,7 @@ chapter-line-height = "2em"
section-line-height = "1.5em"
# if true, never read and touch the files in theme dir
turn-off = true
turn-off = false
[preprocessor.admonish]
command = "mdbook-admonish"
@@ -49,7 +48,8 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
# https://gitlab.com/tglman/mdbook-variables/
[preprocessor.variables.variables]
minimum_rust_version = "1.66"
wallet_release_version = "1.2.8"
platform_release_version = "v1.1.28"
wallet_release_version = "v1.2.7"
[preprocessor.last-changed]
command = "mdbook-last-changed"
+8 -20
View File
@@ -20,7 +20,6 @@
- [NymConnect Monero](tutorials/monero.md)
- [NymConnect Matrix](tutorials/matrix.md)
- [NymConnect Telegram](tutorials/telegram.md)
# Integrations
@@ -31,25 +30,14 @@
# Tutorials
- [Rust SDK](tutorials/rust-sdk.md)
- [Blockchain Service pt1](tutorials/cosmos-service/intro.md)
- [Tutorial Overview](tutorials/cosmos-service/overview.md)
- [Preparing Your Environment](tutorials/cosmos-service/preparing-env.md)
- [Preparing Your Lib](tutorials/cosmos-service/lib.md)
- [Preparing Your Client](tutorials/cosmos-service/client.md)
- [Preparing Your Client pt2](tutorials/cosmos-service/client-src.md)
- [Preparing Your Service](tutorials/cosmos-service/service.md)
- [Preparing Your Service pt2](tutorials/cosmos-service/service-src.md)
- [Querying the Chain](tutorials/cosmos-service/querying.md)
- [Typescript](tutorials/typescript.md)
- [Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
- [Tutorial Overview](tutorials/simple-service-provider/overview.md)
- [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md)
- [Building Your User Client](tutorials/simple-service-provider/user-client.md)
- [Preparing Your Service Provider Environment](tutorials/simple-service-provider/preparating-env2.md)
- [Building Your Service Provider](tutorials/simple-service-provider/service-provider.md)
- [Sending a Message Through the Mixnet](tutorials/simple-service-provider/sending-message.md)
- [Simple Service Provider](tutorials/simple-service-provider.md)
- [Tutorial Overview](tutorials/simple-service-provider/overview.md)
- [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md)
- [Building Your User Client](tutorials/simple-service-provider/user-client.md)
- [Preparing Your Service Provider Environment](tutorials/simple-service-provider/preparating-env2.md)
- [Building Your Service Provider](tutorials/simple-service-provider/service-provider.md)
- [Sending a Message Through the Mixnet](tutorials/simple-service-provider/sending-message.md)
- [IPFS Service Provider (coming soon)](tutorials/ipfs-service-provider.md)
# Community Resources

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