Compare commits

..

21 Commits

Author SHA1 Message Date
Bogdan-Ștefan Neacșu c835ab24fd Bump some versions for helios integration 2023-03-02 17:53:09 +02:00
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
106 changed files with 1013 additions and 509 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
+12 -10
View File
@@ -677,7 +677,7 @@ dependencies = [
[[package]] [[package]]
name = "client-core" name = "client-core"
version = "1.1.10" version = "1.1.11"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"dashmap 5.4.0", "dashmap 5.4.0",
@@ -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",
@@ -1800,7 +1802,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]] [[package]]
name = "explorer-api" name = "explorer-api"
version = "1.1.10" version = "1.1.11"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap 4.1.4", "clap 4.1.4",
@@ -3279,7 +3281,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-api" name = "nym-api"
version = "1.1.11" version = "1.1.12"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -3389,7 +3391,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-cli" name = "nym-cli"
version = "1.1.10" version = "1.1.11"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64 0.13.1", "base64 0.13.1",
@@ -3447,7 +3449,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-client" name = "nym-client"
version = "1.1.10" version = "1.1.11"
dependencies = [ dependencies = [
"clap 4.1.4", "clap 4.1.4",
"client-core", "client-core",
@@ -3541,7 +3543,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-gateway" name = "nym-gateway"
version = "1.1.10" version = "1.1.11"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -3618,7 +3620,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-mixnode" name = "nym-mixnode"
version = "1.1.11" version = "1.1.12"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"atty", "atty",
@@ -3673,7 +3675,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-network-requester" name = "nym-network-requester"
version = "1.1.10" version = "1.1.11"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"clap 4.1.4", "clap 4.1.4",
@@ -3711,7 +3713,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-network-statistics" name = "nym-network-statistics"
version = "1.1.10" version = "1.1.11"
dependencies = [ dependencies = [
"dirs", "dirs",
"log", "log",
@@ -3786,7 +3788,7 @@ dependencies = [
[[package]] [[package]]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.10" version = "1.1.11"
dependencies = [ dependencies = [
"clap 4.1.4", "clap 4.1.4",
"client-core", "client-core",
+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}"))?;
@@ -36,9 +36,9 @@ nym-api-requests = { path = "../../../nym-api/nym-api-requests" }
# at some point it might be possible to make it wasm-compatible # at some point it might be possible to make it wasm-compatible
# perhaps after https://github.com/cosmos/cosmos-rust/pull/97 is resolved (and tendermint-rs is updated) # perhaps after https://github.com/cosmos/cosmos-rust/pull/97 is resolved (and tendermint-rs is updated)
async-trait = { version = "0.1.51", optional = true } async-trait = { version = "0.1.51", optional = true }
bip39 = { version = "1", features = ["rand"], optional = true } bip39 = { version = "2", features = ["rand"], optional = true }
nym-config = { path = "../../config", optional = true } nym-config = { path = "../../config", optional = true }
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true} cosmrs = { version = "0.8.0", features = ["rpc", "bip32", "cosmwasm"], optional = true}
cw3 = { version = "0.13.4", optional = true } cw3 = { version = "0.13.4", optional = true }
cw4 = { version = "0.13.4", optional = true } cw4 = { version = "0.13.4", optional = true }
prost = { version = "0.10", default-features = false, optional = true } prost = { version = "0.10", default-features = false, optional = true }
@@ -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};
@@ -121,7 +121,7 @@ impl GasAdjustable for Gas {
mod sealed { mod sealed {
use cosmrs::tx::{self, Gas}; use cosmrs::tx::{self, Gas};
use cosmrs::Coin as CosmosCoin; use cosmrs::Coin as CosmosCoin;
use cosmrs::{AccountId, Decimal as CosmosDecimal, Denom as CosmosDenom}; use cosmrs::{AccountId, Denom as CosmosDenom};
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
fn cosmos_denom_inner_getter(val: &CosmosDenom) -> String { fn cosmos_denom_inner_getter(val: &CosmosDenom) -> String {
@@ -138,29 +138,11 @@ mod sealed {
} }
} }
fn cosmos_decimal_inner_getter(val: &CosmosDecimal) -> u64 {
// haha, this code is so disgusting. I'll make a PR on cosmrs to slightly alleviate those issues...
// note: unwrap here is fine as the to_string is just returning a stringified u64 which, well, is a valid u64
val.to_string().parse().unwrap()
}
// at the time of writing it the current cosmrs' Decimal is extremely limited...
#[derive(Serialize, Deserialize)]
#[serde(remote = "CosmosDecimal")]
struct Decimal(#[serde(getter = "cosmos_decimal_inner_getter")] u64);
impl From<Decimal> for CosmosDecimal {
fn from(val: Decimal) -> Self {
val.0.into()
}
}
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
struct Coin { struct Coin {
#[serde(with = "Denom")] #[serde(with = "Denom")]
denom: CosmosDenom, denom: CosmosDenom,
#[serde(with = "Decimal")] amount: u128,
amount: CosmosDecimal,
} }
impl From<Coin> for CosmosCoin { impl From<Coin> for CosmosCoin {
@@ -42,7 +42,7 @@ pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
pub use cosmrs::tendermint::Time as TendermintTime; pub use cosmrs::tendermint::Time as TendermintTime;
pub use cosmrs::tx::{self, Gas}; pub use cosmrs::tx::{self, Gas};
pub use cosmrs::Coin as CosmosCoin; pub use cosmrs::Coin as CosmosCoin;
pub use cosmrs::{bip32, AccountId, Decimal, Denom}; pub use cosmrs::{bip32, AccountId, Denom};
pub use cosmwasm_std::Coin as CosmWasmCoin; pub use cosmwasm_std::Coin as CosmWasmCoin;
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment}; pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
pub use signing_client::Client as SigningNyxdClient; pub use signing_client::Client as SigningNyxdClient;
+1 -1
View File
@@ -25,7 +25,7 @@ toml = "0.5.6"
url = "2.2" url = "2.2"
tap = "1" tap = "1"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } cosmrs = "0.8.0"
cosmwasm-std = { version = "1.0.0" } cosmwasm-std = { version = "1.0.0" }
validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] } validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
+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;
+1 -1
View File
@@ -7,7 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] } bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } cosmrs = "0.8.0"
thiserror = "1.0" thiserror = "1.0"
# I guess temporarily until we get serde support in coconut up and running # I guess temporarily until we get serde support in coconut up and running
+1 -1
View File
@@ -20,7 +20,7 @@ url = "2.2"
ts-rs = "6.1.2" ts-rs = "6.1.2"
cosmwasm-std = "1.0.0" cosmwasm-std = "1.0.0"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } cosmrs = "0.8.0"
validator-client = { path = "../../common/client-libs/validator-client", features = [ validator-client = { path = "../../common/client-libs/validator-client", features = [
"nyxd-client", "nyxd-client",
@@ -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
@@ -11,7 +11,7 @@ thiserror = "1.0"
k256 = { version = "0.10", features = ["ecdsa", "sha256"] } k256 = { version = "0.10", features = ["ecdsa", "sha256"] }
eyre = "0.6.5" eyre = "0.6.5"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } cosmrs = "0.8.0"
nym-cli-commands = { path = "../../common/commands" } nym-cli-commands = { path = "../../common/commands" }
validator-client = { path = "../../common/client-libs/validator-client", features = [ validator-client = { path = "../../common/client-libs/validator-client", features = [
+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>",
+1 -1
View File
@@ -7,7 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
bs58 = "0.4.0" bs58 = "0.4.0"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } cosmrs = "0.8.0"
cosmwasm-std = { version = "1.0.0", default-features = false } cosmwasm-std = { version = "1.0.0", default-features = false }
getset = "0.1.1" getset = "0.1.1"
schemars = { version = "0.8", features = ["preserve_order"] } schemars = { version = "0.8", features = ["preserve_order"] }
+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?;
+1 -1
View File
@@ -12,7 +12,7 @@ strum = { version = "0.23", features = ["derive"] }
ts-rs = "6.1.2" ts-rs = "6.1.2"
cosmwasm-std = "1.0.0-beta8" cosmwasm-std = "1.0.0-beta8"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } cosmrs = "0.8.0"
nym-config = { path = "../../common/config" } nym-config = { path = "../../common/config" }
nym-network-defaults = { path = "../../common/network-defaults" } nym-network-defaults = { path = "../../common/network-defaults" }
+1 -1
View File
@@ -52,7 +52,7 @@ base64 = "0.13"
zeroize = "1.4.3" zeroize = "1.4.3"
cosmwasm-std = "1.0.0" cosmwasm-std = "1.0.0"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } cosmrs = "0.8.0"
validator-client = { path = "../../common/client-libs/validator-client", features = [ validator-client = { path = "../../common/client-libs/validator-client", features = [
"nyxd-client", "nyxd-client",
+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?;

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