Merge branch 'develop' into feature/wallet-delegation-fees
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
name: Nym Connect (rust)
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'explorer/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
env:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path nym-connect/Cargo.toml --workspace
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --manifest-path nym-connect/Cargo.toml --workspace
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path nym-connect/Cargo.toml --all -- --check
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --manifest-path nym-connect/Cargo.toml --workspace --all-features
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path nym-connect/Cargo.toml --workspace --all-features -- -D warnings
|
||||
@@ -1,5 +1,6 @@
|
||||
name: Publish Nym Wallet (MacOS)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
@@ -19,7 +20,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Check the release tag starts with `nym-wallet-`
|
||||
if: startsWith(github.ref, 'refs/tags/nym-wallet-') == false
|
||||
if: startsWith(github.ref, 'refs/tags/nym-wallet-') == false && github.event_name != 'workflow_dispatch'
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
@@ -55,6 +56,11 @@ jobs:
|
||||
security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
|
||||
- name: Create env file
|
||||
run: |
|
||||
touch .env
|
||||
echo ADMIN_ADDRESS="${{ secrets.WALLET_ADMIN_ADDRESS }}" >> .env
|
||||
|
||||
- name: Install app dependencies and build it
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -66,17 +72,24 @@ jobs:
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
ADMIN_ADDRESS: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
run: yarn && yarn build
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
files: |
|
||||
nym-wallet/target/release/bundle/dmg/*.dmg
|
||||
nym-wallet/target/release/bundle/macos/*.app.tar.gz*
|
||||
name: nym-wallet.app.tar.gz
|
||||
path: nym-wallet/target/release/bundle/macos/nym-wallet.app.tar.gz
|
||||
retention-days: 5
|
||||
|
||||
- name: Clean up keychain
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
nym-wallet/target/release/bundle/dmg/*.dmg
|
||||
nym-wallet/target/release/bundle/macos/*.app.tar.gz*
|
||||
|
||||
@@ -39,12 +39,15 @@ jobs:
|
||||
toolchain: stable
|
||||
- name: Install app dependencies
|
||||
run: yarn
|
||||
- name: Create env file
|
||||
run: |
|
||||
touch .env
|
||||
echo ADMIN_ADDRESS="${{ secrets.WALLET_ADMIN_ADDRESS }}" >> .env
|
||||
- name: Build app
|
||||
run: yarn build
|
||||
env:
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
ADMIN_ADDRESS: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
|
||||
@@ -54,6 +54,11 @@ jobs:
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Create env file
|
||||
run: |
|
||||
touch .env
|
||||
echo ADMIN_ADDRESS="${{ secrets.WALLET_ADMIN_ADDRESS }}" >> .env
|
||||
|
||||
- name: Install app dependencies
|
||||
run: yarn
|
||||
|
||||
@@ -65,7 +70,6 @@ jobs:
|
||||
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
ADMIN_ADDRESS: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
run: yarn build
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
|
||||
@@ -6,6 +6,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
### Added
|
||||
|
||||
- socks5 client/websocket client: add `--force-register-gateway` flag, useful when rerunning init ([#1353])
|
||||
- nym-connect: initial proof-of-concept of a UI around the socks5 client was added
|
||||
- all: added network compilation target to `--help` (or `--version`) commands ([#1256]).
|
||||
- explorer-api: learned how to sum the delegations by owner in a new endpoint.
|
||||
@@ -34,6 +35,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
- mixnode, gateway: attempting to determine reconnection backoff to persistently failing mixnode could result in a crash ([#1260])
|
||||
- mixnode: the mixnode learned how to shutdown gracefully
|
||||
- native & socks5 clients: fail early when clients try to re-init with a different gateway, which is not supported yet ([#1322])
|
||||
- native & socks5 clients: rerun init will now reuse previous gateway configuration instead of failing ([#1353])
|
||||
- validator: fixed local docker-compose setup to work on Apple M1 ([#1329])
|
||||
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
|
||||
- wallet: undelegating now uses either the mixnet or vesting contract, or both, depending on how delegations were made
|
||||
@@ -43,7 +45,9 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
- validator-client: created internal `Coin` type that replaces coins from `cosmrs` and `cosmwasm` for API entrypoints [[#1295]]
|
||||
- all: updated all `cosmwasm`-related dependencies to `1.0.0` and `cw-storage-plus` to `0.13.4` [[#1318]]
|
||||
- all: updated `rocket` to `0.5.0-rc.2`.
|
||||
- network-requester: allow to voluntarily store and send statistical data about the number of bytes the proxied server serves ([#1328])
|
||||
- gateway: allow to voluntarily send statistical data about the number of active inboxes served by a gateway ([#1376])
|
||||
|
||||
[#1249]: https://github.com/nymtech/nym/pull/1249
|
||||
[#1256]: https://github.com/nymtech/nym/pull/1256
|
||||
@@ -64,6 +68,8 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
[#1324]: https://github.com/nymtech/nym/pull/1324
|
||||
[#1328]: https://github.com/nymtech/nym/pull/1328
|
||||
[#1329]: https://github.com/nymtech/nym/pull/1329
|
||||
[#1353]: https://github.com/nymtech/nym/pull/1353
|
||||
[#1376]: https://github.com/nymtech/nym/pull/1376
|
||||
|
||||
## [nym-wallet-v1.0.5](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.5) (2022-06-14)
|
||||
|
||||
|
||||
Generated
+130
-210
@@ -14,6 +14,15 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
|
||||
dependencies = [
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.7.5"
|
||||
@@ -38,6 +47,20 @@ dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes 0.7.5",
|
||||
"cipher 0.3.0",
|
||||
"ctr 0.8.0",
|
||||
"ghash",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
@@ -198,12 +221,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base-x"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.1.1"
|
||||
@@ -250,7 +267,7 @@ dependencies = [
|
||||
"pbkdf2",
|
||||
"rand_core 0.6.3",
|
||||
"ripemd160",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"subtle 2.4.1",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -410,7 +427,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -467,7 +484,7 @@ version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a"
|
||||
dependencies = [
|
||||
"rustc_version 0.4.0",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -701,12 +718,6 @@ version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
|
||||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.5"
|
||||
@@ -728,12 +739,19 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.15.1"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d"
|
||||
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"base64",
|
||||
"hkdf 0.12.3",
|
||||
"hmac 0.12.1",
|
||||
"percent-encoding",
|
||||
"time 0.2.27",
|
||||
"rand 0.8.5",
|
||||
"sha2 0.10.2",
|
||||
"subtle 2.4.1",
|
||||
"time 0.3.9",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
@@ -1246,7 +1264,7 @@ dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version 0.4.0",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
@@ -1332,12 +1350,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "discard"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
||||
|
||||
[[package]]
|
||||
name = "dkg"
|
||||
version = "0.1.0"
|
||||
@@ -1354,7 +1366,7 @@ dependencies = [
|
||||
"rand_core 0.6.3",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -1410,7 +1422,7 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -1424,7 +1436,7 @@ dependencies = [
|
||||
"hex",
|
||||
"rand_core 0.6.3",
|
||||
"serde",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -2024,6 +2036,16 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghash"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
|
||||
dependencies = [
|
||||
"opaque-debug 0.3.0",
|
||||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.14.2"
|
||||
@@ -2596,7 +2618,7 @@ dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"sec1",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"sha3",
|
||||
]
|
||||
|
||||
@@ -3074,6 +3096,7 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"sqlx",
|
||||
"statistics-common",
|
||||
"subtle-encoding",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@@ -3130,6 +3153,7 @@ dependencies = [
|
||||
name = "nym-network-requester"
|
||||
version = "1.0.1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"clap 2.34.0",
|
||||
"dirs",
|
||||
"futures",
|
||||
@@ -3146,7 +3170,7 @@ dependencies = [
|
||||
"serde",
|
||||
"socks5-requests",
|
||||
"sqlx",
|
||||
"statistics",
|
||||
"statistics-common",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
@@ -3163,7 +3187,7 @@ dependencies = [
|
||||
"rocket",
|
||||
"serde",
|
||||
"sqlx",
|
||||
"statistics",
|
||||
"statistics-common",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
@@ -3321,7 +3345,7 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -3852,6 +3876,18 @@ dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"opaque-debug 0.3.0",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
@@ -3915,12 +3951,6 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.37"
|
||||
@@ -4482,9 +4512,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket"
|
||||
version = "0.5.0-rc.1"
|
||||
version = "0.5.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a71c18c42a0eb15bf3816831caf0dad11e7966f2a41aaf486a701979c4dd1f2"
|
||||
checksum = "98ead083fce4a405feb349cf09abdf64471c6077f14e0ce59364aa90d4b99317"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
@@ -4500,7 +4530,7 @@ dependencies = [
|
||||
"memchr",
|
||||
"multer",
|
||||
"num_cpus",
|
||||
"parking_lot 0.11.2",
|
||||
"parking_lot 0.12.0",
|
||||
"pin-project-lite",
|
||||
"rand 0.8.5",
|
||||
"ref-cast",
|
||||
@@ -4510,10 +4540,10 @@ dependencies = [
|
||||
"serde_json",
|
||||
"state",
|
||||
"tempfile",
|
||||
"time 0.2.27",
|
||||
"time 0.3.9",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util 0.6.9",
|
||||
"tokio-util 0.7.3",
|
||||
"ubyte",
|
||||
"version_check",
|
||||
"yansi",
|
||||
@@ -4521,9 +4551,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket_codegen"
|
||||
version = "0.5.0-rc.1"
|
||||
version = "0.5.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66f5fa462f7eb958bba8710c17c5d774bbbd59809fa76fb1957af7e545aea8bb"
|
||||
checksum = "d6aeb6bb9c61e9cd2c00d70ea267bf36f76a4cc615e5908b349c2f9d93999b47"
|
||||
dependencies = [
|
||||
"devise",
|
||||
"glob",
|
||||
@@ -4552,19 +4582,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket_http"
|
||||
version = "0.5.0-rc.1"
|
||||
version = "0.5.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23c8b7d512d2fcac2316ebe590cde67573844b99e6cc9ee0f53375fa16e25ebd"
|
||||
checksum = "2ded65d127954de3c12471630bf4b81a2792f065984461e65b91d0fdaafc17a2"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"either",
|
||||
"futures",
|
||||
"http",
|
||||
"hyper",
|
||||
"indexmap",
|
||||
"log",
|
||||
"memchr",
|
||||
"mime",
|
||||
"parking_lot 0.11.2",
|
||||
"pear",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
@@ -4573,16 +4602,16 @@ dependencies = [
|
||||
"smallvec 1.8.0",
|
||||
"stable-pattern",
|
||||
"state",
|
||||
"time 0.2.27",
|
||||
"time 0.3.9",
|
||||
"tokio",
|
||||
"uncased",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket_okapi"
|
||||
version = "0.8.0-rc.1"
|
||||
version = "0.8.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0025aa04994af8cd8e1fcdd5a73579a395c941ae090ecb0a39b41cca7e237a20"
|
||||
checksum = "489f4f5b120762f7974e65b919fc462d0660fd8b839026d8985b850fe5acccb0"
|
||||
dependencies = [
|
||||
"either",
|
||||
"log",
|
||||
@@ -4596,9 +4625,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket_okapi_codegen"
|
||||
version = "0.8.0-rc.1"
|
||||
version = "0.8.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc114779fc27afb78179233e966f469e47fd7a98dc15181cff2574cdddb65612"
|
||||
checksum = "54f94d1ffe41472e08463d7a2674f1db04dc4df745285e8369b33d3cfd6b0308"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
@@ -4609,9 +4638,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket_sync_db_pools"
|
||||
version = "0.1.0-rc.1"
|
||||
version = "0.1.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38cfdfebd552d075c368e641c88a5cd6ce1c58c5c710548aeb777abb48830f4b"
|
||||
checksum = "5fa48b6ab25013e9812f1b0c592741900b3a2a83c0936292e0565c0ac842f558"
|
||||
dependencies = [
|
||||
"r2d2",
|
||||
"rocket",
|
||||
@@ -4622,9 +4651,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rocket_sync_db_pools_codegen"
|
||||
version = "0.1.0-rc.1"
|
||||
version = "0.1.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267808c094db5366e1d8925aaf9f2ce05ff9b3bd92cb18c7040a1fe219c2e25"
|
||||
checksum = "280ef2d232923e69cb93da156972eb5476a7cce5ba44843f6608f46a4abf7aab"
|
||||
dependencies = [
|
||||
"devise",
|
||||
"quote",
|
||||
@@ -4636,15 +4665,6 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
@@ -4721,9 +4741,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.8"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6b5a3c80cea1ab61f4260238409510e814e38b4b563c06044edf91e7dc070e3"
|
||||
checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"indexmap",
|
||||
@@ -4734,9 +4754,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.8"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41ae4dce13e8614c46ac3c38ef1c0d668b101df6ac39817aebdaa26642ddae9b"
|
||||
checksum = "af4d7e1b012cb3d9129567661a63755ea4b8a7386d339dc945ae187e403c6743"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4820,22 +4840,13 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser 0.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||
dependencies = [
|
||||
"semver-parser 0.10.2",
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4844,12 +4855,6 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.10.2"
|
||||
@@ -4915,9 +4920,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.25.0"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6"
|
||||
checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5028,21 +5033,6 @@ dependencies = [
|
||||
"digest 0.10.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
|
||||
dependencies = [
|
||||
"sha1_smol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.9"
|
||||
@@ -5056,6 +5046,17 @@ dependencies = [
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest 0.10.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.9.1"
|
||||
@@ -5206,7 +5207,7 @@ dependencies = [
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"rand_distr",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
@@ -5289,7 +5290,7 @@ dependencies = [
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rustls",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"smallvec 1.8.0",
|
||||
"sqlformat",
|
||||
"sqlx-rt",
|
||||
@@ -5313,7 +5314,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"sqlx-core",
|
||||
"sqlx-rt",
|
||||
"syn",
|
||||
@@ -5340,20 +5341,11 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "standback"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "state"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cf4f5369e6d3044b5e365c9690f451516ac8f0954084622b49ea3fde2f6de5"
|
||||
checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b"
|
||||
dependencies = [
|
||||
"loom",
|
||||
]
|
||||
@@ -5365,64 +5357,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "statistics"
|
||||
name = "statistics-common"
|
||||
version = "1.0.1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stdweb"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
|
||||
dependencies = [
|
||||
"discard",
|
||||
"rustc_version 0.2.3",
|
||||
"stdweb-derive",
|
||||
"stdweb-internal-macros",
|
||||
"stdweb-internal-runtime",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stdweb-derive"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stdweb-internal-macros"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
|
||||
dependencies = [
|
||||
"base-x",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stdweb-internal-runtime"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.2"
|
||||
@@ -5562,7 +5510,7 @@ dependencies = [
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"signature",
|
||||
"subtle 2.4.1",
|
||||
"subtle-encoding",
|
||||
@@ -5700,21 +5648,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242"
|
||||
dependencies = [
|
||||
"const_fn",
|
||||
"libc",
|
||||
"standback",
|
||||
"stdweb",
|
||||
"time-macros 0.1.1",
|
||||
"version_check",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.9"
|
||||
@@ -5725,17 +5658,7 @@ dependencies = [
|
||||
"libc",
|
||||
"num_threads",
|
||||
"serde",
|
||||
"time-macros 0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"time-macros-impl",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5744,19 +5667,6 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros-impl"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"standback",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
@@ -6242,6 +6152,16 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
|
||||
dependencies = [
|
||||
"generic-array 0.14.5",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
@@ -6309,7 +6229,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"ts-rs",
|
||||
@@ -6349,7 +6269,7 @@ dependencies = [
|
||||
"enum-iterator",
|
||||
"getset",
|
||||
"git2",
|
||||
"rustc_version 0.4.0",
|
||||
"rustc_version",
|
||||
"rustversion",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -32,4 +32,4 @@ validator-client = { path = "../../common/client-libs/validator-client" }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[features]
|
||||
coconut = ["gateway-client/coconut", "gateway-requests/coconut"]
|
||||
coconut = ["gateway-client/coconut", "gateway-requests/coconut"]
|
||||
|
||||
@@ -114,12 +114,8 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.disabled_credentials_mode = disabled_credentials_mode;
|
||||
}
|
||||
|
||||
pub fn with_gateway_endpoint<S: Into<String>>(&mut self, id: S, owner: S, listener: S) {
|
||||
self.client.gateway_endpoint = GatewayEndpoint {
|
||||
gateway_id: id.into(),
|
||||
gateway_owner: owner.into(),
|
||||
gateway_listener: listener.into(),
|
||||
};
|
||||
pub fn with_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpoint) {
|
||||
self.client.gateway_endpoint = gateway_endpoint;
|
||||
}
|
||||
|
||||
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
|
||||
@@ -142,7 +138,7 @@ impl<T: NymConfig> Config<T> {
|
||||
|
||||
pub fn set_high_default_traffic_volume(&mut self) {
|
||||
self.debug.average_packet_delay = Duration::from_millis(10);
|
||||
self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2000000); // basically don't really send cover messages
|
||||
self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2_000_000); // basically don't really send cover messages
|
||||
self.debug.message_sending_average_delay = Duration::from_millis(4); // 250 "real" messages / s
|
||||
}
|
||||
|
||||
@@ -206,6 +202,10 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.gateway_endpoint.gateway_listener.clone()
|
||||
}
|
||||
|
||||
pub fn get_gateway_endpoint(&self) -> &GatewayEndpoint {
|
||||
&self.client.gateway_endpoint
|
||||
}
|
||||
|
||||
pub fn get_database_path(&self) -> PathBuf {
|
||||
self.client.database_path.clone()
|
||||
}
|
||||
@@ -272,17 +272,28 @@ impl<T: NymConfig> Default for Config<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
struct GatewayEndpoint {
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
pub struct GatewayEndpoint {
|
||||
/// gateway_id specifies ID of the gateway to which the client should send messages.
|
||||
/// If initially omitted, a random gateway will be chosen from the available topology.
|
||||
gateway_id: String,
|
||||
pub gateway_id: String,
|
||||
|
||||
/// Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner: String,
|
||||
pub gateway_owner: String,
|
||||
|
||||
/// Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener: String,
|
||||
pub gateway_listener: String,
|
||||
}
|
||||
|
||||
impl From<topology::gateway::Node> for GatewayEndpoint {
|
||||
fn from(node: topology::gateway::Node) -> GatewayEndpoint {
|
||||
let gateway_listener = node.clients_address();
|
||||
GatewayEndpoint {
|
||||
gateway_id: node.identity_key.to_base58_string(),
|
||||
gateway_owner: node.owner,
|
||||
gateway_listener,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
|
||||
@@ -26,4 +26,4 @@ pemstore = { path = "../../common/pemstore" }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
|
||||
|
||||
[features]
|
||||
coconut = ["credentials/coconut"]
|
||||
coconut = ["credentials/coconut"]
|
||||
|
||||
@@ -55,4 +55,4 @@ eth = []
|
||||
serde_json = "1.0" # for the "textsend" example
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
|
||||
@@ -42,6 +42,11 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
.help("Id of the gateway we are going to connect to.")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("force-register-gateway")
|
||||
.long("force-register-gateway")
|
||||
.help("Force register gateway. WARNING: this will overwrite any existing keys for the given id, potentially causing loss of access.")
|
||||
.takes_value(false)
|
||||
)
|
||||
.arg(Arg::with_name("validators")
|
||||
.long("validators")
|
||||
.help("Comma separated list of rest endpoints of the validators")
|
||||
@@ -181,25 +186,68 @@ fn show_address(config: &Config) {
|
||||
println!("\nThe address of this client is: {}", client_recipient);
|
||||
}
|
||||
|
||||
async fn set_gateway_config(config: &mut Config, chosen_gateway_id: Option<&str>) -> gateway::Node {
|
||||
println!("Setting gateway config");
|
||||
log::trace!("Chosen gateway: {:?}", chosen_gateway_id);
|
||||
|
||||
// Get the gateway details by querying the validator-api, and using the chosen one if it's
|
||||
// among the available ones.
|
||||
let gateway_details = gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
log::trace!("Used gateway: {}", gateway_details);
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_endpoint(gateway_details.clone().into());
|
||||
gateway_details
|
||||
}
|
||||
|
||||
async fn register_and_store_gateway_keys(gateway_details: gateway::Node, config: &Config) {
|
||||
println!("Registering gateway");
|
||||
|
||||
let mut rng = OsRng;
|
||||
let mut key_manager = KeyManager::new(&mut rng);
|
||||
let shared_keys = register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
key_manager
|
||||
.store_keys(&pathfinder)
|
||||
.expect("Failed to generated keys");
|
||||
println!("Saved all generated keys");
|
||||
}
|
||||
|
||||
pub async fn execute(matches: ArgMatches<'static>) {
|
||||
println!("Initialising client...");
|
||||
|
||||
let id = matches.value_of("id").unwrap(); // required for now
|
||||
|
||||
let already_init = if Config::default_config_file_path(Some(id)).exists() {
|
||||
if matches.is_present("gateway") {
|
||||
panic!("At the moment, gateway information can't be overwritten. If you want to point to a different gateway, client {}'s directory will need to be manually removed", id);
|
||||
}
|
||||
println!("Client \"{}\" was already initialised before! Config information will be overwritten (but keys will be kept)!", id);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let already_init = Config::default_config_file_path(Some(id)).exists();
|
||||
if already_init {
|
||||
println!(
|
||||
"Client \"{}\" was already initialised before! \
|
||||
Config information will be overwritten (but keys will be kept)!",
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
// re-registering if wanted.
|
||||
let should_force_register = matches.is_present("force-register-gateway");
|
||||
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
// Unless the user really wants to.
|
||||
let will_register_gateway = !already_init || should_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let is_gateway_provided = matches.value_of("gateway").is_some();
|
||||
|
||||
let mut config = Config::new(id);
|
||||
|
||||
let mut rng = OsRng;
|
||||
|
||||
// TODO: ideally that should be the last thing that's being done to config.
|
||||
// However, we are later further overriding it with gateway id
|
||||
config = override_config(config, &matches);
|
||||
@@ -207,36 +255,26 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
|
||||
// if client was already initialised, don't generate new keys, not re-register with gateway
|
||||
// (because this would create new shared key)
|
||||
if !already_init {
|
||||
// create identity, encryption and ack keys.
|
||||
let mut key_manager = KeyManager::new(&mut rng);
|
||||
|
||||
let chosen_gateway_id = matches.value_of("gateway");
|
||||
log::trace!("Chosen gateway: {:?}", chosen_gateway_id);
|
||||
|
||||
let gateway_details = gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
log::trace!("Used gateway: {}", gateway_details);
|
||||
let shared_keys =
|
||||
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
|
||||
|
||||
config.get_base_mut().with_gateway_endpoint(
|
||||
gateway_details.identity_key.to_base58_string(),
|
||||
gateway_details.owner.clone(),
|
||||
gateway_details.clients_address(),
|
||||
);
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
key_manager
|
||||
.store_keys(&pathfinder)
|
||||
.expect("Failed to generated keys");
|
||||
println!("Saved all generated keys");
|
||||
if will_register_gateway {
|
||||
// Create identity, encryption and ack keys.
|
||||
let gateway_details = set_gateway_config(&mut config, matches.value_of("gateway")).await;
|
||||
register_and_store_gateway_keys(gateway_details, &config).await;
|
||||
} else if is_gateway_provided {
|
||||
// Just set the config, don't register or create any keys
|
||||
set_gateway_config(&mut config, matches.value_of("gateway")).await;
|
||||
} else {
|
||||
// Read the existing config to reuse the gateway configuration
|
||||
println!("Not registering gateway, will reuse existing config and keys");
|
||||
if let Ok(existing_config) = Config::load_from_file(Some(id)) {
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_endpoint(existing_config.get_base().get_gateway_endpoint().clone());
|
||||
} else {
|
||||
log::warn!(
|
||||
"Existing configuration found, but enable to load gateway details. \
|
||||
Proceeding anyway."
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
let config_save_location = config.get_config_file_save_location();
|
||||
|
||||
@@ -10,4 +10,4 @@ edition = "2021"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
nymsphinx = { path = "../../../common/nymsphinx" }
|
||||
nymsphinx = { path = "../../../common/nymsphinx" }
|
||||
|
||||
@@ -47,4 +47,4 @@ coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gate
|
||||
eth = []
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
|
||||
@@ -46,6 +46,11 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
.help("Id of the gateway we are going to connect to.")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("force-register-gateway")
|
||||
.long("force-register-gateway")
|
||||
.help("Force register gateway. WARNING: this will overwrite any existing keys for the given id, potentially causing loss of access.")
|
||||
.takes_value(false)
|
||||
)
|
||||
.arg(Arg::with_name("validators")
|
||||
.long("validators")
|
||||
.help("Comma separated list of rest endpoints of the validators")
|
||||
@@ -121,6 +126,7 @@ pub async fn gateway_details(
|
||||
.expect("The list of validator apis is empty");
|
||||
let validator_client = validator_client::ApiClient::new(validator_api.clone());
|
||||
|
||||
log::trace!("Fetching list of gateways from: {}", validator_api);
|
||||
let gateways = validator_client.get_cached_gateways().await.unwrap();
|
||||
let valid_gateways = gateways
|
||||
.into_iter()
|
||||
@@ -183,26 +189,70 @@ pub fn show_address(config: &Config) {
|
||||
println!("\nThe address of this client is: {}", client_recipient);
|
||||
}
|
||||
|
||||
async fn set_gateway_config(config: &mut Config, chosen_gateway_id: Option<&str>) -> gateway::Node {
|
||||
println!("Setting gateway config");
|
||||
log::trace!("Chosen gateway: {:?}", chosen_gateway_id);
|
||||
|
||||
// Get the gateway details by querying the validator-api, and using the chosen one if it's
|
||||
// among the available ones.
|
||||
let gateway_details = gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
|
||||
log::trace!("Used gateway: {}", gateway_details);
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_endpoint(gateway_details.clone().into());
|
||||
gateway_details
|
||||
}
|
||||
|
||||
async fn register_and_store_gateway_keys(gateway_details: gateway::Node, config: &Config) {
|
||||
println!("Registering gateway");
|
||||
|
||||
let mut rng = OsRng;
|
||||
let mut key_manager = KeyManager::new(&mut rng);
|
||||
let shared_keys = register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
key_manager
|
||||
.store_keys(&pathfinder)
|
||||
.expect("Failed to generated keys");
|
||||
println!("Saved all generated keys");
|
||||
}
|
||||
|
||||
pub async fn execute(matches: ArgMatches<'static>) {
|
||||
println!("Initialising client...");
|
||||
|
||||
let id = matches.value_of("id").unwrap(); // required for now
|
||||
let provider_address = matches.value_of("provider").unwrap();
|
||||
|
||||
let already_init = if Config::default_config_file_path(Some(id)).exists() {
|
||||
if matches.is_present("gateway") {
|
||||
panic!("At the moment, gateway information can't be overwritten. If you want to point to a different gateway, client {}'s directory will need to be manually removed", id);
|
||||
}
|
||||
println!("Socks5 client \"{}\" was already initialised before! Config information will be overwritten (but keys will be kept)!", id);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let already_init = Config::default_config_file_path(Some(id)).exists();
|
||||
if already_init {
|
||||
println!(
|
||||
"SOCKS5 client \"{}\" was already initialised before! \
|
||||
Config information will be overwritten (but keys will be kept)!",
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
// re-registering if wanted.
|
||||
let should_force_register = matches.is_present("force-register-gateway");
|
||||
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
// Unless the user really wants to.
|
||||
let will_register_gateway = !already_init || should_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let is_gateway_provided = matches.value_of("gateway").is_some();
|
||||
|
||||
let mut config = Config::new(id, provider_address);
|
||||
|
||||
let mut rng = OsRng;
|
||||
|
||||
// TODO: ideally that should be the last thing that's being done to config.
|
||||
// However, we are later further overriding it with gateway id
|
||||
config = override_config(config, &matches);
|
||||
@@ -210,34 +260,26 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
|
||||
// if client was already initialised, don't generate new keys, not re-register with gateway
|
||||
// (because this would create new shared key)
|
||||
if !already_init {
|
||||
// create identity, encryption and ack keys.
|
||||
let mut key_manager = KeyManager::new(&mut rng);
|
||||
|
||||
let chosen_gateway_id = matches.value_of("gateway");
|
||||
|
||||
let gateway_details = gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
let shared_keys =
|
||||
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
|
||||
|
||||
config.get_base_mut().with_gateway_endpoint(
|
||||
gateway_details.identity_key.to_base58_string(),
|
||||
gateway_details.owner.clone(),
|
||||
gateway_details.clients_address(),
|
||||
);
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
key_manager
|
||||
.store_keys(&pathfinder)
|
||||
.expect("Failed to generated keys");
|
||||
println!("Saved all generated keys");
|
||||
if will_register_gateway {
|
||||
// Create identity, encryption and ack keys.
|
||||
let gateway_details = set_gateway_config(&mut config, matches.value_of("gateway")).await;
|
||||
register_and_store_gateway_keys(gateway_details, &config).await;
|
||||
} else if is_gateway_provided {
|
||||
// Just set the config, don't register or create any keys
|
||||
set_gateway_config(&mut config, matches.value_of("gateway")).await;
|
||||
} else {
|
||||
// Read the existing config to reuse the gateway configuration
|
||||
println!("Not registering gateway, will reuse existing config and keys");
|
||||
if let Ok(existing_config) = Config::load_from_file(Some(id)) {
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_endpoint(existing_config.get_base().get_gateway_endpoint().clone());
|
||||
} else {
|
||||
log::warn!(
|
||||
"Existing configuration found, but enable to load gateway details. \
|
||||
Proceeding anyway."
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
let config_save_location = config.get_config_file_save_location();
|
||||
@@ -245,8 +287,14 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
.save_to_file(None)
|
||||
.expect("Failed to save the config file");
|
||||
println!("Saved configuration file to {:?}", config_save_location);
|
||||
println!("Using gateway: {}", config.get_base().get_gateway_id(),);
|
||||
println!("Client configuration completed.\n\n\n");
|
||||
println!("Using gateway: {}", config.get_base().get_gateway_id());
|
||||
log::debug!("Gateway id: {}", config.get_base().get_gateway_id());
|
||||
log::debug!("Gateway owner: {}", config.get_base().get_gateway_owner());
|
||||
log::debug!(
|
||||
"Gateway listener: {}",
|
||||
config.get_base().get_gateway_listener()
|
||||
);
|
||||
println!("Client configuration completed.");
|
||||
|
||||
show_address(&config);
|
||||
}
|
||||
|
||||
@@ -34,16 +34,16 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
.help("Address of the socks5 provider to send messages to.")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("validators")
|
||||
.long("validators")
|
||||
.help("Comma separated list of rest endpoints of the validators")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(Arg::with_name("gateway")
|
||||
.long("gateway")
|
||||
.help("Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("validators")
|
||||
.long("validators")
|
||||
.help("Comma separated list of rest endpoints of the validators")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(Arg::with_name("port")
|
||||
.short("p")
|
||||
.long("port")
|
||||
|
||||
@@ -74,4 +74,4 @@ features = ["js"]
|
||||
[features]
|
||||
coconut = ["gateway-requests/coconut", "coconut-interface", "validator-client", "credentials/coconut"]
|
||||
wasm = ["web3/wasm", "web3/http", "web3/signing"]
|
||||
default = ["web3/default"]
|
||||
default = ["web3/default"]
|
||||
|
||||
@@ -11,4 +11,4 @@ cw3 = { version = "0.13.1" }
|
||||
cw4 = { version = "0.13.1" }
|
||||
cosmwasm-std = "1.0.0"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -17,4 +17,4 @@ tokio = { version = "1.19.1", features = [ "rt-multi-thread", "net", "signal", "
|
||||
|
||||
[build-dependencies]
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
tokio = { version = "1.19.1", features = ["rt-multi-thread", "macros"] }
|
||||
tokio = { version = "1.19.1", features = ["rt-multi-thread", "macros"] }
|
||||
|
||||
@@ -21,4 +21,4 @@ validator-client = { path = "../client-libs/validator-client" }
|
||||
rand = "0.7.3"
|
||||
|
||||
[features]
|
||||
coconut = ["cosmrs"]
|
||||
coconut = ["cosmrs"]
|
||||
|
||||
@@ -38,4 +38,4 @@ criterion = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmarks"
|
||||
harness = false
|
||||
harness = false
|
||||
|
||||
@@ -64,6 +64,10 @@ impl Network {
|
||||
self.details().rewarding_validator_address
|
||||
}
|
||||
|
||||
pub fn statistics_service_url(&self) -> &str {
|
||||
self.details().statistics_service_url
|
||||
}
|
||||
|
||||
pub fn validators(&self) -> impl Iterator<Item = &ValidatorDetails> {
|
||||
self.details().validators.iter()
|
||||
}
|
||||
@@ -101,6 +105,7 @@ pub struct NetworkDetails {
|
||||
mixnet_contract_address: String,
|
||||
vesting_contract_address: String,
|
||||
bandwidth_claim_contract_address: String,
|
||||
statistics_service_url: String,
|
||||
validators: Vec<ValidatorDetails>,
|
||||
}
|
||||
|
||||
@@ -112,6 +117,7 @@ impl From<&DefaultNetworkDetails<'_>> for NetworkDetails {
|
||||
mixnet_contract_address: details.mixnet_contract_address.into(),
|
||||
vesting_contract_address: details.vesting_contract_address.into(),
|
||||
bandwidth_claim_contract_address: details.bandwidth_claim_contract_address.into(),
|
||||
statistics_service_url: details.statistics_service_url.into(),
|
||||
validators: details.validators.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ pub struct DefaultNetworkDetails<'a> {
|
||||
coconut_bandwidth_contract_address: &'a str,
|
||||
multisig_contract_address: &'a str,
|
||||
rewarding_validator_address: &'a str,
|
||||
statistics_service_url: &'a str,
|
||||
validators: Vec<ValidatorDetails>,
|
||||
}
|
||||
|
||||
@@ -67,6 +68,7 @@ static MAINNET_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> =
|
||||
coconut_bandwidth_contract_address: mainnet::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
||||
multisig_contract_address: mainnet::MULTISIG_CONTRACT_ADDRESS,
|
||||
rewarding_validator_address: mainnet::REWARDING_VALIDATOR_ADDRESS,
|
||||
statistics_service_url: mainnet::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||
validators: mainnet::validators(),
|
||||
});
|
||||
|
||||
@@ -80,6 +82,7 @@ static SANDBOX_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> =
|
||||
coconut_bandwidth_contract_address: sandbox::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
||||
multisig_contract_address: sandbox::MULTISIG_CONTRACT_ADDRESS,
|
||||
rewarding_validator_address: sandbox::REWARDING_VALIDATOR_ADDRESS,
|
||||
statistics_service_url: sandbox::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||
validators: sandbox::validators(),
|
||||
});
|
||||
|
||||
@@ -92,6 +95,7 @@ static QA_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> = Lazy::new(|| DefaultN
|
||||
coconut_bandwidth_contract_address: qa::COCONUT_BANDWIDTH_CONTRACT_ADDRESS,
|
||||
multisig_contract_address: qa::MULTISIG_CONTRACT_ADDRESS,
|
||||
rewarding_validator_address: qa::REWARDING_VALIDATOR_ADDRESS,
|
||||
statistics_service_url: qa::STATISTICS_SERVICE_DOMAIN_ADDRESS,
|
||||
validators: qa::validators(),
|
||||
});
|
||||
|
||||
@@ -132,6 +136,13 @@ impl ValidatorDetails {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_statistics_service_url() -> Url {
|
||||
DEFAULT_NETWORK
|
||||
.statistics_service_url()
|
||||
.parse()
|
||||
.expect("the provided statistics service url is invalid!")
|
||||
}
|
||||
|
||||
pub fn default_nymd_endpoints() -> Vec<Url> {
|
||||
DEFAULT_NETWORK
|
||||
.validators()
|
||||
|
||||
@@ -22,6 +22,7 @@ pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
|
||||
|
||||
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "http://127.0.0.1:8090";
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(
|
||||
"https://rpc.nyx.nodes.guru/",
|
||||
|
||||
@@ -22,6 +22,7 @@ pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq";
|
||||
|
||||
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "";
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(
|
||||
"https://qa-validator.nymtech.net",
|
||||
|
||||
@@ -20,6 +20,7 @@ pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("E8883BAeF3869e14E4823F46662e81D4F7d2A81F");
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt1jh0s6qu6tuw9ut438836mmn7f3f2wencrnmdj4";
|
||||
|
||||
pub(crate) const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "";
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(
|
||||
"https://sandbox-validator.nymtech.net",
|
||||
|
||||
@@ -12,4 +12,4 @@ nymsphinx-types = { path = "../types" } # we need to be able to refer to some ty
|
||||
serde = "1.0" # implementing serialization/deserialization for some types, like `Recipient`
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.7"
|
||||
rand = "0.7"
|
||||
|
||||
@@ -16,4 +16,4 @@ nymsphinx-chunking = { path = "../chunking" }
|
||||
nymsphinx-params = { path = "../params" }
|
||||
nymsphinx-forwarding = { path = "../forwarding" }
|
||||
nymsphinx-types = { path = "../types" }
|
||||
topology = { path = "../../topology" }
|
||||
topology = { path = "../../topology" }
|
||||
|
||||
@@ -8,4 +8,4 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
crypto = { path = "../../crypto", features = ["hashing", "symmetric"] }
|
||||
nymsphinx-types = { path = "../types" }
|
||||
nymsphinx-types = { path = "../types" }
|
||||
|
||||
@@ -18,4 +18,4 @@ socks5-requests = { path = "../requests" }
|
||||
ordered-buffer = { path = "../ordered-buffer" }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.4.2"
|
||||
tokio-test = "0.4.2"
|
||||
|
||||
@@ -2,14 +2,20 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
[package]
|
||||
name = "statistics"
|
||||
name = "statistics-common"
|
||||
version = "1.0.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
reqwest = "0.11"
|
||||
async-trait = { version = "0.1.51" }
|
||||
log = "0.4"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
thiserror = "1"
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "chrono"]}
|
||||
thiserror = "1"
|
||||
tokio = { version = "1.19.1", features = [ "time" ] }
|
||||
|
||||
network-defaults = { path = "../network-defaults" }
|
||||
@@ -10,13 +10,29 @@ pub const DEFAULT_STATISTICS_SERVICE_PORT: u16 = 8090;
|
||||
pub const STATISTICS_SERVICE_VERSION: &str = "/v1";
|
||||
pub const STATISTICS_SERVICE_API_STATISTICS: &str = "statistic";
|
||||
|
||||
pub async fn build_and_send_statistics_request(
|
||||
msg: StatsMessage,
|
||||
url: String,
|
||||
) -> Result<(), StatsError> {
|
||||
reqwest::Client::new()
|
||||
.post(format!(
|
||||
"{}{}/{}",
|
||||
url, STATISTICS_SERVICE_VERSION, STATISTICS_SERVICE_API_STATISTICS
|
||||
))
|
||||
.json(&msg)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn build_statistics_request_bytes(msg: StatsMessage) -> Result<Vec<u8>, StatsError> {
|
||||
let json_msg = msg.to_json()?;
|
||||
|
||||
let req = reqwest::Request::new(
|
||||
reqwest::Method::POST,
|
||||
reqwest::Url::parse(&format!(
|
||||
"http://{}:{}/{}/{}",
|
||||
"http://{}:{}{}/{}",
|
||||
DEFAULT_STATISTICS_SERVICE_ADDRESS,
|
||||
DEFAULT_STATISTICS_SERVICE_PORT,
|
||||
STATISTICS_SERVICE_VERSION,
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use async_trait::async_trait;
|
||||
use log::error;
|
||||
use sqlx::types::chrono::{DateTime, Utc};
|
||||
use std::time::Duration;
|
||||
use tokio::time;
|
||||
|
||||
use crate::error::StatsError;
|
||||
use crate::StatsMessage;
|
||||
|
||||
const STATISTICS_TIMER_INTERVAL: Duration = Duration::from_secs(60);
|
||||
|
||||
#[async_trait]
|
||||
pub trait StatisticsCollector {
|
||||
async fn create_stats_message(
|
||||
&self,
|
||||
interval: Duration,
|
||||
timestamp: DateTime<Utc>,
|
||||
) -> StatsMessage;
|
||||
async fn send_stats_message(&self, stats_message: StatsMessage) -> Result<(), StatsError>;
|
||||
async fn reset_stats(&mut self);
|
||||
}
|
||||
|
||||
pub struct StatisticsSender<T: StatisticsCollector> {
|
||||
collector: T,
|
||||
interval: Duration,
|
||||
timestamp: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl<T: StatisticsCollector> StatisticsSender<T> {
|
||||
pub fn new(collector: T) -> Self {
|
||||
StatisticsSender {
|
||||
collector,
|
||||
interval: STATISTICS_TIMER_INTERVAL,
|
||||
timestamp: Utc::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) {
|
||||
let mut interval = time::interval(self.interval);
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
let stats_message = self
|
||||
.collector
|
||||
.create_stats_message(self.interval, self.timestamp)
|
||||
.await;
|
||||
if let Err(e) = self.collector.send_stats_message(stats_message).await {
|
||||
error!("Statistics not sent: {}", e);
|
||||
}
|
||||
self.collector.reset_stats().await;
|
||||
self.timestamp = Utc::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,7 @@ use thiserror::Error;
|
||||
pub enum StatsError {
|
||||
#[error("Serde JSON error: {0}")]
|
||||
SerdeJsonError(#[from] serde_json::Error),
|
||||
|
||||
#[error("Reqwest error: {0}")]
|
||||
ReqwestError(#[from] reqwest::Error),
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ use serde::{Deserialize, Serialize};
|
||||
use error::StatsError;
|
||||
|
||||
pub mod api;
|
||||
pub mod collector;
|
||||
pub mod error;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct StatsMessage {
|
||||
pub stats_data: Vec<StatsServiceData>,
|
||||
pub stats_data: Vec<StatsData>,
|
||||
pub interval_seconds: u32,
|
||||
pub timestamp: String,
|
||||
}
|
||||
@@ -25,6 +26,23 @@ impl StatsMessage {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum StatsData {
|
||||
Service(StatsServiceData),
|
||||
Gateway(StatsGatewayData),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct StatsGatewayData {
|
||||
pub inbox_count: u32,
|
||||
}
|
||||
|
||||
impl StatsGatewayData {
|
||||
pub fn new(inbox_count: u32) -> Self {
|
||||
StatsGatewayData { inbox_count }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct StatsServiceData {
|
||||
pub requested_service: String,
|
||||
|
||||
@@ -7,4 +7,4 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
semver = "0.11"
|
||||
semver = "0.11"
|
||||
|
||||
@@ -29,4 +29,4 @@ features = [
|
||||
"ProgressEvent",
|
||||
"WebSocket",
|
||||
"Window",
|
||||
]
|
||||
]
|
||||
|
||||
@@ -14,9 +14,9 @@ log = "0.4.0"
|
||||
okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] }
|
||||
pretty_env_logger = "0.4.0"
|
||||
reqwest = "0.11.4"
|
||||
rocket = {version = "0.5.0-rc.1", features=["json"] }
|
||||
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||
rocket_cors = { git="https://github.com/lawliet89/rocket_cors", rev="dfd3662c49e2f6fc37df35091cb94d82f7fb5915" }
|
||||
rocket_okapi = { version = "0.8.0-rc.1", features = ["swagger"] }
|
||||
rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] }
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
serde = "1.0.126"
|
||||
serde_json = "1.0.66"
|
||||
|
||||
@@ -51,6 +51,7 @@ mixnode-common = { path = "../common/mixnode-common" }
|
||||
network-defaults = { path = "../common/network-defaults" }
|
||||
nymsphinx = { path = "../common/nymsphinx" }
|
||||
pemstore = { path = "../common/pemstore" }
|
||||
statistics-common = { path = "../common/statistics" }
|
||||
validator-client = { path = "../common/client-libs/validator-client", features = ["nymd-client"] }
|
||||
version-checker = { path = "../common/version-checker" }
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ pub struct Init {
|
||||
|
||||
/// Cosmos wallet mnemonic needed for double spending protection
|
||||
#[clap(long)]
|
||||
mnemonic: String,
|
||||
mnemonic: Option<String>,
|
||||
|
||||
/// Set this gateway to work in a enabled credentials mode that would disallow clients to bypass bandwidth credential requirement
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
@@ -57,6 +57,14 @@ pub struct Init {
|
||||
#[clap(long)]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
#[clap(long)]
|
||||
enabled_statistics: Option<bool>,
|
||||
|
||||
/// URL where a statistics aggregator is running. The default value is a Nym aggregator server
|
||||
#[clap(long)]
|
||||
statistics_service_url: Option<String>,
|
||||
|
||||
/// Comma separated list of endpoints of the validator
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
@@ -73,7 +81,7 @@ impl From<Init> for OverrideConfig {
|
||||
datastore: init_config.datastore,
|
||||
announce_host: init_config.announce_host,
|
||||
validator_apis: init_config.validator_apis,
|
||||
mnemonic: Some(init_config.mnemonic),
|
||||
mnemonic: init_config.mnemonic,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
@@ -81,6 +89,9 @@ impl From<Init> for OverrideConfig {
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Some(init_config.eth_endpoint),
|
||||
|
||||
enabled_statistics: init_config.enabled_statistics,
|
||||
statistics_service_url: init_config.statistics_service_url,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
validators: init_config.validators,
|
||||
}
|
||||
@@ -163,7 +174,9 @@ mod tests {
|
||||
announce_host: Some("foo-announce-host".to_string()),
|
||||
datastore: Some("foo-datastore".to_string()),
|
||||
validator_apis: None,
|
||||
mnemonic: "a b c".to_string(),
|
||||
mnemonic: Some("a b c".to_string()),
|
||||
statistics_service_url: None,
|
||||
enabled_statistics: None,
|
||||
};
|
||||
|
||||
let config = Config::new(&args.id);
|
||||
|
||||
@@ -46,6 +46,8 @@ pub(crate) struct OverrideConfig {
|
||||
clients_port: Option<u16>,
|
||||
datastore: Option<String>,
|
||||
announce_host: Option<String>,
|
||||
enabled_statistics: Option<bool>,
|
||||
statistics_service_url: Option<String>,
|
||||
validator_apis: Option<String>,
|
||||
mnemonic: Option<String>,
|
||||
|
||||
@@ -102,6 +104,18 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
config = config.announce_host_from_listening_host();
|
||||
}
|
||||
|
||||
if let Some(enabled_statistics) = args.enabled_statistics {
|
||||
config = config.with_enabled_statistics(enabled_statistics);
|
||||
}
|
||||
|
||||
if let Some(raw_url) = args.statistics_service_url {
|
||||
config = config.with_custom_statistics_service_url(
|
||||
raw_url
|
||||
.parse()
|
||||
.expect("the provided statistics service url is invalid!"),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(raw_validators) = args.validator_apis {
|
||||
config = config.with_custom_validator_apis(parse_validators(&raw_validators));
|
||||
}
|
||||
|
||||
@@ -57,6 +57,14 @@ pub struct Run {
|
||||
#[clap(long)]
|
||||
eth_endpoint: Option<String>,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
#[clap(long)]
|
||||
enabled_statistics: Option<bool>,
|
||||
|
||||
/// URL where a statistics aggregator is running. The default value is a Nym aggregator server
|
||||
#[clap(long)]
|
||||
statistics_service_url: Option<String>,
|
||||
|
||||
/// Comma separated list of endpoints of the validator
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
@@ -81,6 +89,9 @@ impl From<Run> for OverrideConfig {
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: run_config.eth_endpoint,
|
||||
|
||||
enabled_statistics: run_config.enabled_statistics,
|
||||
statistics_service_url: run_config.statistics_service_url,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
validators: run_config.validators,
|
||||
}
|
||||
|
||||
@@ -127,6 +127,16 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_enabled_statistics(mut self, enabled_statistics: bool) -> Self {
|
||||
self.gateway.enabled_statistics = enabled_statistics;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_custom_statistics_service_url(mut self, statistics_service_url: Url) -> Self {
|
||||
self.gateway.statistics_service_url = statistics_service_url;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_custom_validator_apis(mut self, validator_api_urls: Vec<Url>) -> Self {
|
||||
self.gateway.validator_api_urls = validator_api_urls;
|
||||
self
|
||||
@@ -227,6 +237,14 @@ impl Config {
|
||||
self.gateway.eth_endpoint.clone()
|
||||
}
|
||||
|
||||
pub fn get_enabled_statistics(&self) -> bool {
|
||||
self.gateway.enabled_statistics
|
||||
}
|
||||
|
||||
pub fn get_statistics_service_url(&self) -> Url {
|
||||
self.gateway.statistics_service_url.clone()
|
||||
}
|
||||
|
||||
pub fn get_validator_api_endpoints(&self) -> Vec<Url> {
|
||||
self.gateway.validator_api_urls.clone()
|
||||
}
|
||||
@@ -339,6 +357,12 @@ pub struct Gateway {
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// Wheather gateway collects and sends anonymized statistics
|
||||
enabled_statistics: bool,
|
||||
|
||||
/// Domain address of the statistics service
|
||||
statistics_service_url: Url,
|
||||
|
||||
/// Addresses to APIs running on validator from which the node gets the view of the network.
|
||||
validator_api_urls: Vec<Url>,
|
||||
|
||||
@@ -399,10 +423,12 @@ impl Default for Gateway {
|
||||
public_sphinx_key_file: Default::default(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_endpoint: "".to_string(),
|
||||
enabled_statistics: false,
|
||||
statistics_service_url: default_statistics_service_url(),
|
||||
validator_api_urls: default_api_endpoints(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
validator_nymd_urls: default_nymd_endpoints(),
|
||||
cosmos_mnemonic: "".to_string(),
|
||||
cosmos_mnemonic: "exact antique hybrid width raise anchor puzzle degree fee quit long crack net vague hip despair write put useless civil mechanic broom music day".to_string(),
|
||||
nym_root_directory: Config::default_root_directory(),
|
||||
persistent_storage: Default::default(),
|
||||
wallet_address: "nymXXXXXXXX".to_string(),
|
||||
|
||||
@@ -56,6 +56,12 @@ mix_port = {{ gateway.mix_port }}
|
||||
# (default: 9000)
|
||||
clients_port = {{ gateway.clients_port }}
|
||||
|
||||
# Wheather gateway collects and sends anonymized statistics
|
||||
enabled_statistics = {{ gateway.enabled_statistics }}
|
||||
|
||||
# Domain address of the statistics service
|
||||
statistics_service_url = '{{ gateway.statistics_service_url }}'
|
||||
|
||||
# Addresses to APIs running on validator from which the node gets the view of the network.
|
||||
validator_api_urls = [
|
||||
{{#each gateway.validator_api_urls }}
|
||||
|
||||
@@ -55,4 +55,9 @@ impl ActiveClientsStore {
|
||||
pub(crate) fn insert(&self, client: DestinationAddressBytes, handle: MixMessageSender) {
|
||||
self.0.insert(client, handle);
|
||||
}
|
||||
|
||||
/// Get number of active clients in store
|
||||
pub(crate) fn size(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,14 @@ use crate::config::Config;
|
||||
use crate::node::client_handling::active_clients::ActiveClientsStore;
|
||||
use crate::node::client_handling::websocket;
|
||||
use crate::node::mixnet_handling::receiver::connection_handler::ConnectionHandler;
|
||||
use crate::node::statistics::collector::GatewayStatisticsCollector;
|
||||
use crate::node::storage::Storage;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use log::*;
|
||||
use mixnet_client::forwarder::{MixForwardingSender, PacketForwarder};
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use statistics_common::collector::StatisticsSender;
|
||||
use std::net::SocketAddr;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
@@ -29,6 +31,7 @@ use self::storage::PersistentStorage;
|
||||
|
||||
pub(crate) mod client_handling;
|
||||
pub(crate) mod mixnet_handling;
|
||||
pub(crate) mod statistics;
|
||||
pub(crate) mod storage;
|
||||
|
||||
/// Wire up and create Gateway instance
|
||||
@@ -304,6 +307,18 @@ where
|
||||
active_clients_store.clone(),
|
||||
);
|
||||
|
||||
if self.config.get_enabled_statistics() {
|
||||
let statistics_service_url = self.config.get_statistics_service_url();
|
||||
let stats_collector = GatewayStatisticsCollector::new(
|
||||
active_clients_store.clone(),
|
||||
statistics_service_url,
|
||||
);
|
||||
let mut stats_sender = StatisticsSender::new(stats_collector);
|
||||
tokio::spawn(async move {
|
||||
stats_sender.run().await;
|
||||
});
|
||||
}
|
||||
|
||||
self.start_client_websocket_listener(
|
||||
mix_forwarding_channel,
|
||||
active_clients_store,
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use async_trait::async_trait;
|
||||
use sqlx::types::chrono::{DateTime, Utc};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
use statistics_common::{
|
||||
api::build_and_send_statistics_request, collector::StatisticsCollector, error::StatsError,
|
||||
StatsData, StatsGatewayData, StatsMessage,
|
||||
};
|
||||
|
||||
use crate::node::client_handling::active_clients::ActiveClientsStore;
|
||||
|
||||
pub(crate) struct GatewayStatisticsCollector {
|
||||
active_clients_store: ActiveClientsStore,
|
||||
statistics_service_url: Url,
|
||||
}
|
||||
|
||||
impl GatewayStatisticsCollector {
|
||||
pub fn new(active_clients_store: ActiveClientsStore, statistics_service_url: Url) -> Self {
|
||||
GatewayStatisticsCollector {
|
||||
active_clients_store,
|
||||
statistics_service_url,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl StatisticsCollector for GatewayStatisticsCollector {
|
||||
async fn create_stats_message(
|
||||
&self,
|
||||
interval: Duration,
|
||||
timestamp: DateTime<Utc>,
|
||||
) -> StatsMessage {
|
||||
let inbox_count = self.active_clients_store.size() as u32;
|
||||
let stats_data = vec![StatsData::Gateway(StatsGatewayData { inbox_count })];
|
||||
StatsMessage {
|
||||
stats_data,
|
||||
interval_seconds: interval.as_secs() as u32,
|
||||
timestamp: timestamp.to_rfc3339(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_stats_message(&self, stats_message: StatsMessage) -> Result<(), StatsError> {
|
||||
build_and_send_statistics_request(stats_message, self.statistics_service_url.to_string())
|
||||
.await
|
||||
}
|
||||
|
||||
async fn reset_stats(&mut self) {}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod collector;
|
||||
+1
-1
@@ -26,7 +26,7 @@ humantime-serde = "1.0"
|
||||
log = "0.4.0"
|
||||
pretty_env_logger = "0.4.0"
|
||||
rand = "0.7.3"
|
||||
rocket = { version="0.5.0-rc.1", features = ["json"] }
|
||||
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||
serde = { version="1.0", features = ["derive"] }
|
||||
tokio = { version="1.19.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||
tokio-util = { version="0.7.3", features = ["codec"] }
|
||||
|
||||
Vendored
@@ -56,11 +56,9 @@ pub async fn init_socks5(provider_address: &str, chosen_gateway_id: Option<&str>
|
||||
.await;
|
||||
|
||||
info!("Setting gateway endpoint");
|
||||
config.get_base_mut().with_gateway_endpoint(
|
||||
gateway_details.identity_key.to_base58_string(),
|
||||
gateway_details.owner.clone(),
|
||||
gateway_details.clients_address(),
|
||||
);
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_endpoint(gateway_details.into());
|
||||
|
||||
info!("Insert gateway shared key");
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::window_toggle;
|
||||
use tauri::{
|
||||
AppHandle, CustomMenuItem, Menu,
|
||||
SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem, Wry,
|
||||
AppHandle, CustomMenuItem, Menu, SystemTray, SystemTrayEvent, SystemTrayMenu,
|
||||
SystemTrayMenuItem, Wry,
|
||||
};
|
||||
#[cfg(target_os = "macos")]
|
||||
use tauri::{CustomMenuItem, MenuItem, Submenu, SystemTray, SystemTrayMenu, SystemTrayMenuItem};
|
||||
use tauri::{MenuItem, Submenu};
|
||||
|
||||
pub trait AddDefaultSubmenus {
|
||||
fn add_default_app_submenu_if_macos(self) -> Self;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use futures::channel::mpsc;
|
||||
use futures::SinkExt;
|
||||
use log::info;
|
||||
use futures::channel::mpsc;
|
||||
|
||||
use config::NymConfig;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
|
||||
@@ -308,10 +308,27 @@ fn _archive_wallet_file(path: &Path) -> Result<(), BackendError> {
|
||||
}
|
||||
|
||||
pub(crate) fn archive_wallet_file() -> Result<(), BackendError> {
|
||||
log::info!("Archiving wallet file");
|
||||
let store_dir = get_storage_directory()?;
|
||||
let filepath = store_dir.join(WALLET_INFO_FILENAME);
|
||||
_archive_wallet_file(&filepath)
|
||||
|
||||
if filepath.exists() {
|
||||
if let Some(filepath) = filepath.to_str() {
|
||||
log::info!("Archiving wallet file: {}", filepath);
|
||||
} else {
|
||||
log::info!("Archiving wallet file");
|
||||
}
|
||||
_archive_wallet_file(&filepath)
|
||||
} else {
|
||||
if let Some(filepath) = filepath.to_str() {
|
||||
log::info!(
|
||||
"Skipping archiving wallet file, as it's not found: {}",
|
||||
filepath
|
||||
);
|
||||
} else {
|
||||
log::info!("Skipping archiving wallet file, as it's not found");
|
||||
}
|
||||
Err(BackendError::WalletFileNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an account from inside the encrypted login.
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
Box,
|
||||
Chip,
|
||||
CircularProgress,
|
||||
Link,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
@@ -19,6 +18,7 @@ import { visuallyHidden } from '@mui/utils';
|
||||
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
|
||||
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard';
|
||||
import { DelegationWithEverything } from '@nymproject/types';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { format, formatDistanceToNow, parseISO } from 'date-fns';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { tableCellClasses } from '@mui/material/TableCell';
|
||||
@@ -163,11 +163,8 @@ export const DelegationList: React.FC<{
|
||||
<Link
|
||||
target="_blank"
|
||||
href={`${explorerUrl}/network-components/mixnode/${item.node_identity}`}
|
||||
color="inherit"
|
||||
underline="none"
|
||||
>
|
||||
{item.node_identity.slice(0, 6)}...{item.node_identity.slice(-6)}
|
||||
</Link>
|
||||
text={`${item.node_identity.slice(0, 6)}...${item.node_identity.slice(-6)}`}
|
||||
/>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell align="center">{!item.avg_uptime_percent ? '-' : `${item.avg_uptime_percent}%`}</TableCell>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Box, Button, CircularProgress, Link, Modal, Stack, Typography } from '@mui/material';
|
||||
import { Box, Button, CircularProgress, Modal, Stack, Typography } from '@mui/material';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { modalStyle } from '../Modals/styles';
|
||||
|
||||
export type ActionType = 'delegate' | 'undelegate' | 'redeem' | 'redeem-all' | 'compound';
|
||||
@@ -101,9 +102,13 @@ export const DelegationModal: React.FC<
|
||||
<Typography mb={1} fontSize="small" color={(theme) => theme.palette.text.secondary}>
|
||||
Check the transaction {transactions.length > 1 ? 'hashes' : 'hash'}:
|
||||
{transactions.map((transaction) => (
|
||||
<Link key={transaction.hash} href={transaction.url} target="_blank" sx={{ ml: 1 }}>
|
||||
{transaction.hash.slice(0, 6)}
|
||||
</Link>
|
||||
<Link
|
||||
key={transaction.hash}
|
||||
href={transaction.url}
|
||||
target="_blank"
|
||||
sx={{ ml: 1 }}
|
||||
text={transaction.hash.slice(0, 6)}
|
||||
/>
|
||||
))}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Box, Link, Typography } from '@mui/material';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { DelegationWithEverything } from '@nymproject/types';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { DelegationList } from './DelegationList';
|
||||
import { DelegationListItemActions } from './DelegationActions';
|
||||
|
||||
@@ -18,13 +19,7 @@ export const Delegations: React.FC<{
|
||||
onItemActionClick={onDelegationItemActionClick}
|
||||
/>
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<Link
|
||||
href={`${explorerUrl}/network-components/mixnodes/`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
underline="hover"
|
||||
color={(theme) => theme.palette.text.primary}
|
||||
>
|
||||
<Link href={`${explorerUrl}/network-components/mixnodes/`} target="_blank" rel="noreferrer">
|
||||
Check the{' '}
|
||||
<Typography color="primary.main" component="span">
|
||||
list of mixnodes
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { FC } from 'react';
|
||||
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
|
||||
import {
|
||||
Box,
|
||||
Link,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
@@ -17,6 +16,7 @@ import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard';
|
||||
import { DelegationEvent } from '@nymproject/types';
|
||||
import { ArrowDropDown } from '@mui/icons-material';
|
||||
import { visuallyHidden } from '@mui/utils';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
|
||||
type Order = 'asc' | 'desc';
|
||||
|
||||
@@ -138,11 +138,8 @@ export const PendingEvents: FC<{ pendingEvents: DelegationEvent[]; explorerUrl:
|
||||
<Link
|
||||
target="_blank"
|
||||
href={`${explorerUrl}/network-components/mixnode/${item.node_identity}`}
|
||||
color="inherit"
|
||||
underline="none"
|
||||
>
|
||||
{item.node_identity.slice(0, 6)}...{item.node_identity.slice(-6)}
|
||||
</Link>
|
||||
text={`${item.node_identity.slice(0, 6)}...${item.node_identity.slice(-6)}`}
|
||||
/>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell>{!item.amount ? '-' : `${item.amount?.amount} ${item.amount?.denom}`}</TableCell>
|
||||
|
||||
@@ -90,6 +90,9 @@ export const Nav = () => {
|
||||
<ListItemText
|
||||
sx={{
|
||||
color: location.pathname === route ? 'primary.main' : 'text.primary',
|
||||
'& .MuiListItemText-primary': {
|
||||
fontWeight: '600',
|
||||
},
|
||||
}}
|
||||
primary={label}
|
||||
/>
|
||||
|
||||
@@ -143,14 +143,25 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
useEffect(() => {
|
||||
let newValue = false;
|
||||
if (network && appEnv?.ADMIN_ADDRESS && clientDetails?.client_address) {
|
||||
const adminAddressMap = JSON.parse(appEnv.ADMIN_ADDRESS);
|
||||
const adminAddresses = adminAddressMap[network] || [];
|
||||
if (adminAddresses.length) {
|
||||
newValue = adminAddresses.includes(clientDetails?.client_address);
|
||||
try {
|
||||
const adminAddressMap = JSON.parse(appEnv.ADMIN_ADDRESS);
|
||||
const adminAddresses = adminAddressMap[network] || [];
|
||||
if (adminAddresses.length) {
|
||||
newValue = adminAddresses.includes(clientDetails?.client_address);
|
||||
if (newValue) {
|
||||
Console.log('Wallet is in admin mode: ', {
|
||||
network,
|
||||
adminAddress: adminAddressMap[network],
|
||||
clientAddress: clientDetails?.client_address,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Console.error('Failed to check admin addresses', e);
|
||||
}
|
||||
}
|
||||
setIsAdminAddress(newValue);
|
||||
}, [appEnv, network]);
|
||||
}, [appEnv, network, clientDetails?.client_address]);
|
||||
|
||||
const logIn = async ({ type, value }: { type: TLoginType; value: string }) => {
|
||||
if (value.length === 0) {
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { Button, CircularProgress, FormControl, Stack } from '@mui/material';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { AuthContext } from 'src/context/auth';
|
||||
import { createPassword } from 'src/requests';
|
||||
import { PasswordInput } from 'src/components';
|
||||
import { archiveWalletFile, createPassword, isPasswordCreated } from 'src/requests';
|
||||
import { Subtitle, Title, PasswordStrength } from '../components';
|
||||
|
||||
export const ConnectPassword = () => {
|
||||
@@ -20,6 +20,12 @@ export const ConnectPassword = () => {
|
||||
const storePassword = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
|
||||
const exists = await isPasswordCreated();
|
||||
if (exists) {
|
||||
await archiveWalletFile();
|
||||
}
|
||||
|
||||
await createPassword({ mnemonic, password });
|
||||
resetState();
|
||||
enqueueSnackbar('Password successfully created', { variant: 'success' });
|
||||
|
||||
@@ -2,32 +2,15 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Button, Stack, Typography } from '@mui/material';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { archiveWalletFile } from 'src/requests';
|
||||
import { Console } from 'src/utils/console';
|
||||
import { Subtitle } from '../components';
|
||||
|
||||
export const ForgotPassword = () => {
|
||||
const navigate = useNavigate();
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
return (
|
||||
<>
|
||||
<Subtitle subtitle="Create a new password or sign in with mnemonic" />
|
||||
<Stack spacing={2} sx={{ width: 300 }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
onClick={async () => {
|
||||
try {
|
||||
await archiveWalletFile();
|
||||
} catch (e) {
|
||||
Console.error(e);
|
||||
enqueueSnackbar('Failed to archive your existing wallet file', { variant: 'error' });
|
||||
}
|
||||
navigate('/confirm-mnemonic');
|
||||
}}
|
||||
fullWidth
|
||||
>
|
||||
<Button variant="contained" size="large" onClick={() => navigate('/confirm-mnemonic')} fullWidth>
|
||||
Create a new password
|
||||
</Button>
|
||||
<Typography sx={{ textAlign: 'center', fontWeight: 600 }}>or</Typography>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { Alert, Button, Grid, Link, Typography } from '@mui/material';
|
||||
import { OpenInNew } from '@mui/icons-material';
|
||||
import { Alert, Grid, Typography } from '@mui/material';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { NymCard, ClientAddress } from '../../components';
|
||||
import { AppContext, urls } from '../../context/main';
|
||||
|
||||
@@ -33,9 +33,11 @@ export const BalanceCard = () => {
|
||||
</Grid>
|
||||
{network && (
|
||||
<Grid item>
|
||||
<Link href={`${urls(network).blockExplorer}/account/${clientDetails?.client_address}`} target="_blank">
|
||||
<Button endIcon={<OpenInNew />}>Last transactions</Button>
|
||||
</Link>
|
||||
<Link
|
||||
href={`${urls(network).blockExplorer}/account/${clientDetails?.client_address}`}
|
||||
target="_blank"
|
||||
text="Last transactions"
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { Alert, AlertTitle, Box, Button, Link, Typography } from '@mui/material';
|
||||
import { Alert, AlertTitle, Box, Button, Typography } from '@mui/material';
|
||||
import { TransactionExecuteResult } from '@nymproject/types';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { DelegateForm } from './DelegateForm';
|
||||
import { NymCard } from '../../components';
|
||||
import { EnumRequestStatus, RequestStatus } from '../../components/RequestStatus';
|
||||
@@ -76,9 +77,11 @@ export const Delegate = () => {
|
||||
</NymCard>
|
||||
<Typography sx={{ p: 3 }}>
|
||||
Checkout the{' '}
|
||||
<Link href={`${urls(network).networkExplorer}/network-components/mixnodes`} target="_blank">
|
||||
list of mixnodes
|
||||
</Link>{' '}
|
||||
<Link
|
||||
href={`${urls(network).networkExplorer}/network-components/mixnodes`}
|
||||
target="_blank"
|
||||
text="list of mixnodes"
|
||||
/>{' '}
|
||||
for uptime and performances to help make delegation decisions
|
||||
</Typography>
|
||||
</PageLayout>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { FC, useContext, useEffect, useState } from 'react';
|
||||
import { Box, Button, Link, Paper, Stack, Typography } from '@mui/material';
|
||||
import { Box, Button, Paper, Stack, Typography } from '@mui/material';
|
||||
import { DelegationWithEverything, MajorCurrencyAmount } from '@nymproject/types';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { AppContext, urls } from 'src/context/main';
|
||||
import { DelegationList } from 'src/components/Delegation/DelegationList';
|
||||
import { PendingEvents } from 'src/components/Delegation/PendingEvents';
|
||||
@@ -273,13 +274,8 @@ export const Delegation: FC = () => {
|
||||
href={`${urls(network).networkExplorer}/network-components/mixnodes/`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
underline="hover"
|
||||
sx={{ color: 'primary.main', textDecorationColor: 'primary.main' }}
|
||||
>
|
||||
<Typography color="primary.main" variant="body2">
|
||||
Network Explorer
|
||||
</Typography>
|
||||
</Link>
|
||||
text="Network Explorer"
|
||||
/>
|
||||
</Box>
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<RewardsSummary isLoading={isLoading} totalDelegation={totalDelegations} totalRewards={totalRewards} />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Box, CircularProgress, Link, Typography } from '@mui/material';
|
||||
import { Box, CircularProgress, Typography } from '@mui/material';
|
||||
import { TransactionDetails as TTransactionDetails } from '@nymproject/types';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { SendError } from './SendError';
|
||||
import { AppContext, urls } from '../../context/main';
|
||||
import { SuccessReponse } from '../../components';
|
||||
@@ -38,9 +39,11 @@ export const SendConfirmation = ({
|
||||
subtitle={
|
||||
<>
|
||||
Check the transaction hash{' '}
|
||||
<Link href={`${urls(network).blockExplorer}/transactions/${data.tx_hash}`} target="_blank">
|
||||
here
|
||||
</Link>
|
||||
<Link
|
||||
href={`${urls(network).blockExplorer}/transactions/${data.tx_hash}`}
|
||||
target="_blank"
|
||||
text="here"
|
||||
/>
|
||||
</>
|
||||
}
|
||||
caption={
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { OpenInNew } from '@mui/icons-material';
|
||||
import { Button, Link, Stack, Typography } from '@mui/material';
|
||||
import { Stack, Typography } from '@mui/material';
|
||||
import { Link } from '@nymproject/react/link/Link';
|
||||
import { urls, AppContext } from '../../context/main';
|
||||
|
||||
export const NodeStats = ({ mixnodeId }: { mixnodeId?: string }) => {
|
||||
@@ -8,9 +8,11 @@ export const NodeStats = ({ mixnodeId }: { mixnodeId?: string }) => {
|
||||
return (
|
||||
<Stack spacing={2} sx={{ p: 4 }}>
|
||||
<Typography>All your node stats are available on the link below</Typography>
|
||||
<Link href={`${urls(network).networkExplorer}/network-components/mixnode/${mixnodeId}`} target="_blank">
|
||||
<Button endIcon={<OpenInNew />}>Network Explorer</Button>
|
||||
</Link>
|
||||
<Link
|
||||
href={`${urls(network).networkExplorer}/network-components/mixnode/${mixnodeId}`}
|
||||
target="_blank"
|
||||
text="Network Explorer"
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
Vendored
+1
@@ -38,6 +38,7 @@ declare module '@mui/material/styles' {
|
||||
dark: string;
|
||||
muted: string;
|
||||
};
|
||||
linkHover: string;
|
||||
}
|
||||
|
||||
interface NymPaletteVariant {
|
||||
|
||||
@@ -30,6 +30,7 @@ const nymPalette: NymPalette = {
|
||||
dark: '#121726',
|
||||
muted: '#7D7D7D',
|
||||
},
|
||||
linkHover: '#AF4D36',
|
||||
};
|
||||
|
||||
const darkMode: NymPaletteVariant = {
|
||||
@@ -235,6 +236,11 @@ export const getDesignTokens = (mode: PaletteMode): ThemeOptions => {
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiLink: {
|
||||
defaultProps: {
|
||||
underline: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
palette,
|
||||
};
|
||||
|
||||
+3
-1
@@ -13,7 +13,9 @@
|
||||
"clean": "lerna run clean",
|
||||
"build": "run-s build:types build:packages",
|
||||
"build:types": "lerna run --scope @nymproject/types build --stream",
|
||||
"build:packages": "lerna run --scope @nymproject/mui-theme --scope @nymproject/react --stream build",
|
||||
"build:packages": "run-s build:packages:theme build:packages:react",
|
||||
"build:packages:theme": "lerna run --scope @nymproject/mui-theme build",
|
||||
"build:packages:react": "lerna run --scope @nymproject/react build",
|
||||
"build:react-example": "lerna run --scope @nymproject/react-webpack-with-theme-example build --stream",
|
||||
"build:playground": "lerna run --scope @nymproject/react storybook:build --stream",
|
||||
"build:ci": "yarn build && run-p build:react-example build:playground && yarn build:ci:collect-artifacts",
|
||||
|
||||
@@ -10,6 +10,7 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-trait = { version = "0.1.51" }
|
||||
clap = "2.33.0"
|
||||
dirs = "3.0"
|
||||
futures = "0.3"
|
||||
@@ -22,7 +23,7 @@ reqwest = { version = "0.11", features = ["json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "chrono"]}
|
||||
thiserror = "1"
|
||||
tokio = { version = "1.19.1", features = [ "net", "rt-multi-thread", "macros", "time" ] }
|
||||
tokio = { version = "1.19.1", features = [ "net", "rt-multi-thread", "macros" ] }
|
||||
tokio-tungstenite = "0.14"
|
||||
|
||||
|
||||
@@ -32,5 +33,5 @@ nymsphinx = { path = "../../common/nymsphinx" }
|
||||
ordered-buffer = {path = "../../common/socks5/ordered-buffer"}
|
||||
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
||||
socks5-requests = { path = "../../common/socks5/requests" }
|
||||
statistics = { path = "../../common/statistics" }
|
||||
statistics-common = { path = "../../common/statistics" }
|
||||
websocket-requests = { path = "../../clients/native/websocket-requests" }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::allowed_hosts::{HostsStore, OutboundRequestFilter};
|
||||
use crate::connection::Connection;
|
||||
use crate::statistics::{StatisticsCollector, StatisticsSender, Timer};
|
||||
use crate::statistics::ServiceStatisticsCollector;
|
||||
use crate::websocket;
|
||||
use crate::websocket::TSWebsocketStream;
|
||||
use futures::channel::mpsc;
|
||||
@@ -14,6 +14,7 @@ use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::receiver::ReconstructedMessage;
|
||||
use proxy_helpers::connection_controller::{Controller, ControllerCommand, ControllerSender};
|
||||
use socks5_requests::{ConnectionId, Message as Socks5Message, Request, Response};
|
||||
use statistics_common::collector::StatisticsSender;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use tokio_tungstenite::tungstenite::protocol::Message;
|
||||
@@ -63,7 +64,7 @@ impl ServiceProvider {
|
||||
async fn mixnet_response_listener(
|
||||
mut websocket_writer: SplitSink<TSWebsocketStream, Message>,
|
||||
mut mix_reader: mpsc::UnboundedReceiver<(Socks5Message, Recipient)>,
|
||||
stats_collector: Option<StatisticsCollector>,
|
||||
stats_collector: Option<ServiceStatisticsCollector>,
|
||||
) {
|
||||
// TODO: wire SURBs in here once they're available
|
||||
while let Some((msg, return_address)) = mix_reader.next().await {
|
||||
@@ -228,7 +229,7 @@ impl ServiceProvider {
|
||||
raw_request: &[u8],
|
||||
controller_sender: &mut ControllerSender,
|
||||
mix_input_sender: &mpsc::UnboundedSender<(Socks5Message, Recipient)>,
|
||||
stats_collector: Option<StatisticsCollector>,
|
||||
stats_collector: Option<ServiceStatisticsCollector>,
|
||||
) {
|
||||
let deserialized_msg = match Socks5Message::try_from_bytes(raw_request) {
|
||||
Ok(msg) => msg,
|
||||
@@ -290,12 +291,6 @@ impl ServiceProvider {
|
||||
let (mix_input_sender, mix_input_receiver) =
|
||||
mpsc::unbounded::<(Socks5Message, Recipient)>();
|
||||
|
||||
let (mut timer_sender, timer_receiver) = Timer::new();
|
||||
let interval = timer_sender.interval();
|
||||
tokio::spawn(async move {
|
||||
timer_sender.run().await;
|
||||
});
|
||||
|
||||
// controller for managing all active connections
|
||||
let (mut active_connections_controller, mut controller_sender) = Controller::new();
|
||||
tokio::spawn(async move {
|
||||
@@ -303,15 +298,14 @@ impl ServiceProvider {
|
||||
});
|
||||
|
||||
let stats_collector = if self.enable_statistics {
|
||||
let mut stats_sender =
|
||||
StatisticsSender::new(interval, timer_receiver, self.stats_provider_addr)
|
||||
let stats_collector =
|
||||
ServiceStatisticsCollector::new(self.stats_provider_addr, mix_input_sender.clone())
|
||||
.await
|
||||
.expect("Statistics controller could not be bootstrapped");
|
||||
let stats_collector = StatisticsCollector::from(&stats_sender);
|
||||
.expect("Service statistics collector could not be bootstrapped");
|
||||
let mut stats_sender = StatisticsSender::new(stats_collector.clone());
|
||||
|
||||
let mix_input_sender_clone = mix_input_sender.clone();
|
||||
tokio::spawn(async move {
|
||||
stats_sender.run(&mix_input_sender_clone).await;
|
||||
stats_sender.run().await;
|
||||
});
|
||||
Some(stats_collector)
|
||||
} else {
|
||||
|
||||
@@ -36,7 +36,7 @@ fn parse_args<'a>() -> ArgMatches<'a> {
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(ENABLE_STATISTICS)
|
||||
.help("enable service statistics that get sent to a statistics aggregator server")
|
||||
.help("enable service anonymized statistics that get sent to a statistics aggregator server")
|
||||
.long(ENABLE_STATISTICS),
|
||||
)
|
||||
.arg(
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures::channel::mpsc;
|
||||
use log::*;
|
||||
use rand::RngCore;
|
||||
use serde::Deserialize;
|
||||
use sqlx::types::chrono::{DateTime, Utc};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use network_defaults::DEFAULT_NETWORK;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use ordered_buffer::OrderedMessageSender;
|
||||
use socks5_requests::{ConnectionId, Message as Socks5Message, RemoteAddress, Request};
|
||||
use statistics_common::api::{
|
||||
build_statistics_request_bytes, DEFAULT_STATISTICS_SERVICE_ADDRESS,
|
||||
DEFAULT_STATISTICS_SERVICE_PORT,
|
||||
};
|
||||
use statistics_common::{
|
||||
collector::StatisticsCollector, error::StatsError as CommonStatsError, StatsMessage,
|
||||
StatsServiceData,
|
||||
};
|
||||
|
||||
use super::error::StatsError;
|
||||
|
||||
const REMOTE_SOURCE_OF_STATS_PROVIDER_CONFIG: &str =
|
||||
"https://nymtech.net/.wellknown/network-requester/stats-provider.json";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct StatsData {
|
||||
client_processed_bytes: HashMap<String, u32>,
|
||||
}
|
||||
|
||||
impl StatsData {
|
||||
pub fn new() -> Self {
|
||||
StatsData {
|
||||
client_processed_bytes: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn processed(&mut self, remote_addr: &str, bytes: u32) {
|
||||
if let Some(curr_bytes) = self.client_processed_bytes.get_mut(remote_addr) {
|
||||
*curr_bytes += bytes;
|
||||
} else {
|
||||
self.client_processed_bytes
|
||||
.insert(remote_addr.to_string(), bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct StatsProviderConfigEntry {
|
||||
stats_client_address: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct OptionalStatsProviderConfig {
|
||||
mainnet: Option<StatsProviderConfigEntry>,
|
||||
sandbox: Option<StatsProviderConfigEntry>,
|
||||
qa: Option<StatsProviderConfigEntry>,
|
||||
}
|
||||
|
||||
impl OptionalStatsProviderConfig {
|
||||
pub fn stats_client_address(&self) -> Option<String> {
|
||||
let entry_config = match DEFAULT_NETWORK {
|
||||
network_defaults::all::Network::MAINNET => self.mainnet.clone(),
|
||||
network_defaults::all::Network::SANDBOX => self.sandbox.clone(),
|
||||
network_defaults::all::Network::QA => self.qa.clone(),
|
||||
};
|
||||
entry_config.map(|e| e.stats_client_address)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ServiceStatisticsCollector {
|
||||
pub(crate) request_stats_data: Arc<RwLock<StatsData>>,
|
||||
pub(crate) response_stats_data: Arc<RwLock<StatsData>>,
|
||||
pub(crate) connected_services: Arc<RwLock<HashMap<ConnectionId, RemoteAddress>>>,
|
||||
stats_provider_addr: Recipient,
|
||||
mix_input_sender: mpsc::UnboundedSender<(Socks5Message, Recipient)>,
|
||||
}
|
||||
|
||||
impl ServiceStatisticsCollector {
|
||||
pub async fn new(
|
||||
stats_provider_addr: Option<Recipient>,
|
||||
mix_input_sender: mpsc::UnboundedSender<(Socks5Message, Recipient)>,
|
||||
) -> Result<Self, StatsError> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(3))
|
||||
.build()?;
|
||||
let stats_provider_config: OptionalStatsProviderConfig = client
|
||||
.get(REMOTE_SOURCE_OF_STATS_PROVIDER_CONFIG.to_string())
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
let stats_provider_addr = stats_provider_addr.unwrap_or(
|
||||
Recipient::try_from_base58_string(
|
||||
stats_provider_config
|
||||
.stats_client_address()
|
||||
.ok_or(StatsError::InvalidClientAddress)?,
|
||||
)
|
||||
.map_err(|_| StatsError::InvalidClientAddress)?,
|
||||
);
|
||||
|
||||
Ok(ServiceStatisticsCollector {
|
||||
request_stats_data: Arc::new(RwLock::new(StatsData::new())),
|
||||
response_stats_data: Arc::new(RwLock::new(StatsData::new())),
|
||||
connected_services: Arc::new(RwLock::new(HashMap::new())),
|
||||
stats_provider_addr,
|
||||
mix_input_sender,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl StatisticsCollector for ServiceStatisticsCollector {
|
||||
async fn create_stats_message(
|
||||
&self,
|
||||
interval: Duration,
|
||||
timestamp: DateTime<Utc>,
|
||||
) -> StatsMessage {
|
||||
let stats_data = {
|
||||
let request_data_bytes = self.request_stats_data.read().await;
|
||||
let response_data_bytes = self.response_stats_data.read().await;
|
||||
let services: HashSet<String> = request_data_bytes
|
||||
.client_processed_bytes
|
||||
.keys()
|
||||
.chain(response_data_bytes.client_processed_bytes.keys())
|
||||
.cloned()
|
||||
.collect();
|
||||
services
|
||||
.into_iter()
|
||||
.map(|requested_service| {
|
||||
let request_bytes = request_data_bytes
|
||||
.client_processed_bytes
|
||||
.get(&requested_service)
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
let response_bytes = response_data_bytes
|
||||
.client_processed_bytes
|
||||
.get(&requested_service)
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
statistics_common::StatsData::Service(StatsServiceData::new(
|
||||
requested_service,
|
||||
request_bytes,
|
||||
response_bytes,
|
||||
))
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
StatsMessage {
|
||||
stats_data,
|
||||
interval_seconds: interval.as_secs() as u32,
|
||||
timestamp: timestamp.to_rfc3339(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_stats_message(
|
||||
&self,
|
||||
stats_message: StatsMessage,
|
||||
) -> Result<(), CommonStatsError> {
|
||||
let msg = build_statistics_request_bytes(stats_message)?;
|
||||
|
||||
trace!("Connecting to statistics service");
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
let conn_id = rng.next_u64();
|
||||
let connect_req = Request::new_connect(
|
||||
conn_id,
|
||||
format!(
|
||||
"{}:{}",
|
||||
DEFAULT_STATISTICS_SERVICE_ADDRESS, DEFAULT_STATISTICS_SERVICE_PORT
|
||||
),
|
||||
self.stats_provider_addr,
|
||||
);
|
||||
self.mix_input_sender
|
||||
.unbounded_send((
|
||||
Socks5Message::Request(connect_req),
|
||||
self.stats_provider_addr,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
trace!("Sending data to statistics service");
|
||||
let mut message_sender = OrderedMessageSender::new();
|
||||
let ordered_msg = message_sender.wrap_message(msg).into_bytes();
|
||||
let send_req = Request::new_send(conn_id, ordered_msg, true);
|
||||
self.mix_input_sender
|
||||
.unbounded_send((Socks5Message::Request(send_req), self.stats_provider_addr))
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn reset_stats(&mut self) {
|
||||
self.request_stats_data
|
||||
.write()
|
||||
.await
|
||||
.client_processed_bytes
|
||||
.iter_mut()
|
||||
.for_each(|(_, b)| *b = 0);
|
||||
self.response_stats_data
|
||||
.write()
|
||||
.await
|
||||
.client_processed_bytes
|
||||
.iter_mut()
|
||||
.for_each(|(_, b)| *b = 0);
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use rand::RngCore;
|
||||
use serde::Deserialize;
|
||||
use sqlx::types::chrono::{DateTime, Utc};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use network_defaults::DEFAULT_NETWORK;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use ordered_buffer::OrderedMessageSender;
|
||||
use socks5_requests::{ConnectionId, Message as Socks5Message, RemoteAddress, Request};
|
||||
use statistics::api::{
|
||||
build_statistics_request_bytes, DEFAULT_STATISTICS_SERVICE_ADDRESS,
|
||||
DEFAULT_STATISTICS_SERVICE_PORT,
|
||||
};
|
||||
use statistics::{StatsMessage, StatsServiceData};
|
||||
|
||||
use super::error::StatsError;
|
||||
|
||||
const REMOTE_SOURCE_OF_STATS_PROVIDER_CONFIG: &str =
|
||||
"https://nymtech.net/.wellknown/network-requester/stats-provider.json";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct StatsData {
|
||||
client_processed_bytes: HashMap<String, u32>,
|
||||
}
|
||||
|
||||
impl StatsData {
|
||||
pub fn new() -> Self {
|
||||
StatsData {
|
||||
client_processed_bytes: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn processed(&mut self, remote_addr: &str, bytes: u32) {
|
||||
if let Some(curr_bytes) = self.client_processed_bytes.get_mut(remote_addr) {
|
||||
*curr_bytes += bytes;
|
||||
} else {
|
||||
self.client_processed_bytes
|
||||
.insert(remote_addr.to_string(), bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct StatsProviderConfigEntry {
|
||||
stats_client_address: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct OptionalStatsProviderConfig {
|
||||
mainnet: Option<StatsProviderConfigEntry>,
|
||||
sandbox: Option<StatsProviderConfigEntry>,
|
||||
qa: Option<StatsProviderConfigEntry>,
|
||||
}
|
||||
|
||||
impl OptionalStatsProviderConfig {
|
||||
pub fn stats_client_address(&self) -> Option<String> {
|
||||
let entry_config = match DEFAULT_NETWORK {
|
||||
network_defaults::all::Network::MAINNET => self.mainnet.clone(),
|
||||
network_defaults::all::Network::SANDBOX => self.sandbox.clone(),
|
||||
network_defaults::all::Network::QA => self.qa.clone(),
|
||||
};
|
||||
entry_config.map(|e| e.stats_client_address)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StatisticsCollector {
|
||||
pub(crate) request_stats_data: Arc<RwLock<StatsData>>,
|
||||
pub(crate) response_stats_data: Arc<RwLock<StatsData>>,
|
||||
pub(crate) connected_services: Arc<RwLock<HashMap<ConnectionId, RemoteAddress>>>,
|
||||
}
|
||||
|
||||
impl StatisticsCollector {
|
||||
pub fn from(stats: &StatisticsSender) -> Self {
|
||||
Self {
|
||||
request_stats_data: Arc::clone(&stats.request_data),
|
||||
response_stats_data: Arc::clone(&stats.response_data),
|
||||
connected_services: Arc::new(RwLock::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StatisticsSender {
|
||||
request_data: Arc<RwLock<StatsData>>,
|
||||
response_data: Arc<RwLock<StatsData>>,
|
||||
interval_seconds: u32,
|
||||
timestamp: DateTime<Utc>,
|
||||
timer_receiver: mpsc::Receiver<()>,
|
||||
stats_provider_addr: Recipient,
|
||||
}
|
||||
|
||||
impl StatisticsSender {
|
||||
pub async fn new(
|
||||
interval_seconds: Duration,
|
||||
timer_receiver: mpsc::Receiver<()>,
|
||||
stats_provider_addr: Option<Recipient>,
|
||||
) -> Result<Self, StatsError> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(3))
|
||||
.build()?;
|
||||
let stats_provider_config: OptionalStatsProviderConfig = client
|
||||
.get(REMOTE_SOURCE_OF_STATS_PROVIDER_CONFIG.to_string())
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
let stats_provider_addr = stats_provider_addr.unwrap_or(
|
||||
Recipient::try_from_base58_string(
|
||||
stats_provider_config
|
||||
.stats_client_address()
|
||||
.ok_or(StatsError::InvalidClientAddress)?,
|
||||
)
|
||||
.map_err(|_| StatsError::InvalidClientAddress)?,
|
||||
);
|
||||
|
||||
Ok(StatisticsSender {
|
||||
request_data: Arc::new(RwLock::new(StatsData::new())),
|
||||
response_data: Arc::new(RwLock::new(StatsData::new())),
|
||||
timestamp: Utc::now(),
|
||||
interval_seconds: interval_seconds.as_secs() as u32,
|
||||
timer_receiver,
|
||||
stats_provider_addr,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn run(
|
||||
&mut self,
|
||||
mix_input_sender: &mpsc::UnboundedSender<(Socks5Message, Recipient)>,
|
||||
) {
|
||||
loop {
|
||||
if self.timer_receiver.next().await == None {
|
||||
error!("Timer thread has died. No more statistics will be sent");
|
||||
} else {
|
||||
let stats_data = {
|
||||
let request_data_bytes = self.request_data.read().await;
|
||||
let response_data_bytes = self.response_data.read().await;
|
||||
let services: HashSet<String> = request_data_bytes
|
||||
.client_processed_bytes
|
||||
.keys()
|
||||
.chain(response_data_bytes.client_processed_bytes.keys())
|
||||
.cloned()
|
||||
.collect();
|
||||
services
|
||||
.into_iter()
|
||||
.map(|requested_service| {
|
||||
let request_bytes = request_data_bytes
|
||||
.client_processed_bytes
|
||||
.get(&requested_service)
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
let response_bytes = response_data_bytes
|
||||
.client_processed_bytes
|
||||
.get(&requested_service)
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
StatsServiceData::new(requested_service, request_bytes, response_bytes)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
let stats_message = StatsMessage {
|
||||
stats_data,
|
||||
interval_seconds: self.interval_seconds,
|
||||
timestamp: self.timestamp.to_rfc3339(),
|
||||
};
|
||||
match build_statistics_request_bytes(stats_message) {
|
||||
Ok(data) => {
|
||||
trace!("Connecting to statistics service");
|
||||
let mut rng = rand::rngs::OsRng;
|
||||
let conn_id = rng.next_u64();
|
||||
let connect_req = Request::new_connect(
|
||||
conn_id,
|
||||
format!(
|
||||
"{}:{}",
|
||||
DEFAULT_STATISTICS_SERVICE_ADDRESS, DEFAULT_STATISTICS_SERVICE_PORT
|
||||
),
|
||||
self.stats_provider_addr,
|
||||
);
|
||||
mix_input_sender
|
||||
.unbounded_send((
|
||||
Socks5Message::Request(connect_req),
|
||||
self.stats_provider_addr,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
trace!("Sending data to statistics service");
|
||||
let mut message_sender = OrderedMessageSender::new();
|
||||
let ordered_msg = message_sender.wrap_message(data).into_bytes();
|
||||
let send_req = Request::new_send(conn_id, ordered_msg, true);
|
||||
mix_input_sender
|
||||
.unbounded_send((
|
||||
Socks5Message::Request(send_req),
|
||||
self.stats_provider_addr,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
Err(e) => error!("Statistics not sent: {}", e),
|
||||
}
|
||||
self.reset_stats().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn reset_stats(&mut self) {
|
||||
self.request_data
|
||||
.write()
|
||||
.await
|
||||
.client_processed_bytes
|
||||
.iter_mut()
|
||||
.for_each(|(_, b)| *b = 0);
|
||||
self.response_data
|
||||
.write()
|
||||
.await
|
||||
.client_processed_bytes
|
||||
.iter_mut()
|
||||
.for_each(|(_, b)| *b = 0);
|
||||
self.timestamp = Utc::now();
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,7 @@ pub enum StatsError {
|
||||
|
||||
#[error("Invalid stats provider client address")]
|
||||
InvalidClientAddress,
|
||||
|
||||
#[error("Common statistics error {0}")]
|
||||
CommonError(#[from] statistics_common::error::StatsError),
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod comm;
|
||||
mod collector;
|
||||
mod error;
|
||||
mod timer;
|
||||
|
||||
pub use comm::{StatisticsCollector, StatisticsSender, StatsData};
|
||||
pub use timer::Timer;
|
||||
pub use collector::ServiceStatisticsCollector;
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use std::time::Duration;
|
||||
use tokio::time;
|
||||
|
||||
const STATISTICS_INTERVAL: Duration = Duration::from_secs(60);
|
||||
|
||||
pub type TimerReceiver = mpsc::Receiver<()>;
|
||||
|
||||
pub struct Timer {
|
||||
interval: Duration,
|
||||
stats_sender: mpsc::Sender<()>,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
pub fn new() -> (Self, TimerReceiver) {
|
||||
let (stats_sender, stats_receiver) = mpsc::channel::<()>(1);
|
||||
(
|
||||
Timer {
|
||||
interval: STATISTICS_INTERVAL,
|
||||
stats_sender,
|
||||
},
|
||||
stats_receiver,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn interval(&self) -> Duration {
|
||||
self.interval
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) {
|
||||
let mut interval = time::interval(self.interval);
|
||||
loop {
|
||||
interval.tick().await;
|
||||
let _ = self.stats_sender.try_send(());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,14 @@ edition = "2021"
|
||||
dirs = "3.0"
|
||||
log = "0.4"
|
||||
pretty_env_logger = "0.4"
|
||||
rocket = { version = "0.5.0-rc.1", features = ["json"] }
|
||||
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "chrono"]}
|
||||
thiserror = "1"
|
||||
tokio = { version = "1.4", features = [ "net", "rt-multi-thread", "macros", "time" ] }
|
||||
tokio-tungstenite = "0.14"
|
||||
|
||||
statistics = { path = "../../common/statistics" }
|
||||
statistics-common = { path = "../../common/statistics" }
|
||||
|
||||
[build-dependencies]
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
|
||||
@@ -11,4 +11,11 @@ CREATE TABLE service_statistics
|
||||
response_processed_bytes INTEGER NOT NULL,
|
||||
interval_seconds INTEGER NOT NULL,
|
||||
timestamp DATETIME NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE gateway_statistics
|
||||
(
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
inbox_count INTEGER NOT NULL,
|
||||
timestamp DATETIME NOT NULL
|
||||
);
|
||||
@@ -8,7 +8,7 @@ use crate::storage::NetworkStatisticsStorage;
|
||||
use error::Result;
|
||||
use routes::{post_all_statistics, post_statistic};
|
||||
|
||||
use statistics::api::STATISTICS_SERVICE_VERSION;
|
||||
use statistics_common::api::STATISTICS_SERVICE_VERSION;
|
||||
|
||||
mod error;
|
||||
mod routes;
|
||||
|
||||
@@ -5,19 +5,25 @@ use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use statistics::StatsMessage;
|
||||
use statistics_common::StatsMessage;
|
||||
|
||||
use crate::api::error::Result;
|
||||
use crate::storage::NetworkStatisticsStorage;
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct ServiceStatisticsRequest {
|
||||
pub struct StatisticsRequest {
|
||||
// date, RFC 3339 format
|
||||
since: String,
|
||||
// date, RFC 3339 format
|
||||
until: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub enum GenericStatistic {
|
||||
Service(ServiceStatistic),
|
||||
Gateway(GatewayStatistic),
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct ServiceStatistic {
|
||||
pub requested_service: String,
|
||||
@@ -27,28 +33,51 @@ pub struct ServiceStatistic {
|
||||
pub timestamp: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct GatewayStatistic {
|
||||
pub inbox_count: u32,
|
||||
pub timestamp: String,
|
||||
}
|
||||
|
||||
#[rocket::post("/all-statistics", data = "<all_statistics_request>")]
|
||||
pub(crate) async fn post_all_statistics(
|
||||
all_statistics_request: Json<ServiceStatisticsRequest>,
|
||||
all_statistics_request: Json<StatisticsRequest>,
|
||||
storage: &State<NetworkStatisticsStorage>,
|
||||
) -> Result<Json<Vec<ServiceStatistic>>> {
|
||||
let service_statistics = storage
|
||||
) -> Result<Json<Vec<GenericStatistic>>> {
|
||||
let all_statistics = storage
|
||||
.get_service_statistics_in_interval(
|
||||
&all_statistics_request.since,
|
||||
&all_statistics_request.until,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|data| ServiceStatistic {
|
||||
requested_service: data.requested_service,
|
||||
request_processed_bytes: data.request_processed_bytes as u32,
|
||||
response_processed_bytes: data.response_processed_bytes as u32,
|
||||
interval_seconds: data.interval_seconds as u32,
|
||||
timestamp: data.timestamp.to_string(),
|
||||
.map(|data| {
|
||||
GenericStatistic::Service(ServiceStatistic {
|
||||
requested_service: data.requested_service,
|
||||
request_processed_bytes: data.request_processed_bytes as u32,
|
||||
response_processed_bytes: data.response_processed_bytes as u32,
|
||||
interval_seconds: data.interval_seconds as u32,
|
||||
timestamp: data.timestamp.to_string(),
|
||||
})
|
||||
})
|
||||
.chain(
|
||||
storage
|
||||
.get_gateway_statistics_in_interval(
|
||||
&all_statistics_request.since,
|
||||
&all_statistics_request.until,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|data| {
|
||||
GenericStatistic::Gateway(GatewayStatistic {
|
||||
inbox_count: data.inbox_count as u32,
|
||||
timestamp: data.timestamp.to_string(),
|
||||
})
|
||||
}),
|
||||
)
|
||||
.collect();
|
||||
|
||||
Ok(Json(service_statistics))
|
||||
Ok(Json(all_statistics))
|
||||
}
|
||||
|
||||
#[rocket::post("/statistic", data = "<statistic>")]
|
||||
@@ -56,6 +85,6 @@ pub(crate) async fn post_statistic(
|
||||
statistic: Json<StatsMessage>,
|
||||
storage: &State<NetworkStatisticsStorage>,
|
||||
) -> Result<Json<()>> {
|
||||
storage.insert_service_statistics(statistic.0).await?;
|
||||
storage.insert_statistics(statistic.0).await?;
|
||||
Ok(Json(()))
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use sqlx::types::chrono::{DateTime, Utc};
|
||||
|
||||
use crate::storage::models::ServiceStatistics;
|
||||
use crate::storage::models::{GatewayStatistics, ServiceStatistics};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct StorageManager {
|
||||
@@ -12,7 +12,7 @@ pub(crate) struct StorageManager {
|
||||
|
||||
// all SQL goes here
|
||||
impl StorageManager {
|
||||
/// Adds an entry for some statistical data.
|
||||
/// Adds an entry for some service statistical data.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
@@ -20,6 +20,7 @@ impl StorageManager {
|
||||
/// * `request_processed_bytes`: Number of bytes for socks5 requests.
|
||||
/// * `response_processed_bytes`: Number of bytes for socks5 responses.
|
||||
/// * `interval_seconds`: Duration in seconds in which the data was gathered.
|
||||
/// * `timestamp`: The moment in time when the data started being collected.
|
||||
pub(super) async fn insert_service_statistics(
|
||||
&self,
|
||||
requested_service: String,
|
||||
@@ -42,7 +43,30 @@ impl StorageManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns data submitted within the provided time interval.
|
||||
/// Adds an entry for some gateway statistical data.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `inbox_count`: Number of clients of a gateway.
|
||||
/// * `interval_seconds`: Duration in seconds in which the data was gathered.
|
||||
/// * `timestamp`: The moment in time when the data started being collected.
|
||||
pub(super) async fn insert_gateway_statistics(
|
||||
&self,
|
||||
inbox_count: u32,
|
||||
timestamp: DateTime<Utc>,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO gateway_statistics(inbox_count, timestamp) VALUES (?, ?)",
|
||||
inbox_count,
|
||||
timestamp,
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns service statistical data submitted within the provided time interval.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
@@ -62,4 +86,25 @@ impl StorageManager {
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns gateway statistical data submitted within the provided time interval.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `since`: indicates the lower bound timestamp for the data
|
||||
/// * `until`: indicates the upper bound timestamp for the data
|
||||
pub(super) async fn get_gateway_statistics_in_interval(
|
||||
&self,
|
||||
since: DateTime<Utc>,
|
||||
until: DateTime<Utc>,
|
||||
) -> Result<Vec<GatewayStatistics>, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
GatewayStatistics,
|
||||
"SELECT * FROM gateway_statistics WHERE timestamp BETWEEN ? AND ?",
|
||||
since,
|
||||
until
|
||||
)
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ use sqlx::types::chrono::{DateTime, Utc};
|
||||
use sqlx::ConnectOptions;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use statistics::StatsMessage;
|
||||
use statistics_common::StatsMessage;
|
||||
|
||||
use crate::storage::error::NetworkStatisticsStorageError;
|
||||
use crate::storage::manager::StorageManager;
|
||||
use crate::storage::models::ServiceStatistics;
|
||||
use crate::storage::models::{GatewayStatistics, ServiceStatistics};
|
||||
|
||||
pub(crate) mod error;
|
||||
mod manager;
|
||||
@@ -49,29 +49,38 @@ impl NetworkStatisticsStorage {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `msg`: Message containing the statistical data.
|
||||
pub(super) async fn insert_service_statistics(
|
||||
pub(super) async fn insert_statistics(
|
||||
&self,
|
||||
msg: StatsMessage,
|
||||
) -> Result<(), NetworkStatisticsStorageError> {
|
||||
let timestamp: DateTime<Utc> = DateTime::parse_from_rfc3339(&msg.timestamp)
|
||||
.map_err(|_| NetworkStatisticsStorageError::TimestampParse)?
|
||||
.into();
|
||||
for service_data in msg.stats_data {
|
||||
self.manager
|
||||
.insert_service_statistics(
|
||||
service_data.requested_service.clone(),
|
||||
service_data.request_bytes,
|
||||
service_data.response_bytes,
|
||||
msg.interval_seconds,
|
||||
timestamp,
|
||||
)
|
||||
.await?;
|
||||
for stats_data in msg.stats_data {
|
||||
match stats_data {
|
||||
statistics_common::StatsData::Service(service_data) => {
|
||||
self.manager
|
||||
.insert_service_statistics(
|
||||
service_data.requested_service.clone(),
|
||||
service_data.request_bytes,
|
||||
service_data.response_bytes,
|
||||
msg.interval_seconds,
|
||||
timestamp,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
statistics_common::StatsData::Gateway(gateway_data) => {
|
||||
self.manager
|
||||
.insert_gateway_statistics(gateway_data.inbox_count, timestamp)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns data submitted within the provided time interval.
|
||||
/// Returns service data submitted within the provided time interval.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
@@ -93,4 +102,27 @@ impl NetworkStatisticsStorage {
|
||||
.get_service_statistics_in_interval(since, until)
|
||||
.await?)
|
||||
}
|
||||
|
||||
/// Returns gateway data submitted within the provided time interval.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `since`: indicates the lower bound timestamp for the data, RFC 3339 format
|
||||
/// * `until`: indicates the upper bound timestamp for the data, RFC 3339 format
|
||||
pub(super) async fn get_gateway_statistics_in_interval(
|
||||
&self,
|
||||
since: &str,
|
||||
until: &str,
|
||||
) -> Result<Vec<GatewayStatistics>, NetworkStatisticsStorageError> {
|
||||
let since = DateTime::parse_from_rfc3339(since)
|
||||
.map_err(|_| NetworkStatisticsStorageError::TimestampParse)?
|
||||
.into();
|
||||
let until = DateTime::parse_from_rfc3339(until)
|
||||
.map_err(|_| NetworkStatisticsStorageError::TimestampParse)?
|
||||
.into();
|
||||
Ok(self
|
||||
.manager
|
||||
.get_gateway_statistics_in_interval(since, until)
|
||||
.await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,3 +13,10 @@ pub(crate) struct ServiceStatistics {
|
||||
pub(crate) interval_seconds: i64,
|
||||
pub(crate) timestamp: NaiveDateTime,
|
||||
}
|
||||
|
||||
pub(crate) struct GatewayStatistics {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) id: i64,
|
||||
pub(crate) inbox_count: i64,
|
||||
pub(crate) timestamp: NaiveDateTime,
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ export interface NymPalette {
|
||||
muted: {
|
||||
onDarkBg: string;
|
||||
};
|
||||
|
||||
linkHover: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,6 +81,8 @@ export const nymPalette: NymPalette = {
|
||||
muted: {
|
||||
onDarkBg: '#666B77',
|
||||
},
|
||||
|
||||
linkHover: '#AF4D36',
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -126,6 +126,11 @@ export const getDesignTokens = (mode: PaletteMode): ThemeOptions => {
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiLink: {
|
||||
defaultProps: {
|
||||
underline: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
palette,
|
||||
};
|
||||
|
||||
@@ -50,3 +50,9 @@ This project uses [shared asset files](../../assets/README.md) such as favicons
|
||||
## Publishing
|
||||
|
||||
This package is not published to NPM ... yet.
|
||||
|
||||
## TODO
|
||||
|
||||
- ugprade to React 18
|
||||
- upgrade Storybook
|
||||
- upgrade MUI
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import { ComponentMeta } from '@storybook/react';
|
||||
import { Typography } from '@mui/material';
|
||||
import { Link as LinkIcon } from '@mui/icons-material';
|
||||
import { Link } from './Link';
|
||||
|
||||
export default {
|
||||
title: 'Basics/Link',
|
||||
component: Link,
|
||||
} as ComponentMeta<typeof Link>;
|
||||
|
||||
export const Default = () => <Link text="link" href="https://nymtech.net/" target="_blank" />;
|
||||
|
||||
export const NoIcon = () => <Link text="link" href="https://nymtech.net/" target="_blank" noIcon />;
|
||||
|
||||
export const WithCustomChildren = () => (
|
||||
<Link href="https://nymtech.net/" target="_blank">
|
||||
<LinkIcon />
|
||||
</Link>
|
||||
);
|
||||
|
||||
export const InTextExample = () => (
|
||||
<Typography>
|
||||
You can find the Nym website <Link href="https://nymtech.net/" target="_blank" text="here" />.
|
||||
</Typography>
|
||||
);
|
||||
@@ -0,0 +1,42 @@
|
||||
import * as React from 'react';
|
||||
import { Box, Typography, Link as MUILink, LinkProps as MUILinkProps } from '@mui/material';
|
||||
import { OpenInNew } from '@mui/icons-material';
|
||||
|
||||
export interface LinkProps {
|
||||
text?: string;
|
||||
icon?: React.ReactNode;
|
||||
noIcon?: boolean;
|
||||
}
|
||||
|
||||
export const Link = (props: MUILinkProps & LinkProps) => {
|
||||
const { text, icon, underline, noIcon, children } = props;
|
||||
let typoProps = {};
|
||||
if (!noIcon) {
|
||||
typoProps = { mr: 0.5 };
|
||||
}
|
||||
return (
|
||||
<MUILink
|
||||
{...props}
|
||||
sx={{
|
||||
display: 'inline-block',
|
||||
':hover': {
|
||||
color: (theme) => theme.palette.nym.linkHover,
|
||||
},
|
||||
}}
|
||||
underline={underline || 'none'}
|
||||
>
|
||||
{children || (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexFlow: 'row nowrap',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ ...typoProps, fontWeight: 400 }}>{text}</Typography>
|
||||
{!noIcon && (icon || <OpenInNew fontSize="inherit" />)}
|
||||
</Box>
|
||||
)}
|
||||
</MUILink>
|
||||
);
|
||||
};
|
||||
@@ -27,7 +27,7 @@ pretty_env_logger = "0.4"
|
||||
rand = "0.8"
|
||||
rand-07 = { package = "rand", version = "0.7" } # required for compatibility
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
rocket = { version = "0.5.0-rc.1", features = ["json"] }
|
||||
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||
rocket_cors = { git="https://github.com/lawliet89/rocket_cors", rev="dfd3662c49e2f6fc37df35091cb94d82f7fb5915" }
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
@@ -42,11 +42,11 @@ ts-rs = "6.1.2"
|
||||
anyhow = "1"
|
||||
getset = "0.1.1"
|
||||
|
||||
rocket_sync_db_pools = { version = "0.1.0-rc.1", default-features = false }
|
||||
rocket_sync_db_pools = { version = "0.1.0-rc.2", default-features = false }
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]}
|
||||
|
||||
okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] }
|
||||
rocket_okapi = { version = "0.8.0-rc.1", features = ["swagger"] }
|
||||
rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] }
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
|
||||
## internal
|
||||
|
||||
Reference in New Issue
Block a user