Compare commits

..

20 Commits

Author SHA1 Message Date
farbanas 36fb0eba29 Update versions as part of release/v1.1.11 2023-02-28 13:53:14 +01:00
farbanas 1b37e85418 Update changelogs as part of release/v1.1.11 2023-02-28 13:43:32 +01:00
Bogdan-Ștefan Neacşu 6a93497c8f Extend public key submission in case no dealer registered (#3106) 2023-02-28 12:58:01 +01:00
Tommy Verrall ffbd76539a Merge pull request #3108 from nymtech/feature/nym-connect-select-sp
Feature/nym connect select sp
2023-02-27 17:48:31 +02:00
fmtabbara bdcc19e86a PR update 2023-02-27 15:33:54 +00:00
fmtabbara 7929bac685 turn off user defined SP address is input it empty 2023-02-27 14:20:16 +00:00
Jon Häggblad f590aad42c nym-api: uptime rework (#3053)
* nym-api: cache updates as node performance

* nym-api: update get mixnode avg_uptime endpoint

* nym-api: mixnode report to use cached data

* nym-api: annotate gateway bond with node performance

* nym-api: gateway report to use cached data

* wip

* Add get_gateway_avg_uptime

* Add comment

* update NR gateways to include node_performance on frontend

* use node_performance values on frontend

* fixup select gateway from list

* fix up lint errors

---------

Co-authored-by: fmtabbara <fmtabbara@hotmail.co.uk>
2023-02-27 12:40:00 +01:00
fmtabbara ec23f3dcb7 disable input when connected
fix lint errors
2023-02-27 11:28:38 +00:00
fmtabbara 9644eb4329 pick service provider 2023-02-27 11:28:38 +00:00
fmtabbara 7a4c6e4ed4 storage type
update types
2023-02-27 11:28:36 +00:00
fmtabbara d5ad504104 reuseable storage functions 2023-02-27 11:28:36 +00:00
Tommy Verrall d684f6d7ae Merge pull request #3099 from nymtech/feature/nym-connect-select-sp
NymConnect Select a service provider
2023-02-27 12:26:00 +02:00
Bogdan-Ștefan Neacşu f0d9703587 Feature/fix db name collision (#3103)
* Fix db naming collision

* Increase gateway timeout to accommodate bandwidth spending time
2023-02-24 16:52:56 +02:00
fmtabbara c08efef8ed add autofocus to IdentityKeyComponent 2023-02-23 22:58:59 +00:00
fmtabbara 6d29774744 style tweaks 2023-02-23 22:57:21 +00:00
fmtabbara af6bab7703 fix lint error 2023-02-23 17:40:39 +00:00
fmtabbara 7033f92d82 add UI for picking service provider 2023-02-23 17:33:03 +00:00
Jędrzej Stuczyński 128dfa6d81 Feature/latency based gateway selection (#3081)
* wip

* new option to select gateways based on latency

* further changes for wasm-compatibility

* post rebase fixes + clippy

I know, I should have probably included them properly during rebasing ¯\_(ツ)_/¯

* android change

* wasm: the gift that keeps on giving
2023-02-23 17:09:22 +00:00
Bogdan-Ștefan Neacşu 0b0ec075bb Use saturating_sub with an additional 1 second buffer (#3095) 2023-02-23 13:44:54 +01:00
Tommy Verrall cca4d21e7c Merge pull request #3097 from nymtech/feature/update-checker-to-use-master
Feature/update checker to use master
2023-02-23 14:24:59 +02:00
96 changed files with 991 additions and 469 deletions
@@ -69,7 +69,7 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: build command: build
args: --workspace --release --all args: --workspace --release
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@@ -95,8 +95,7 @@ jobs:
cp target/release/nym-network-requester $OUTPUT_DIR cp target/release/nym-network-requester $OUTPUT_DIR
cp target/release/nym-network-statistics $OUTPUT_DIR cp target/release/nym-network-statistics $OUTPUT_DIR
cp target/release/nym-cli $OUTPUT_DIR cp target/release/nym-cli $OUTPUT_DIR
cp target/release/credential $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
-56
View File
@@ -1,56 +0,0 @@
name: Run config checks on all binaries
on:
workflow_dispatch:
release:
types: [created]
push:
paths:
- 'clients/**'
- 'common/**'
- 'contracts/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
pull_request:
paths:
- 'clients/**'
- 'common/**'
- 'gateway/**'
- 'integrations/**'
- 'mixnode/**'
- 'sdk/rust/nym-sdk/**'
- 'service-providers/**'
env:
NETWORK: mainnet
jobs:
publish-nym:
strategy:
fail-fast: false
matrix:
platform: [custom-runner-linux]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install jq vim libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
continue-on-error: true
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Branch name
run: echo running on branch ${GITHUB_REF##*/}
- name: Run tests against binaries
run: ./build_and_run.sh ${{ github.head_ref || github.ref_name }}
working-directory: tests/
+3 -7
View File
@@ -23,12 +23,15 @@ jobs:
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get -y install \ sudo apt-get -y install \
libwebkit2gtk-4.0-dev \
build-essential \ build-essential \
unzip \ unzip \
curl \ curl \
wget \ wget \
libssl-dev \ libssl-dev \
libgtk-3-dev \
squashfs-tools \ squashfs-tools \
libayatana-appindicator3-dev \
librsvg2-dev librsvg2-dev
- name: Checkout - name: Checkout
@@ -61,11 +64,6 @@ jobs:
- name: Install Rust toolchain - name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
# TODO this step takes a considerable amount of time
# We could avoid to compile from source tauri-cli and use instead
# pre-compiled binary provided by the node package `@tauri-apps/cli`
# But when using the later the build fails for some reason
# so keep installing and using tauri-cli
- name: Install tauri cli - name: Install tauri cli
run: cargo install tauri-cli --version "^2.0.0-alpha.2" run: cargo install tauri-cli --version "^2.0.0-alpha.2"
@@ -95,13 +93,11 @@ jobs:
- name: Build APK - name: Build APK
working-directory: nym-connect/mobile working-directory: nym-connect/mobile
env: env:
# NODE_TAURI_CLI=${{ github.workspace }}/nym-connect/mobile/node_modules/.bin/tauri
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }} ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
WRY_ANDROID_PACKAGE: net.nymtech.nym_connect WRY_ANDROID_PACKAGE: net.nymtech.nym_connect
WRY_ANDROID_LIBRARY: nym_connect WRY_ANDROID_LIBRARY: nym_connect
# TODO build with release profile (--release), it will requires # TODO build with release profile (--release), it will requires
# to sign the APK. For now build with debug profile to avoid that # to sign the APK. For now build with debug profile to avoid that
# TODO build using `yarn tauri`, provide NODE_TAURI_CLI, see TODO notes above
run: cargo tauri android build --debug --apk --split-per-abi -t aarch64 run: cargo tauri android build --debug --apk --split-per-abi -t aarch64
# TODO add the version number to APK name # TODO add the version number to APK name
+1
View File
@@ -49,6 +49,7 @@ jobs:
librsvg2-dev \ librsvg2-dev \
libsoup-3.0-dev \ libsoup-3.0-dev \
libjavascriptcoregtk-4.1-dev libjavascriptcoregtk-4.1-dev
#continue-on-error: true
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -10,11 +10,11 @@ defaults:
jobs: jobs:
publish-tauri: publish-tauri:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }} if: ${{ startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release' }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
platform: [custom-runner-mac-m1] platform: [macos-latest]
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
@@ -24,15 +24,10 @@ jobs:
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 16 node-version: 16
- name: Setup yarn
run: npm install -g yarn
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
- name: Install the Apple developer certificate for code signing - name: Install the Apple developer certificate for code signing
env: env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
@@ -44,7 +39,7 @@ jobs:
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
# import certificate and provisioning profile from secrets # import certificate and provisioning profile from secrets
echo -n "$APPLE_CERTIFICATE" | base64 --decode --output=$CERTIFICATE_PATH echo -n "$APPLE_CERTIFICATE" | base64 --decode --output $CERTIFICATE_PATH
# create temporary keychain # create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
@@ -1,14 +1,9 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry: > 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message: Commit message:
``` ```
@@ -1,16 +1,10 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/ > :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
>
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION_STORYBOOK }}.{{ env.NYM_CI_WWW_BASE }} > `storybook`: https://{{ env.NYM_CI_WWW_LOCATION_STORYBOOK }}.{{ env.NYM_CI_WWW_BASE }}
>
> ✅ **SUCCESS** > ✅ **SUCCESS**
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}: Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
``` ```
@@ -151,7 +151,7 @@ async function getMessageBody(context) {
return `${icon} ${job.conclusion}: ${job.name} - ${job.html_url}`; return `${icon} ${job.conclusion}: ${job.name} - ${job.html_url}`;
}) })
// and join with newlines for display in the template // and join with newlines for display in the template
.join('\n\n'); .join('\n');
return template({ ...context, jobResults }); return template({ ...context, jobResults });
} }
@@ -1,15 +1,9 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry: > 🔴 **FAILURE** :cry:
>
> `when` {{ timestamp }} > `when` {{ timestamp }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
{{ jobResults }} {{ jobResults }}
@@ -1,15 +1,9 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> ✅ **SUCCESS** > ✅ **SUCCESS**
>
> `when` {{ timestamp }} > `when` {{ timestamp }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
{{ jobResults }} {{ jobResults }}
@@ -5,7 +5,6 @@ const localStorage = new LocalStorage('./scratch');
const { const {
LocalStorageCryptoStore, LocalStorageCryptoStore,
} = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store'); } = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
var showdown = require('showdown');
// hide all matrix client output // hide all matrix client output
console.error = (error) => console.log('❌ error: ', error); console.error = (error) => console.log('❌ error: ', error);
@@ -55,9 +54,7 @@ function createClient(context, room, message) {
} }
async function sendMatrixMessage(contextArg, messageAsMarkdown, roomId) { async function sendMatrixMessage(contextArg, messageAsMarkdown, roomId) {
const converter = new showdown.Converter(); const client = createClient(contextArg, roomId, messageAsMarkdown);
const messageAsHtml = converter.makeHtml(messageAsMarkdown);
const client = createClient(contextArg, roomId, messageAsHtml);
await client.initCrypto(); await client.initCrypto();
await client.startClient({ initialSyncLimit: 1 }); await client.startClient({ initialSyncLimit: 1 });
} }
@@ -1,14 +1,9 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry: > 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message: Commit message:
``` ```
@@ -1,14 +1,9 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View storybook:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/ > :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View storybook:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
>
> ✅ **SUCCESS** > ✅ **SUCCESS**
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}: Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
``` ```
@@ -1,14 +1,9 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry: > 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message: Commit message:
``` ```
@@ -1,18 +1,13 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> ✅ **SUCCESS** > ✅ **SUCCESS**
>
> ➡️➡️➡️➡️➡️ **View output:** > ➡️➡️➡️➡️➡️ **View output:**
>
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }} > `storybook`: https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}: Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
``` ```
@@ -17,7 +17,6 @@
"remark-emoji": "^2.2.0", "remark-emoji": "^2.2.0",
"remark-html": "^13.0.2", "remark-html": "^13.0.2",
"remark-parse": "^9.0.0", "remark-parse": "^9.0.0",
"showdown": "^2.1.0",
"to-vfile": "^6.1.0", "to-vfile": "^6.1.0",
"unified": "^9.2.2" "unified": "^9.2.2"
}, },
@@ -1,14 +1,9 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> 🔴 **FAILURE** :cry: > 🔴 **FAILURE** :cry:
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message: Commit message:
``` ```
@@ -1,20 +1,14 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} > :rocket: {{ env.NYM_PROJECT_NAME }}
>
> ✅ **SUCCESS** > ✅ **SUCCESS**
>
> ➡️➡️➡️➡️➡️ **View output:** > ➡️➡️➡️➡️➡️ **View output:**
>
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }} > `storybook`: https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}
>
> `example`: https://{{ env.NYM_CI_WWW_LOCATION }}-example.{{ env.NYM_CI_WWW_BASE }} > `example`: https://{{ env.NYM_CI_WWW_LOCATION }}-example.{{ env.NYM_CI_WWW_BASE }}
>
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
>
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
>
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }} > `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
>
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}: Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
``` ```
+20
View File
@@ -4,6 +4,26 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased] ## [Unreleased]
## [v1.0.11] (2023-02-28)
- Fix empty dealer set loop ([#3105])
- The nym-api db.sqlite is broken when trying to run against it it in `enabled-credentials-mode true` there is an ordering issue with migrations when using the credential binary to purchase bandwidth ([#3100])
- Feature/latency based gateway selection ([#3081])
- Fix the credential binary to handle transactions to sleep when in non-inProgress epochs ([#3057])
- Publish mixnet contract to crates.io ([#1919])
- Publish vesting contract to crates.io ([#1920])
- Feature/update checker to use master ([#3097])
- Feature/improve binary checks ([#3094])
[#3105]: https://github.com/nymtech/nym/issues/3105
[#3100]: https://github.com/nymtech/nym/issues/3100
[#3081]: https://github.com/nymtech/nym/pull/3081
[#3057]: https://github.com/nymtech/nym/issues/3057
[#1919]: https://github.com/nymtech/nym/issues/1919
[#1920]: https://github.com/nymtech/nym/issues/1920
[#3097]: https://github.com/nymtech/nym/pull/3097
[#3094]: https://github.com/nymtech/nym/pull/3094
## [v1.1.10] (2023-02-21) ## [v1.1.10] (2023-02-21)
- Verloc listener causing mixnode unexpected shutdown ([#3038]) - Verloc listener causing mixnode unexpected shutdown ([#3038])
Generated
+2
View File
@@ -705,6 +705,8 @@ dependencies = [
"time 0.3.17", "time 0.3.17",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tokio-tungstenite 0.14.0",
"tungstenite 0.13.0",
"url", "url",
"validator-client", "validator-client",
"wasm-bindgen", "wasm-bindgen",
+6 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "client-core" name = "client-core"
version = "1.1.10" version = "1.1.11"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021" edition = "2021"
rust-version = "1.66" rust-version = "1.66"
@@ -20,6 +20,7 @@ serde_json = "1.0.89"
tap = "1.0.1" tap = "1.0.1"
thiserror = "1.0.34" thiserror = "1.0.34"
url = { version ="2.2", features = ["serde"] } url = { version ="2.2", features = ["serde"] }
tungstenite = { version = "0.13.0", default-features = false }
tokio = { version = "1.24.1", features = ["macros"]} tokio = { version = "1.24.1", features = ["macros"]}
time = "0.3.17" time = "0.3.17"
@@ -44,6 +45,9 @@ features = ["time"]
version = "1.24.1" version = "1.24.1"
features = ["time"] features = ["time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
version = "0.6.2" version = "0.6.2"
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
@@ -65,6 +69,7 @@ features = ["futures"]
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils] [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
path = "../../common/wasm-utils" path = "../../common/wasm-utils"
features = ["websocket"]
[target."cfg(target_arch = \"wasm32\")".dependencies.time] [target."cfg(target_arch = \"wasm32\")".dependencies.time]
version = "0.3.17" version = "0.3.17"
@@ -304,7 +304,7 @@ where
} }
let gateway_address = self.gateway_config.gateway_listener.clone(); let gateway_address = self.gateway_config.gateway_listener.clone();
if gateway_address.is_empty() { if gateway_address.is_empty() {
return Err(ClientCoreError::GatwayAddressUnknown); return Err(ClientCoreError::GatewayAddressUnknown);
} }
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id) let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
+2 -2
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use nym_config::defaults::NymNetworkDetails; use nym_config::defaults::NymNetworkDetails;
use nym_config::{NymConfig, OptionalSet, DB_FILE_NAME}; use nym_config::{NymConfig, OptionalSet, CRED_DB_FILE_NAME};
use nym_sphinx::params::PacketSize; use nym_sphinx::params::PacketSize;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::marker::PhantomData; use std::marker::PhantomData;
@@ -579,7 +579,7 @@ impl<T: NymConfig> Client<T> {
} }
fn default_database_path(id: &str) -> PathBuf { fn default_database_path(id: &str) -> PathBuf {
T::default_data_directory(id).join(DB_FILE_NAME) T::default_data_directory(id).join(CRED_DB_FILE_NAME)
} }
} }
+27 -1
View File
@@ -3,6 +3,7 @@
use gateway_client::error::GatewayClientError; use gateway_client::error::GatewayClientError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError; use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_topology::gateway::GatewayConversionError;
use nym_topology::NymTopologyError; use nym_topology::NymTopologyError;
use validator_client::ValidatorClientError; use validator_client::ValidatorClientError;
@@ -53,7 +54,32 @@ pub enum ClientCoreError {
GatewayOwnerUnknown, GatewayOwnerUnknown,
#[error("The address of the gateway is unknown - did you run init?")] #[error("The address of the gateway is unknown - did you run init?")]
GatwayAddressUnknown, GatewayAddressUnknown,
#[error("The gateway is malformed: {source}")]
MalformedGateway {
#[from]
source: GatewayConversionError,
},
#[error("failed to establish connection to gateway: {source}")]
GatewayConnectionFailure {
#[from]
source: tungstenite::Error,
},
#[cfg(target_arch = "wasm32")]
#[error("failed to establish gateway connection (wasm)")]
GatewayJsConnectionFailure,
#[error("Gateway connection was abruptly closed")]
GatewayConnectionAbruptlyClosed,
#[error("Timed out while trying to establish gateway connection")]
GatewayConnectionTimeout,
#[error("No ping measurements for the gateway ({identity}) performed")]
NoGatewayMeasurements { identity: String },
#[error("failed to register receiver for reconstructed mixnet messages")] #[error("failed to register receiver for reconstructed mixnet messages")]
FailedToRegisterReceiver, FailedToRegisterReceiver,
+195 -24
View File
@@ -6,52 +6,223 @@ use crate::{
config::{persistence::key_pathfinder::ClientKeyPathfinder, Config}, config::{persistence::key_pathfinder::ClientKeyPathfinder, Config},
error::ClientCoreError, error::ClientCoreError,
}; };
#[cfg(target_arch = "wasm32")] use futures::{SinkExt, StreamExt};
use gateway_client::wasm_mockups::SigningNyxdClient;
use gateway_client::GatewayClient; use gateway_client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys; use gateway_requests::registration::handshake::SharedKeys;
use log::{debug, info, trace, warn};
use nym_config::NymConfig; use nym_config::NymConfig;
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_topology::{filter::VersionFilterable, gateway}; use nym_topology::{filter::VersionFilterable, gateway};
use rand::{seq::SliceRandom, thread_rng}; use rand::{seq::SliceRandom, thread_rng, Rng};
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
use tap::TapFallible; use tap::TapFallible;
use tungstenite::Message;
use url::Url; use url::Url;
#[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time::Instant;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use validator_client::nyxd::SigningNyxdClient; use validator_client::nyxd::SigningNyxdClient;
pub(super) async fn query_gateway_details( #[cfg(not(target_arch = "wasm32"))]
validator_servers: Vec<Url>, type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
chosen_gateway_id: Option<identity::PublicKey>,
) -> Result<gateway::Node, ClientCoreError> { #[cfg(target_arch = "wasm32")]
let nym_api = validator_servers use gateway_client::wasm_mockups::SigningNyxdClient;
.choose(&mut thread_rng()) #[cfg(target_arch = "wasm32")]
use wasm_timer::Instant;
#[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket;
#[cfg(target_arch = "wasm32")]
type WsConn = JSWebsocket;
const MEASUREMENTS: usize = 3;
#[cfg(not(target_arch = "wasm32"))]
const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
const PING_TIMEOUT: Duration = Duration::from_millis(1000);
struct GatewayWithLatency {
gateway: gateway::Node,
latency: Duration,
}
impl GatewayWithLatency {
fn new(gateway: gateway::Node, latency: Duration) -> Self {
GatewayWithLatency { gateway, latency }
}
}
async fn current_gateways<R: Rng>(
rng: &mut R,
nym_apis: Vec<Url>,
) -> Result<Vec<gateway::Node>, ClientCoreError> {
let nym_api = nym_apis
.choose(rng)
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?; .ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
let validator_client = validator_client::client::NymApiClient::new(nym_api.clone()); let client = validator_client::client::NymApiClient::new(nym_api.clone());
log::trace!("Fetching list of gateways from: {}", nym_api); log::trace!("Fetching list of gateways from: {}", nym_api);
let gateways = validator_client.get_cached_gateways().await?;
let gateways = client.get_cached_gateways().await?;
let valid_gateways = gateways let valid_gateways = gateways
.into_iter() .into_iter()
.filter_map(|gateway| gateway.try_into().ok()) .filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<gateway::Node>>(); .collect::<Vec<gateway::Node>>();
// we were always filtering by version so I'm not removing that 'feature'
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION")); let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
Ok(filtered_gateways)
}
// if we have chosen particular gateway - use it, otherwise choose a random one. #[cfg(not(target_arch = "wasm32"))]
// (remember that in active topology all gateways have at least 100 reputation so should async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
// be working correctly) match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
if let Some(gateway_id) = chosen_gateway_id { Err(_elapsed) => Err(ClientCoreError::GatewayConnectionTimeout),
filtered_gateways Ok(Err(conn_failure)) => Err(conn_failure.into()),
.iter() Ok(Ok((stream, _))) => Ok(stream),
.find(|gateway| gateway.identity_key == gateway_id) }
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_id.to_string())) }
.cloned()
#[cfg(target_arch = "wasm32")]
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure)
}
async fn measure_latency(gateway: gateway::Node) -> Result<GatewayWithLatency, ClientCoreError> {
let addr = gateway.clients_address();
trace!(
"establishing connection to {} ({addr})...",
gateway.identity_key,
);
let mut stream = connect(&addr).await?;
let mut results = Vec::new();
for _ in 0..MEASUREMENTS {
let measurement_future = async {
let ping_content = vec![1, 2, 3];
let start = Instant::now();
stream.send(Message::Ping(ping_content.clone())).await?;
match stream.next().await {
Some(Ok(Message::Pong(content))) => {
if content == ping_content {
let elapsed = Instant::now().duration_since(start);
trace!("current ping time: {elapsed:?}");
results.push(elapsed);
} else {
warn!("received a pong message with different content? wtf.")
}
}
Some(Ok(_)) => warn!("received a message that's not a pong!"),
Some(Err(err)) => return Err(err.into()),
None => return Err(ClientCoreError::GatewayConnectionAbruptlyClosed),
}
Ok::<(), ClientCoreError>(())
};
// thanks to wasm we can't use tokio::time::timeout : (
#[cfg(not(target_arch = "wasm32"))]
let timeout = tokio::time::sleep(PING_TIMEOUT);
#[cfg(not(target_arch = "wasm32"))]
tokio::pin!(timeout);
#[cfg(target_arch = "wasm32")]
let mut timeout = wasm_timer::Delay::new(PING_TIMEOUT);
tokio::select! {
_ = &mut timeout => {
warn!("timed out while trying to perform measurement...")
}
res = measurement_future => res?,
}
}
let count = results.len() as u64;
if count == 0 {
return Err(ClientCoreError::NoGatewayMeasurements {
identity: gateway.identity_key.to_base58_string(),
});
}
let sum: Duration = results.into_iter().sum();
let avg = Duration::from_nanos(sum.as_nanos() as u64 / count);
Ok(GatewayWithLatency::new(gateway, avg))
}
async fn choose_gateway_by_latency<R: Rng>(
rng: &mut R,
gateways: Vec<gateway::Node>,
) -> Result<gateway::Node, ClientCoreError> {
info!("choosing gateway by latency...");
let mut gateways_with_latency = Vec::new();
for gateway in gateways {
let id = *gateway.identity();
trace!("measuring latency to {id}...");
let with_latency = match measure_latency(gateway).await {
Ok(res) => res,
Err(err) => {
warn!("failed to measure {id}: {err}");
continue;
}
};
debug!(
"{id} ({}): {:?}",
with_latency.gateway.location, with_latency.latency
);
gateways_with_latency.push(with_latency)
}
let chosen = gateways_with_latency
.choose_weighted(rng, |item| 1. / item.latency.as_secs_f32())
.expect("invalid selection weight!");
info!(
"chose gateway {} (located at {}) with average latency of {:?}",
chosen.gateway.identity_key, chosen.gateway.location, chosen.latency
);
Ok(chosen.gateway.clone())
}
fn uniformly_random_gateway<R: Rng>(
rng: &mut R,
gateways: Vec<gateway::Node>,
) -> Result<gateway::Node, ClientCoreError> {
gateways
.choose(rng)
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
.cloned()
}
pub(super) async fn query_gateway_details(
validator_servers: Vec<Url>,
chosen_gateway_id: Option<identity::PublicKey>,
by_latency: bool,
) -> Result<gateway::Node, ClientCoreError> {
let mut rng = thread_rng();
let gateways = current_gateways(&mut rng, validator_servers).await?;
// if we set an explicit gateway, use that one and nothing else
if let Some(explicitly_chosen) = chosen_gateway_id {
gateways
.into_iter()
.find(|gateway| gateway.identity_key == explicitly_chosen)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(explicitly_chosen.to_string()))
} else if by_latency {
choose_gateway_by_latency(&mut rng, gateways).await
} else { } else {
filtered_gateways uniformly_random_gateway(&mut rng, gateways)
.choose(&mut rand::thread_rng())
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
.cloned()
} }
} }
+10 -4
View File
@@ -77,9 +77,11 @@ pub async fn register_with_gateway(
key_manager: &mut KeyManager, key_manager: &mut KeyManager,
nym_api_endpoints: Vec<Url>, nym_api_endpoints: Vec<Url>,
chosen_gateway_id: Option<identity::PublicKey>, chosen_gateway_id: Option<identity::PublicKey>,
by_latency: bool,
) -> Result<GatewayEndpointConfig, ClientCoreError> { ) -> Result<GatewayEndpointConfig, ClientCoreError> {
// Get the gateway details of the gateway we will use // Get the gateway details of the gateway we will use
let gateway = helpers::query_gateway_details(nym_api_endpoints, chosen_gateway_id).await?; let gateway =
helpers::query_gateway_details(nym_api_endpoints, chosen_gateway_id, by_latency).await?;
log::debug!("Querying gateway gives: {}", gateway); log::debug!("Querying gateway gives: {}", gateway);
let our_identity = key_manager.identity_keypair(); let our_identity = key_manager.identity_keypair();
@@ -102,6 +104,7 @@ pub async fn setup_gateway_from_config<C, T>(
register_gateway: bool, register_gateway: bool,
user_chosen_gateway_id: Option<identity::PublicKey>, user_chosen_gateway_id: Option<identity::PublicKey>,
config: &Config<T>, config: &Config<T>,
by_latency: bool,
) -> Result<GatewayEndpointConfig, ClientCoreError> ) -> Result<GatewayEndpointConfig, ClientCoreError>
where where
C: NymConfig + ClientCoreConfigTrait, C: NymConfig + ClientCoreConfigTrait,
@@ -117,9 +120,12 @@ where
} }
// Else, we preceed by querying the nym-api // Else, we preceed by querying the nym-api
let gateway = let gateway = helpers::query_gateway_details(
helpers::query_gateway_details(config.get_nym_api_endpoints(), user_chosen_gateway_id) config.get_nym_api_endpoints(),
.await?; user_chosen_gateway_id,
by_latency,
)
.await?;
log::debug!("Querying gateway gives: {}", gateway); log::debug!("Querying gateway gives: {}", gateway);
// If we are not registering, just return this and assume the caller has the keys already and // If we are not registering, just return this and assume the caller has the keys already and
+10 -4
View File
@@ -11,7 +11,7 @@ use commands::*;
use error::Result; use error::Result;
use log::*; use log::*;
use nym_bin_common::completions::fig_generate; use nym_bin_common::completions::fig_generate;
use nym_config::{DATA_DIR, DB_FILE_NAME}; use nym_config::{CRED_DB_FILE_NAME, DATA_DIR};
use nym_network_defaults::{setup_env, NymNetworkDetails}; use nym_network_defaults::{setup_env, NymNetworkDetails};
use std::process::exit; use std::process::exit;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
@@ -51,8 +51,11 @@ async fn block_until_coconut_is_available<C: Clone + CosmWasmClient + Send + Syn
break; break;
} else { } else {
// Use 20 additional seconds to avoid the exact moment of going into the final epoch state // 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() + 20 - current_timestamp_secs; 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); 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)); std::thread::sleep(Duration::from_secs(secs_until_final));
} }
@@ -70,7 +73,10 @@ async fn main() -> Result<()> {
match args.command { match args.command {
Command::Run(r) => { Command::Run(r) => {
let db_path = r.client_home_directory.join(DATA_DIR).join(DB_FILE_NAME); let db_path = r
.client_home_directory
.join(DATA_DIR)
.join(CRED_DB_FILE_NAME);
let shared_storage = credential_storage::initialise_storage(db_path).await; let shared_storage = credential_storage::initialise_storage(db_path).await;
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?; let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.1.10" version = "1.1.11"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client" description = "Implementation of the Nym Client"
edition = "2021" edition = "2021"
+6
View File
@@ -25,6 +25,11 @@ pub(crate) struct Init {
#[clap(long)] #[clap(long)]
gateway: Option<identity::PublicKey>, gateway: Option<identity::PublicKey>,
/// Specifies whether the new gateway should be determined based by latency as opposed to being chosen
/// uniformly.
#[clap(long, conflicts_with = "gateway")]
latency_based_selection: bool,
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id, /// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
/// potentially causing loss of access. /// potentially causing loss of access.
#[clap(long)] #[clap(long)]
@@ -143,6 +148,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
register_gateway, register_gateway,
user_chosen_gateway_id, user_chosen_gateway_id,
config.get_base(), config.get_base(),
args.latency_based_selection,
) )
.await .await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.10" version = "1.1.11"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] 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" description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021" edition = "2021"
+6
View File
@@ -37,6 +37,11 @@ pub(crate) struct Init {
#[clap(long)] #[clap(long)]
gateway: Option<identity::PublicKey>, gateway: Option<identity::PublicKey>,
/// Specifies whether the new gateway should be determined based by latency as opposed to being chosen
/// uniformly.
#[clap(long, conflicts_with = "gateway")]
latency_based_selection: bool,
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id, /// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
/// potentially causing loss of access. /// potentially causing loss of access.
#[clap(long)] #[clap(long)]
@@ -149,6 +154,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
register_gateway, register_gateway,
user_chosen_gateway_id, user_chosen_gateway_id,
config.get_base(), config.get_base(),
args.latency_based_selection,
) )
.await .await
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
@@ -11,9 +11,7 @@ use nym_api_requests::models::{
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse, GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse, RewardEstimationResponse, StakeSaturationResponse,
}; };
use nym_mixnet_contract_common::mixnode::MixNodeDetails; pub use nym_mixnet_contract_common::{mixnode::MixNodeDetails, GatewayBond, IdentityKeyRef, MixId};
use nym_mixnet_contract_common::MixId;
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef};
#[cfg(feature = "nyxd-client")] #[cfg(feature = "nyxd-client")]
use crate::nyxd::traits::{DkgQueryClient, MixnetQueryClient, MultisigQueryClient}; use crate::nyxd::traits::{DkgQueryClient, MixnetQueryClient, MultisigQueryClient};
+1 -1
View File
@@ -18,7 +18,7 @@ pub mod defaults;
pub const CONFIG_DIR: &str = "config"; pub const CONFIG_DIR: &str = "config";
pub const DATA_DIR: &str = "data"; pub const DATA_DIR: &str = "data";
pub const DB_FILE_NAME: &str = "db.sqlite"; pub const CRED_DB_FILE_NAME: &str = "credentials_database.db";
pub trait NymConfig: Default + Serialize + DeserializeOwned { pub trait NymConfig: Default + Serialize + DeserializeOwned {
fn template() -> &'static str; fn template() -> &'static str;
@@ -80,7 +80,7 @@ pub(crate) mod tests {
use crate::epoch_state::transactions::advance_epoch_state; use crate::epoch_state::transactions::advance_epoch_state;
use crate::support::tests::fixtures::dealer_details_fixture; use crate::support::tests::fixtures::dealer_details_fixture;
use crate::support::tests::helpers; use crate::support::tests::helpers;
use crate::support::tests::helpers::GROUP_MEMBERS; use crate::support::tests::helpers::{add_fixture_dealer, GROUP_MEMBERS};
use coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration}; use coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::testing::{mock_env, mock_info};
use cw4::Member; use cw4::Member;
@@ -147,6 +147,8 @@ pub(crate) mod tests {
.block .block
.time .time
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs); .plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
add_fixture_dealer(deps.as_mut());
advance_epoch_state(deps.as_mut(), env).unwrap(); advance_epoch_state(deps.as_mut(), env).unwrap();
let ret = try_add_dealer( let ret = try_add_dealer(
@@ -53,6 +53,7 @@ pub(crate) mod tests {
use crate::epoch_state::transactions::advance_epoch_state; use crate::epoch_state::transactions::advance_epoch_state;
use crate::support::tests::fixtures::{dealer_details_fixture, dealing_bytes_fixture}; use crate::support::tests::fixtures::{dealer_details_fixture, dealing_bytes_fixture};
use crate::support::tests::helpers; use crate::support::tests::helpers;
use crate::support::tests::helpers::add_fixture_dealer;
use coconut_dkg_common::dealer::DealerDetails; use coconut_dkg_common::dealer::DealerDetails;
use coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration}; use coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::testing::{mock_env, mock_info};
@@ -80,6 +81,7 @@ pub(crate) mod tests {
.block .block
.time .time
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs); .plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
add_fixture_dealer(deps.as_mut());
advance_epoch_state(deps.as_mut(), env).unwrap(); advance_epoch_state(deps.as_mut(), env).unwrap();
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), false) let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), false)
@@ -89,23 +89,29 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
let current_epoch = CURRENT_EPOCH.load(deps.storage)?; let current_epoch = CURRENT_EPOCH.load(deps.storage)?;
let next_epoch = if let Some(state) = current_epoch.state.next() { let next_epoch = if let Some(state) = current_epoch.state.next() {
// We are during DKG process // We are during DKG process
let mut new_state = state;
if let EpochState::DealingExchange { resharing } = state { if let EpochState::DealingExchange { resharing } = state {
let current_dealers = current_dealers() let current_dealers = current_dealers()
.keys(deps.storage, None, None, Order::Ascending) .keys(deps.storage, None, None, Order::Ascending)
.collect::<Result<Vec<Addr>, _>>()?; .collect::<Result<Vec<Addr>, _>>()?;
// note: ceiling in integer division can be achieved via q = (x + y - 1) / y; if current_dealers.is_empty() {
let threshold = (2 * current_dealers.len() as u64 + 3 - 1) / 3; // If no dealer registered yet, we just stay in the same state until there's at least one
THRESHOLD.save(deps.storage, &threshold)?; new_state = current_epoch.state;
if !resharing { } else {
let replacement_data = InitialReplacementData { // note: ceiling in integer division can be achieved via q = (x + y - 1) / y;
initial_dealers: current_dealers, let threshold = (2 * current_dealers.len() as u64 + 3 - 1) / 3;
initial_height: None, THRESHOLD.save(deps.storage, &threshold)?;
}; if !resharing {
INITIAL_REPLACEMENT_DATA.save(deps.storage, &replacement_data)?; let replacement_data = InitialReplacementData {
initial_dealers: current_dealers,
initial_height: None,
};
INITIAL_REPLACEMENT_DATA.save(deps.storage, &replacement_data)?;
}
} }
} };
Epoch::new( Epoch::new(
state, new_state,
current_epoch.epoch_id, current_epoch.epoch_id,
current_epoch.time_configuration, current_epoch.time_configuration,
env.block.time, env.block.time,
@@ -392,6 +398,14 @@ pub(crate) mod tests {
EarlyEpochStateAdvancement(1) EarlyEpochStateAdvancement(1)
); );
env.block.time = env.block.time.plus_seconds(1);
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
assert_eq!(
epoch.state,
EpochState::PublicKeySubmission { resharing: false }
);
// setup dealer details // setup dealer details
let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1)); let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1));
for details in all_details.iter() { for details in all_details.iter() {
@@ -404,7 +418,7 @@ pub(crate) mod tests {
.may_load(&deps.storage) .may_load(&deps.storage)
.unwrap() .unwrap()
.is_none()); .is_none());
env.block.time = env.block.time.plus_seconds(1); env.block.time = env.block.time.plus_seconds(epoch.time_configuration.public_key_submission_time_secs);
advance_epoch_state(deps.as_mut(), env.clone()).unwrap(); advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap(); let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
assert_eq!( assert_eq!(
@@ -2,11 +2,13 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::contract::instantiate; use crate::contract::instantiate;
use crate::dealers::storage::current_dealers;
use coconut_dkg_common::msg::InstantiateMsg; use coconut_dkg_common::msg::InstantiateMsg;
use coconut_dkg_common::types::DealerDetails;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
use cosmwasm_std::{ use cosmwasm_std::{
from_binary, to_binary, ContractResult, Empty, MemoryStorage, OwnedDeps, QuerierResult, from_binary, to_binary, Addr, ContractResult, DepsMut, Empty, MemoryStorage, OwnedDeps,
SystemResult, WasmQuery, QuerierResult, SystemResult, WasmQuery,
}; };
use cw4::{Cw4QueryMsg, Member, MemberListResponse, MemberResponse}; use cw4::{Cw4QueryMsg, Member, MemberListResponse, MemberResponse};
use lazy_static::lazy_static; use lazy_static::lazy_static;
@@ -22,6 +24,22 @@ lazy_static! {
pub static ref GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(vec![]); pub static ref GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(vec![]);
} }
pub fn add_fixture_dealer(deps: DepsMut<'_>) {
let owner = Addr::unchecked("owner");
current_dealers()
.save(
deps.storage,
&owner,
&DealerDetails {
address: owner.clone(),
bte_public_key_with_proof: String::new(),
announce_address: String::new(),
assigned_index: 100,
},
)
.unwrap();
}
fn querier_handler(query: &WasmQuery) -> QuerierResult { fn querier_handler(query: &WasmQuery) -> QuerierResult {
let bin = match query { let bin = match query {
WasmQuery::Smart { contract_addr, msg } => { WasmQuery::Smart { contract_addr, msg } => {
@@ -91,7 +91,7 @@ mod tests {
use super::*; use super::*;
use crate::epoch_state::transactions::advance_epoch_state; use crate::epoch_state::transactions::advance_epoch_state;
use crate::support::tests::helpers; use crate::support::tests::helpers;
use crate::support::tests::helpers::MULTISIG_CONTRACT; use crate::support::tests::helpers::{add_fixture_dealer, MULTISIG_CONTRACT};
use coconut_dkg_common::dealer::DealerDetails; use coconut_dkg_common::dealer::DealerDetails;
use coconut_dkg_common::types::{EpochState, TimeConfiguration}; use coconut_dkg_common::types::{EpochState, TimeConfiguration};
use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::testing::{mock_env, mock_info};
@@ -104,6 +104,7 @@ mod tests {
let info = mock_info("requester", &[]); let info = mock_info("requester", &[]);
let share = "share".to_string(); let share = "share".to_string();
add_fixture_dealer(deps.as_mut());
env.block.time = env env.block.time = env
.block .block
.time .time
@@ -171,6 +172,7 @@ mod tests {
.to_string() .to_string()
} }
); );
add_fixture_dealer(deps.as_mut());
env.block.time = env env.block.time = env
.block .block
.time .time
@@ -247,6 +249,7 @@ mod tests {
} }
); );
add_fixture_dealer(deps.as_mut());
env.block.time = env env.block.time = env
.block .block
.time .time
@@ -292,6 +295,7 @@ mod tests {
let share = "share".to_string(); let share = "share".to_string();
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]); let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
add_fixture_dealer(deps.as_mut());
env.block.time = env env.block.time = env
.block .block
.time .time
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "explorer-api" name = "explorer-api"
version = "1.1.10" version = "1.1.11"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@nym/network-explorer", "name": "@nym/network-explorer",
"version": "1.0.5", "version": "1.0.6",
"private": true, "private": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -121,4 +121,4 @@
"last 1 safari version" "last 1 safari version"
] ]
} }
} }
+2 -3
View File
@@ -29,7 +29,6 @@ import {
GatewayBondAnnotated, GatewayBondAnnotated,
GatewayBond, GatewayBond,
} from '../typeDefs/explorer-api'; } from '../typeDefs/explorer-api';
import { toPercentIntegerString } from '../utils';
function getFromCache(key: string) { function getFromCache(key: string) {
const ts = Number(localStorage.getItem('ts')); const ts = Number(localStorage.getItem('ts'));
@@ -94,9 +93,9 @@ export class Api {
static fetchGateways = async (): Promise<GatewayBond[]> => { static fetchGateways = async (): Promise<GatewayBond[]> => {
const res = await fetch(GATEWAYS_API); const res = await fetch(GATEWAYS_API);
const gatewaysAnnotated: GatewayBondAnnotated[] = await res.json(); const gatewaysAnnotated: GatewayBondAnnotated[] = await res.json();
return gatewaysAnnotated.map(({ gateway_bond, performance }) => ({ return gatewaysAnnotated.map(({ gateway_bond, node_performance }) => ({
...gateway_bond, ...gateway_bond,
performance: toPercentIntegerString(performance), node_performance,
})); }));
}; };
+4 -3
View File
@@ -1,4 +1,5 @@
import { GatewayResponse, GatewayBond, GatewayReportResponse } from '../typeDefs/explorer-api'; import { GatewayResponse, GatewayBond, GatewayReportResponse } from '../typeDefs/explorer-api';
import { toPercentIntegerString } from '../utils';
export type GatewayRowType = { export type GatewayRowType = {
id: string; id: string;
@@ -8,7 +9,7 @@ export type GatewayRowType = {
host: string; host: string;
location: string; location: string;
version: string; version: string;
performance: string; node_performance: string;
}; };
export type GatewayEnrichedRowType = GatewayRowType & { export type GatewayEnrichedRowType = GatewayRowType & {
@@ -29,7 +30,7 @@ export function gatewayToGridRow(arrayOfGateways: GatewayResponse): GatewayRowTy
bond: gw.pledge_amount.amount || 0, bond: gw.pledge_amount.amount || 0,
host: gw.gateway.host || '', host: gw.gateway.host || '',
version: gw.gateway.version || '', version: gw.gateway.version || '',
performance: gw.performance, node_performance: toPercentIntegerString(gw.node_performance.last_24h),
})); }));
} }
@@ -46,6 +47,6 @@ export function gatewayEnrichedToGridRow(gateway: GatewayBond, report: GatewayRe
mixPort: gateway.gateway.mix_port || 0, mixPort: gateway.gateway.mix_port || 0,
routingScore: `${report.most_recent}%`, routingScore: `${report.most_recent}%`,
avgUptime: `${report.last_day || report.last_hour}%`, avgUptime: `${report.last_day || report.last_hour}%`,
performance: gateway.performance, node_performance: toPercentIntegerString(gateway.node_performance.most_recent),
}; };
} }
@@ -38,7 +38,7 @@ const CustomPagination = () => {
color="primary" color="primary"
count={state.pagination.pageCount} count={state.pagination.pageCount}
page={state.pagination.page + 1} page={state.pagination.page + 1}
onChange={(event, value) => apiRef.current.setPage(value - 1)} onChange={(_, value) => apiRef.current.setPage(value - 1)}
/> />
); );
}; };
+3 -3
View File
@@ -26,7 +26,7 @@ const columns: ColumnsType[] = [
headerAlign: 'left', headerAlign: 'left',
}, },
{ {
field: 'routingScore', field: 'node_performance',
title: 'Routing Score', title: 'Routing Score',
flex: 1, flex: 1,
headerAlign: 'left', headerAlign: 'left',
@@ -130,13 +130,13 @@ const PageGatewayDetailsWithState = ({ selectedGateway }: { selectedGateway: Gat
* Guard component to handle loading and not found states * Guard component to handle loading and not found states
*/ */
const PageGatewayDetailGuard: FCWithChildren = () => { const PageGatewayDetailGuard: FCWithChildren = () => {
const [selectedGateway, setSelectedGateway] = React.useState<GatewayBond | undefined>(); const [selectedGateway, setSelectedGateway] = React.useState<GatewayBond>();
const { gateways } = useMainContext(); const { gateways } = useMainContext();
const { id } = useParams<{ id: string | undefined }>(); const { id } = useParams<{ id: string | undefined }>();
React.useEffect(() => { React.useEffect(() => {
if (gateways?.data) { if (gateways?.data) {
setSelectedGateway(gateways.data.find((gateway) => gateway.gateway.identity_key === id)); setSelectedGateway(gateways.data.find((g) => g.gateway.identity_key === id));
} }
}, [gateways, id]); }, [gateways, id]);
+8 -11
View File
@@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { Link as RRDLink } from 'react-router-dom'; import { Link as RRDLink } from 'react-router-dom';
import { Box, Button, Card, Grid, Link as MuiLink } from '@mui/material'; import { Box, Card, Grid, Link as MuiLink } from '@mui/material';
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'; import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard';
import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid'; import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
import { SelectChangeEvent } from '@mui/material/Select'; import { SelectChangeEvent } from '@mui/material/Select';
@@ -86,7 +86,6 @@ export const PageGateways: FCWithChildren = () => {
const columns: GridColDef[] = [ const columns: GridColDef[] = [
{ {
field: 'identity_key', field: 'identity_key',
headerName: 'Identity Key',
renderHeader: () => <CustomColumnHeading headingTitle="Identity Key" />, renderHeader: () => <CustomColumnHeading headingTitle="Identity Key" />,
headerClassName: 'MuiDataGrid-header-override', headerClassName: 'MuiDataGrid-header-override',
width: 380, width: 380,
@@ -119,7 +118,7 @@ export const PageGateways: FCWithChildren = () => {
<MuiLink <MuiLink
sx={{ ...cellStyles }} sx={{ ...cellStyles }}
component={RRDLink} component={RRDLink}
to={`/network-components/gateway/${params.row.identityKey}`} to={`/network-components/gateway/${params.row.identity_key}`}
data-testid="pledge-amount" data-testid="pledge-amount"
> >
{unymToNym(params.value, 6)} {unymToNym(params.value, 6)}
@@ -127,8 +126,7 @@ export const PageGateways: FCWithChildren = () => {
), ),
}, },
{ {
field: 'performance', field: 'node_performance',
headerName: 'Routing Score',
renderHeader: () => <CustomColumnHeading headingTitle="Routing Score" />, renderHeader: () => <CustomColumnHeading headingTitle="Routing Score" />,
width: 150, width: 150,
headerAlign: 'left', headerAlign: 'left',
@@ -137,7 +135,7 @@ export const PageGateways: FCWithChildren = () => {
<MuiLink <MuiLink
sx={{ ...cellStyles }} sx={{ ...cellStyles }}
component={RRDLink} component={RRDLink}
to={`/network-components/gateway/${params.row.identityKey}`} to={`/network-components/gateway/${params.row.identity_key}`}
data-testid="pledge-amount" data-testid="pledge-amount"
> >
{`${params.value}%`} {`${params.value}%`}
@@ -154,7 +152,7 @@ export const PageGateways: FCWithChildren = () => {
<MuiLink <MuiLink
sx={{ ...cellStyles }} sx={{ ...cellStyles }}
component={RRDLink} component={RRDLink}
to={`/network-components/gateway/${params.row.identityKey}`} to={`/network-components/gateway/${params.row.identity_key}`}
data-testid="host" data-testid="host"
> >
{params.value} {params.value}
@@ -168,9 +166,9 @@ export const PageGateways: FCWithChildren = () => {
headerAlign: 'left', headerAlign: 'left',
headerClassName: 'MuiDataGrid-header-override', headerClassName: 'MuiDataGrid-header-override',
renderCell: (params: GridRenderCellParams) => ( renderCell: (params: GridRenderCellParams) => (
<Button <Box
onClick={() => handleSearch(params.value as string)} onClick={() => handleSearch(params.value as string)}
sx={{ ...cellStyles, justifyContent: 'flex-start' }} sx={{ ...cellStyles, justifyContent: 'flex-start', cursor: 'pointer' }}
data-testid="location-button" data-testid="location-button"
> >
<Tooltip text={params.value} id="gateway-location-text"> <Tooltip text={params.value} id="gateway-location-text">
@@ -184,7 +182,7 @@ export const PageGateways: FCWithChildren = () => {
{params.value} {params.value}
</Box> </Box>
</Tooltip> </Tooltip>
</Button> </Box>
), ),
}, },
{ {
@@ -207,7 +205,6 @@ export const PageGateways: FCWithChildren = () => {
}, },
{ {
field: 'version', field: 'version',
headerName: 'Version',
renderHeader: () => <CustomColumnHeading headingTitle="Version" />, renderHeader: () => <CustomColumnHeading headingTitle="Version" />,
width: 150, width: 150,
headerAlign: 'left', headerAlign: 'left',
+8 -2
View File
@@ -114,6 +114,12 @@ export interface StatsResponse {
packets_explicitly_dropped_since_last_update: number; packets_explicitly_dropped_since_last_update: number;
} }
export interface NodePerformance {
most_recent: string;
last_hour: string;
last_24h: string;
}
export type MixNodeHistoryResponse = StatsResponse; export type MixNodeHistoryResponse = StatsResponse;
export interface GatewayBond { export interface GatewayBond {
@@ -122,12 +128,12 @@ export interface GatewayBond {
total_delegation: Amount; total_delegation: Amount;
owner: string; owner: string;
gateway: Gateway; gateway: Gateway;
performance: string; node_performance: NodePerformance;
} }
export interface GatewayBondAnnotated { export interface GatewayBondAnnotated {
gateway_bond: GatewayBond; gateway_bond: GatewayBond;
performance: string; node_performance: NodePerformance;
} }
export type GatewayResponse = GatewayBond[]; export type GatewayResponse = GatewayBond[];
+1 -1
View File
@@ -3,7 +3,7 @@
[package] [package]
name = "nym-gateway" name = "nym-gateway"
version = "1.1.10" version = "1.1.11"
authors = [ authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>", "Jędrzej Stuczyński <andrew@nymtech.net>",
+1 -1
View File
@@ -3,7 +3,7 @@
[package] [package]
name = "nym-mixnode" name = "nym-mixnode"
version = "1.1.11" version = "1.1.12"
authors = [ authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>", "Jędrzej Stuczyński <andrew@nymtech.net>",
+1 -1
View File
@@ -3,7 +3,7 @@
[package] [package]
name = "nym-api" name = "nym-api"
version = "1.1.11" version = "1.1.12"
authors = [ authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>", "Jędrzej Stuczyński <andrew@nymtech.net>",
+40 -1
View File
@@ -1,7 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net> // Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Coin, Decimal}; use cosmwasm_std::{Addr, Coin, Decimal};
use nym_mixnet_contract_common::families::FamilyHead; use nym_mixnet_contract_common::families::FamilyHead;
use nym_mixnet_contract_common::mixnode::MixNodeDetails; use nym_mixnet_contract_common::mixnode::MixNodeDetails;
use nym_mixnet_contract_common::reward_params::{Performance, RewardingParams}; use nym_mixnet_contract_common::reward_params::{Performance, RewardingParams};
@@ -93,12 +93,21 @@ pub struct MixnodeStatusResponse {
pub status: MixnodeStatus, pub status: MixnodeStatus,
} }
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct NodePerformance {
pub most_recent: Performance,
pub last_hour: Performance,
pub last_24h: Performance,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct MixNodeBondAnnotated { pub struct MixNodeBondAnnotated {
pub mixnode_details: MixNodeDetails, pub mixnode_details: MixNodeDetails,
pub stake_saturation: StakeSaturation, pub stake_saturation: StakeSaturation,
pub uncapped_stake_saturation: StakeSaturation, pub uncapped_stake_saturation: StakeSaturation,
// NOTE: the performance field is deprecated in favour of node_performance
pub performance: Performance, pub performance: Performance,
pub node_performance: NodePerformance,
pub estimated_operator_apy: Decimal, pub estimated_operator_apy: Decimal,
pub estimated_delegators_apy: Decimal, pub estimated_delegators_apy: Decimal,
pub family: Option<FamilyHead>, pub family: Option<FamilyHead>,
@@ -112,12 +121,32 @@ impl MixNodeBondAnnotated {
pub fn mix_id(&self) -> MixId { pub fn mix_id(&self) -> MixId {
self.mixnode_details.mix_id() self.mixnode_details.mix_id()
} }
pub fn identity_key(&self) -> &str {
self.mixnode_details.bond_information.identity()
}
pub fn owner(&self) -> &Addr {
self.mixnode_details.bond_information.owner()
}
} }
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct GatewayBondAnnotated { pub struct GatewayBondAnnotated {
pub gateway_bond: GatewayBond, pub gateway_bond: GatewayBond,
// NOTE: the performance field is deprecated in favour of node_performance
pub performance: Performance, pub performance: Performance,
pub node_performance: NodePerformance,
}
impl GatewayBondAnnotated {
pub fn identity(&self) -> &String {
self.gateway_bond.identity()
}
pub fn owner(&self) -> &Addr {
self.gateway_bond.owner()
}
} }
#[derive(Debug, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
@@ -147,7 +176,17 @@ pub struct RewardEstimationResponse {
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct UptimeResponse { pub struct UptimeResponse {
pub mix_id: MixId, pub mix_id: MixId,
// The same as node_performance.last_24h. Legacy
pub avg_uptime: u8, pub avg_uptime: u8,
pub node_performance: NodePerformance,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct GatewayUptimeResponse {
pub identity: String,
// The same as node_performance.last_24h. Legacy
pub avg_uptime: u8,
pub node_performance: NodePerformance,
} }
#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema)]
+27 -4
View File
@@ -1,6 +1,6 @@
use crate::node_status_api::reward_estimate::{compute_apy_from_reward, compute_reward_estimate}; use crate::node_status_api::reward_estimate::{compute_apy_from_reward, compute_reward_estimate};
use crate::support::storage::NymApiStorage; use crate::support::storage::NymApiStorage;
use nym_api_requests::models::{GatewayBondAnnotated, MixNodeBondAnnotated}; use nym_api_requests::models::{GatewayBondAnnotated, MixNodeBondAnnotated, NodePerformance};
use nym_mixnet_contract_common::families::FamilyHead; use nym_mixnet_contract_common::families::FamilyHead;
use nym_mixnet_contract_common::{reward_params::Performance, Interval, MixId}; use nym_mixnet_contract_common::{reward_params::Performance, Interval, MixId};
use nym_mixnet_contract_common::{ use nym_mixnet_contract_common::{
@@ -99,16 +99,15 @@ pub(super) async fn annotate_nodes_with_details(
.rewarding_details .rewarding_details
.uncapped_bond_saturation(&interval_reward_params); .uncapped_bond_saturation(&interval_reward_params);
let rewarded_set_status = rewarded_set.get(&mixnode.mix_id()).copied();
// If the performance can't be obtained, because the nym-api was not started with // If the performance can't be obtained, because the nym-api was not started with
// the monitoring (and hence, storage), then reward estimates will be all zero // the monitoring (and hence, storage), then reward estimates will be all zero
let performance = let performance =
get_mixnode_performance_from_storage(storage, mixnode.mix_id(), current_interval) get_mixnode_performance_from_storage(storage, mixnode.mix_id(), current_interval)
.await .await
.unwrap_or_default(); .unwrap_or_default();
let rewarded_set_status = rewarded_set.get(&mixnode.mix_id()).copied();
let reward_estimate = compute_reward_estimate( let reward_estimate = compute_reward_estimate(
&mixnode, &mixnode,
performance, performance,
@@ -117,6 +116,17 @@ pub(super) async fn annotate_nodes_with_details(
current_interval, current_interval,
); );
let node_performance = if let Some(storage) = storage {
storage
.construct_mixnode_report(mixnode.mix_id())
.await
.map(NodePerformance::from)
.ok()
} else {
None
}
.unwrap_or_default();
let (estimated_operator_apy, estimated_delegators_apy) = let (estimated_operator_apy, estimated_delegators_apy) =
compute_apy_from_reward(&mixnode, reward_estimate, current_interval); compute_apy_from_reward(&mixnode, reward_estimate, current_interval);
@@ -129,6 +139,7 @@ pub(super) async fn annotate_nodes_with_details(
stake_saturation, stake_saturation,
uncapped_stake_saturation, uncapped_stake_saturation,
performance, performance,
node_performance,
estimated_operator_apy, estimated_operator_apy,
estimated_delegators_apy, estimated_delegators_apy,
family, family,
@@ -152,9 +163,21 @@ pub(crate) async fn annotate_gateways_with_details(
.await .await
.unwrap_or_default(); .unwrap_or_default();
let node_performance = if let Some(storage) = storage {
storage
.construct_gateway_report(gateway_bond.identity())
.await
.map(NodePerformance::from)
.ok()
} else {
None
}
.unwrap_or_default();
annotated.push(GatewayBondAnnotated { annotated.push(GatewayBondAnnotated {
gateway_bond, gateway_bond,
performance, performance,
node_performance,
}); });
} }
annotated annotated
+115 -37
View File
@@ -8,26 +8,112 @@ use crate::{NodeStatusCache, NymContractCache};
use cosmwasm_std::Decimal; use cosmwasm_std::Decimal;
use nym_api_requests::models::{ use nym_api_requests::models::{
AllInclusionProbabilitiesResponse, ComputeRewardEstParam, GatewayBondAnnotated, AllInclusionProbabilitiesResponse, ComputeRewardEstParam, GatewayBondAnnotated,
InclusionProbabilityResponse, MixNodeBondAnnotated, MixnodeCoreStatusResponse, GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
MixnodeStatusReportResponse, MixnodeStatusResponse, MixnodeUptimeHistoryResponse, GatewayUptimeResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse, MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse,
MixnodeUptimeHistoryResponse, RewardEstimationResponse, StakeSaturationResponse,
UptimeResponse,
}; };
use nym_mixnet_contract_common::reward_params::Performance; use nym_mixnet_contract_common::{MixId, RewardedSetNodeStatus};
use nym_mixnet_contract_common::{Interval, MixId, RewardedSetNodeStatus};
use rocket::http::Status; use rocket::http::Status;
use rocket::State; use rocket::State;
use super::reward_estimate::compute_reward_estimate; use super::reward_estimate::compute_reward_estimate;
pub(crate) async fn _mixnode_report( async fn get_gateway_bond_annotated(
cache: &NodeStatusCache,
identity: &str,
) -> Result<GatewayBondAnnotated, ErrorResponse> {
let gateways = cache.gateways_annotated().await.ok_or(ErrorResponse::new(
"no data available",
Status::ServiceUnavailable,
))?;
gateways
.into_inner()
.into_iter()
.find(|gateway| gateway.identity() == identity)
.ok_or(ErrorResponse::new(
"mixnode bond not found",
Status::NotFound,
))
}
async fn get_mixnode_bond_annotated(
cache: &NodeStatusCache,
mix_id: MixId,
) -> Result<MixNodeBondAnnotated, ErrorResponse> {
let mixnodes = cache.mixnodes_annotated().await.ok_or(ErrorResponse::new(
"no data available",
Status::ServiceUnavailable,
))?;
mixnodes
.into_inner()
.into_iter()
.find(|mixnode| mixnode.mix_id() == mix_id)
.ok_or(ErrorResponse::new(
"mixnode bond not found",
Status::NotFound,
))
}
pub(crate) async fn _gateway_report(
cache: &NodeStatusCache,
identity: &str,
) -> Result<GatewayStatusReportResponse, ErrorResponse> {
let gateway = get_gateway_bond_annotated(cache, identity).await?;
Ok(GatewayStatusReportResponse {
identity: gateway.identity().to_owned(),
owner: gateway.owner().to_string(),
most_recent: gateway.node_performance.most_recent.round_to_integer(),
last_hour: gateway.node_performance.last_hour.round_to_integer(),
last_day: gateway.node_performance.last_24h.round_to_integer(),
})
}
pub(crate) async fn _gateway_uptime_history(
storage: &NymApiStorage, storage: &NymApiStorage,
identity: &str,
) -> Result<GatewayUptimeHistoryResponse, ErrorResponse> {
storage
.get_gateway_uptime_history(identity)
.await
.map(GatewayUptimeHistoryResponse::from)
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
}
pub(crate) async fn _gateway_core_status_count(
storage: &State<NymApiStorage>,
identity: &str,
since: Option<i64>,
) -> Result<GatewayCoreStatusResponse, ErrorResponse> {
let count = storage
.get_core_gateway_status_count(identity, since)
.await
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))?;
Ok(GatewayCoreStatusResponse {
identity: identity.to_string(),
count,
})
}
pub(crate) async fn _mixnode_report(
cache: &NodeStatusCache,
mix_id: MixId, mix_id: MixId,
) -> Result<MixnodeStatusReportResponse, ErrorResponse> { ) -> Result<MixnodeStatusReportResponse, ErrorResponse> {
storage let mixnode = get_mixnode_bond_annotated(cache, mix_id).await?;
.construct_mixnode_report(mix_id)
.await Ok(MixnodeStatusReportResponse {
.map(MixnodeStatusReportResponse::from) mix_id,
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound)) identity: mixnode.identity_key().to_owned(),
owner: mixnode.owner().to_string(),
most_recent: mixnode.node_performance.most_recent.round_to_integer(),
last_hour: mixnode.node_performance.last_hour.round_to_integer(),
last_day: mixnode.node_performance.last_24h.round_to_integer(),
})
} }
pub(crate) async fn _mixnode_uptime_history( pub(crate) async fn _mixnode_uptime_history(
@@ -103,21 +189,6 @@ pub(crate) async fn _get_mixnode_reward_estimation(
} }
} }
async fn average_mixnode_performance(
mix_id: MixId,
current_interval: Interval,
storage: &NymApiStorage,
) -> Result<Performance, ErrorResponse> {
storage
.get_average_mixnode_uptime_in_the_last_24hrs(
mix_id,
current_interval.current_epoch_end_unix_timestamp(),
)
.await
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
.map(Into::into)
}
pub(crate) async fn _compute_mixnode_reward_estimation( pub(crate) async fn _compute_mixnode_reward_estimation(
user_reward_param: ComputeRewardEstParam, user_reward_param: ComputeRewardEstParam,
cache: &NodeStatusCache, cache: &NodeStatusCache,
@@ -254,21 +325,28 @@ pub(crate) async fn _get_mixnode_inclusion_probability(
} }
pub(crate) async fn _get_mixnode_avg_uptime( pub(crate) async fn _get_mixnode_avg_uptime(
cache: &NymContractCache, cache: &NodeStatusCache,
storage: &NymApiStorage,
mix_id: MixId, mix_id: MixId,
) -> Result<UptimeResponse, ErrorResponse> { ) -> Result<UptimeResponse, ErrorResponse> {
let current_interval = cache let mixnode = get_mixnode_bond_annotated(cache, mix_id).await?;
.current_interval()
.await
.into_inner()
.ok_or_else(|| ErrorResponse::new("server error", Status::InternalServerError))?;
let performance = average_mixnode_performance(mix_id, current_interval, storage).await?;
Ok(UptimeResponse { Ok(UptimeResponse {
mix_id, mix_id,
avg_uptime: performance.round_to_integer(), avg_uptime: mixnode.node_performance.last_24h.round_to_integer(),
node_performance: mixnode.node_performance,
})
}
pub(crate) async fn _get_gateway_avg_uptime(
cache: &NodeStatusCache,
identity: &str,
) -> Result<GatewayUptimeResponse, ErrorResponse> {
let gateway = get_gateway_bond_annotated(cache, identity).await?;
Ok(GatewayUptimeResponse {
identity: identity.to_string(),
avg_uptime: gateway.node_performance.last_24h.round_to_integer(),
node_performance: gateway.node_performance,
}) })
} }
@@ -288,7 +366,7 @@ pub(crate) async fn _get_mixnode_inclusion_probabilities(
}) })
} else { } else {
Err(ErrorResponse::new( Err(ErrorResponse::new(
"No data available".to_string(), "No data available",
Status::ServiceUnavailable, Status::ServiceUnavailable,
)) ))
} }
+1
View File
@@ -45,6 +45,7 @@ pub(crate) fn node_status_routes(
routes::get_mixnode_stake_saturation, routes::get_mixnode_stake_saturation,
routes::get_mixnode_inclusion_probability, routes::get_mixnode_inclusion_probability,
routes::get_mixnode_avg_uptime, routes::get_mixnode_avg_uptime,
routes::get_gateway_avg_uptime,
routes::get_mixnode_inclusion_probabilities, routes::get_mixnode_inclusion_probabilities,
routes::get_mixnodes_detailed, routes::get_mixnodes_detailed,
routes::get_rewarded_set_detailed, routes::get_rewarded_set_detailed,
+21 -1
View File
@@ -5,7 +5,7 @@ use crate::node_status_api::utils::NodeUptimes;
use crate::storage::models::NodeStatus; use crate::storage::models::NodeStatus;
use nym_api_requests::models::{ use nym_api_requests::models::{
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, HistoricalUptimeResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse, HistoricalUptimeResponse,
MixnodeStatusReportResponse, MixnodeUptimeHistoryResponse, RequestError, MixnodeStatusReportResponse, MixnodeUptimeHistoryResponse, NodePerformance, RequestError,
}; };
use nym_mixnet_contract_common::reward_params::Performance; use nym_mixnet_contract_common::reward_params::Performance;
use nym_mixnet_contract_common::{IdentityKey, MixId}; use nym_mixnet_contract_common::{IdentityKey, MixId};
@@ -179,6 +179,16 @@ impl From<MixnodeStatusReport> for MixnodeStatusReportResponse {
} }
} }
impl From<MixnodeStatusReport> for NodePerformance {
fn from(report: MixnodeStatusReport) -> Self {
NodePerformance {
most_recent: report.most_recent.into(),
last_hour: report.last_hour.into(),
last_24h: report.last_day.into(),
}
}
}
#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)] #[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
pub struct GatewayStatusReport { pub struct GatewayStatusReport {
pub(crate) identity: String, pub(crate) identity: String,
@@ -228,6 +238,16 @@ impl From<GatewayStatusReport> for GatewayStatusReportResponse {
} }
} }
impl From<GatewayStatusReport> for NodePerformance {
fn from(report: GatewayStatusReport) -> Self {
NodePerformance {
most_recent: report.most_recent.into(),
last_hour: report.last_hour.into(),
last_24h: report.last_day.into(),
}
}
}
#[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)] #[derive(Clone, Serialize, Deserialize, Debug, JsonSchema)]
pub struct MixnodeUptimeHistory { pub struct MixnodeUptimeHistory {
pub(crate) mix_id: MixId, pub(crate) mix_id: MixId,
+31 -37
View File
@@ -4,11 +4,13 @@
use super::helpers::_get_gateways_detailed; use super::helpers::_get_gateways_detailed;
use super::NodeStatusCache; use super::NodeStatusCache;
use crate::node_status_api::helpers::{ use crate::node_status_api::helpers::{
_compute_mixnode_reward_estimation, _get_active_set_detailed, _get_mixnode_avg_uptime, _compute_mixnode_reward_estimation, _gateway_core_status_count, _gateway_report,
_get_mixnode_inclusion_probabilities, _get_mixnode_inclusion_probability, _gateway_uptime_history, _get_active_set_detailed, _get_gateway_avg_uptime,
_get_mixnode_reward_estimation, _get_mixnode_stake_saturation, _get_mixnode_status, _get_mixnode_avg_uptime, _get_mixnode_inclusion_probabilities,
_get_mixnodes_detailed, _get_rewarded_set_detailed, _mixnode_core_status_count, _get_mixnode_inclusion_probability, _get_mixnode_reward_estimation,
_mixnode_report, _mixnode_uptime_history, _get_mixnode_stake_saturation, _get_mixnode_status, _get_mixnodes_detailed,
_get_rewarded_set_detailed, _mixnode_core_status_count, _mixnode_report,
_mixnode_uptime_history,
}; };
use crate::node_status_api::models::ErrorResponse; use crate::node_status_api::models::ErrorResponse;
use crate::storage::NymApiStorage; use crate::storage::NymApiStorage;
@@ -16,12 +18,12 @@ use crate::NymContractCache;
use nym_api_requests::models::{ use nym_api_requests::models::{
AllInclusionProbabilitiesResponse, ComputeRewardEstParam, GatewayBondAnnotated, AllInclusionProbabilitiesResponse, ComputeRewardEstParam, GatewayBondAnnotated,
GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse, GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
InclusionProbabilityResponse, MixNodeBondAnnotated, MixnodeCoreStatusResponse, GatewayUptimeResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
MixnodeStatusReportResponse, MixnodeStatusResponse, MixnodeUptimeHistoryResponse, MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse, StakeSaturationResponse,
UptimeResponse,
}; };
use nym_mixnet_contract_common::MixId; use nym_mixnet_contract_common::MixId;
use rocket::http::Status;
use rocket::serde::json::Json; use rocket::serde::json::Json;
use rocket::State; use rocket::State;
use rocket_okapi::openapi; use rocket_okapi::openapi;
@@ -29,15 +31,10 @@ use rocket_okapi::openapi;
#[openapi(tag = "status")] #[openapi(tag = "status")]
#[get("/gateway/<identity>/report")] #[get("/gateway/<identity>/report")]
pub(crate) async fn gateway_report( pub(crate) async fn gateway_report(
storage: &State<NymApiStorage>, cache: &State<NodeStatusCache>,
identity: &str, identity: &str,
) -> Result<Json<GatewayStatusReportResponse>, ErrorResponse> { ) -> Result<Json<GatewayStatusReportResponse>, ErrorResponse> {
storage Ok(Json(_gateway_report(cache, identity).await?))
.construct_gateway_report(identity)
.await
.map(GatewayStatusReportResponse::from)
.map(Json)
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
} }
#[openapi(tag = "status")] #[openapi(tag = "status")]
@@ -46,12 +43,7 @@ pub(crate) async fn gateway_uptime_history(
storage: &State<NymApiStorage>, storage: &State<NymApiStorage>,
identity: &str, identity: &str,
) -> Result<Json<GatewayUptimeHistoryResponse>, ErrorResponse> { ) -> Result<Json<GatewayUptimeHistoryResponse>, ErrorResponse> {
storage Ok(Json(_gateway_uptime_history(storage, identity).await?))
.get_gateway_uptime_history(identity)
.await
.map(GatewayUptimeHistoryResponse::from)
.map(Json)
.map_err(|err| ErrorResponse::new(err.to_string(), Status::NotFound))
} }
#[openapi(tag = "status")] #[openapi(tag = "status")]
@@ -60,25 +52,19 @@ pub(crate) async fn gateway_core_status_count(
storage: &State<NymApiStorage>, storage: &State<NymApiStorage>,
identity: &str, identity: &str,
since: Option<i64>, since: Option<i64>,
) -> Json<GatewayCoreStatusResponse> { ) -> Result<Json<GatewayCoreStatusResponse>, ErrorResponse> {
let count = storage Ok(Json(
.get_core_gateway_status_count(identity, since) _gateway_core_status_count(storage, identity, since).await?,
.await ))
.unwrap_or_default();
Json(GatewayCoreStatusResponse {
identity: identity.to_string(),
count,
})
} }
#[openapi(tag = "status")] #[openapi(tag = "status")]
#[get("/mixnode/<mix_id>/report")] #[get("/mixnode/<mix_id>/report")]
pub(crate) async fn mixnode_report( pub(crate) async fn mixnode_report(
storage: &State<NymApiStorage>, cache: &State<NodeStatusCache>,
mix_id: MixId, mix_id: MixId,
) -> Result<Json<MixnodeStatusReportResponse>, ErrorResponse> { ) -> Result<Json<MixnodeStatusReportResponse>, ErrorResponse> {
Ok(Json(_mixnode_report(storage, mix_id).await?)) Ok(Json(_mixnode_report(cache, mix_id).await?))
} }
#[openapi(tag = "status")] #[openapi(tag = "status")]
@@ -171,11 +157,19 @@ pub(crate) async fn get_mixnode_inclusion_probability(
#[openapi(tag = "status")] #[openapi(tag = "status")]
#[get("/mixnode/<mix_id>/avg_uptime")] #[get("/mixnode/<mix_id>/avg_uptime")]
pub(crate) async fn get_mixnode_avg_uptime( pub(crate) async fn get_mixnode_avg_uptime(
cache: &State<NymContractCache>, cache: &State<NodeStatusCache>,
storage: &State<NymApiStorage>,
mix_id: MixId, mix_id: MixId,
) -> Result<Json<UptimeResponse>, ErrorResponse> { ) -> Result<Json<UptimeResponse>, ErrorResponse> {
Ok(Json(_get_mixnode_avg_uptime(cache, storage, mix_id).await?)) Ok(Json(_get_mixnode_avg_uptime(cache, mix_id).await?))
}
#[openapi(tag = "status")]
#[get("/gateway/<identity>/avg_uptime")]
pub(crate) async fn get_gateway_avg_uptime(
cache: &State<NodeStatusCache>,
identity: &str,
) -> Result<Json<GatewayUptimeResponse>, ErrorResponse> {
Ok(Json(_get_gateway_avg_uptime(cache, identity).await?))
} }
#[openapi(tag = "status")] #[openapi(tag = "status")]
+2 -1
View File
@@ -26,7 +26,8 @@ const DEFAULT_GATEWAY_PING_INTERVAL: Duration = Duration::from_secs(60);
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the // bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
// bandwidth bridging protocol, we can come back to a smaller timeout value // bandwidth bridging protocol, we can come back to a smaller timeout value
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60); const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
const DEFAULT_GATEWAY_CONNECTION_TIMEOUT: Duration = Duration::from_millis(2_500); // This timeout value should be big enough to accommodate an initial bandwidth acquirement
const DEFAULT_GATEWAY_CONNECTION_TIMEOUT: Duration = Duration::from_secs(2 * 60);
const DEFAULT_TEST_ROUTES: usize = 3; const DEFAULT_TEST_ROUTES: usize = 3;
const DEFAULT_MINIMUM_TEST_ROUTES: usize = 1; const DEFAULT_MINIMUM_TEST_ROUTES: usize = 1;
@@ -34,7 +34,7 @@ describe("Get mixnode data", (): void => {
expect(typeof mixnode.bond_information.mix_node.verloc_port).toBe("number"); expect(typeof mixnode.bond_information.mix_node.verloc_port).toBe("number");
expect(typeof mixnode.bond_information.mix_node.mix_port).toBe("number"); expect(typeof mixnode.bond_information.mix_node.mix_port).toBe("number");
expect(mixnode.bond_information.mix_node.mix_port).toStrictEqual(1789); expect(mixnode.bond_information.mix_node.mix_port).toStrictEqual(1789);
expect(typeof mixnode.bond_information.mix_node.verloc_port).toBe("number"); expect(mixnode.bond_information.mix_node.verloc_port).toStrictEqual(1790);
const identitykey = mixnode.bond_information.mix_node.identity_key; const identitykey = mixnode.bond_information.mix_node.identity_key;
if (typeof identitykey === "string") { if (typeof identitykey === "string") {
@@ -96,7 +96,7 @@ describe("Get mixnode data", (): void => {
expect(typeof mixnode.mixnode_details.bond_information.mix_node.verloc_port).toBe("number"); expect(typeof mixnode.mixnode_details.bond_information.mix_node.verloc_port).toBe("number");
expect(typeof mixnode.mixnode_details.bond_information.mix_node.mix_port).toBe("number"); expect(typeof mixnode.mixnode_details.bond_information.mix_node.mix_port).toBe("number");
expect(mixnode.mixnode_details.bond_information.mix_node.mix_port).toStrictEqual(1789); expect(mixnode.mixnode_details.bond_information.mix_node.mix_port).toStrictEqual(1789);
expect(typeof mixnode.mixnode_details.bond_information.mix_node.verloc_port).toBe("number"); expect(mixnode.mixnode_details.bond_information.mix_node.verloc_port).toStrictEqual(1790);
const identitykey2 = mixnode.mixnode_details.bond_information.mix_node.identity_key const identitykey2 = mixnode.mixnode_details.bond_information.mix_node.identity_key
if (typeof identitykey2 === "string") { if (typeof identitykey2 === "string") {
@@ -15,24 +15,19 @@ describe("Get gateway data", (): void => {
response.forEach((x) => { response.forEach((x) => {
expect(typeof x.gateway_bond.owner).toBe("string"); expect(typeof x.gateway_bond.owner).toBe("string");
expect(typeof x.performance).toBe("string"); expect(typeof x.performance).toBe("string");
expect(typeof x.node_performance.last_24h).toBe("string");
}); });
}); });
it("Get a gateway history", async (): Promise<void> => { it("Get a gateway history", async (): Promise<void> => {
const identity_key = config.environmnetConfig.gateway_identity; const identity_key = config.environmnetConfig.gateway_identity;
const response = await status.getGatewayHistory(identity_key); const response = await status.getGatewayHistory(identity_key);
response.history.forEach((x) => {
expect(typeof x.date).toBe("string");
expect(typeof x.uptime).toBe("number");
});
if ("identity" in response) { expect(identity_key).toStrictEqual(response.identity);
response.history.forEach((x) => { expect(typeof response.owner).toBe("string");
expect(typeof x.date).toBe("string");
expect(typeof x.uptime).toBe("number");
});
expect(identity_key).toStrictEqual(response.identity);
expect(typeof response.owner).toBe("string");
} else if ("message" in response) {
expect(response.message).toContain("could not find uptime history associated with gateway");
}
}); });
it("Get gateway core status count", async (): Promise<void> => { it("Get gateway core status count", async (): Promise<void> => {
@@ -39,16 +39,13 @@ describe("Get mixnode data", (): void => {
const identity_key = config.environmnetConfig.mix_id; const identity_key = config.environmnetConfig.mix_id;
const response = await status.getMixnodeHistory(identity_key); const response = await status.getMixnodeHistory(identity_key);
if ("mix_id" in response) { response.history.forEach((x) => {
response.history.forEach((x) => { expect(typeof x.date).toBe("string");
expect(typeof x.date).toBe("string"); expect(typeof x.uptime).toBe("number");
expect(typeof x.uptime).toBe("number"); });
});
expect(identity_key).toStrictEqual(response.mix_id); expect(identity_key).toStrictEqual(response.mix_id);
expect(typeof response.owner).toBe("string"); expect(typeof response.owner).toBe("string");
} else if ("message" in response) {
expect(response.message).toContain("could not find uptime history associated with mixnode");
}
}); });
it("Get a mixnode core count", async (): Promise<void> => { it("Get a mixnode core count", async (): Promise<void> => {
@@ -112,7 +109,7 @@ describe("Compute mixnode reward estimation", (): void => {
config = ConfigHandler.getInstance(); config = ConfigHandler.getInstance();
}); });
it("with correct data", async (): Promise<void> => { it("with correct data", async (): Promise<void> => {
const response = await status.sendMixnodeRewardEstimatedComputation(63); const response = await status.sendMixnodeRewardEstimatedComputation(7);
expect(typeof response.estimation.delegates).toBe("string"); expect(typeof response.estimation.delegates).toBe("string");
}); });
+1 -1
View File
@@ -4,7 +4,7 @@ common:
Content-Type: application/json Content-Type: application/json
qa: qa:
api_base_url: https://qwerty-validator-api.qa.nymte.ch/api/v1 api_base_url: https://qwerty-validator-api.qa.nymte.ch/api/v1
mix_id: 63 mix_id: 7
identity_key: 4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz identity_key: 4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz
gateway_identity: 336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9 gateway_identity: 336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9
log_level: error log_level: error
+2 -3
View File
@@ -7,7 +7,6 @@ import {
InclusionProbabilities, InclusionProbabilities,
InclusionProbability, InclusionProbability,
NodeHistory, NodeHistory,
NoUptime,
Report, Report,
RewardEstimation, RewardEstimation,
StakeSaturation, StakeSaturation,
@@ -37,7 +36,7 @@ export default class Status extends APIClient {
return response.data; return response.data;
} }
public async getGatewayHistory(identity_key: string): Promise<NodeHistory | NoUptime> { public async getGatewayHistory(identity_key: string): Promise<NodeHistory> {
const response = await this.restClient.sendGet({ const response = await this.restClient.sendGet({
route: `/gateway/${identity_key}/history`, route: `/gateway/${identity_key}/history`,
}); });
@@ -112,7 +111,7 @@ export default class Status extends APIClient {
return response.data; return response.data;
} }
public async getMixnodeHistory(mix_id: number): Promise<NodeHistory | NoUptime> { public async getMixnodeHistory(mix_id: number): Promise<NodeHistory> {
const response = await this.restClient.sendGet({ const response = await this.restClient.sendGet({
route: `/mixnode/${mix_id}/history`, route: `/mixnode/${mix_id}/history`,
}); });
+7 -18
View File
@@ -27,13 +27,6 @@ export interface Node {
in_active_set: boolean; in_active_set: boolean;
} }
export interface Estimation {
total_node_reward: string;
operator: string;
delegates: string;
operating_cost: string;
}
export interface RewardEstimation { export interface RewardEstimation {
estimation: Estimation; estimation: Estimation;
reward_params: RewardParams; reward_params: RewardParams;
@@ -41,6 +34,13 @@ export interface RewardEstimation {
as_at: number; as_at: number;
} }
export interface Estimation {
total_node_reward: string;
operator: string;
delegates: string;
operating_cost: string;
}
export interface RewardParams { export interface RewardParams {
interval: Interval; interval: Interval;
rewarded_set_size: number; rewarded_set_size: number;
@@ -129,10 +129,6 @@ export interface NodeHistory {
history: History[]; history: History[];
} }
export interface NoUptime {
message: string;
}
export interface CoreCount { export interface CoreCount {
mix_id: number; mix_id: number;
identity: string; identity: string;
@@ -166,16 +162,9 @@ export interface GatewayBond {
proxy?: any; proxy?: any;
} }
export interface nodePerformance {
most_recent: string;
last_hour: string;
last_24h: string;
}
export interface DetailedGateway { export interface DetailedGateway {
gateway_bond: GatewayBond; gateway_bond: GatewayBond;
performance: string; performance: string;
node_performance: nodePerformance;
} }
export interface OriginalPledge { export interface OriginalPledge {
+6
View File
@@ -2,6 +2,12 @@
## [Unreleased] ## [Unreleased]
## [v1.0.11] (2023-02-28)
- NC - add the option to manually select and use a specific Service Provider ([#2953])
[#2953]: https://github.com/nymtech/nym/issues/2953
## [v1.1.10] (2023-02-21) ## [v1.1.10] (2023-02-21)
- NC - add logs window for troubleshooting ([#2951]) - NC - add logs window for troubleshooting ([#2951])
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@nym/nym-connect", "name": "@nym/nym-connect",
"version": "1.1.10", "version": "1.1.11",
"main": "index.js", "main": "index.js",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
@@ -113,4 +113,4 @@
"webpack-merge": "^5.8.0", "webpack-merge": "^5.8.0",
"yaml-loader": "^0.8.0" "yaml-loader": "^0.8.0"
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-connect" name = "nym-connect"
version = "1.1.10" version = "1.1.11"
description = "nym-connect" description = "nym-connect"
authors = ["Nym Technologies SA"] authors = ["Nym Technologies SA"]
license = "" license = ""
@@ -138,6 +138,8 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
register_gateway, register_gateway,
Some(chosen_gateway_id), Some(chosen_gateway_id),
config.get_base(), config.get_base(),
// TODO: another instance where this setting should probably get used
false,
) )
.await?; .await?;
@@ -1,7 +1,7 @@
{ {
"package": { "package": {
"productName": "nym-connect", "productName": "nym-connect",
"version": "1.1.10" "version": "1.1.11"
}, },
"build": { "build": {
"distDir": "../dist", "distDir": "../dist",
@@ -6,12 +6,13 @@ import { CustomTitleBar } from './CustomTitleBar';
export const AppWindowFrame: FCWithChildren = ({ children }) => { export const AppWindowFrame: FCWithChildren = ({ children }) => {
const location = useLocation(); const location = useLocation();
const { userDefinedGateway, setUserDefinedGateway } = useClientContext(); const { userDefinedGateway, setUserDefinedGateway, userDefinedSPAddress, setUserDefinedSPAddress } =
useClientContext();
// defined functions to be used when moving away from pages // defined functions to be used when moving away from pages
const onBack = () => { const onBack = () => {
switch (location.pathname) { switch (location.pathname) {
case '/menu/settings': case '/menu/settings/gateway':
return () => { return () => {
// when the user moves away from the settings page and the gateway is not valid // when the user moves away from the settings page and the gateway is not valid
// set isActive to false // set isActive to false
@@ -19,6 +20,14 @@ export const AppWindowFrame: FCWithChildren = ({ children }) => {
setUserDefinedGateway((current) => ({ ...current, isActive: false })); setUserDefinedGateway((current) => ({ ...current, isActive: false }));
} }
}; };
case '/menu/settings/service-provider':
return () => {
// when the user moves away from the settings page and the sp is not valid
// set isActive to false
if (!userDefinedSPAddress?.address) {
setUserDefinedSPAddress((current) => ({ ...current, isActive: false }));
}
};
default: default:
return undefined; return undefined;
} }
@@ -39,7 +39,8 @@ const ArrowBackIcon = ({ onBack }: { onBack?: () => void }) => {
return <CustomButton Icon={ArrowBack} onClick={handleBack} />; return <CustomButton Icon={ArrowBack} onClick={handleBack} />;
}; };
const getTitleIcon = (path: string) => { const getTitle = (path: string) => {
if (path.includes('settings')) return 'Settings';
if (path !== '/') { if (path !== '/') {
const title = path.split('/').slice(-1); const title = path.split('/').slice(-1);
return ( return (
@@ -61,7 +62,7 @@ export const CustomTitleBar = ({ path = '/', onBack }: { path?: string; onBack?:
<Box data-tauri-drag-region style={customTitleBarStyles.titlebar}> <Box data-tauri-drag-region style={customTitleBarStyles.titlebar}>
{/* set width to keep logo centered */} {/* set width to keep logo centered */}
<Box sx={{ width: '40px' }}>{path === '/' ? <MenuIcon /> : <ArrowBackIcon onBack={onBack} />}</Box> <Box sx={{ width: '40px' }}>{path === '/' ? <MenuIcon /> : <ArrowBackIcon onBack={onBack} />}</Box>
{getTitleIcon(path)} {getTitle(path)}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<CustomButton Icon={Minimize} onClick={() => appWindow.minimize()} /> <CustomButton Icon={Minimize} onClick={() => appWindow.minimize()} />
<CustomButton Icon={Close} onClick={() => appWindow.close()} /> <CustomButton Icon={Close} onClick={() => appWindow.close()} />
+50 -35
View File
@@ -4,13 +4,14 @@ import { invoke } from '@tauri-apps/api';
import { Error } from 'src/types/error'; import { Error } from 'src/types/error';
import { getVersion } from '@tauri-apps/api/app'; import { getVersion } from '@tauri-apps/api/app';
import { useEvents } from 'src/hooks/events'; import { useEvents } from 'src/hooks/events';
import { UserDefinedGateway } from 'src/types/gateway'; import { UserDefinedGateway, UserDefinedSPAddress } from 'src/types/service-provider';
import { forage } from '@tauri-apps/tauri-forage'; import { getItemFromStorage, setItemInStorage } from 'src/utils';
import { ConnectionStatusKind, GatewayPerformance } from '../types'; import { ConnectionStatusKind, GatewayPerformance } from '../types';
import { ConnectionStatsItem } from '../components/ConnectionStats'; import { ConnectionStatsItem } from '../components/ConnectionStats';
import { ServiceProvider } from '../types/directory'; import { ServiceProvider } from '../types/directory';
const FORAGE_KEY = 'nym-connect-user-gateway'; const FORAGE_GATEWAY_KEY = 'nym-connect-user-gateway';
const FORAGE_SP_KEY = 'nym-connect-user-sp';
type ModeType = 'light' | 'dark'; type ModeType = 'light' | 'dark';
@@ -25,16 +26,19 @@ export type TClientContext = {
selectedProvider?: ServiceProvider; selectedProvider?: ServiceProvider;
showInfoModal: boolean; showInfoModal: boolean;
userDefinedGateway?: UserDefinedGateway; userDefinedGateway?: UserDefinedGateway;
userDefinedSPAddress: UserDefinedSPAddress;
serviceProviders?: ServiceProvider[];
setMode: (mode: ModeType) => void; setMode: (mode: ModeType) => void;
clearError: () => void; clearError: () => void;
setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void; setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void;
setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void; setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void;
setConnectedSince: (connectedSince: DateTime | undefined) => void; setConnectedSince: (connectedSince: DateTime | undefined) => void;
setShowInfoModal: (show: boolean) => void; setShowInfoModal: (show: boolean) => void;
setRandomSerivceProvider: () => void; setSerivceProvider: () => void;
startConnecting: () => Promise<void>; startConnecting: () => Promise<void>;
startDisconnecting: () => Promise<void>; startDisconnecting: () => Promise<void>;
setUserDefinedGateway: React.Dispatch<React.SetStateAction<UserDefinedGateway>>; setUserDefinedGateway: React.Dispatch<React.SetStateAction<UserDefinedGateway>>;
setUserDefinedSPAddress: React.Dispatch<React.SetStateAction<UserDefinedSPAddress>>;
}; };
export const ClientContext = createContext({} as TClientContext); export const ClientContext = createContext({} as TClientContext);
@@ -50,43 +54,39 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
const [appVersion, setAppVersion] = useState<string>(); const [appVersion, setAppVersion] = useState<string>();
const [gatewayPerformance, setGatewayPerformance] = useState<GatewayPerformance>('Good'); const [gatewayPerformance, setGatewayPerformance] = useState<GatewayPerformance>('Good');
const [showInfoModal, setShowInfoModal] = useState(false); const [showInfoModal, setShowInfoModal] = useState(false);
const [userDefinedGateway, setUserDefinedGateway] = useState<UserDefinedGateway>({ isActive: false, gateway: '' }); const [userDefinedGateway, setUserDefinedGateway] = useState<UserDefinedGateway>({
isActive: false,
gateway: undefined,
});
const [userDefinedSPAddress, setUserDefinedSPAddress] = useState<UserDefinedSPAddress>({
isActive: false,
address: undefined,
});
const getAppVersion = async () => { const getAppVersion = async () => {
const version = await getVersion(); const version = await getVersion();
return version; return version;
}; };
const setUserGatewayInStorage = async (gateway: UserDefinedGateway) => { useEffect(() => {
try { setItemInStorage({ key: FORAGE_GATEWAY_KEY, value: userDefinedGateway });
await forage.setItem({ }, [userDefinedGateway]);
key: FORAGE_KEY,
value: gateway,
} as any)();
} catch (e) {
console.warn(e);
}
return undefined;
};
const getUserGatewayFromStorage = async (): Promise<UserDefinedGateway | undefined> => { useEffect(() => {
try { setItemInStorage({ key: FORAGE_SP_KEY, value: userDefinedSPAddress });
const gatewayFromStorage = await forage.getItem({ key: FORAGE_KEY })(); }, [userDefinedSPAddress]);
return gatewayFromStorage;
} catch (e) {
console.warn(e);
}
return undefined;
};
const initialiseApp = async () => { const initialiseApp = async () => {
const services = await invoke('get_services'); const services = await invoke('get_services');
const AppVersion = await getAppVersion(); const AppVersion = await getAppVersion();
const storedUserDefinedGateway = await getUserGatewayFromStorage(); const storedUserDefinedGateway = await getItemFromStorage({ key: FORAGE_GATEWAY_KEY });
const storedUserDefinedSP = await getItemFromStorage({ key: FORAGE_SP_KEY });
setAppVersion(AppVersion); setAppVersion(AppVersion);
setServiceProviders(services as ServiceProvider[]); setServiceProviders(services as ServiceProvider[]);
if (storedUserDefinedGateway) setUserDefinedGateway(storedUserDefinedGateway); if (storedUserDefinedGateway) setUserDefinedGateway(storedUserDefinedGateway);
if (storedUserDefinedSP) setUserDefinedSPAddress(storedUserDefinedSP);
}; };
useEvents({ useEvents({
@@ -125,27 +125,37 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
}, []); }, []);
const shouldUseUserGateway = !!userDefinedGateway.gateway && userDefinedGateway.isActive; const shouldUseUserGateway = !!userDefinedGateway.gateway && userDefinedGateway.isActive;
const shouldUseUserSP = !!userDefinedSPAddress.address && userDefinedSPAddress.isActive;
const setServiceProvider = async (newServiceProvider: ServiceProvider) => { const setServiceProvider = async (newServiceProvider: ServiceProvider) => {
await invoke('set_gateway', { await invoke('set_gateway', {
gateway: newServiceProvider.gateway, gateway: shouldUseUserGateway ? userDefinedGateway.gateway : newServiceProvider.gateway,
});
await invoke('set_service_provider', {
serviceProvider: shouldUseUserSP ? userDefinedSPAddress.address : newServiceProvider.address,
}); });
await invoke('set_service_provider', { serviceProvider: newServiceProvider.address });
}; };
const getRandomSPFromList = (services: ServiceProvider[]) => { const getRandomSPFromList = (services: ServiceProvider[]) => {
const randomSelection = services[Math.floor(Math.random() * services.length)]; const randomSelection = services[Math.floor(Math.random() * services.length)];
if (shouldUseUserGateway) return { ...randomSelection, gateway: userDefinedGateway.gateway } as ServiceProvider;
return randomSelection; return randomSelection;
}; };
const setRandomSerivceProvider = async () => { const buildServiceProvider = async (serviceProvider: ServiceProvider) => {
const sp = { ...serviceProvider };
if (shouldUseUserGateway) sp.gateway = userDefinedGateway.gateway as string;
if (shouldUseUserSP) sp.address = userDefinedSPAddress.address as string;
return sp;
};
const setSerivceProvider = async () => {
if (serviceProviders) { if (serviceProviders) {
const randomServiceProvider = getRandomSPFromList(serviceProviders); const randomServiceProvider = getRandomSPFromList(serviceProviders);
await setServiceProvider(randomServiceProvider); const withUserDefinitions = await buildServiceProvider(randomServiceProvider);
await setUserGatewayInStorage(userDefinedGateway); await setServiceProvider(withUserDefinitions);
setSelectedProvider(randomServiceProvider); setSelectedProvider(withUserDefinitions);
} }
return undefined; return undefined;
}; };
@@ -165,21 +175,25 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
showInfoModal, showInfoModal,
setConnectionStats, setConnectionStats,
selectedProvider, selectedProvider,
serviceProviders,
connectedSince, connectedSince,
setConnectedSince, setConnectedSince,
setRandomSerivceProvider, setSerivceProvider,
startConnecting, startConnecting,
startDisconnecting, startDisconnecting,
gatewayPerformance, gatewayPerformance,
setShowInfoModal, setShowInfoModal,
userDefinedSPAddress,
userDefinedGateway, userDefinedGateway,
setUserDefinedGateway, setUserDefinedGateway,
setUserDefinedSPAddress,
}), }),
[ [
mode, mode,
appVersion, appVersion,
error, error,
showInfoModal, showInfoModal,
serviceProviders,
connectedSince, connectedSince,
connectionStatus, connectionStatus,
connectionStats, connectionStats,
@@ -187,6 +201,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => {
gatewayPerformance, gatewayPerformance,
selectedProvider, selectedProvider,
userDefinedGateway, userDefinedGateway,
userDefinedSPAddress,
], ],
); );
@@ -10,6 +10,7 @@ const mockValues: TClientContext = {
gatewayPerformance: 'Good', gatewayPerformance: 'Good',
showInfoModal: false, showInfoModal: false,
userDefinedGateway: { isActive: false, gateway: '' }, userDefinedGateway: { isActive: false, gateway: '' },
userDefinedSPAddress: { isActive: false, address: '' },
setShowInfoModal: () => {}, setShowInfoModal: () => {},
setMode: () => {}, setMode: () => {},
clearError: () => {}, clearError: () => {},
@@ -18,8 +19,9 @@ const mockValues: TClientContext = {
setConnectionStatus: () => {}, setConnectionStatus: () => {},
startConnecting: async () => {}, startConnecting: async () => {},
startDisconnecting: async () => {}, startDisconnecting: async () => {},
setRandomSerivceProvider: () => {}, setSerivceProvider: () => {},
setUserDefinedGateway: () => {}, setUserDefinedGateway: () => {},
setUserDefinedSPAddress: () => {},
}; };
export const MockProvider: FCWithChildren<{ export const MockProvider: FCWithChildren<{
@@ -28,7 +28,7 @@ export const ConnectionPage = () => {
// eslint-disable-next-line default-case // eslint-disable-next-line default-case
switch (currentStatus) { switch (currentStatus) {
case 'disconnected': case 'disconnected':
await context.setRandomSerivceProvider(); await context.setSerivceProvider();
await context.startConnecting(); await context.startConnecting();
context.setConnectedSince(DateTime.now()); context.setConnectedSince(DateTime.now());
context.setShowInfoModal(true); context.setShowInfoModal(true);
@@ -6,7 +6,7 @@ import { ConnectionStatusKind } from 'src/types';
import { AppVersion } from 'src/components/AppVersion'; import { AppVersion } from 'src/components/AppVersion';
export const GatewaySettings = () => { export const GatewaySettings = () => {
const { userDefinedGateway, setUserDefinedGateway } = useClientContext(); const { userDefinedGateway, setUserDefinedGateway, connectionStatus } = useClientContext();
const [gatewayKey, setGatewayKey] = useState<string | undefined>(userDefinedGateway?.gateway); const [gatewayKey, setGatewayKey] = useState<string | undefined>(userDefinedGateway?.gateway);
const handleIsValidGatewayKey = (isValid: boolean) => { const handleIsValidGatewayKey = (isValid: boolean) => {
@@ -23,8 +23,6 @@ export const GatewaySettings = () => {
setUserDefinedGateway((current) => ({ ...current, isActive: e.target.checked })); setUserDefinedGateway((current) => ({ ...current, isActive: e.target.checked }));
}; };
const { connectionStatus } = useClientContext();
return ( return (
<Box height="100%"> <Box height="100%">
<Stack justifyContent="space-between" height="100%"> <Stack justifyContent="space-between" height="100%">
@@ -60,6 +58,7 @@ export const GatewaySettings = () => {
onValidate={handleIsValidGatewayKey} onValidate={handleIsValidGatewayKey}
sx={{ mt: 1 }} sx={{ mt: 1 }}
disabled={connectionStatus === 'connected' || !userDefinedGateway?.isActive} disabled={connectionStatus === 'connected' || !userDefinedGateway?.isActive}
autoFocus
/> />
)} )}
</FormControl> </FormControl>
@@ -0,0 +1,96 @@
import React, { ChangeEvent } from 'react';
import {
Autocomplete,
Box,
FormControl,
FormControlLabel,
FormHelperText,
Stack,
Switch,
TextField,
Typography,
} from '@mui/material';
import { AppVersion } from 'src/components/AppVersion';
import { ConnectionStatusKind } from 'src/types';
import { useClientContext } from 'src/context/main';
export const ServiceProviderSettings = () => {
const { connectionStatus, serviceProviders, userDefinedSPAddress, setUserDefinedSPAddress } = useClientContext();
const toggleOnOff = (e: ChangeEvent<HTMLInputElement>) => {
setUserDefinedSPAddress((current) => ({ ...current, isActive: e.target.checked }));
};
const handleSelectFromList = (value: string | null) => {
setUserDefinedSPAddress((current) => ({ ...current, address: value ?? undefined }));
};
const getSPDescription = (spAddress: string) => {
const match = serviceProviders?.find((sp) => sp.address === spAddress);
if (match) return match.description;
return 'The service provider specified is not in our known list.';
};
const validateInput = (value: string) => {
setUserDefinedSPAddress((current) => ({ ...current, address: !value.length ? undefined : value }));
};
return (
<Box height="100%">
<Stack justifyContent="space-between" height="100%">
<Box>
<Typography fontWeight="bold" variant="body2" mb={1}>
Select your Service Provider
</Typography>
<Typography color="grey.300" variant="body2" mb={2}>
Pick a service provider from the list or enter your own
</Typography>
<FormControl fullWidth>
<FormControlLabel
control={
<Switch
checked={userDefinedSPAddress.isActive}
onChange={toggleOnOff}
disabled={connectionStatus === ConnectionStatusKind.connected}
size="small"
sx={{ ml: 1 }}
/>
}
label={userDefinedSPAddress.isActive ? 'On' : 'Off'}
/>
{connectionStatus === ConnectionStatusKind.connected && (
<FormHelperText sx={{ m: 0, my: 1 }}>This setting is disabled during an active connection</FormHelperText>
)}
{userDefinedSPAddress.isActive && serviceProviders && (
<Autocomplete
clearOnEscape
disabled={connectionStatus === 'connected'}
sx={{ mt: 1 }}
options={serviceProviders.map((sp) => sp.address)}
freeSolo
value={userDefinedSPAddress.address || ''}
onChange={(e, value) => handleSelectFromList(value)}
size="small"
renderInput={(params) => (
<TextField
autoFocus
{...params}
placeholder="Service provider"
onChange={(e) => validateInput(e.target.value)}
/>
)}
ListboxProps={{ style: { background: 'unset', fontSize: '14px' } }}
/>
)}
</FormControl>
{userDefinedSPAddress.address && userDefinedSPAddress.isActive && (
<Typography sx={{ mt: 1 }}>{getSPDescription(userDefinedSPAddress.address)}</Typography>
)}
</Box>
<AppVersion />
</Stack>
</Box>
);
};
@@ -4,7 +4,10 @@ import { Link, List, ListItem, ListItemButton, ListItemText, Stack } from '@mui/
import { AppVersion } from 'src/components/AppVersion'; import { AppVersion } from 'src/components/AppVersion';
import { toggleLogViewer } from 'src/utils'; import { toggleLogViewer } from 'src/utils';
const menuSchema = [{ title: 'Select your gateway', path: 'gateway' }]; const menuSchema = [
{ title: 'Select your gateway', path: 'gateway' },
{ title: 'Select a service provider', path: 'service-provider' },
];
export const SettingsMenu = () => ( export const SettingsMenu = () => (
<Stack justifyContent="space-between" height="100%"> <Stack justifyContent="space-between" height="100%">
+2
View File
@@ -6,6 +6,7 @@ import { CompatibleApps } from 'src/pages/menu/Apps';
import { HelpGuide } from 'src/pages/menu/Guide'; import { HelpGuide } from 'src/pages/menu/Guide';
import { SettingsMenu } from 'src/pages/menu/settings'; import { SettingsMenu } from 'src/pages/menu/settings';
import { GatewaySettings } from 'src/pages/menu/settings/GatewaySettings'; import { GatewaySettings } from 'src/pages/menu/settings/GatewaySettings';
import { ServiceProviderSettings } from 'src/pages/menu/settings/ServiceProviderSettings';
export const AppRoutes = () => ( export const AppRoutes = () => (
<Routes> <Routes>
@@ -17,6 +18,7 @@ export const AppRoutes = () => (
<Route path="settings"> <Route path="settings">
<Route index element={<SettingsMenu />} /> <Route index element={<SettingsMenu />} />
<Route path="gateway" element={<GatewaySettings />} /> <Route path="gateway" element={<GatewaySettings />} />
<Route path="service-provider" element={<ServiceProviderSettings />} />
</Route> </Route>
</Route> </Route>
</Routes> </Routes>
-4
View File
@@ -1,4 +0,0 @@
export interface UserDefinedGateway {
isActive: boolean;
gateway?: string;
}
@@ -0,0 +1,9 @@
export interface UserDefinedGateway {
isActive: boolean;
gateway?: string;
}
export interface UserDefinedSPAddress {
isActive: boolean;
address?: string;
}
+4
View File
@@ -0,0 +1,4 @@
export interface StorageKeyValue<T> {
key: string;
value: T;
}
+24
View File
@@ -1,6 +1,8 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { EventName, listen, UnlistenFn, EventCallback } from '@tauri-apps/api/event'; import { EventName, listen, UnlistenFn, EventCallback } from '@tauri-apps/api/event';
import { invoke } from '@tauri-apps/api'; import { invoke } from '@tauri-apps/api';
import { forage } from '@tauri-apps/tauri-forage';
import { StorageKeyValue } from 'src/types/storage';
export const useTauriEvents = <T>(event: EventName, handler: EventCallback<T>) => { export const useTauriEvents = <T>(event: EventName, handler: EventCallback<T>) => {
const unlisten = useRef<UnlistenFn>(); const unlisten = useRef<UnlistenFn>();
@@ -22,3 +24,25 @@ export const useTauriEvents = <T>(event: EventName, handler: EventCallback<T>) =
export const toggleLogViewer = async () => { export const toggleLogViewer = async () => {
await invoke('help_log_toggle_window'); await invoke('help_log_toggle_window');
}; };
export async function setItemInStorage<T>({ key, value }: StorageKeyValue<T>) {
try {
await forage.setItem({
key,
value,
} as any)();
} catch (e) {
console.warn(e);
}
return undefined;
}
export const getItemFromStorage = async ({ key }: Pick<StorageKeyValue<undefined>, 'key'>) => {
try {
const gatewayFromStorage = await forage.getItem({ key })();
return gatewayFromStorage;
} catch (e) {
console.warn(e);
}
return undefined;
};
+1 -2
View File
@@ -26,8 +26,7 @@
"clean:dist": "rm -rf dist", "clean:dist": "rm -rf dist",
"clean:node": "rm -rf node_modules", "clean:node": "rm -rf node_modules",
"clean:rust": "cargo clean --manifest-path src-tauri/Cargo.toml", "clean:rust": "cargo clean --manifest-path src-tauri/Cargo.toml",
"clean:android": "cd src-tauri/gen/android/nym_connect/ && gradlew clean", "clean:": "run-p clean:node clean:rust"
"clean:": "run-p clean:node clean:rust clean:android"
}, },
"dependencies": { "dependencies": {
"@emotion/react": "^11.7.0", "@emotion/react": "^11.7.0",
@@ -0,0 +1,41 @@
[build]
target = 'x86_64-unknown-linux-gnu'
[target.aarch64-linux-android]
linker = '/home/pierre/.local/share/android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang'
rustflags = [
'-L',
'/home/pierre/Documents/nym/nym/nym-connect-new/mobile/src-tauri/.cargo',
'-Clink-arg=-landroid',
'-Clink-arg=-llog',
'-Clink-arg=-lOpenSLES',
]
[target.armv7-linux-androideabi]
linker = '/home/pierre/.local/share/android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang'
rustflags = [
'-L',
'/home/pierre/Documents/nym/nym/nym-connect-new/mobile/src-tauri/.cargo',
'-Clink-arg=-landroid',
'-Clink-arg=-llog',
'-Clink-arg=-lOpenSLES',
]
[target.i686-linux-android]
linker = '/home/pierre/.local/share/android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android24-clang'
rustflags = [
'-L',
'/home/pierre/Documents/nym/nym/nym-connect-new/mobile/src-tauri/.cargo',
'-Clink-arg=-landroid',
'-Clink-arg=-llog',
'-Clink-arg=-lOpenSLES',
]
[target.x86_64-linux-android]
linker = '/home/pierre/.local/share/android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang'
rustflags = [
'-L',
'/home/pierre/Documents/nym/nym/nym-connect-new/mobile/src-tauri/.cargo',
'-Clink-arg=-landroid',
'-Clink-arg=-llog',
'-Clink-arg=-lOpenSLES',
]
@@ -0,0 +1 @@
INPUT(-lunwind)
-1
View File
@@ -2,4 +2,3 @@
# will have compiled files and executables # will have compiled files and executables
/target/ /target/
WixTools WixTools
/.cargo/
+45 -46
View File
@@ -245,9 +245,9 @@ dependencies = [
[[package]] [[package]]
name = "bip39" name = "bip39"
version = "1.1.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5b9d9748b5770d1539657653dc5ac3cd9353549e74238dc0d96c22919128b94" checksum = "b9e89470017230c38e52b82b3ee3f530db1856ba1d434e3a67a3456a8a8dec5f"
dependencies = [ dependencies = [
"bitcoin_hashes", "bitcoin_hashes",
"rand 0.6.5", "rand 0.6.5",
@@ -258,9 +258,9 @@ dependencies = [
[[package]] [[package]]
name = "bitcoin_hashes" name = "bitcoin_hashes"
version = "0.11.0" version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" checksum = "7ce18265ec2324ad075345d5814fbeed4f41f0a660055dc78840b74d19b874b1"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
@@ -397,9 +397,9 @@ dependencies = [
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.3.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832"
dependencies = [ dependencies = [
"memchr", "memchr",
"serde", "serde",
@@ -571,9 +571,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_complete" name = "clap_complete"
version = "4.1.3" version = "4.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0012995dc3a54314f4710f5631d74767e73c534b8757221708303e48eef7a19b" checksum = "bd125be87bf4c255ebc50de0b7f4d2a6201e8ac3dc86e39c0ad081dc5e7236fe"
dependencies = [ dependencies = [
"clap", "clap",
] ]
@@ -603,9 +603,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.3.2" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
dependencies = [ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
@@ -636,7 +636,7 @@ dependencies = [
"sqlx 0.6.2", "sqlx 0.6.2",
"tap", "tap",
"thiserror", "thiserror",
"time 0.3.19", "time 0.3.18",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"url", "url",
@@ -2040,15 +2040,15 @@ dependencies = [
[[package]] [[package]]
name = "generator" name = "generator"
version = "0.7.3" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33a20a288a94683f5f4da0adecdbe095c94a77c295e514cc6484e9394dd8376e" checksum = "d266041a359dfa931b370ef684cceb84b166beb14f7f0421f4a6a3d0c446d12e"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
"log", "log",
"rustversion", "rustversion",
"windows 0.44.0", "windows 0.39.0",
] ]
[[package]] [[package]]
@@ -2533,9 +2533,9 @@ dependencies = [
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.9" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@@ -2826,9 +2826,9 @@ checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.4" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef"
dependencies = [ dependencies = [
"hermit-abi 0.3.1", "hermit-abi 0.3.1",
"io-lifetimes", "io-lifetimes",
@@ -3159,7 +3159,7 @@ dependencies = [
"dirs-next", "dirs-next",
"objc-foundation", "objc-foundation",
"objc_id", "objc_id",
"time 0.3.19", "time 0.3.18",
] ]
[[package]] [[package]]
@@ -3355,12 +3355,11 @@ dependencies = [
[[package]] [[package]]
name = "notify-rust" name = "notify-rust"
version = "4.8.0" version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bfa211d18e360f08e36c364308f394b5eb23a6629150690e109a916dc6f610e" checksum = "260208751689b605138bb55ab6af43ad75f628619a7e0b818d63bf6629e59467"
dependencies = [ dependencies = [
"dbus", "dbus",
"log",
"mac-notification-sys", "mac-notification-sys",
"tauri-winrt-notification", "tauri-winrt-notification",
] ]
@@ -3429,18 +3428,18 @@ dependencies = [
[[package]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.5.11" version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b"
dependencies = [ dependencies = [
"num_enum_derive", "num_enum_derive",
] ]
[[package]] [[package]]
name = "num_enum_derive" name = "num_enum_derive"
version = "0.5.11" version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e"
dependencies = [ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
@@ -3585,7 +3584,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_repr", "serde_repr",
"thiserror", "thiserror",
"time 0.3.19", "time 0.3.18",
] ]
[[package]] [[package]]
@@ -4349,16 +4348,16 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]] [[package]]
name = "plist" name = "plist"
version = "1.4.1" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9469799ca90293a376f68f6fcb8f11990d9cff55602cfba0ba83893c973a7f46" checksum = "5329b8f106a176ab0dce4aae5da86bfcb139bb74fb00882859e03745011f3635"
dependencies = [ dependencies = [
"base64 0.21.0", "base64 0.13.1",
"indexmap", "indexmap",
"line-wrap", "line-wrap",
"quick-xml 0.26.0", "quick-xml 0.26.0",
"serde", "serde",
"time 0.3.19", "time 0.3.18",
] ]
[[package]] [[package]]
@@ -5445,9 +5444,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.8" version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
dependencies = [ dependencies = [
"autocfg 1.1.0", "autocfg 1.1.0",
] ]
@@ -5874,9 +5873,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -6074,7 +6073,7 @@ dependencies = [
"sha2 0.10.6", "sha2 0.10.6",
"tauri-utils", "tauri-utils",
"thiserror", "thiserror",
"time 0.3.19", "time 0.3.18",
"url", "url",
"uuid 1.3.0", "uuid 1.3.0",
"walkdir", "walkdir",
@@ -6214,7 +6213,7 @@ dependencies = [
"subtle 2.4.1", "subtle 2.4.1",
"subtle-encoding", "subtle-encoding",
"tendermint-proto", "tendermint-proto",
"time 0.3.19", "time 0.3.18",
"zeroize", "zeroize",
] ]
@@ -6247,7 +6246,7 @@ dependencies = [
"serde", "serde",
"serde_bytes", "serde_bytes",
"subtle-encoding", "subtle-encoding",
"time 0.3.19", "time 0.3.18",
] ]
[[package]] [[package]]
@@ -6275,7 +6274,7 @@ dependencies = [
"tendermint-config", "tendermint-config",
"tendermint-proto", "tendermint-proto",
"thiserror", "thiserror",
"time 0.3.19", "time 0.3.18",
"tokio", "tokio",
"tracing", "tracing",
"url", "url",
@@ -6352,9 +6351,9 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.19" version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" checksum = "af0097eaf301d576d0b2aead7a59facab6d53cc636340f0291fab8446a2e8613"
dependencies = [ dependencies = [
"itoa 1.0.5", "itoa 1.0.5",
"js-sys", "js-sys",
@@ -6371,9 +6370,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.7" version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c" checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
dependencies = [ dependencies = [
"time-core", "time-core",
] ]
@@ -6454,9 +6453,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-stream" name = "tokio-stream"
version = "0.1.12" version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"pin-project-lite", "pin-project-lite",
@@ -6840,7 +6839,7 @@ dependencies = [
"rustc_version 0.4.0", "rustc_version 0.4.0",
"rustversion", "rustversion",
"thiserror", "thiserror",
"time 0.3.19", "time 0.3.18",
] ]
[[package]] [[package]]
@@ -13,4 +13,3 @@ build
.externalNativeBuild .externalNativeBuild
.cxx .cxx
local.properties local.properties
app/src/main/jniLibs/**
@@ -0,0 +1 @@
/home/pierre/Documents/nym/nym/nym-connect-new/mobile/src-tauri/target/x86_64-linux-android/debug/libnym_connect.so
@@ -26,20 +26,17 @@ open class BuildTask : DefaultTask() {
val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null")
val target = target ?: throw GradleException("target cannot be null") val target = target ?: throw GradleException("target cannot be null")
val release = release ?: throw GradleException("release cannot be null") val release = release ?: throw GradleException("release cannot be null")
val nodeExecutable = System.getenv("NODE_TAURI_CLI")
val home = (System.getenv("HOME") ?: "") val home = (System.getenv("HOME") ?: "")
val cargoHome = (System.getenv("CARGO_HOME") ?: "$home/.cargo") val cargoHome = (System.getenv("CARGO_HOME") ?: "$home/.cargo")
val rustExecutable = "$cargoHome/bin/cargo-tauri" val tauriCli = "$cargoHome/bin/cargo-tauri"
val tauriCli = when { if (!File(tauriCli).isFile()) {
!nodeExecutable.isNullOrEmpty() && File(nodeExecutable).isFile() -> nodeExecutable throw GradleException("$tauriCli no shuch file")
File(rustExecutable).isFile() -> rustExecutable
else -> throw GradleException("couldn't find tauri-cli executable")
} }
println("gradle Rust plugin, using tauri cli executable: $tauriCli") println("gradle Rust plugin, using tauri cli executable: $tauriCli")
project.exec { project.exec {
workingDir(File(project.projectDir, rootDirRel.path)) workingDir(File(project.projectDir, rootDirRel.path))
executable(tauriCli) executable(tauriCli)
args(listOf("android", "android-studio-script")) args(listOf("tauri", "android", "android-studio-script"))
if (project.logger.isEnabled(LogLevel.DEBUG)) { if (project.logger.isEnabled(LogLevel.DEBUG)) {
args("-vv") args("-vv")
} else if (project.logger.isEnabled(LogLevel.INFO)) { } else if (project.logger.isEnabled(LogLevel.INFO)) {
@@ -118,6 +118,7 @@ pub async fn init_socks5_config(
&mut key_manager, &mut key_manager,
nym_api_endpoints, nym_api_endpoints,
Some(chosen_gateway_id), Some(chosen_gateway_id),
false,
) )
.await?; .await?;
+2
View File
@@ -259,6 +259,8 @@ where
&mut self.key_manager, &mut self.key_manager,
self.config.nym_api_endpoints.clone(), self.config.nym_api_endpoints.clone(),
user_chosen_gateway, user_chosen_gateway,
// TODO: this should probably be configurable with the config
false,
) )
.await?; .await?;
@@ -3,7 +3,7 @@
[package] [package]
name = "nym-network-requester" name = "nym-network-requester"
version = "1.1.10" version = "1.1.11"
authors.workspace = true authors.workspace = true
edition.workspace = true edition.workspace = true
rust-version = "1.65" rust-version = "1.65"
@@ -24,6 +24,11 @@ pub(crate) struct Init {
#[clap(long)] #[clap(long)]
gateway: Option<identity::PublicKey>, gateway: Option<identity::PublicKey>,
/// Specifies whether the new gateway should be determined based by latency as opposed to being chosen
/// uniformly.
#[clap(long, conflicts_with = "gateway")]
latency_based_selection: bool,
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id, /// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
/// potentially causing loss of access. /// potentially causing loss of access.
#[clap(long)] #[clap(long)]
@@ -115,6 +120,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> {
register_gateway, register_gateway,
user_chosen_gateway_id, user_chosen_gateway_id,
config.get_base(), config.get_base(),
args.latency_based_selection,
) )
.await .await
.map_err(|source| { .map_err(|source| {
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-network-statistics" name = "nym-network-statistics"
version = "1.1.10" version = "1.1.11"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+6 -2
View File
@@ -2,12 +2,16 @@
A simple tool to ensure that all binaries init with the correct format, using the `assert.sh` library A simple tool to ensure that all binaries init with the correct format, using the `assert.sh` library
Simply run `./build_and_run.sh $WORKING_BRANCH` Simply run `./build_and_run.sh $RELEASE_BRANCH` `$CURRENT_PRODUCTION_RELEASE_VERSION`
For example: For example:
`./build_and_run.sh release/v1.1.11` `./build_and_run.sh release/v1.1.11 v1.1.10`
Currently, this is run on linux based machines as the nym-core binaries are published via a linux build agent Currently, this is run on linux based machines as the nym-core binaries are published via a linux build agent
This will run through all the binaries and check the fields that we expect to be initialised when passing the parameters into nyms core binaries This will run through all the binaries and check the fields that we expect to be initialised when passing the parameters into nyms core binaries
## TODO
- Create GH workflow and Run in CI
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-cli" name = "nym-cli"
version = "1.1.10" version = "1.1.11"
authors.workspace = true authors.workspace = true
edition = "2021" edition = "2021"
@@ -22,6 +22,7 @@ export const IdentityKeyFormField: FCWithChildren<{
size?: 'small' | 'medium'; size?: 'small' | 'medium';
sx?: SxProps; sx?: SxProps;
disabled?: boolean; disabled?: boolean;
autoFocus?: boolean;
}> = ({ }> = ({
required, required,
fullWidth, fullWidth,
@@ -37,6 +38,7 @@ export const IdentityKeyFormField: FCWithChildren<{
showTickOnValid = true, showTickOnValid = true,
size, size,
disabled, disabled,
autoFocus,
}) => { }) => {
const [value, setValue] = React.useState<string | undefined>(initialValue); const [value, setValue] = React.useState<string | undefined>(initialValue);
const [validationError, setValidationError] = React.useState<string | undefined>(); const [validationError, setValidationError] = React.useState<string | undefined>();
@@ -106,6 +108,7 @@ export const IdentityKeyFormField: FCWithChildren<{
InputLabelProps={{ shrink: true }} InputLabelProps={{ shrink: true }}
size={size} size={size}
disabled={disabled} disabled={disabled}
autoFocus={autoFocus}
/> />
); );
}; };