Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f758f47ba | |||
| e0274bb394 | |||
| 607d4dc743 | |||
| 25f7b7a083 | |||
| fd1fb7ca7b | |||
| 6252b66724 | |||
| b770cab3f0 | |||
| e69552b19d | |||
| e3cc43487a | |||
| 41be555aa6 | |||
| bdc0bcbd56 | |||
| 0baa8b2c92 | |||
| 2ab969b2c6 | |||
| 9f2e7e16e5 | |||
| 1c99446bcc | |||
| 90d9c9ec41 | |||
| 2e38c5e38e | |||
| dbb7a27441 | |||
| 89c05387f8 | |||
| 7952277c4b | |||
| c5866db137 | |||
| 37187c79cc | |||
| 24839770ff | |||
| 0238499e33 | |||
| 3363230c4c | |||
| 1f8b373780 | |||
| 7ac3ec3598 | |||
| 77ae71eba4 | |||
| d4b836277e | |||
| b92ee84874 | |||
| 2eb0ce381a | |||
| 037cd54573 | |||
| 9f42f0152b | |||
| 5217edcca3 | |||
| e306effdac | |||
| dc2b1c6d2a | |||
| 4232801e80 | |||
| 96df3ad4ce | |||
| d614a2b81b | |||
| d27245e184 | |||
| 5dbfcadfdb | |||
| 035dada0e0 | |||
| 1d867156e3 | |||
| ed9be47ec4 | |||
| 3aa2e6c54d | |||
| eb96fc72b9 | |||
| 59cec6f03c | |||
| c0a0d89a90 | |||
| 3099f2ead3 | |||
| 9881a94757 | |||
| 76b07d487b |
@@ -34,7 +34,7 @@ on:
|
||||
- 'tools/ts-rs-cli/**'
|
||||
|
||||
env:
|
||||
NETWORK: mainnet
|
||||
NETWORK: mainnet
|
||||
|
||||
jobs:
|
||||
publish-nym:
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
- name: Prepare build output directory
|
||||
shell: bash
|
||||
env:
|
||||
@@ -110,4 +110,3 @@ jobs:
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build APK
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: custom-runner-linux
|
||||
env:
|
||||
ANDROID_HOME: ${{ github.workspace }}/android-sdk
|
||||
NDK_VERSION: 25.1.8937393
|
||||
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/${{ env.NDK_VERSION }}
|
||||
NDK_HOME: ${{ github.workspace }}/android-sdk/ndk/25.1.8937393
|
||||
SDK_PLATFORM_VERSION: android-33
|
||||
SDK_BUILDTOOLS_VERSION: 33.0.1
|
||||
|
||||
@@ -25,6 +25,7 @@ jobs:
|
||||
sudo apt-get -y install \
|
||||
libwebkit2gtk-4.0-dev \
|
||||
build-essential \
|
||||
unzip \
|
||||
curl \
|
||||
wget \
|
||||
libssl-dev \
|
||||
@@ -33,6 +34,9 @@ jobs:
|
||||
libayatana-appindicator3-dev \
|
||||
librsvg2-dev
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
@@ -80,9 +84,6 @@ jobs:
|
||||
npm i -g yarn
|
||||
yarn --version
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build frontend code
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
@@ -92,36 +93,42 @@ jobs:
|
||||
- name: Build APK
|
||||
working-directory: nym-connect-android
|
||||
env:
|
||||
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
|
||||
WRY_ANDROID_PACKAGE: net.nymtech.nym_connect_android
|
||||
WRY_ANDROID_LIBRARY: nym_connect_android
|
||||
# TODO build with release profile (--release), it will requires
|
||||
# to sign the APK. For now build with debug profile to avoid that
|
||||
run: cargo tauri android build --debug --apk
|
||||
run: cargo tauri android build --debug --apk --split-per-abi -t aarch64
|
||||
|
||||
# TODO add the version number to APK name
|
||||
- name: Rename APK artifact
|
||||
run: |
|
||||
mv nym-connect-android/src-tauri/gen/android/nym_connect_android/app/build/outputs/apk/universal/debug/app-universal-debug.apk \
|
||||
nym-connect-debug.apk
|
||||
mkdir apk/
|
||||
mv nym-connect-android/src-tauri/gen/android/nym_connect_android/app/build/outputs/apk/arm64/debug/app-arm64-debug.apk \
|
||||
apk/nym-connect-arm64-debug.apk
|
||||
mv nym-connect-android/src-tauri/gen/android/nym_connect_android/app/build/outputs/apk/x86_64/debug/app-x86_64-debug.apk \
|
||||
apk/nym-connect-x86_64-debug.apk
|
||||
|
||||
- name: Upload APK artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nc-apk-debug
|
||||
path: nym-connect-debug.apk
|
||||
path: |
|
||||
apk/nym-connect-arm64-debug.apk
|
||||
apk/nym-connect-x86_64-debug.apk
|
||||
|
||||
publish:
|
||||
name: Publish APK
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Download binary artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: nc-apk-debug
|
||||
path: apk
|
||||
# TODO add a step to upload the APK somewhere
|
||||
# - name: Publish
|
||||
# uses: ???
|
||||
# publish:
|
||||
# name: Publish APK
|
||||
# needs: build
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v3
|
||||
# - name: Download binary artifact
|
||||
# uses: actions/download-artifact@v3
|
||||
# with:
|
||||
# name: nc-apk-debug
|
||||
# path: apk
|
||||
# # TODO add a step to upload the APK somewhere
|
||||
# - name: Publish
|
||||
# uses: ???
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
# creates the matrix strategy from nightly_build_matrix_includes.json
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- id: set-matrix
|
||||
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||
with:
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: install npm
|
||||
uses: actions/setup-node@v3
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
|
||||
@@ -54,6 +54,12 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -66,6 +72,18 @@ jobs:
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Build all examples
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -74,7 +92,7 @@ jobs:
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
@@ -83,13 +101,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace --all-features -- --ignored
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
args: --workspace -- --ignored
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -102,7 +114,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
args: --workspace
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
|
||||
@@ -54,6 +54,12 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -66,6 +72,18 @@ jobs:
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Build all examples
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -74,7 +92,7 @@ jobs:
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
@@ -83,13 +101,7 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace --all-features -- --ignored
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
args: --workspace -- --ignored
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -102,7 +114,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
args: --workspace
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
|
||||
@@ -6,14 +6,28 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
### Added
|
||||
|
||||
- remove coconut feature and unify builds ([#2890])
|
||||
- native-client: is now capable of listening for requests on sockets different than `127.0.0.1` ([#2939]). This can be specified via `--host` flag during `init` or `run`. Alternatively a custom `host` can be set in `config.toml` file under `socket` section.
|
||||
- dkg resharing mode ([#2936])
|
||||
|
||||
[#2890]: https://github.com/nymtech/nym/pull/2890
|
||||
[#2939]: https://github.com/nymtech/nym/pull/2939
|
||||
[#2936]: https://github.com/nymtech/nym/pull/2936
|
||||
|
||||
|
||||
# [v1.1.9] (2023-02-07)
|
||||
|
||||
### Added
|
||||
|
||||
- Separate `nym-api` endpoints with values of "total-supply" and "circulating-supply" in `nym` ([#2964])
|
||||
- Add `host` option to client init ([#2912])
|
||||
- Remove Coconut feature flag ([#2793])
|
||||
- Don't drop in mixnet connection handler ([#2963])
|
||||
|
||||
### Changed
|
||||
- native-client: is now capable of listening for requests on sockets different than `127.0.0.1` ([#2939]). This can be specified via `--host` flag during `init` or `run`. Alternatively a custom `host` can be set in `config.toml` file under `socket` section.
|
||||
- mixnode, gateway: fix unexpected shutdown on corrupted connection ([#2963])
|
||||
|
||||
[#2939]: https://github.com/nymtech/nym/pull/2939
|
||||
[#2963]: https://github.com/nymtech/nym/pull/2963
|
||||
|
||||
|
||||
# [v1.1.8] (2023-01-31)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -99,9 +99,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.68"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
@@ -138,9 +138,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.63"
|
||||
version = "0.1.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1"
|
||||
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -290,15 +290,6 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bip32"
|
||||
version = "0.3.0"
|
||||
@@ -459,7 +450,7 @@ name = "build-information"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"vergen 7.5.0",
|
||||
"vergen 7.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -482,9 +473,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
@@ -494,9 +485,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.78"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
@@ -629,9 +620,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.1.3"
|
||||
version = "4.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8d93d855ce6a0aa87b8473ef9169482f40abaa2e9e0993024c35c902cbd5920"
|
||||
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
@@ -648,7 +639,7 @@ version = "4.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75"
|
||||
dependencies = [
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -657,7 +648,7 @@ version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf0c76d8fcf782a1102ccfcd10ca8246e7fdd609c1cd6deddbb96cb638e9bb5c"
|
||||
dependencies = [
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"clap_complete",
|
||||
]
|
||||
|
||||
@@ -667,7 +658,7 @@ version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
|
||||
dependencies = [
|
||||
"heck 0.4.0",
|
||||
"heck 0.4.1",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -818,7 +809,7 @@ dependencies = [
|
||||
name = "completions"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
]
|
||||
@@ -978,9 +969,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-derive"
|
||||
version = "1.1.9"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fca30d51f7e5fbfa6440d8b10d7df0231bdf77e97fd3fe5d0cb79cc4822e50c"
|
||||
checksum = "d5abeeb891e6d0098402e4d3d042f90451db52651d2fe14b170e69a1dd3e4115"
|
||||
dependencies = [
|
||||
"syn",
|
||||
]
|
||||
@@ -1022,9 +1013,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.0.0"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3"
|
||||
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
|
||||
dependencies = [
|
||||
"crc-catalog 2.2.0",
|
||||
]
|
||||
@@ -1055,13 +1046,15 @@ name = "credential"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"coconut-interface",
|
||||
"completions",
|
||||
"config",
|
||||
"credential-storage",
|
||||
"credentials",
|
||||
"crypto",
|
||||
"log",
|
||||
"logging",
|
||||
"network-defaults",
|
||||
"pemstore",
|
||||
"rand 0.7.3",
|
||||
@@ -1471,9 +1464,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.87"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e"
|
||||
checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@@ -1483,9 +1476,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.87"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200"
|
||||
checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -1498,15 +1491,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.87"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea"
|
||||
checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.87"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e"
|
||||
checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1568,7 +1561,7 @@ dependencies = [
|
||||
"hashbrown 0.12.3",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core 0.9.6",
|
||||
"parking_lot_core 0.9.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1762,9 +1755,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
@@ -1786,9 +1779,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.31"
|
||||
version = "0.8.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
|
||||
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -1895,7 +1888,7 @@ name = "explorer-api"
|
||||
version = "1.1.8"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"contracts-common",
|
||||
"dotenv",
|
||||
"humantime-serde",
|
||||
@@ -2006,7 +1999,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project",
|
||||
"spin 0.9.4",
|
||||
"spin 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2059,9 +2052,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -2074,9 +2067,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -2084,15 +2077,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
|
||||
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -2112,15 +2105,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2129,21 +2122,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -2295,19 +2288,6 @@ dependencies = [
|
||||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.16.1"
|
||||
@@ -2496,9 +2476,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
@@ -2521,6 +2501,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
@@ -2650,9 +2636,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.23"
|
||||
version = "0.14.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
|
||||
checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@@ -2839,12 +2825,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
|
||||
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2873,14 +2859,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef"
|
||||
dependencies = [
|
||||
"hermit-abi 0.2.6",
|
||||
"hermit-abi 0.3.1",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3215,7 +3201,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3298,7 +3284,7 @@ dependencies = [
|
||||
"log",
|
||||
"memchr",
|
||||
"mime",
|
||||
"spin 0.9.4",
|
||||
"spin 0.9.5",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"version_check",
|
||||
@@ -3439,7 +3425,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3447,7 +3433,7 @@ dependencies = [
|
||||
"bs58",
|
||||
"build-information",
|
||||
"cfg-if",
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"coconut-bandwidth-contract-common",
|
||||
"coconut-dkg-common",
|
||||
"coconut-interface",
|
||||
@@ -3543,7 +3529,7 @@ dependencies = [
|
||||
"base64 0.13.1",
|
||||
"bip39",
|
||||
"bs58",
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
"dotenv",
|
||||
@@ -3567,7 +3553,7 @@ dependencies = [
|
||||
"bip39",
|
||||
"bs58",
|
||||
"cfg-if",
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"coconut-bandwidth-contract-common",
|
||||
"coconut-dkg-common",
|
||||
"comfy-table",
|
||||
@@ -3598,7 +3584,7 @@ name = "nym-client"
|
||||
version = "1.1.8"
|
||||
dependencies = [
|
||||
"build-information",
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"client-connections",
|
||||
"client-core",
|
||||
"coconut-interface",
|
||||
@@ -3643,7 +3629,7 @@ dependencies = [
|
||||
"bip39",
|
||||
"bs58",
|
||||
"build-information",
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"coconut-interface",
|
||||
"colored",
|
||||
"completions",
|
||||
@@ -3693,7 +3679,7 @@ dependencies = [
|
||||
"atty",
|
||||
"bs58",
|
||||
"build-information",
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"colored",
|
||||
"completions",
|
||||
"config",
|
||||
@@ -3736,7 +3722,7 @@ version = "1.1.8"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"build-information",
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"client-connections",
|
||||
"completions",
|
||||
"dirs",
|
||||
@@ -3826,7 +3812,7 @@ name = "nym-socks5-client"
|
||||
version = "1.1.8"
|
||||
dependencies = [
|
||||
"build-information",
|
||||
"clap 4.1.3",
|
||||
"clap 4.1.4",
|
||||
"client-connections",
|
||||
"client-core",
|
||||
"coconut-interface",
|
||||
@@ -3915,7 +3901,6 @@ dependencies = [
|
||||
name = "nymcoconut"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bls12_381 0.6.0",
|
||||
"bs58",
|
||||
"criterion 0.3.6",
|
||||
@@ -3939,6 +3924,7 @@ dependencies = [
|
||||
name = "nymsphinx"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"criterion 0.3.6",
|
||||
"crypto",
|
||||
"mixnet-contract-common",
|
||||
"nymsphinx-acknowledgements",
|
||||
@@ -4199,7 +4185,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.6",
|
||||
"parking_lot_core 0.9.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4218,15 +4204,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.6"
|
||||
version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf"
|
||||
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec 1.10.0",
|
||||
"windows-sys",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4320,9 +4306,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.5.3"
|
||||
version = "2.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a"
|
||||
checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
@@ -4330,9 +4316,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.5.3"
|
||||
version = "2.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "241cda393b0cdd65e62e07e12454f1f25d57017dcc514b1514cd3c4645e3a0a6"
|
||||
checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
@@ -4340,9 +4326,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.5.3"
|
||||
version = "2.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46b53634d8c8196302953c74d5352f33d0c512a9499bd2ce468fc9f4128fa27c"
|
||||
checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
@@ -4353,9 +4339,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.5.3"
|
||||
version = "2.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef4f1332a8d4678b41966bb4cc1d0676880e84183a1ecc3f4b69f03e99c7a51"
|
||||
checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
@@ -4504,9 +4490,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.50"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -5178,16 +5164,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.7"
|
||||
version = "0.36.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
|
||||
checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5263,7 +5249,7 @@ version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5344,9 +5330,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.8.0"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "645926f31b250a2dca3c232496c2d898d91036e45ca0e97e0e2390c54e11be36"
|
||||
checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
@@ -5424,9 +5410,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.8"
|
||||
version = "0.11.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "718dc5fff5b36f99093fc49b280cfc96ce6fc824317783bff5a1fed0c7a64819"
|
||||
checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -5465,9 +5451,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.91"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
|
||||
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
|
||||
dependencies = [
|
||||
"itoa 1.0.5",
|
||||
"ryu",
|
||||
@@ -5673,7 +5659,7 @@ version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "475b3bbe5245c26f2d8a6f62d67c1f30eb9fffeccee721c45d162c3ebbdf81b2"
|
||||
dependencies = [
|
||||
"heck 0.4.0",
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -5731,9 +5717,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.4"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
|
||||
checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
@@ -5848,7 +5834,7 @@ dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc 3.0.0",
|
||||
"crc 3.0.1",
|
||||
"crossbeam-queue",
|
||||
"dotenvy",
|
||||
"either",
|
||||
@@ -5891,7 +5877,7 @@ checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1"
|
||||
dependencies = [
|
||||
"dotenv",
|
||||
"either",
|
||||
"heck 0.4.0",
|
||||
"heck 0.4.1",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5910,7 +5896,7 @@ checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9"
|
||||
dependencies = [
|
||||
"dotenvy",
|
||||
"either",
|
||||
"heck 0.4.0",
|
||||
"heck 0.4.1",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6031,7 +6017,7 @@ version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||
dependencies = [
|
||||
"heck 0.4.0",
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
@@ -6072,9 +6058,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
@@ -6335,9 +6321,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.24.2"
|
||||
version = "1.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
|
||||
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"bytes",
|
||||
@@ -6351,7 +6337,7 @@ dependencies = [
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"tracing",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6377,9 +6363,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
@@ -6820,9 +6806,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.0"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
|
||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
@@ -6945,7 +6931,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"enum-iterator 0.8.1",
|
||||
"getset",
|
||||
"git2 0.16.1",
|
||||
"git2",
|
||||
"rustc_version 0.4.0",
|
||||
"rustversion",
|
||||
"thiserror",
|
||||
@@ -6953,15 +6939,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "7.5.0"
|
||||
version = "7.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "571b69f690c855821462709b6f41d42ceccc316fbd17b60bd06d06928cfe6a99"
|
||||
checksum = "f21b881cd6636ece9735721cf03c1fe1e774fe258683d084bb2812ab67435749"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
"enum-iterator 1.2.0",
|
||||
"getset",
|
||||
"git2 0.15.0",
|
||||
"git2",
|
||||
"rustc_version 0.4.0",
|
||||
"rustversion",
|
||||
"thiserror",
|
||||
@@ -7255,6 +7241,30 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.42.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc 0.42.1",
|
||||
"windows_i686_gnu 0.42.1",
|
||||
"windows_i686_msvc 0.42.1",
|
||||
"windows_x86_64_gnu 0.42.1",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc 0.42.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.1"
|
||||
|
||||
@@ -107,8 +107,11 @@ edition = "2021"
|
||||
|
||||
[workspace.dependencies]
|
||||
async-trait = "0.1.63"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
thiserror = "1.0.38"
|
||||
serde = "1.0.152"
|
||||
serde_json = "1.0.91"
|
||||
tap = "1.0.1"
|
||||
thiserror = "1.0.38"
|
||||
tokio = "1.24.1"
|
||||
url = "2.2"
|
||||
|
||||
@@ -51,11 +51,22 @@ pub mod non_wasm_helpers;
|
||||
|
||||
pub mod helpers;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClientInput {
|
||||
pub connection_command_sender: ConnectionCommandSender,
|
||||
pub input_sender: InputMessageSender,
|
||||
}
|
||||
|
||||
impl ClientInput {
|
||||
pub async fn send(
|
||||
&self,
|
||||
message: InputMessage,
|
||||
) -> Result<(), tokio::sync::mpsc::error::SendError<InputMessage>> {
|
||||
self.input_sender.send(message).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClientOutput {
|
||||
pub received_buffer_request_sender: ReceivedBufferRequestSender,
|
||||
}
|
||||
@@ -303,6 +314,7 @@ where
|
||||
let shared_key = if self.key_manager.is_gateway_key_set() {
|
||||
Some(self.key_manager.gateway_shared_key())
|
||||
} else {
|
||||
log::info!("Gateway key not set! Will proceed anyway.");
|
||||
None
|
||||
};
|
||||
|
||||
|
||||
@@ -77,4 +77,12 @@ impl InputMessage {
|
||||
lane,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lane(&self) -> &TransmissionLane {
|
||||
match self {
|
||||
InputMessage::Regular { lane, .. }
|
||||
| InputMessage::Anonymous { lane, .. }
|
||||
| InputMessage::Reply { lane, .. } => lane,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ use crate::client::replies::reply_storage::backend::Empty;
|
||||
use crate::client::replies::reply_storage::{CombinedReplyStorage, ReplyStorageBackend};
|
||||
use async_trait::async_trait;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
// well, right now we don't have the browser storage : (
|
||||
// so we keep everything in memory
|
||||
#[derive(Debug)]
|
||||
@@ -27,6 +29,18 @@ impl Backend {
|
||||
impl ReplyStorageBackend for Backend {
|
||||
type StorageError = <Empty as ReplyStorageBackend>::StorageError;
|
||||
|
||||
async fn new(
|
||||
debug_config: &crate::config::DebugConfig,
|
||||
_db_path: Option<PathBuf>,
|
||||
) -> Result<Self, Self::StorageError> {
|
||||
Ok(Backend {
|
||||
empty: Empty {
|
||||
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
|
||||
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn flush_surb_storage(
|
||||
&mut self,
|
||||
storage: &CombinedReplyStorage,
|
||||
|
||||
@@ -56,4 +56,9 @@ pub enum StorageError {
|
||||
details: String,
|
||||
// err: Option<Box<dyn std::error::Error>>
|
||||
},
|
||||
|
||||
#[error("failed to create storage")]
|
||||
FailedToCreateStorage {
|
||||
source: Box<dyn std::error::Error + Send + Sync>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::base_client::non_wasm_helpers;
|
||||
use crate::client::replies::reply_storage::backend::fs_backend::manager::StorageManager;
|
||||
use crate::client::replies::reply_storage::backend::fs_backend::models::{
|
||||
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender,
|
||||
@@ -367,6 +368,20 @@ impl Backend {
|
||||
impl ReplyStorageBackend for Backend {
|
||||
type StorageError = error::StorageError;
|
||||
|
||||
async fn new(
|
||||
debug_config: &crate::config::DebugConfig,
|
||||
db_path: Option<PathBuf>,
|
||||
) -> Result<Self, Self::StorageError> {
|
||||
non_wasm_helpers::setup_fs_reply_surb_backend(db_path, debug_config)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
log::error!("Failed to create storage: {err}");
|
||||
Self::StorageError::FailedToCreateStorage {
|
||||
source: Box::new(err),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.manager.is_active()
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::client::replies::reply_storage::CombinedReplyStorage;
|
||||
use async_trait::async_trait;
|
||||
use std::error::Error;
|
||||
use std::{error::Error, path::PathBuf};
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@@ -30,6 +30,16 @@ pub struct Empty {
|
||||
impl ReplyStorageBackend for Empty {
|
||||
type StorageError = UndefinedError;
|
||||
|
||||
async fn new(
|
||||
debug_config: &crate::config::DebugConfig,
|
||||
_db_path: Option<PathBuf>,
|
||||
) -> Result<Self, Self::StorageError> {
|
||||
Ok(Self {
|
||||
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
|
||||
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
|
||||
})
|
||||
}
|
||||
|
||||
async fn flush_surb_storage(
|
||||
&mut self,
|
||||
_storage: &CombinedReplyStorage,
|
||||
@@ -63,6 +73,11 @@ impl ReplyStorageBackend for Empty {
|
||||
pub trait ReplyStorageBackend: Sized {
|
||||
type StorageError: Error + 'static;
|
||||
|
||||
async fn new(
|
||||
debug_config: &crate::config::DebugConfig,
|
||||
db_path: Option<PathBuf>,
|
||||
) -> Result<Self, Self::StorageError>;
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
bip39 = "1.0.1"
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
log = "0.4"
|
||||
rand = "0.7.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
@@ -20,6 +21,7 @@ completions = { path = "../../common/completions" }
|
||||
credentials = { path = "../../common/credentials" }
|
||||
credential-storage = { path = "../../common/credential-storage" }
|
||||
crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
||||
logging = { path = "../../common/logging"}
|
||||
network-defaults = { path = "../../common/network-defaults" }
|
||||
pemstore = { path = "../../common/pemstore" }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
use clap::{ArgGroup, Args, Subcommand};
|
||||
use completions::ArgShell;
|
||||
use log::*;
|
||||
use rand::rngs::OsRng;
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -12,18 +13,20 @@ use credential_storage::PersistentStorage;
|
||||
use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
|
||||
use credentials::coconut::utils::obtain_aggregate_signature;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use network_defaults::{NymNetworkDetails, VOUCHER_INFO};
|
||||
use network_defaults::VOUCHER_INFO;
|
||||
use validator_client::nyxd::traits::DkgQueryClient;
|
||||
use validator_client::nyxd::tx::Hash;
|
||||
use validator_client::{CoconutApiClient, Config};
|
||||
use validator_client::nyxd::CosmWasmClient;
|
||||
use validator_client::CoconutApiClient;
|
||||
|
||||
use crate::client::Client;
|
||||
use crate::error::{CredentialClientError, Result};
|
||||
use crate::recovery_storage::RecoveryStorage;
|
||||
use crate::state::{KeyPair, State};
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum Command {
|
||||
/// Run the binary
|
||||
/// Run the binary to obtain a credential
|
||||
Run(Run),
|
||||
|
||||
/// Generate shell completions
|
||||
@@ -34,6 +37,11 @@ pub(crate) enum Command {
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[clap(group(
|
||||
ArgGroup::new("recov")
|
||||
.required(true)
|
||||
.args(&["amount", "recovery_mode"]),
|
||||
))]
|
||||
pub(crate) struct Run {
|
||||
/// Home directory of the client that is supposed to use the credential.
|
||||
#[clap(long)]
|
||||
@@ -47,15 +55,25 @@ pub(crate) struct Run {
|
||||
#[clap(long)]
|
||||
pub(crate) mnemonic: String,
|
||||
|
||||
/// The amount of utokens the credential will hold
|
||||
#[clap(long)]
|
||||
/// The amount of utokens the credential will hold. If recovery mode is enabled, this value
|
||||
/// is not needed
|
||||
#[clap(long, default_value = "0")]
|
||||
pub(crate) amount: u64,
|
||||
|
||||
/// Path to a directory used to store recovery files for unconsumed deposits
|
||||
#[clap(long)]
|
||||
pub(crate) recovery_dir: std::path::PathBuf,
|
||||
|
||||
/// Recovery mode, when enabled, tries to recover any deposit data dumped in recovery_dir
|
||||
#[clap(long)]
|
||||
pub(crate) recovery_mode: bool,
|
||||
}
|
||||
|
||||
pub(crate) async fn deposit(nyxd_url: &str, mnemonic: &str, amount: u64) -> Result<State> {
|
||||
let mut rng = OsRng;
|
||||
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
|
||||
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
|
||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
||||
|
||||
let client = Client::new(nyxd_url, mnemonic);
|
||||
let tx_hash = client
|
||||
@@ -67,20 +85,25 @@ pub(crate) async fn deposit(nyxd_url: &str, mnemonic: &str, amount: u64) -> Resu
|
||||
)
|
||||
.await?;
|
||||
|
||||
let state = State {
|
||||
amount,
|
||||
tx_hash,
|
||||
signing_keypair,
|
||||
encryption_keypair,
|
||||
};
|
||||
let voucher = BandwidthVoucher::new(
|
||||
¶ms,
|
||||
amount.to_string(),
|
||||
VOUCHER_INFO.to_string(),
|
||||
Hash::from_str(&tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?,
|
||||
identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?,
|
||||
encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?,
|
||||
);
|
||||
|
||||
let state = State { voucher, params };
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_credential(state: &State, shared_storage: PersistentStorage) -> Result<()> {
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let config = Config::try_from_nym_network_details(&network_details)?;
|
||||
let client = validator_client::Client::new_query(config)?;
|
||||
pub(crate) async fn get_credential<C: Clone + CosmWasmClient + Send + Sync>(
|
||||
state: &State,
|
||||
client: validator_client::Client<C>,
|
||||
shared_storage: PersistentStorage,
|
||||
) -> Result<()> {
|
||||
let epoch_id = client.nyxd.get_current_epoch().await?.epoch_id;
|
||||
let threshold = client
|
||||
.nyxd
|
||||
@@ -89,30 +112,20 @@ pub(crate) async fn get_credential(state: &State, shared_storage: PersistentStor
|
||||
.ok_or(CredentialClientError::NoThreshold)?;
|
||||
let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client, epoch_id).await?;
|
||||
|
||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
||||
let bandwidth_credential_attributes = BandwidthVoucher::new(
|
||||
¶ms,
|
||||
state.amount.to_string(),
|
||||
VOUCHER_INFO.to_string(),
|
||||
Hash::from_str(&state.tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?,
|
||||
identity::PrivateKey::from_base58_string(&state.signing_keypair.private_key)?,
|
||||
encryption::PrivateKey::from_base58_string(&state.encryption_keypair.private_key)?,
|
||||
);
|
||||
|
||||
let signature = obtain_aggregate_signature(
|
||||
¶ms,
|
||||
&bandwidth_credential_attributes,
|
||||
&state.params,
|
||||
&state.voucher,
|
||||
&coconut_api_clients,
|
||||
threshold,
|
||||
)
|
||||
.await?;
|
||||
println!("Signature: {:?}", signature.to_bs58());
|
||||
info!("Signature: {:?}", signature.to_bs58());
|
||||
shared_storage
|
||||
.insert_coconut_credential(
|
||||
state.amount.to_string(),
|
||||
state.voucher.get_voucher_value(),
|
||||
VOUCHER_INFO.to_string(),
|
||||
bandwidth_credential_attributes.get_private_attributes()[0].to_bs58(),
|
||||
bandwidth_credential_attributes.get_private_attributes()[1].to_bs58(),
|
||||
state.voucher.get_private_attributes()[0].to_bs58(),
|
||||
state.voucher.get_private_attributes()[1].to_bs58(),
|
||||
signature.to_bs58(),
|
||||
epoch_id.to_string(),
|
||||
)
|
||||
@@ -120,3 +133,33 @@ pub(crate) async fn get_credential(state: &State, shared_storage: PersistentStor
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn recover_credentials<C: Clone + CosmWasmClient + Send + Sync>(
|
||||
client: validator_client::Client<C>,
|
||||
recovery_storage: &RecoveryStorage,
|
||||
shared_storage: PersistentStorage,
|
||||
) -> Result<()> {
|
||||
for voucher in recovery_storage.unconsumed_vouchers()? {
|
||||
let state = State {
|
||||
voucher,
|
||||
params: Parameters::new(TOTAL_ATTRIBUTES).unwrap(),
|
||||
};
|
||||
if let Err(e) = get_credential(&state, client.clone(), shared_storage.clone()).await {
|
||||
error!(
|
||||
"Could not recover deposit {} due to {:?}, try again later",
|
||||
state.voucher.tx_hash(),
|
||||
e
|
||||
)
|
||||
} else {
|
||||
info!(
|
||||
"Converted deposit {} to a credential, removing recovery data for it",
|
||||
state.voucher.tx_hash()
|
||||
);
|
||||
if let Err(e) = recovery_storage.remove_voucher(state.voucher.tx_hash().to_string()) {
|
||||
warn!("Could not remove recovery data - {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::time::SystemTimeError;
|
||||
use thiserror::Error;
|
||||
|
||||
use credential_storage::error::StorageError;
|
||||
@@ -14,6 +15,9 @@ pub type Result<T> = std::result::Result<T, CredentialClientError>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CredentialClientError {
|
||||
#[error("IO error: {0}")]
|
||||
IOError(#[from] std::io::Error),
|
||||
|
||||
#[error("Nyxd error: {0}")]
|
||||
Nyxd(#[from] NyxdError),
|
||||
|
||||
@@ -35,6 +39,9 @@ pub enum CredentialClientError {
|
||||
#[error("Could not use shared storage")]
|
||||
SharedStorageError(#[from] StorageError),
|
||||
|
||||
#[error("Could not get system time")]
|
||||
SysTimeError(#[from] SystemTimeError),
|
||||
|
||||
#[error("Threshold not set yet")]
|
||||
NoThreshold,
|
||||
}
|
||||
|
||||
@@ -4,15 +4,25 @@
|
||||
mod client;
|
||||
mod commands;
|
||||
mod error;
|
||||
mod recovery_storage;
|
||||
mod state;
|
||||
|
||||
use commands::*;
|
||||
use completions::fig_generate;
|
||||
use config::{DATA_DIR, DB_FILE_NAME};
|
||||
use error::Result;
|
||||
use network_defaults::setup_env;
|
||||
use log::*;
|
||||
use network_defaults::{setup_env, NymNetworkDetails};
|
||||
use std::process::exit;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use clap::{CommandFactory, Parser};
|
||||
use logging::setup_logging;
|
||||
use validator_client::nyxd::traits::DkgQueryClient;
|
||||
use validator_client::nyxd::CosmWasmClient;
|
||||
use validator_client::Config;
|
||||
|
||||
const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author = "Nymtech", version, about)]
|
||||
@@ -25,9 +35,36 @@ struct Cli {
|
||||
pub(crate) command: Command,
|
||||
}
|
||||
|
||||
async fn block_until_coconut_is_available<C: Clone + CosmWasmClient + Send + Sync>(
|
||||
client: &validator_client::Client<C>,
|
||||
) -> Result<()> {
|
||||
loop {
|
||||
let epoch = client.nyxd.get_current_epoch().await?;
|
||||
let current_timestamp_secs = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)?
|
||||
.as_secs();
|
||||
if epoch.state.is_final() {
|
||||
if current_timestamp_secs + SAFETY_BUFFER_SECS >= epoch.finish_timestamp.seconds() {
|
||||
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
// Use 20 additional seconds to avoid the exact moment of going into the final epoch state
|
||||
let secs_until_final = epoch.final_timestamp_secs() + 20 - current_timestamp_secs;
|
||||
info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final);
|
||||
std::thread::sleep(Duration::from_secs(secs_until_final));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let args = Cli::parse();
|
||||
setup_logging();
|
||||
setup_env(args.config_env_file.as_ref());
|
||||
let bin_name = "nym-credential-client";
|
||||
|
||||
@@ -35,9 +72,34 @@ async fn main() -> Result<()> {
|
||||
Command::Run(r) => {
|
||||
let db_path = r.client_home_directory.join(DATA_DIR).join(DB_FILE_NAME);
|
||||
let shared_storage = credential_storage::initialise_storage(db_path).await;
|
||||
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
|
||||
|
||||
let state = deposit(&r.nyxd_url, &r.mnemonic, r.amount).await?;
|
||||
get_credential(&state, shared_storage).await?;
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let config = Config::try_from_nym_network_details(&network_details)?;
|
||||
let client = validator_client::Client::new_query(config)?;
|
||||
|
||||
block_until_coconut_is_available(&client).await?;
|
||||
info!("Starting depositing funds, don't kill the process");
|
||||
|
||||
if !r.recovery_mode {
|
||||
let state = deposit(&r.nyxd_url, &r.mnemonic, r.amount).await?;
|
||||
if get_credential(&state, client, shared_storage)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
warn!("Failed to obtain credential. Dumping recovery data.",);
|
||||
match recovery_storage.insert_voucher(&state.voucher) {
|
||||
Ok(file_path) => {
|
||||
warn!("Dumped recovery data to {:?}. Try using recovery mode to convert it to a credential", file_path);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Could not dump recovery data to file system due to {:?}, the deposit will be lost!", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
recover_credentials(client, &recovery_storage, shared_storage).await?;
|
||||
}
|
||||
}
|
||||
Command::Completions(c) => c.generate(&mut crate::Cli::command(), bin_name),
|
||||
Command::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name),
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use credentials::coconut::bandwidth::BandwidthVoucher;
|
||||
use std::fs::{create_dir_all, read_dir, File};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct RecoveryStorage {
|
||||
recovery_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl RecoveryStorage {
|
||||
pub fn new(recovery_dir: PathBuf) -> std::io::Result<Self> {
|
||||
create_dir_all(&recovery_dir)?;
|
||||
Ok(Self { recovery_dir })
|
||||
}
|
||||
|
||||
pub fn unconsumed_vouchers(&self) -> std::io::Result<impl Iterator<Item = BandwidthVoucher>> {
|
||||
Ok(read_dir(&self.recovery_dir)?
|
||||
.filter_map(|entry| entry.ok())
|
||||
.filter_map(|entry| {
|
||||
let path = entry.path();
|
||||
if path.is_file() {
|
||||
Some(path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|path| File::open(path).ok())
|
||||
.filter_map(|mut f| {
|
||||
let mut buff = Vec::new();
|
||||
if f.read_to_end(&mut buff).is_ok() {
|
||||
Some(buff)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|buff| BandwidthVoucher::try_from_bytes(&buff).ok()))
|
||||
}
|
||||
|
||||
pub fn insert_voucher(&self, voucher: &BandwidthVoucher) -> std::io::Result<PathBuf> {
|
||||
let file_name = voucher.tx_hash().to_string();
|
||||
let file_path = self.recovery_dir.join(file_name);
|
||||
let mut file = File::create(&file_path)?;
|
||||
let buff = voucher.to_bytes();
|
||||
file.write_all(&buff)?;
|
||||
|
||||
Ok(file_path)
|
||||
}
|
||||
|
||||
pub fn remove_voucher(&self, file_name: String) -> std::io::Result<()> {
|
||||
let file_path = self.recovery_dir.join(file_name);
|
||||
std::fs::remove_file(file_path)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use coconut_interface::Parameters;
|
||||
use credentials::coconut::bandwidth::BandwidthVoucher;
|
||||
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub(crate) struct KeyPair {
|
||||
pub public_key: String,
|
||||
pub private_key: String,
|
||||
@@ -29,10 +29,7 @@ impl From<encryption::KeyPair> for KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub(crate) struct State {
|
||||
pub amount: u64,
|
||||
pub tx_hash: String,
|
||||
pub signing_keypair: KeyPair,
|
||||
pub encryption_keypair: KeyPair,
|
||||
pub voucher: BandwidthVoucher,
|
||||
pub params: Parameters,
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ pub(crate) struct Init {
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
@@ -62,7 +62,7 @@ pub(crate) struct Init {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long)]
|
||||
#[clap(long, hide = true)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Save a summary of the initialization to a json file
|
||||
|
||||
@@ -23,7 +23,7 @@ pub(crate) struct Run {
|
||||
id: String,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
@@ -59,7 +59,7 @@ pub(crate) struct Run {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long)]
|
||||
#[clap(long, hide = true)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ use client_core::client::key_manager::KeyManager;
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
#[cfg(not(feature = "mobile"))]
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
use log::*;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
@@ -69,6 +70,7 @@ impl NymClient {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "mobile"))]
|
||||
async fn create_bandwidth_controller(config: &Config) -> BandwidthController<QueryNyxdClient> {
|
||||
let details = network_defaults::NymNetworkDetails::new_from_env();
|
||||
let mut client_config = validator_client::Config::try_from_nym_network_details(&details)
|
||||
|
||||
@@ -43,7 +43,7 @@ pub(crate) struct Init {
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
@@ -66,7 +66,7 @@ pub(crate) struct Init {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long)]
|
||||
#[clap(long, hide = true)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Save a summary of the initialization to a json file
|
||||
|
||||
@@ -43,7 +43,7 @@ pub(crate) struct Run {
|
||||
gateway: Option<identity::PublicKey>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the Nym APIs
|
||||
@@ -65,7 +65,7 @@ pub(crate) struct Run {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long)]
|
||||
#[clap(long, hide = true)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
@@ -441,6 +441,7 @@ where
|
||||
}
|
||||
|
||||
debug_assert!(self.connection.is_available());
|
||||
log::trace!("Registering gateway");
|
||||
|
||||
// it's fine to instantiate it here as it's only used once (during authentication or registration)
|
||||
// and putting it into the GatewayClient struct would be a hassle
|
||||
|
||||
@@ -934,16 +934,6 @@ impl NymApiClient {
|
||||
Ok(self.nym_api_client.blind_sign(request_body).await?)
|
||||
}
|
||||
|
||||
pub async fn partial_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &str,
|
||||
) -> Result<BlindedSignatureResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.nym_api_client
|
||||
.partial_bandwidth_credential(request_body)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn verify_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
|
||||
@@ -449,23 +449,6 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn partial_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &str,
|
||||
) -> Result<BlindedSignatureResponse, NymAPIError> {
|
||||
self.post_nym_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
routes::BANDWIDTH,
|
||||
routes::COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL,
|
||||
],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn verify_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
|
||||
@@ -14,7 +14,6 @@ pub const COCONUT_ROUTES: &str = "coconut";
|
||||
pub const BANDWIDTH: &str = "bandwidth";
|
||||
|
||||
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
|
||||
pub const COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL: &str = "partial-bandwidth-credential";
|
||||
pub const COCONUT_VERIFY_BANDWIDTH_CREDENTIAL: &str = "verify-bandwidth-credential";
|
||||
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
|
||||
@@ -114,6 +114,33 @@ impl Epoch {
|
||||
finish_timestamp: current_timestamp.plus_seconds(duration),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn final_timestamp_secs(&self) -> u64 {
|
||||
let mut finish = self.finish_timestamp.seconds();
|
||||
let time_configuration = self.time_configuration;
|
||||
let mut curr_epoch_state = self.state;
|
||||
while let Some(state) = curr_epoch_state.next() {
|
||||
curr_epoch_state = state;
|
||||
let adding = match curr_epoch_state {
|
||||
EpochState::PublicKeySubmission { .. } => {
|
||||
time_configuration.public_key_submission_time_secs
|
||||
}
|
||||
EpochState::DealingExchange { .. } => time_configuration.dealing_exchange_time_secs,
|
||||
EpochState::VerificationKeySubmission { .. } => {
|
||||
time_configuration.verification_key_submission_time_secs
|
||||
}
|
||||
EpochState::VerificationKeyValidation { .. } => {
|
||||
time_configuration.verification_key_validation_time_secs
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { .. } => {
|
||||
time_configuration.verification_key_finalization_time_secs
|
||||
}
|
||||
EpochState::InProgress { .. } => 0,
|
||||
};
|
||||
finish += adding;
|
||||
}
|
||||
finish
|
||||
}
|
||||
}
|
||||
|
||||
// currently (it is still extremely likely to change, we might be able to get rid of verification key-related complaints),
|
||||
@@ -195,4 +222,8 @@ impl EpochState {
|
||||
|
||||
states
|
||||
}
|
||||
|
||||
pub fn is_final(&self) -> bool {
|
||||
*self == EpochState::InProgress
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,41 +42,9 @@ pub struct BandwidthVoucher {
|
||||
encryption_key: encryption::PrivateKey,
|
||||
pedersen_commitments_openings: Vec<Attribute>,
|
||||
blind_sign_request: BlindSignRequest,
|
||||
use_request: bool,
|
||||
}
|
||||
|
||||
impl BandwidthVoucher {
|
||||
pub fn new_with_blind_sign_req(
|
||||
private_attributes: [PrivateAttribute; PRIVATE_ATTRIBUTES as usize],
|
||||
public_attributes_plain: [&str; PUBLIC_ATTRIBUTES as usize],
|
||||
tx_hash: Hash,
|
||||
signing_key: identity::PrivateKey,
|
||||
encryption_key: encryption::PrivateKey,
|
||||
pedersen_commitments_openings: Vec<Attribute>,
|
||||
blind_sign_request: BlindSignRequest,
|
||||
) -> Self {
|
||||
let voucher_value = public_attributes_plain[0];
|
||||
let voucher_info = public_attributes_plain[1];
|
||||
let voucher_value_plain = voucher_value.to_string();
|
||||
let voucher_info_plain = voucher_info.to_string();
|
||||
let voucher_value = hash_to_scalar(voucher_value.as_bytes());
|
||||
let voucher_info = hash_to_scalar(voucher_info.as_bytes());
|
||||
|
||||
BandwidthVoucher {
|
||||
serial_number: private_attributes[0],
|
||||
binding_number: private_attributes[1],
|
||||
voucher_value,
|
||||
voucher_value_plain,
|
||||
voucher_info,
|
||||
voucher_info_plain,
|
||||
tx_hash,
|
||||
signing_key,
|
||||
encryption_key,
|
||||
pedersen_commitments_openings,
|
||||
blind_sign_request,
|
||||
use_request: false,
|
||||
}
|
||||
}
|
||||
pub fn new(
|
||||
params: &Parameters,
|
||||
voucher_value: String,
|
||||
@@ -109,10 +77,135 @@ impl BandwidthVoucher {
|
||||
encryption_key,
|
||||
pedersen_commitments_openings,
|
||||
blind_sign_request,
|
||||
use_request: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let serial_number_b = self.serial_number.to_bytes();
|
||||
let binding_number_b = self.binding_number.to_bytes();
|
||||
let voucher_value_plain_b = self.voucher_value_plain.as_bytes();
|
||||
let voucher_info_plain_b = self.voucher_info_plain.as_bytes();
|
||||
let tx_hash_b = self.tx_hash.as_bytes();
|
||||
let signing_key_b = self.signing_key.to_bytes();
|
||||
let encryption_key_b = self.encryption_key.to_bytes();
|
||||
let blind_sign_request_b = self.blind_sign_request.to_bytes();
|
||||
|
||||
let mut ret = Vec::new();
|
||||
|
||||
ret.extend_from_slice(&serial_number_b);
|
||||
ret.extend_from_slice(&binding_number_b);
|
||||
ret.extend_from_slice(tx_hash_b);
|
||||
ret.extend_from_slice(&signing_key_b);
|
||||
ret.extend_from_slice(&encryption_key_b);
|
||||
ret.extend_from_slice(&(voucher_value_plain_b.len() as u64).to_be_bytes());
|
||||
ret.extend_from_slice(&(voucher_info_plain_b.len() as u64).to_be_bytes());
|
||||
ret.extend_from_slice(&(blind_sign_request_b.len() as u64).to_be_bytes());
|
||||
ret.extend_from_slice(&(self.pedersen_commitments_openings.len() as u64).to_be_bytes());
|
||||
ret.extend_from_slice(voucher_value_plain_b);
|
||||
ret.extend_from_slice(voucher_info_plain_b);
|
||||
ret.extend_from_slice(&blind_sign_request_b);
|
||||
for commitment in self.pedersen_commitments_openings.iter() {
|
||||
ret.extend_from_slice(&commitment.to_bytes());
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, Error> {
|
||||
if bytes.len() < 32 * 5 + 4 * 8 {
|
||||
return Err(Error::BandwidthVoucherDeserializationError(format!(
|
||||
"Less then {} bytes needed",
|
||||
32 * 5 + 4 * 8
|
||||
)));
|
||||
}
|
||||
let mut buff = [0u8; 32];
|
||||
let mut small_buff = [0u8; 8];
|
||||
let scalar_err =
|
||||
|| Error::BandwidthVoucherDeserializationError(String::from("Invalid Scalar"));
|
||||
buff.copy_from_slice(&bytes[..32]);
|
||||
let serial_number = Option::<PrivateAttribute>::from(PrivateAttribute::from_bytes(&buff))
|
||||
.ok_or_else(scalar_err)?;
|
||||
buff.copy_from_slice(&bytes[32..2 * 32]);
|
||||
let binding_number = Option::<PrivateAttribute>::from(PrivateAttribute::from_bytes(&buff))
|
||||
.ok_or_else(scalar_err)?;
|
||||
buff.copy_from_slice(&bytes[2 * 32..3 * 32]);
|
||||
let tx_hash = Hash::new(buff);
|
||||
buff.copy_from_slice(&bytes[3 * 32..4 * 32]);
|
||||
let signing_key = identity::PrivateKey::from_bytes(&buff).map_err(|_| {
|
||||
Error::BandwidthVoucherDeserializationError(String::from("Invalid key"))
|
||||
})?;
|
||||
buff.copy_from_slice(&bytes[4 * 32..5 * 32]);
|
||||
let encryption_key = encryption::PrivateKey::from_bytes(&buff).map_err(|_| {
|
||||
Error::BandwidthVoucherDeserializationError(String::from("Invalid key"))
|
||||
})?;
|
||||
small_buff.copy_from_slice(&bytes[5 * 32..5 * 32 + 8]);
|
||||
let voucher_value_plain_no = u64::from_be_bytes(small_buff) as usize;
|
||||
small_buff.copy_from_slice(&bytes[5 * 32 + 8..5 * 32 + 2 * 8]);
|
||||
let voucher_info_plain_no = u64::from_be_bytes(small_buff) as usize;
|
||||
small_buff.copy_from_slice(&bytes[5 * 32 + 2 * 8..5 * 32 + 3 * 8]);
|
||||
let blind_sign_request_no = u64::from_be_bytes(small_buff) as usize;
|
||||
small_buff.copy_from_slice(&bytes[5 * 32 + 3 * 8..5 * 32 + 4 * 8]);
|
||||
let pedersen_commitments_openings_no = u64::from_be_bytes(small_buff) as usize;
|
||||
|
||||
let total_length = 32 * 5
|
||||
+ 4 * 8
|
||||
+ voucher_value_plain_no
|
||||
+ voucher_info_plain_no
|
||||
+ blind_sign_request_no
|
||||
+ pedersen_commitments_openings_no * 32;
|
||||
if bytes.len() != total_length {
|
||||
return Err(Error::BandwidthVoucherDeserializationError(format!(
|
||||
"Expected {total_length} bytes",
|
||||
)));
|
||||
}
|
||||
|
||||
let utf_err = |_| {
|
||||
Err(Error::BandwidthVoucherDeserializationError(String::from(
|
||||
"Invalid UTF8 string",
|
||||
)))
|
||||
};
|
||||
let mut var_length_pointer = 5 * 32 + 4 * 8;
|
||||
let voucher_value_plain = String::from_utf8(
|
||||
bytes[var_length_pointer..var_length_pointer + voucher_value_plain_no].to_vec(),
|
||||
)
|
||||
.or_else(utf_err)?;
|
||||
let voucher_value = hash_to_scalar(&voucher_value_plain);
|
||||
var_length_pointer += voucher_value_plain_no;
|
||||
let voucher_info_plain = String::from_utf8(
|
||||
bytes[var_length_pointer..var_length_pointer + voucher_info_plain_no].to_vec(),
|
||||
)
|
||||
.or_else(utf_err)?;
|
||||
let voucher_info = hash_to_scalar(&voucher_info_plain);
|
||||
var_length_pointer += voucher_info_plain_no;
|
||||
let blind_sign_request = BlindSignRequest::from_bytes(
|
||||
&bytes[var_length_pointer..var_length_pointer + blind_sign_request_no],
|
||||
)?;
|
||||
var_length_pointer += blind_sign_request_no;
|
||||
|
||||
let mut pedersen_commitments_openings = Vec::new();
|
||||
for _ in 0..pedersen_commitments_openings_no {
|
||||
buff.copy_from_slice(&bytes[var_length_pointer..var_length_pointer + 32]);
|
||||
let commitment =
|
||||
Option::<Attribute>::from(Attribute::from_bytes(&buff)).ok_or_else(scalar_err)?;
|
||||
var_length_pointer += 32;
|
||||
pedersen_commitments_openings.push(commitment);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
serial_number,
|
||||
binding_number,
|
||||
voucher_value,
|
||||
voucher_value_plain,
|
||||
voucher_info,
|
||||
voucher_info_plain,
|
||||
tx_hash,
|
||||
signing_key,
|
||||
encryption_key,
|
||||
pedersen_commitments_openings,
|
||||
blind_sign_request,
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if the plain values correspond to the PublicAttributes
|
||||
pub fn verify_against_plain(values: &[PublicAttribute], plain_values: &[String]) -> bool {
|
||||
values.len() == 2
|
||||
@@ -141,8 +234,8 @@ impl BandwidthVoucher {
|
||||
&self.blind_sign_request
|
||||
}
|
||||
|
||||
pub fn use_request(&self) -> bool {
|
||||
self.use_request
|
||||
pub fn get_voucher_value(&self) -> String {
|
||||
self.voucher_value_plain.clone()
|
||||
}
|
||||
|
||||
pub fn get_public_attributes_plain(&self) -> Vec<String> {
|
||||
@@ -189,13 +282,13 @@ pub fn prepare_for_spending(
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use coconut_interface::Base58;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
#[test]
|
||||
fn voucher_consistency() {
|
||||
fn voucher_fixture() -> BandwidthVoucher {
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let mut rng = OsRng;
|
||||
let voucher = BandwidthVoucher::new(
|
||||
BandwidthVoucher::new(
|
||||
¶ms,
|
||||
"1234".to_string(),
|
||||
"voucher info".to_string(),
|
||||
@@ -210,7 +303,48 @@ mod test {
|
||||
&encryption::KeyPair::new(&mut rng).private_key().to_bytes(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_voucher() {
|
||||
let voucher = voucher_fixture();
|
||||
let bytes = voucher.to_bytes();
|
||||
let deserialized_voucher = BandwidthVoucher::try_from_bytes(&bytes).unwrap();
|
||||
assert_eq!(voucher.serial_number, deserialized_voucher.serial_number);
|
||||
assert_eq!(voucher.binding_number, deserialized_voucher.binding_number);
|
||||
assert_eq!(voucher.voucher_value, deserialized_voucher.voucher_value);
|
||||
assert_eq!(
|
||||
voucher.voucher_value_plain,
|
||||
deserialized_voucher.voucher_value_plain
|
||||
);
|
||||
assert_eq!(voucher.voucher_info, deserialized_voucher.voucher_info);
|
||||
assert_eq!(
|
||||
voucher.voucher_info_plain,
|
||||
deserialized_voucher.voucher_info_plain
|
||||
);
|
||||
assert_eq!(voucher.tx_hash, deserialized_voucher.tx_hash);
|
||||
assert_eq!(
|
||||
voucher.signing_key.to_string(),
|
||||
deserialized_voucher.signing_key.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
voucher.encryption_key.to_string(),
|
||||
deserialized_voucher.encryption_key.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
voucher.pedersen_commitments_openings,
|
||||
deserialized_voucher.pedersen_commitments_openings
|
||||
);
|
||||
assert_eq!(
|
||||
voucher.blind_sign_request.to_bs58(),
|
||||
deserialized_voucher.blind_sign_request.to_bs58()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn voucher_consistency() {
|
||||
let voucher = voucher_fixture();
|
||||
assert!(!BandwidthVoucher::verify_against_plain(
|
||||
&[],
|
||||
&voucher.get_public_attributes_plain()
|
||||
|
||||
@@ -45,21 +45,15 @@ async fn obtain_partial_credential(
|
||||
let private_attributes = attributes.get_private_attributes();
|
||||
let blind_sign_request = attributes.blind_sign_request();
|
||||
|
||||
let response = if attributes.use_request() {
|
||||
let blind_sign_request_body = BlindSignRequestBody::new(
|
||||
blind_sign_request,
|
||||
attributes.tx_hash().to_string(),
|
||||
attributes.sign(blind_sign_request).to_base58_string(),
|
||||
&public_attributes,
|
||||
public_attributes_plain,
|
||||
(public_attributes.len() + private_attributes.len()) as u32,
|
||||
);
|
||||
client.blind_sign(&blind_sign_request_body).await?
|
||||
} else {
|
||||
client
|
||||
.partial_bandwidth_credential(&attributes.tx_hash().to_string())
|
||||
.await?
|
||||
};
|
||||
let blind_sign_request_body = BlindSignRequestBody::new(
|
||||
blind_sign_request,
|
||||
attributes.tx_hash().to_string(),
|
||||
attributes.sign(blind_sign_request).to_base58_string(),
|
||||
&public_attributes,
|
||||
public_attributes_plain,
|
||||
(public_attributes.len() + private_attributes.len()) as u32,
|
||||
);
|
||||
let response = client.blind_sign(&blind_sign_request_body).await?;
|
||||
let encrypted_signature = response.encrypted_signature;
|
||||
let remote_key = PublicKey::from_bytes(&response.remote_key)?;
|
||||
|
||||
@@ -109,6 +103,8 @@ pub async fn obtain_aggregate_signature(
|
||||
.iter()
|
||||
.map(|api_client| api_client.node_id)
|
||||
.collect();
|
||||
let verification_key =
|
||||
aggregate_verification_keys(&validators_partial_vks, Some(indices.as_ref()))?;
|
||||
|
||||
for coconut_api_client in coconut_api_clients.iter() {
|
||||
if let Ok(signature) = obtain_partial_credential(
|
||||
@@ -131,15 +127,8 @@ pub async fn obtain_aggregate_signature(
|
||||
attributes.extend_from_slice(&private_attributes);
|
||||
attributes.extend_from_slice(&public_attributes);
|
||||
|
||||
let verification_key =
|
||||
aggregate_verification_keys(&validators_partial_vks, Some(indices.as_ref()))?;
|
||||
|
||||
Ok(aggregate_signature_shares(
|
||||
params,
|
||||
&verification_key,
|
||||
&attributes,
|
||||
&shares,
|
||||
)?)
|
||||
aggregate_signature_shares(params, &verification_key, &attributes, &shares)
|
||||
.map_err(Error::SignatureAggregationError)
|
||||
}
|
||||
|
||||
// TODO: better type flow
|
||||
|
||||
@@ -9,6 +9,9 @@ use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("IO error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
|
||||
#[error("The detailed description is yet to be determined")]
|
||||
BandwidthCredentialError,
|
||||
|
||||
@@ -30,6 +33,12 @@ pub enum Error {
|
||||
#[error("Could not parse the key - {0}")]
|
||||
ParsePublicKey(#[from] KeyRecoveryError),
|
||||
|
||||
#[error("Could not gather enough signature shares")]
|
||||
#[error("Could not gather enough signature shares. Try again using the recovery command")]
|
||||
NotEnoughShares,
|
||||
|
||||
#[error("Could not aggregate signature shares - {0}. Try again using the recovery command")]
|
||||
SignatureAggregationError(CoconutError),
|
||||
|
||||
#[error("Could not deserialize bandwidth voucher - {0}")]
|
||||
BandwidthVoucherDeserializationError(String),
|
||||
}
|
||||
|
||||
@@ -33,9 +33,6 @@ criterion = { version="0.3", features=["html_reports"] }
|
||||
doc-comment = "0.3"
|
||||
rand_chacha = "0.3"
|
||||
|
||||
[dev-dependencies.bincode]
|
||||
version = "1"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmarks"
|
||||
harness = false
|
||||
|
||||
@@ -27,6 +27,11 @@ topology = { path = "../topology" }
|
||||
|
||||
[dev-dependencies]
|
||||
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
|
||||
criterion = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmarks"
|
||||
harness = false
|
||||
|
||||
# do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require
|
||||
# net2 via tokio-util -> tokio -> mio -> net2
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
||||
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use crypto::asymmetric::encryption::{KeyPair, PrivateKey};
|
||||
use crypto::asymmetric::identity::PublicKey;
|
||||
use mixnet_contract_common::Layer;
|
||||
use nymsphinx::{delays, Node, NODE_ADDRESS_LENGTH, NodeAddressBytes, NymsphinxPayloadBuilder, PAYLOAD_OVERHEAD_SIZE, SphinxPacket};
|
||||
use nymsphinx::acknowledgements::AckKey;
|
||||
use nymsphinx::acknowledgements::surb_ack::SurbAck;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::builder::SphinxPacketBuilder;
|
||||
use nymsphinx::chunking::fragment::{Fragment, FragmentHeader, FragmentIdentifier};
|
||||
use nymsphinx::cover::generate_loop_cover_packet;
|
||||
use nymsphinx::crypto::keygen;
|
||||
use nymsphinx::params::packet_sizes::PacketSize::{ExtendedPacket16, ExtendedPacket32, ExtendedPacket8, RegularPacket};
|
||||
use nymsphinx::params::PacketSize;
|
||||
use topology::{gateway, mix, MixLayer, NymTopology};
|
||||
|
||||
const REGULAR_PACKET_SIZE: usize = PAYLOAD_OVERHEAD_SIZE + 2 * 1024;
|
||||
const EXTENDED_PACKET_SIZE_8: usize = PAYLOAD_OVERHEAD_SIZE + 8 * 1024;
|
||||
const EXTENDED_PACKET_SIZE_16: usize = PAYLOAD_OVERHEAD_SIZE + 16 * 1024;
|
||||
const EXTENDED_PACKET_SIZE_32: usize = PAYLOAD_OVERHEAD_SIZE + 32 * 1024;
|
||||
|
||||
struct BenchCase {
|
||||
packet_size: PacketSize,
|
||||
}
|
||||
|
||||
fn feature_topology(sender_gateway_id: PublicKey, recipient_gateway_id: PublicKey) -> (NymTopology, KeyPair) {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let gateway1 = gateway::Node {
|
||||
owner: "N/A".to_string(),
|
||||
stake: 1000,
|
||||
location: "N/A".to_string(),
|
||||
host: "1.1.1.1".parse().unwrap(),
|
||||
mix_host: "1.1.1.1:1789".parse().unwrap(),
|
||||
clients_port: 8888,
|
||||
identity_key: sender_gateway_id,
|
||||
sphinx_key: encryption::PublicKey::from_base58_string(
|
||||
"C7cown6dYCLZpLiMFC1PaBmhvLvmJmLDJGeRTbPD45bX",
|
||||
)
|
||||
.unwrap(),
|
||||
version: "0.x.0".to_string(),
|
||||
};
|
||||
|
||||
let gateway2 = gateway::Node {
|
||||
identity_key: recipient_gateway_id,
|
||||
..gateway1.clone()
|
||||
};
|
||||
|
||||
let node1_enc_keys = KeyPair::new(&mut rng);
|
||||
let node1 = mix::Node {
|
||||
mix_id: 42,
|
||||
owner: "N/A".to_string(),
|
||||
host: "3.3.3.3".parse().unwrap(),
|
||||
mix_host: "3.3.3.3:1789".parse().unwrap(),
|
||||
identity_key: identity::PublicKey::from_base58_string(
|
||||
"3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7",
|
||||
)
|
||||
.unwrap(),
|
||||
sphinx_key: *node1_enc_keys.public_key(),
|
||||
layer: Layer::One,
|
||||
version: "0.x.0".to_string(),
|
||||
};
|
||||
|
||||
let node2 = mix::Node {
|
||||
owner: "Alice".to_string(),
|
||||
..node1.clone()
|
||||
};
|
||||
|
||||
let node3 = mix::Node {
|
||||
owner: "Bob".to_string(),
|
||||
..node1.clone()
|
||||
};
|
||||
|
||||
let mut mixes: HashMap<MixLayer, Vec<mix::Node>> = HashMap::new();
|
||||
mixes.insert(1, vec![node1]);
|
||||
mixes.insert(2, vec![node2]);
|
||||
mixes.insert(3, vec![node3]);
|
||||
|
||||
let topology = NymTopology::new(mixes, vec![gateway1, gateway2]);
|
||||
(topology, node1_enc_keys)
|
||||
}
|
||||
|
||||
fn make_packet_copy(packet: &SphinxPacket) -> SphinxPacket {
|
||||
SphinxPacket::from_bytes(&packet.to_bytes()).unwrap()
|
||||
}
|
||||
|
||||
fn bench_loop_packet_create(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("benchmark-sphinx");
|
||||
// group.sample_size(200);
|
||||
group.measurement_time(Duration::from_secs(500));
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let case = BenchCase {
|
||||
packet_size: RegularPacket,
|
||||
};
|
||||
|
||||
// create sender
|
||||
let sender_client_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let sender_client_enc_pair = encryption::KeyPair::new(&mut rng);
|
||||
let sender_gateway_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let packet_sender = Recipient::new(
|
||||
*sender_client_id_pair.public_key(),
|
||||
*sender_client_enc_pair.public_key(),
|
||||
*sender_gateway_id_pair.public_key(),
|
||||
);
|
||||
|
||||
// build topology
|
||||
let (topology, node_keypair) = feature_topology(*sender_gateway_id_pair.public_key(), *sender_gateway_id_pair.public_key());
|
||||
// generate the encryption key for the ack
|
||||
let ack_key = AckKey::new(&mut rng);
|
||||
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Sphinx] create_loop_cover_packet_with_payload_size_{}",
|
||||
case.packet_size.payload_size(),
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
generate_loop_cover_packet(
|
||||
&mut rng,
|
||||
&topology,
|
||||
&ack_key,
|
||||
&packet_sender,
|
||||
Duration::from_millis(50),
|
||||
Duration::from_millis(50),
|
||||
case.packet_size)
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// let's create the packet to later benchmark the processing
|
||||
let packet = generate_loop_cover_packet(
|
||||
&mut rng,
|
||||
&topology,
|
||||
&ack_key,
|
||||
&packet_sender,
|
||||
Duration::from_millis(50),
|
||||
Duration::from_millis(50),
|
||||
case.packet_size).unwrap();
|
||||
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Sphinx] process_loop_cover_packet_with_payload_size_{}",
|
||||
case.packet_size.payload_size(),
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
make_packet_copy(&packet.sphinx_packet).process(&node_keypair.private_key().into())
|
||||
})
|
||||
},
|
||||
);
|
||||
// let new_packet = packet.sphinx_packet.process(&node_keypair.private_key().into());
|
||||
}
|
||||
|
||||
fn bench_new_no_surb(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("benchmark-sphinx");
|
||||
// group.sample_size(200);
|
||||
group.measurement_time(Duration::from_secs(500));
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let case = BenchCase {
|
||||
packet_size: ExtendedPacket8,
|
||||
};
|
||||
|
||||
// create sender
|
||||
let sender_client_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let sender_client_enc_pair = encryption::KeyPair::new(&mut rng);
|
||||
let sender_gateway_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let packet_sender = Recipient::new(
|
||||
*sender_client_id_pair.public_key(),
|
||||
*sender_client_enc_pair.public_key(),
|
||||
*sender_gateway_id_pair.public_key(),
|
||||
);
|
||||
|
||||
// create recipient
|
||||
let recipient_client_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let recipient_client_enc_pair = encryption::KeyPair::new(&mut rng);
|
||||
let recipient_gateway_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let packet_recipient = Recipient::new(
|
||||
*recipient_client_id_pair.public_key(),
|
||||
*recipient_client_enc_pair.public_key(),
|
||||
*recipient_gateway_id_pair.public_key(),
|
||||
);
|
||||
|
||||
// build topology
|
||||
let (topology, node_keypair) = feature_topology(*sender_gateway_id_pair.public_key(), *recipient_gateway_id_pair.public_key());
|
||||
|
||||
// generate pseudorandom route for the packet
|
||||
let route = topology.random_route_to_gateway(
|
||||
&mut rng,
|
||||
3,
|
||||
packet_recipient.gateway(),
|
||||
).unwrap();
|
||||
|
||||
// generate some payload
|
||||
let mlen = 40;
|
||||
let mut msg = vec![0u8; mlen];
|
||||
let fragment = Fragment {
|
||||
header: FragmentHeader::try_new(
|
||||
12345,
|
||||
u8::max_value(),
|
||||
u8::max_value(),
|
||||
None,
|
||||
Some(1234),
|
||||
)
|
||||
.unwrap(),
|
||||
payload: msg,
|
||||
};
|
||||
|
||||
|
||||
let ack_key = AckKey::new(&mut rng);
|
||||
let surb_ack = SurbAck::construct(
|
||||
&mut rng,
|
||||
&packet_sender,
|
||||
&ack_key,
|
||||
fragment.fragment_identifier().to_bytes(),
|
||||
Duration::from_millis(50),
|
||||
&topology,
|
||||
).unwrap();
|
||||
|
||||
|
||||
let packet_payload = NymsphinxPayloadBuilder::new(fragment, surb_ack)
|
||||
.build_regular(&mut rng, packet_recipient.encryption_key());
|
||||
|
||||
let delays = delays::generate_from_average_duration(route.len(), Duration::from_millis(50));
|
||||
let destination = packet_recipient.as_sphinx_destination();
|
||||
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Sphinx] create_packet_no_reply_surbs_with_payload_size_{}",
|
||||
case.packet_size.payload_size(),
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
SphinxPacketBuilder::new()
|
||||
.with_payload_size(case.packet_size.payload_size())
|
||||
.build_packet(packet_payload.clone(), &route, &destination, &delays)
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// let's create the packet to later benchmark the processing
|
||||
let sphinx_packet = SphinxPacketBuilder::new()
|
||||
.with_payload_size(case.packet_size.payload_size())
|
||||
.build_packet(packet_payload.clone(), &route, &destination, &delays)
|
||||
.unwrap();
|
||||
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Sphinx] process_packet_with_payload_size_{}",
|
||||
case.packet_size.payload_size(),
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
make_packet_copy(&sphinx_packet).process(&node_keypair.private_key().into())
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
criterion_group!(sphinx, bench_loop_packet_create, bench_new_no_surb);
|
||||
criterion_main!(sphinx);
|
||||
@@ -1,11 +1,13 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::ChunkingError;
|
||||
use nymsphinx_params::{SerializedFragmentIdentifier, FRAG_ID_LEN};
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use nymsphinx_params::{FRAG_ID_LEN, SerializedFragmentIdentifier};
|
||||
|
||||
use crate::ChunkingError;
|
||||
|
||||
// Personal reflection: In hindsight I've spent too much time on relatively too little
|
||||
// gain here, as even though I might have saved couple of bytes per packet, the gain
|
||||
// is negligible in the context of having to include SURB-ACKs and reply-SURBs in the packets.
|
||||
@@ -110,8 +112,8 @@ impl FragmentIdentifier {
|
||||
/// header used to reconstruct the message after being received.
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub struct Fragment {
|
||||
header: FragmentHeader,
|
||||
payload: Vec<u8>,
|
||||
pub header: FragmentHeader,
|
||||
pub payload: Vec<u8>,
|
||||
}
|
||||
|
||||
// manual implementation to hide detailed payload that we don't care about
|
||||
@@ -290,7 +292,7 @@ impl Fragment {
|
||||
/// and for the longest messages, without upper bound, there is usually also only 7 bytes
|
||||
/// of overhead apart from first and last fragments in each set that instead have 10 bytes of overhead.
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub(crate) struct FragmentHeader {
|
||||
pub struct FragmentHeader {
|
||||
/// ID associated with `FragmentSet` to which this particular `Fragment` belongs.
|
||||
/// Its value is restricted to (0, i32::max_value()].
|
||||
/// Note that it *excludes* 0, but *includes* i32::max_value().
|
||||
@@ -319,7 +321,7 @@ impl FragmentHeader {
|
||||
/// Tries to create a new `FragmentHeader` using provided metadata. Bunch of logical
|
||||
/// checks are performed to see if the data is not self-contradictory,
|
||||
/// for example if current_fragment > total_fragments.
|
||||
fn try_new(
|
||||
pub fn try_new(
|
||||
id: i32,
|
||||
total_fragments: u8,
|
||||
current_fragment: u8,
|
||||
@@ -460,9 +462,11 @@ impl FragmentHeader {
|
||||
|
||||
#[cfg(test)]
|
||||
mod fragment_tests {
|
||||
use super::*;
|
||||
use rand::{RngCore, thread_rng};
|
||||
|
||||
use nymsphinx_params::packet_sizes::PacketSize;
|
||||
use rand::{thread_rng, RngCore};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn max_plaintext_size() -> usize {
|
||||
PacketSize::default().plaintext_size() - PacketSize::AckPacket.size()
|
||||
@@ -585,7 +589,7 @@ mod fragment_tests {
|
||||
None,
|
||||
Some(1234),
|
||||
)
|
||||
.unwrap(),
|
||||
.unwrap(),
|
||||
payload: msg,
|
||||
};
|
||||
let packet_bytes = fragment.clone().into_bytes();
|
||||
@@ -602,7 +606,7 @@ mod fragment_tests {
|
||||
None,
|
||||
Some(1234),
|
||||
)
|
||||
.unwrap(),
|
||||
.unwrap(),
|
||||
payload: msg,
|
||||
};
|
||||
let packet_bytes = fragment.clone().into_bytes();
|
||||
@@ -644,7 +648,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload,
|
||||
id,
|
||||
@@ -654,7 +658,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
@@ -665,7 +669,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
id,
|
||||
@@ -675,7 +679,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -697,7 +701,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload,
|
||||
id,
|
||||
@@ -707,7 +711,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
@@ -718,7 +722,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
id,
|
||||
@@ -728,7 +732,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
id,
|
||||
@@ -738,7 +742,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
@@ -749,7 +753,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
id,
|
||||
@@ -759,7 +763,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -780,7 +784,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(Fragment::try_new(
|
||||
&full_payload,
|
||||
id,
|
||||
@@ -790,7 +794,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload,
|
||||
id,
|
||||
@@ -800,7 +804,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
id,
|
||||
@@ -810,7 +814,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&full_payload,
|
||||
@@ -821,7 +825,7 @@ mod fragment_tests {
|
||||
Some(link_id),
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -842,7 +846,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
id,
|
||||
@@ -852,7 +856,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
id,
|
||||
@@ -862,7 +866,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
id,
|
||||
@@ -872,7 +876,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload,
|
||||
@@ -883,7 +887,7 @@ mod fragment_tests {
|
||||
Some(link_id),
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
id,
|
||||
@@ -893,7 +897,7 @@ mod fragment_tests {
|
||||
Some(link_id),
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
@@ -904,7 +908,7 @@ mod fragment_tests {
|
||||
Some(link_id),
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1008,7 +1012,7 @@ mod fragment_header {
|
||||
None,
|
||||
Some(0),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1066,7 +1070,7 @@ mod fragment_header {
|
||||
None,
|
||||
Some(1234),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(FragmentHeader::try_new(12345, 10, 2, Some(1234), None).is_err());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
use nymsphinx_addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressError};
|
||||
use nymsphinx_params::{PacketMode, PacketSize};
|
||||
use nymsphinx_types::SphinxPacket;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MixPacketFormattingError {
|
||||
@@ -46,7 +47,7 @@ impl From<NymNodeRoutingAddressError> for MixPacketFormattingError {
|
||||
|
||||
pub struct MixPacket {
|
||||
next_hop: NymNodeRoutingAddress,
|
||||
sphinx_packet: SphinxPacket,
|
||||
pub sphinx_packet: SphinxPacket,
|
||||
packet_mode: PacketMode,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
use crypto::aes::cipher::{KeyIvInit, StreamCipher};
|
||||
use crypto::asymmetric::encryption;
|
||||
use crypto::shared_key::new_ephemeral_shared_key;
|
||||
@@ -12,7 +14,6 @@ use nymsphinx_chunking::fragment::Fragment;
|
||||
use nymsphinx_params::{
|
||||
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, ReplySurbEncryptionAlgorithm,
|
||||
};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
pub struct NymsphinxPayloadBuilder {
|
||||
fragment: Fragment,
|
||||
@@ -27,10 +28,10 @@ impl NymsphinxPayloadBuilder {
|
||||
fn build<C>(
|
||||
self,
|
||||
packet_encryption_key: &CipherKey<C>,
|
||||
variant_data: impl IntoIterator<Item = u8>,
|
||||
variant_data: impl IntoIterator<Item=u8>,
|
||||
) -> NymsphinxPayload
|
||||
where
|
||||
C: StreamCipher + KeyIvInit,
|
||||
where
|
||||
C: StreamCipher + KeyIvInit,
|
||||
{
|
||||
let (_, surb_ack_bytes) = self.surb_ack.prepare_for_sending();
|
||||
|
||||
@@ -68,8 +69,8 @@ impl NymsphinxPayloadBuilder {
|
||||
rng: &mut R,
|
||||
recipient_encryption_key: &encryption::PublicKey,
|
||||
) -> NymsphinxPayload
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
// create keys for 'payload' encryption
|
||||
let (ephemeral_keypair, shared_key) = new_ephemeral_shared_key::<
|
||||
@@ -88,6 +89,7 @@ impl NymsphinxPayloadBuilder {
|
||||
// the actual byte data that will be put into the sphinx packet paylaod.
|
||||
// no more transformations are going to happen to it
|
||||
// TODO: use that fact for some better compile time assertions
|
||||
#[derive(Clone)]
|
||||
pub struct NymsphinxPayload(Vec<u8>);
|
||||
|
||||
impl AsRef<[u8]> for NymsphinxPayload {
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
## UNRELEASED
|
||||
|
||||
- nothing yet
|
||||
|
||||
## [nym-explorer-v1.0.5](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.5) (2023-02-07)
|
||||
|
||||
- NE - link `Owner` field on the node detail page to the account details on NG explorer ([#2923])
|
||||
- NE - Upgrade Sandbox and make below changes: ([#2332])
|
||||
|
||||
[#2923]: https://github.com/nymtech/nym/issues/2923
|
||||
[#2332]: https://github.com/nymtech/nym/issues/2332
|
||||
|
||||
## [nym-explorer-v1.0.4](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.4) (2023-01-31)
|
||||
|
||||
- Add routing score on gateway list ([#2913])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material';
|
||||
import { Link, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Tooltip } from '@nymproject/react/tooltip/Tooltip';
|
||||
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard';
|
||||
@@ -37,9 +37,19 @@ function formatCellValues(val: string | number, field: string) {
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (field === 'bond') {
|
||||
return unymToNym(val, 6);
|
||||
}
|
||||
|
||||
if (field === 'owner') {
|
||||
return (
|
||||
<Link underline="none" color="inherit" target="_blank" href={`https://mixnet.explorers.guru/account/${val}`}>
|
||||
{val}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,8 @@ pub struct Init {
|
||||
long,
|
||||
alias = "validators",
|
||||
alias = "nymd_validators",
|
||||
value_delimiter = ','
|
||||
value_delimiter = ',',
|
||||
hide = true
|
||||
)]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
@@ -66,7 +67,7 @@ pub struct Init {
|
||||
|
||||
/// Set this gateway to work only with coconut credentials; that would disallow clients to
|
||||
/// bypass bandwidth credential requirement
|
||||
#[clap(long)]
|
||||
#[clap(long, hide = true)]
|
||||
only_coconut_credentials: Option<bool>,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
|
||||
@@ -53,7 +53,8 @@ pub struct Run {
|
||||
long,
|
||||
alias = "validators",
|
||||
alias = "nymd_validators",
|
||||
value_delimiter = ','
|
||||
value_delimiter = ',',
|
||||
hide = true
|
||||
)]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
@@ -64,7 +65,7 @@ pub struct Run {
|
||||
|
||||
/// Set this gateway to work only with coconut credentials; that would disallow clients to
|
||||
/// bypass bandwidth credential requirement
|
||||
#[clap(long)]
|
||||
#[clap(long, hide = true)]
|
||||
only_coconut_credentials: Option<bool>,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
|
||||
@@ -181,6 +181,7 @@ impl<St: Storage> ConnectionHandler<St> {
|
||||
mut shutdown: TaskClient,
|
||||
) {
|
||||
debug!("Starting connection handler for {:?}", remote);
|
||||
shutdown.mark_as_success();
|
||||
let mut framed_conn = Framed::new(conn, SphinxCodec);
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"nym-wallet",
|
||||
"nym-connect",
|
||||
"nym-connect-android",
|
||||
"sdk/typescript/**"
|
||||
"sdk/typescript/examples/docs",
|
||||
"sdk/typescript/packages/**"
|
||||
],
|
||||
"version": "0.0.0"
|
||||
}
|
||||
}
|
||||
@@ -77,6 +77,7 @@ impl ConnectionHandler {
|
||||
mut shutdown: TaskClient,
|
||||
) {
|
||||
debug!("Starting connection handler for {:?}", remote);
|
||||
shutdown.mark_as_success();
|
||||
let mut framed_conn = Framed::new(conn, SphinxCodec);
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-api"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
@@ -15,7 +15,11 @@ pub(crate) mod routes;
|
||||
|
||||
/// Merges the routes with http information and returns it to Rocket for serving
|
||||
pub(crate) fn circulating_supply_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![settings: routes::get_circulating_supply]
|
||||
openapi_get_routes_spec![
|
||||
settings: routes::get_full_circulating_supply,
|
||||
routes::get_total_supply,
|
||||
routes::get_circulating_supply
|
||||
]
|
||||
}
|
||||
|
||||
/// Spawn the circulating supply cache refresher.
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
use rocket::http::Status;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::circulating_supply_api::cache::CirculatingSupplyCache;
|
||||
use crate::node_status_api::models::ErrorResponse;
|
||||
use nym_api_requests::models::CirculatingSupplyResponse;
|
||||
use rocket::http::Status;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use rocket_okapi::openapi;
|
||||
use validator_client::nyxd::Coin;
|
||||
|
||||
// TODO: this is not the best place to put it, it should be more centralised,
|
||||
// but for a quick fix, that's good enough for now...
|
||||
// (for proper solution we should be managing `NymNetworkDetails` via rocket and grabbing display exponent
|
||||
// value from the mix denom here.
|
||||
const UNYM_RATIO: f64 = 1000000.;
|
||||
|
||||
fn unym_coin_to_float_unym(coin: Coin) -> f64 {
|
||||
// our total supply can't exceed 1B so an overflow here is impossible
|
||||
// (if it happened, then we SHOULD crash)
|
||||
coin.amount as f64 / UNYM_RATIO
|
||||
}
|
||||
|
||||
#[openapi(tag = "circulating-supply")]
|
||||
#[get("/circulating-supply")]
|
||||
pub(crate) async fn get_circulating_supply(
|
||||
pub(crate) async fn get_full_circulating_supply(
|
||||
cache: &State<CirculatingSupplyCache>,
|
||||
) -> Result<Json<CirculatingSupplyResponse>, ErrorResponse> {
|
||||
match cache.get_circulating_supply().await {
|
||||
@@ -20,3 +35,43 @@ pub(crate) async fn get_circulating_supply(
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[openapi(tag = "circulating-supply")]
|
||||
#[get("/circulating-supply/total-supply-value")]
|
||||
pub(crate) async fn get_total_supply(
|
||||
cache: &State<CirculatingSupplyCache>,
|
||||
) -> Result<Json<f64>, ErrorResponse> {
|
||||
let full_circulating_supply = match cache.get_circulating_supply().await {
|
||||
Some(res) => res,
|
||||
None => {
|
||||
return Err(ErrorResponse::new(
|
||||
"unavailable",
|
||||
Status::InternalServerError,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(unym_coin_to_float_unym(
|
||||
full_circulating_supply.total_supply.into(),
|
||||
)))
|
||||
}
|
||||
|
||||
#[openapi(tag = "circulating-supply")]
|
||||
#[get("/circulating-supply/circulating-supply-value")]
|
||||
pub(crate) async fn get_circulating_supply(
|
||||
cache: &State<CirculatingSupplyCache>,
|
||||
) -> Result<Json<f64>, ErrorResponse> {
|
||||
let full_circulating_supply = match cache.get_circulating_supply().await {
|
||||
Some(res) => res,
|
||||
None => {
|
||||
return Err(ErrorResponse::new(
|
||||
"unavailable",
|
||||
Status::InternalServerError,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(unym_coin_to_float_unym(
|
||||
full_circulating_supply.circulating_supply.into(),
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -70,9 +70,6 @@ pub enum CoconutError {
|
||||
)]
|
||||
DifferentPublicAttributes(String, String),
|
||||
|
||||
#[error("No signature found")]
|
||||
NoSignature,
|
||||
|
||||
#[error("Error in coconut interface - {0}")]
|
||||
CoconutInterfaceError(#[from] coconut_interface::error::CoconutInterfaceError),
|
||||
|
||||
|
||||
@@ -182,11 +182,7 @@ impl InternalSignRequest {
|
||||
rocket.manage(state).mount(
|
||||
// this format! is so ugly...
|
||||
format!("/{}/{}/{}", NYM_API_VERSION, COCONUT_ROUTES, BANDWIDTH),
|
||||
routes![
|
||||
post_blind_sign,
|
||||
post_partial_bandwidth_credential,
|
||||
verify_bandwidth_credential
|
||||
],
|
||||
routes![post_blind_sign, verify_bandwidth_credential],
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -242,18 +238,6 @@ pub async fn post_blind_sign(
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[post("/partial-bandwidth-credential", data = "<tx_hash>")]
|
||||
pub async fn post_partial_bandwidth_credential(
|
||||
tx_hash: Json<String>,
|
||||
state: &RocketState<State>,
|
||||
) -> Result<Json<BlindedSignatureResponse>> {
|
||||
let v = state
|
||||
.signed_before(&tx_hash)
|
||||
.await?
|
||||
.ok_or(CoconutError::NoSignature)?;
|
||||
Ok(Json(v))
|
||||
}
|
||||
|
||||
#[post("/verify-bandwidth-credential", data = "<verify_credential_body>")]
|
||||
pub async fn verify_bandwidth_credential(
|
||||
verify_credential_body: Json<VerifyCredentialBody>,
|
||||
|
||||
@@ -27,8 +27,7 @@ use nymcoconut::{
|
||||
prepare_blind_sign, ttp_keygen, Base58, BlindSignRequest, BlindedSignature, Parameters,
|
||||
};
|
||||
use validator_client::nym_api::routes::{
|
||||
API_VERSION, BANDWIDTH, COCONUT_BLIND_SIGN, COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL,
|
||||
COCONUT_ROUTES, COCONUT_VERIFY_BANDWIDTH_CREDENTIAL,
|
||||
API_VERSION, BANDWIDTH, COCONUT_BLIND_SIGN, COCONUT_ROUTES, COCONUT_VERIFY_BANDWIDTH_CREDENTIAL,
|
||||
};
|
||||
use validator_client::nyxd::Coin;
|
||||
use validator_client::nyxd::{tx::Hash, AccountId, DeliverTx, Event, Fee, Tag, TxResponse};
|
||||
@@ -827,76 +826,6 @@ async fn blind_sign_correct() {
|
||||
assert!(blinded_signature_response.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signature_test() {
|
||||
let tx_hash = String::from("7C41AF8266D91DE55E1C8F4712E6A952A165ED3D8C27C7B00428CBD0DE00A52B");
|
||||
let params = Parameters::new(4).unwrap();
|
||||
|
||||
let key_pair = ttp_keygen(¶ms, 1, 1).unwrap().remove(0);
|
||||
let mut db_dir = std::env::temp_dir();
|
||||
db_dir.push(&key_pair.verification_key().to_bs58()[..8]);
|
||||
let storage = NymApiStorage::init(db_dir).await.unwrap();
|
||||
let nyxd_client =
|
||||
DummyClient::new(AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap());
|
||||
let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key());
|
||||
let staged_key_pair = crate::coconut::KeyPair::new();
|
||||
staged_key_pair.set(Some(key_pair)).await;
|
||||
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nyxd_client,
|
||||
TEST_COIN_DENOM.to_string(),
|
||||
staged_key_pair,
|
||||
comm_channel,
|
||||
storage.clone(),
|
||||
));
|
||||
let client = Client::tracked(rocket)
|
||||
.await
|
||||
.expect("valid rocket instance");
|
||||
|
||||
let response = client
|
||||
.post(format!(
|
||||
"/{}/{}/{}/{}",
|
||||
API_VERSION, COCONUT_ROUTES, BANDWIDTH, COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL
|
||||
))
|
||||
.json(&tx_hash)
|
||||
.dispatch()
|
||||
.await;
|
||||
assert_eq!(response.status(), Status::BadRequest);
|
||||
assert_eq!(
|
||||
response.into_string().await.unwrap(),
|
||||
CoconutError::NoSignature.to_string()
|
||||
);
|
||||
|
||||
let encrypted_signature = vec![1, 2, 3, 4];
|
||||
let remote_key = [42; 32];
|
||||
let expected_response = BlindedSignatureResponse::new(encrypted_signature, remote_key);
|
||||
storage
|
||||
.insert_blinded_signature_response(&tx_hash, &expected_response.to_base58_string())
|
||||
.await
|
||||
.unwrap();
|
||||
let response = client
|
||||
.post(format!(
|
||||
"/{}/{}/{}/{}",
|
||||
API_VERSION, COCONUT_ROUTES, BANDWIDTH, COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL
|
||||
))
|
||||
.json(&tx_hash)
|
||||
.dispatch()
|
||||
.await;
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
// This is a more direct way, but there's a bug which makes it hang https://github.com/SergioBenitez/Rocket/issues/1893
|
||||
// let blinded_signature_response = response
|
||||
// .into_json::<BlindedSignatureResponse>()
|
||||
// .await
|
||||
// .unwrap();
|
||||
let blinded_signature_response =
|
||||
serde_json::from_str::<BlindedSignatureResponse>(&response.into_string().await.unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
blinded_signature_response.to_bytes(),
|
||||
expected_response.to_bytes()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn verification_of_bandwidth_credential() {
|
||||
// Setup variables
|
||||
|
||||
@@ -93,11 +93,16 @@ pub(crate) struct CliArgs {
|
||||
pub(crate) enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Announced address where coconut clients will connect.
|
||||
#[clap(long)]
|
||||
#[clap(long, hide = true)]
|
||||
pub(crate) announce_address: Option<url::Url>,
|
||||
|
||||
/// Flag to indicate whether coconut signer authority is enabled on this API
|
||||
#[clap(long, requires = "mnemonic", requires = "announce_address")]
|
||||
#[clap(
|
||||
long,
|
||||
requires = "mnemonic",
|
||||
requires = "announce_address",
|
||||
hide = true
|
||||
)]
|
||||
pub(crate) enable_coconut: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"webpack:dev": "yarn webpack serve --config webpack.dev.js",
|
||||
"webpack:dev:onlyThis": "yarn webpack serve --config webpack.dev.js",
|
||||
"webpack:prod": "yarn webpack --progress --config webpack.prod.js",
|
||||
"tauri": "tauri",
|
||||
"tauri:dev": "WRY_ANDROID_PACKAGE=net.nymtech.nym_connect_android WRY_ANDROID_LIBRARY=nym_connect_android cargo tauri android dev",
|
||||
"tauri:build": "WRY_ANDROID_PACKAGE=net.nymtech.nym_connect_android WRY_ANDROID_LIBRARY=nym_connect_android cargo tauri android build",
|
||||
"dev": "run-p webpack:dev:onlyThis tauri:dev",
|
||||
@@ -43,7 +44,7 @@
|
||||
"react-error-boundary": "^3.1.3",
|
||||
"react-hook-form": "^7.14.2",
|
||||
"react-markdown": "^8.0.4",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-router-dom": "^6.7.0",
|
||||
"semver": "^6.3.0",
|
||||
"yup": "^0.32.9"
|
||||
},
|
||||
@@ -58,7 +59,7 @@
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
|
||||
"@storybook/react": "^6.5.15",
|
||||
"@svgr/webpack": "^6.1.1",
|
||||
"@tauri-apps/cli": "^2.0.0-alpha.1",
|
||||
"@tauri-apps/cli": "^2.0.0-alpha.2",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@types/jest": "^27.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-connect-android"
|
||||
version = "1.1.7"
|
||||
version = "1.1.9"
|
||||
description = "nym-connect for Android"
|
||||
authors = ["Nym Technologies SA"]
|
||||
license = ""
|
||||
@@ -16,8 +16,8 @@ rust-version = "1.58"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
# tauri-build = { version = "2.0.0-alpha.0", features = [] }
|
||||
tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = [] }
|
||||
tauri-build = { version = "2.0.0-alpha.1", features = [] }
|
||||
# tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = [] }
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
@@ -38,7 +38,8 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_repr = "0.1"
|
||||
tap = "1.0.1"
|
||||
tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open", "system-tray", "window-close", "window-minimize", "window-start-dragging"] }
|
||||
# tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open", "system-tray", "window-close", "window-minimize", "window-start-dragging"] }
|
||||
tauri = { version = "2.0.0-alpha.3", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open", "system-tray", "window-close", "window-minimize", "window-start-dragging"] }
|
||||
tendermint-rpc = "0.23.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.24.1", features = ["sync", "time"] }
|
||||
@@ -46,6 +47,8 @@ url = "2.2"
|
||||
yaml-rust = "0.4"
|
||||
|
||||
client-core = { path = "../../clients/client-core" }
|
||||
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common"}
|
||||
config-common = { path = "../../common/config", package = "config" }
|
||||
crypto = { path = "../../common/crypto" }
|
||||
logging = { path = "../../common/logging"}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.nym_connect_android"
|
||||
android:screenOrientation="portrait"
|
||||
android:usesCleartextTraffic="${usesCleartextTraffic}">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB |
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="grey_900">#212121</color>
|
||||
<color name="grey_900">#1d2125</color>
|
||||
<color name="grey_800">#424242</color>
|
||||
<color name="green_500">#4caf50</color>
|
||||
<color name="green_900">#1b5e20</color>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#1D2125</color>
|
||||
</resources>
|
||||
@@ -14,8 +14,10 @@ open class BuildTask : DefaultTask() {
|
||||
@InputDirectory
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
var rootDirRel: File? = null
|
||||
|
||||
@Input
|
||||
var target: String? = null
|
||||
|
||||
@Input
|
||||
var release: Boolean? = null
|
||||
|
||||
@@ -25,11 +27,15 @@ open class BuildTask : DefaultTask() {
|
||||
val target = target ?: throw GradleException("target cannot be null")
|
||||
val release = release ?: throw GradleException("release cannot be null")
|
||||
val home = (System.getenv("HOME") ?: "")
|
||||
val cargo_home = (System.getenv("CARGO_HOME") ?: "$home/.cargo")
|
||||
println("gradle Rust plugin: CARGO_HOME $cargo_home")
|
||||
val cargoHome = (System.getenv("CARGO_HOME") ?: "$home/.cargo")
|
||||
val tauriCli = "$cargoHome/bin/cargo-tauri"
|
||||
if (!File(tauriCli).isFile()) {
|
||||
throw GradleException("$tauriCli no shuch file")
|
||||
}
|
||||
println("gradle Rust plugin, using tauri cli executable: $tauriCli")
|
||||
project.exec {
|
||||
workingDir(File(project.projectDir, rootDirRel.path))
|
||||
executable("""$cargo_home/bin/cargo-tauri""")
|
||||
executable(tauriCli)
|
||||
args(listOf("tauri", "android", "android-studio-script"))
|
||||
if (project.logger.isEnabled(LogLevel.DEBUG)) {
|
||||
args("-vv")
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
tauri_build::build();
|
||||
}
|
||||
|
||||
@@ -51,9 +51,9 @@ pub enum BackendError {
|
||||
InitializationPanic,
|
||||
#[error("could not get config id before gateway is set")]
|
||||
CouldNotGetIdWithoutGateway,
|
||||
#[error("could initialize without gateway set")]
|
||||
#[error("could not initialize without gateway set")]
|
||||
CouldNotInitWithoutGateway,
|
||||
#[error("could initialize without service provider set")]
|
||||
#[error("could not initialize without service provider set")]
|
||||
CouldNotInitWithoutServiceProvider,
|
||||
#[error("could not get file name")]
|
||||
CouldNotGetFilename,
|
||||
|
||||
@@ -66,14 +66,18 @@ impl AppBuilder {
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
crate::config::get_config_file_location,
|
||||
crate::config::get_config_id,
|
||||
crate::operations::connection::status::get_connection_status,
|
||||
crate::operations::connection::connect::get_gateway,
|
||||
crate::operations::connection::connect::get_service_provider,
|
||||
crate::operations::connection::connect::set_gateway,
|
||||
crate::operations::connection::connect::set_service_provider,
|
||||
crate::operations::connection::connect::start_connecting,
|
||||
crate::operations::connection::disconnect::start_disconnecting,
|
||||
crate::operations::connection::status::get_connection_health_check_status,
|
||||
crate::operations::connection::status::get_connection_status,
|
||||
crate::operations::connection::status::get_gateway_connection_status,
|
||||
crate::operations::connection::status::start_connection_health_check_task,
|
||||
crate::operations::directory::get_services,
|
||||
crate::operations::directory::get_gateways_detailed,
|
||||
crate::operations::export::export_keys,
|
||||
])
|
||||
.setup(move |app| {
|
||||
|
||||
@@ -1,21 +1,35 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::models::{DirectoryService, HarbourMasterService, PagedResult};
|
||||
use crate::models::{
|
||||
DirectoryService, DirectoryServiceProvider, HarbourMasterService, PagedResult,
|
||||
};
|
||||
use contracts_common::types::Percent;
|
||||
use nym_api_requests::models::GatewayBondAnnotated;
|
||||
|
||||
static SERVICE_PROVIDER_WELLKNOWN_URL: &str =
|
||||
"https://nymtech.net/.wellknown/connect/service-providers.json";
|
||||
|
||||
static HARBOUR_MASTER_URL: &str = "https://harbourmaster.nymtech.net/v1/services/?size=100";
|
||||
|
||||
static GATEWAYS_DETAILED_URL: &str =
|
||||
"https://validator.nymtech.net/api/v1/status/gateways/detailed";
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_services() -> Result<Vec<DirectoryService>> {
|
||||
pub async fn get_services() -> Result<Vec<DirectoryServiceProvider>> {
|
||||
log::trace!("Fetching services");
|
||||
let res = reqwest::get(SERVICE_PROVIDER_WELLKNOWN_URL)
|
||||
let services_res = reqwest::get(SERVICE_PROVIDER_WELLKNOWN_URL)
|
||||
.await?
|
||||
.json::<Vec<DirectoryService>>()
|
||||
.await?;
|
||||
log::trace!("Received: {:#?}", res);
|
||||
log::trace!("Received: {:#?}", services_res);
|
||||
|
||||
log::trace!("Fetching gateways");
|
||||
let gateway_res = reqwest::get(GATEWAYS_DETAILED_URL)
|
||||
.await?
|
||||
.json::<Vec<GatewayBondAnnotated>>()
|
||||
.await?;
|
||||
log::trace!("Received: {:#?}", gateway_res);
|
||||
|
||||
// TODO: get paged
|
||||
log::trace!("Fetching active services");
|
||||
@@ -27,7 +41,7 @@ pub async fn get_services() -> Result<Vec<DirectoryService>> {
|
||||
|
||||
let mut filtered: Vec<DirectoryService> = vec![];
|
||||
|
||||
for service in &res {
|
||||
for service in &services_res {
|
||||
let items: _ = service
|
||||
.items
|
||||
.clone()
|
||||
@@ -47,5 +61,32 @@ pub async fn get_services() -> Result<Vec<DirectoryService>> {
|
||||
})
|
||||
}
|
||||
|
||||
Ok(filtered)
|
||||
let perf_threshold = Percent::from_percentage_value(90).unwrap();
|
||||
|
||||
// Use only services that are active AND have a performance of >= 90%
|
||||
let services_with_good_performance: Vec<DirectoryServiceProvider> = filtered
|
||||
.iter_mut()
|
||||
.fold(vec![], |mut acc, sp| {
|
||||
acc.append(&mut sp.items);
|
||||
acc
|
||||
})
|
||||
.into_iter()
|
||||
.filter(|sp| {
|
||||
gateway_res.iter().any(|gateway| {
|
||||
gateway.gateway_bond.gateway.identity_key == sp.gateway
|
||||
&& gateway.performance >= perf_threshold
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(services_with_good_performance)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_gateways_detailed() -> Result<Vec<GatewayBondAnnotated>> {
|
||||
let res = reqwest::get(GATEWAYS_DETAILED_URL)
|
||||
.await?
|
||||
.json::<Vec<GatewayBondAnnotated>>()
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -174,17 +174,6 @@ impl State {
|
||||
) -> Result<(task::StatusReceiver, ExitStatusReceiver)> {
|
||||
self.set_state(ConnectionStatusKind::Connecting, window);
|
||||
|
||||
// Setup configuration by writing to file
|
||||
//if let Err(err) = self.init_config().await {
|
||||
// log::error!("Failed to initialize: {err}");
|
||||
|
||||
// // Wait a little to give the user some rudimentary feedback that the click actually
|
||||
// // registered.
|
||||
// tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
// self.set_state(ConnectionStatusKind::Disconnected, window);
|
||||
// return Err(err);
|
||||
//}
|
||||
|
||||
let res = self.init_config().await;
|
||||
match &res {
|
||||
Ok(_) => {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"package": {
|
||||
"productName": "nym-connect-android",
|
||||
"version": "1.1.7"
|
||||
"version": "1.1.9"
|
||||
},
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { DateTime } from 'luxon';
|
||||
import { forage } from '@tauri-apps/tauri-forage';
|
||||
import { ConnectionStatusKind } from './types';
|
||||
import { useClientContext } from './context/main';
|
||||
import { DefaultLayout } from './layouts/DefaultLayout';
|
||||
import { ConnectedLayout } from './layouts/ConnectedLayout';
|
||||
import { HelpGuideLayout } from './layouts/HelpGuideLayout';
|
||||
import { useTauriEvents } from './utils';
|
||||
|
||||
export const App: FCWithChildren = () => {
|
||||
const context = useClientContext();
|
||||
const [busy, setBusy] = React.useState<boolean>();
|
||||
const [showInfoModal, setShowInfoModal] = React.useState(false);
|
||||
useTauriEvents('help://clear-storage', (_event) => {
|
||||
console.log('About to clear local storage...');
|
||||
// clear local storage
|
||||
try {
|
||||
forage.clear()();
|
||||
console.log('Local storage cleared');
|
||||
} catch (e) {
|
||||
console.error('Failed to clear local storage', e);
|
||||
}
|
||||
});
|
||||
|
||||
const handleConnectClick = React.useCallback(async () => {
|
||||
const currentStatus = context.connectionStatus;
|
||||
if (currentStatus === ConnectionStatusKind.connected || currentStatus === ConnectionStatusKind.disconnected) {
|
||||
setBusy(true);
|
||||
|
||||
// eslint-disable-next-line default-case
|
||||
switch (currentStatus) {
|
||||
case ConnectionStatusKind.disconnected:
|
||||
await context.startConnecting();
|
||||
context.setConnectedSince(DateTime.now());
|
||||
break;
|
||||
case ConnectionStatusKind.connected:
|
||||
await context.startDisconnecting();
|
||||
context.setConnectedSince(undefined);
|
||||
break;
|
||||
}
|
||||
setBusy(false);
|
||||
}
|
||||
}, [context.connectionStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (context.connectionStatus === ConnectionStatusKind.connected) setShowInfoModal(true);
|
||||
}, [context.connectionStatus]);
|
||||
|
||||
if (context.showHelp) return <HelpGuideLayout />;
|
||||
|
||||
if (
|
||||
context.connectionStatus === ConnectionStatusKind.disconnected ||
|
||||
context.connectionStatus === ConnectionStatusKind.connecting
|
||||
) {
|
||||
return (
|
||||
<DefaultLayout
|
||||
error={context.error}
|
||||
clearError={context.clearError}
|
||||
status={context.connectionStatus}
|
||||
busy={busy}
|
||||
onConnectClick={handleConnectClick}
|
||||
services={context.services}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ConnectedLayout
|
||||
showInfoModal={showInfoModal}
|
||||
handleCloseInfoModal={() => setShowInfoModal(false)}
|
||||
status={context.connectionStatus}
|
||||
busy={busy}
|
||||
onConnectClick={handleConnectClick}
|
||||
ipAddress="127.0.0.1"
|
||||
port={1080}
|
||||
gatewayPerformance={context.gatewayPerformance}
|
||||
connectedSince={context.connectedSince}
|
||||
serviceProvider={context.serviceProvider}
|
||||
stats={[
|
||||
{
|
||||
label: 'in:',
|
||||
totalBytes: 1024,
|
||||
rateBytesPerSecond: 1024 * 1024 * 1024 + 10,
|
||||
},
|
||||
{
|
||||
label: 'out:',
|
||||
totalBytes: 1024 * 1024 * 1024 * 1024 * 20,
|
||||
rateBytesPerSecond: 1024 * 1024 + 10,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 18 KiB |
@@ -6,8 +6,8 @@ export const AppVersion = () => {
|
||||
const { appVersion } = useClientContext();
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'grid', width: '100%', justifyContent: 'center' }}>
|
||||
<Box fontSize="small" sx={{ mb: 4, color: 'grey.600' }}>
|
||||
<Box sx={{ display: 'flex', width: '100%', justifyContent: 'center' }}>
|
||||
<Box fontSize="small" sx={{ color: 'grey.600' }}>
|
||||
Version {appVersion}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import React from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { CustomTitleBar } from './CustomTitleBar';
|
||||
import { AppVersion } from './AppVersion';
|
||||
|
||||
export const AppWindowFrame: FCWithChildren = ({ children }) => (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'grid',
|
||||
borderRadius: '12px',
|
||||
gridTemplateRows: '40px 1fr 30px',
|
||||
height: '100vh',
|
||||
overflowY: 'hidden',
|
||||
}}
|
||||
>
|
||||
<CustomTitleBar />
|
||||
<Box style={{ padding: '16px' }}>{children}</Box>
|
||||
<AppVersion />
|
||||
</Box>
|
||||
);
|
||||
export const AppWindowFrame: FCWithChildren = ({ children }) => {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'grid',
|
||||
gridTemplateRows: '40px 1fr',
|
||||
height: '100vh',
|
||||
}}
|
||||
>
|
||||
<CustomTitleBar path={location.pathname} />
|
||||
<Box style={{ padding: '16px' }}>{children}</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,13 +10,13 @@ const getStatusFillColor = (status: ConnectionStatusKind, hover: boolean, isErro
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case ConnectionStatusKind.disconnected:
|
||||
case 'disconnected':
|
||||
if (hover) {
|
||||
return '#FFFF33';
|
||||
}
|
||||
return '#FFE600';
|
||||
case ConnectionStatusKind.connecting:
|
||||
case ConnectionStatusKind.disconnecting:
|
||||
case 'connecting':
|
||||
case 'disconnecting':
|
||||
return '#FFE600';
|
||||
default:
|
||||
// connected
|
||||
@@ -29,11 +29,11 @@ const getStatusFillColor = (status: ConnectionStatusKind, hover: boolean, isErro
|
||||
|
||||
const getStatusText = (status: ConnectionStatusKind, hover: boolean): string => {
|
||||
switch (status) {
|
||||
case ConnectionStatusKind.disconnected:
|
||||
case 'disconnected':
|
||||
return 'Connect';
|
||||
case ConnectionStatusKind.connecting:
|
||||
case 'connecting':
|
||||
return 'Connecting';
|
||||
case ConnectionStatusKind.disconnecting:
|
||||
case 'disconnecting':
|
||||
return 'Connected';
|
||||
default:
|
||||
// connected
|
||||
@@ -89,7 +89,7 @@ export const ConnectionButton: FCWithChildren<{
|
||||
<circle cx="131" cy="131" r="64" stroke={statusFillColor} strokeWidth="2" />
|
||||
</g>
|
||||
<circle cx="131" cy="131" r="73.5" stroke={statusFillColor} strokeOpacity="0.5" />
|
||||
{status === ConnectionStatusKind.connected && hover ? (
|
||||
{status === 'connected' && hover ? (
|
||||
<path
|
||||
d="M120.217 119.833C120.217 117.838 121.838 116.217 123.833 116.217H128.5V114H123.833C120.613 114 118 116.613 118 119.833C118 123.053 120.613 125.667 123.833 125.667H128.5V123.45H123.833C121.838 123.45 120.217 121.828 120.217 119.833ZM127 121H136.333V118.667H127V121ZM139.5 114H134.833V116.217H139.505C141.5 116.217 143.117 117.838 143.117 119.833C143.117 121.828 141.495 123.45 139.5 123.45H134.833V125.667H139.5C142.72 125.667 145.333 123.053 145.333 119.833C145.333 116.613 142.72 114 139.5 114Z"
|
||||
fill="white"
|
||||
|
||||
@@ -1,34 +1,67 @@
|
||||
import React from 'react';
|
||||
import { Box, CircularProgress, Tooltip, Typography } from '@mui/material';
|
||||
import { DateTime } from 'luxon';
|
||||
import { ErrorOutline, InfoOutlined } from '@mui/icons-material';
|
||||
import { ConnectionStatusKind, GatewayPerformance } from '../types';
|
||||
import { ServiceProvider } from '../types/directory';
|
||||
import { ServiceProviderInfo } from './ServiceProviderInfo';
|
||||
import { GatwayWarningInfo, ServiceProviderInfo } from './TooltipInfo';
|
||||
|
||||
const FONT_SIZE = '10px';
|
||||
const FONT_SIZE = '14px';
|
||||
const FONT_WEIGHT = '600';
|
||||
const FONT_STYLE = 'normal';
|
||||
|
||||
const ConnectionStatusContent: FCWithChildren<{
|
||||
status: ConnectionStatusKind;
|
||||
}> = ({ status }) => {
|
||||
serviceProvider?: ServiceProvider;
|
||||
gatewayError: boolean;
|
||||
}> = ({ status, serviceProvider, gatewayError }) => {
|
||||
if (gatewayError) {
|
||||
return (
|
||||
<Tooltip title={serviceProvider ? <GatwayWarningInfo /> : undefined}>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap={0.5}
|
||||
justifyContent="center"
|
||||
sx={{ cursor: 'pointer' }}
|
||||
color="warning.main"
|
||||
>
|
||||
<ErrorOutline sx={{ fontSize: 14 }} />
|
||||
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} fontSize={FONT_SIZE} textAlign="center">
|
||||
Gateway has issues
|
||||
</Typography>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
switch (status) {
|
||||
case ConnectionStatusKind.connected:
|
||||
case 'connected':
|
||||
return (
|
||||
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} fontSize="14px">
|
||||
Connected to
|
||||
<Tooltip title={serviceProvider ? <ServiceProviderInfo serviceProvider={serviceProvider} /> : undefined}>
|
||||
<Box display="flex" alignItems="center" gap={0.5} justifyContent="center" sx={{ cursor: 'pointer' }}>
|
||||
<InfoOutlined sx={{ fontSize: 14 }} />
|
||||
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} fontSize={FONT_SIZE} textAlign="center">
|
||||
Connected to Nym Mixnet
|
||||
</Typography>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
);
|
||||
case 'disconnected':
|
||||
return (
|
||||
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} textAlign="center" fontSize={FONT_SIZE}>
|
||||
Connect to the mixnet
|
||||
</Typography>
|
||||
);
|
||||
case ConnectionStatusKind.disconnecting:
|
||||
case 'disconnecting':
|
||||
return (
|
||||
<Box display="flex" alignItems="center" justifyContent="center">
|
||||
<CircularProgress size={FONT_SIZE} color="inherit" />
|
||||
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} ml={1}>
|
||||
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE}>
|
||||
Disconnecting...
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
case ConnectionStatusKind.connecting:
|
||||
case 'connecting':
|
||||
return (
|
||||
<Box display="flex" alignItems="center" justifyContent="center">
|
||||
<CircularProgress size={FONT_SIZE} color="inherit" />
|
||||
@@ -37,20 +70,7 @@ const ConnectionStatusContent: FCWithChildren<{
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
case ConnectionStatusKind.disconnected:
|
||||
return (
|
||||
<Typography
|
||||
fontWeight={FONT_WEIGHT}
|
||||
fontStyle={FONT_STYLE}
|
||||
ml={1}
|
||||
textTransform="uppercase"
|
||||
textAlign="center"
|
||||
fontSize={FONT_SIZE}
|
||||
sx={{ wordSpacing: 3, letterSpacing: 2 }}
|
||||
>
|
||||
You are not protected
|
||||
</Typography>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -62,29 +82,15 @@ export const ConnectionStatus: FCWithChildren<{
|
||||
connectedSince?: DateTime;
|
||||
serviceProvider?: ServiceProvider;
|
||||
}> = ({ status, serviceProvider, gatewayPerformance }) => {
|
||||
const color =
|
||||
status === ConnectionStatusKind.connected || status === ConnectionStatusKind.disconnecting
|
||||
? '#21D072'
|
||||
: 'warning.main';
|
||||
const color = status === 'connected' || status === 'disconnecting' ? '#21D072' : 'white';
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box color={color} fontSize={FONT_SIZE} sx={{ mb: 1 }}>
|
||||
{status === ConnectionStatusKind.connected && gatewayPerformance !== 'Good' ? (
|
||||
<Typography fontWeight={FONT_WEIGHT} fontStyle={FONT_STYLE} textAlign="left" color="primary">
|
||||
Gateway has issues
|
||||
</Typography>
|
||||
) : (
|
||||
<ConnectionStatusContent status={status} />
|
||||
)}
|
||||
</Box>
|
||||
{serviceProvider ? (
|
||||
<Tooltip title={<ServiceProviderInfo serviceProvider={serviceProvider} />}>
|
||||
<Box sx={{ cursor: 'pointer' }}>
|
||||
{serviceProvider && <Typography>{serviceProvider.description}</Typography>}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</>
|
||||
<Box color={color} sx={{ mb: 2 }}>
|
||||
<ConnectionStatusContent
|
||||
status={status}
|
||||
serviceProvider={serviceProvider}
|
||||
gatewayError={gatewayPerformance !== 'Good'}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { ArrowBack, HelpOutline } from '@mui/icons-material';
|
||||
import { Box, IconButton } from '@mui/material';
|
||||
import { ArrowBack, Menu } from '@mui/icons-material';
|
||||
import { Box, IconButton, Typography } from '@mui/material';
|
||||
import { NymWordmark } from '@nymproject/react/logo/NymWordmark';
|
||||
import { useClientContext } from 'src/context/main';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const customTitleBarStyles = {
|
||||
titlebar: {
|
||||
@@ -19,25 +19,36 @@ const customTitleBarStyles = {
|
||||
|
||||
const CustomButton = ({ Icon, onClick }: { Icon: React.JSXElementConstructor<any>; onClick: () => void }) => (
|
||||
<IconButton size="small" onClick={onClick} sx={{ padding: 0 }}>
|
||||
<Icon style={{ fontSize: 16 }} />
|
||||
<Icon style={{ fontSize: 24 }} />
|
||||
</IconButton>
|
||||
);
|
||||
|
||||
export const CustomTitleBar = () => {
|
||||
const { showHelp, handleShowHelp } = useClientContext();
|
||||
return (
|
||||
<Box data-tauri-drag-region style={customTitleBarStyles.titlebar}>
|
||||
{/* set width to keep logo centered */}
|
||||
<Box sx={{ width: '40px' }}>
|
||||
<CustomButton
|
||||
Icon={!showHelp ? HelpOutline : ArrowBack}
|
||||
onClick={() => {
|
||||
handleShowHelp();
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<NymWordmark width={36} />
|
||||
</Box>
|
||||
);
|
||||
const MenuIcon = () => {
|
||||
const navigate = useNavigate();
|
||||
return <CustomButton Icon={Menu} onClick={() => navigate('/menu')} />;
|
||||
};
|
||||
|
||||
const ArrowBackIcon = () => {
|
||||
const navigate = useNavigate();
|
||||
return <CustomButton Icon={ArrowBack} onClick={() => navigate(-1)} />;
|
||||
};
|
||||
|
||||
const getTitleIcon = (path: string) => {
|
||||
if (path !== '/') {
|
||||
const title = path.split('/').slice(-1);
|
||||
return (
|
||||
<Typography textTransform="capitalize" fontWeight={700}>
|
||||
{title}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
return <NymWordmark width={36} />;
|
||||
};
|
||||
|
||||
export const CustomTitleBar = ({ path = '/' }: { path?: string }) => (
|
||||
<Box data-tauri-drag-region style={customTitleBarStyles.titlebar}>
|
||||
{/* set width to keep logo centered */}
|
||||
<Box sx={{ width: '40px' }}>{path === '/' ? <MenuIcon /> : <ArrowBackIcon />}</Box>
|
||||
{getTitleIcon(path)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
export const ExperimentalWarning = () => (
|
||||
<Box sx={{ color: 'grey.600' }}>
|
||||
<Typography fontSize="12px" textAlign="center">
|
||||
This is experimental software.
|
||||
</Typography>
|
||||
<Typography fontSize="12px" textAlign="center">
|
||||
Do not rely on it for strong anonymity (yet).
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
|
After Width: | Height: | Size: 1.1 MiB |
@@ -6,24 +6,26 @@ import { StepIndicator } from './HelpPageStepIndicator';
|
||||
|
||||
export const HelpPage = ({
|
||||
step,
|
||||
totalSteps,
|
||||
description,
|
||||
img,
|
||||
onNext,
|
||||
onPrev,
|
||||
}: {
|
||||
step: number;
|
||||
totalSteps: number;
|
||||
description: string;
|
||||
img: any;
|
||||
onNext?: () => void;
|
||||
onPrev?: () => void;
|
||||
}) => (
|
||||
<Stack justifyContent="space-between" sx={{ height: '100%' }}>
|
||||
<Stack gap={3}>
|
||||
<Stack gap={2} alignItems="center">
|
||||
<StepIndicator step={step} />
|
||||
<Typography variant="body2" color="white" fontWeight="bold">
|
||||
How to connect guide {step}/4
|
||||
How to connect guide {step}/{totalSteps}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'grey.400' }}>
|
||||
<Typography variant="body2" sx={{ color: 'grey.400' }} textAlign="left">
|
||||
{description}
|
||||
</Typography>
|
||||
<HelpImage img={img} imageDescription="select a provider" />
|
||||
|
||||
@@ -5,14 +5,14 @@ import React from 'react';
|
||||
export const HelpPageActions = ({ onNext, onPrev }: { onNext?: () => void; onPrev?: () => void }) => (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
{onPrev ? (
|
||||
<Button onClick={onPrev} color="inherit" startIcon={<ArrowBack color="inherit" style={{ fontSize: 16 }} />}>
|
||||
<Button onClick={onPrev} color="inherit" startIcon={<ArrowBack color="inherit" style={{ fontSize: 22 }} />}>
|
||||
Back
|
||||
</Button>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
{onNext && (
|
||||
<Button onClick={onNext} endIcon={<ArrowForward style={{ fontSize: 16 }} />}>
|
||||
<Button onClick={onNext} endIcon={<ArrowForward style={{ fontSize: 22 }} />}>
|
||||
Next
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
export const HelpImage = ({ img, imageDescription }: { img: any; imageDescription: string }) => (
|
||||
<img src={img} alt={imageDescription} />
|
||||
export const HelpImage = ({ img, imageDescription }: { img: string; imageDescription: string }) => (
|
||||
<img src={img} alt={imageDescription} width="100%" />
|
||||
);
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { Box } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { Box, Stack } from '@mui/material';
|
||||
|
||||
const Step = ({ highlight }: { highlight: boolean }) => (
|
||||
<Box sx={{ width: '48px', height: '1px', bgcolor: highlight ? 'nym.highlight' : 'grey.600' }} />
|
||||
<Box sx={{ width: '65px', height: '3px', bgcolor: highlight ? 'nym.highlight' : 'grey.600' }} />
|
||||
);
|
||||
|
||||
export const StepIndicator = ({ step }: { step: number }) => (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-evenly' }}>
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between" width="240px">
|
||||
<Step highlight />
|
||||
<Step highlight={step >= 2} />
|
||||
<Step highlight={step >= 3} />
|
||||
<Step highlight={step >= 4} />
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
|
||||