Compare commits
153 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d0bc8ab9e | |||
| 21611a9434 | |||
| 78d9030567 | |||
| f3c16476f9 | |||
| 4678059eaf | |||
| a69d4bb457 | |||
| 134ab2f0d6 | |||
| 1a4e7433b2 | |||
| 9d91a018b2 | |||
| ae9c1b4070 | |||
| adcd8703d3 | |||
| aee4c2d80d | |||
| 5e04f48500 | |||
| a7610a7a88 | |||
| 28e6e9140c | |||
| 271a126556 | |||
| 7da444c7a4 | |||
| 2a7fd71416 | |||
| 6ffd211e51 | |||
| f61ed48240 | |||
| 8d3dc405a5 | |||
| 21bf0fb27b | |||
| 7313f56c01 | |||
| cfef9e96e6 | |||
| 8b9a5cf500 | |||
| ce94ce058f | |||
| 018e4d6079 | |||
| ebadd9799f | |||
| 268fa02b83 | |||
| 4d6d2f0d33 | |||
| c7f8b05604 | |||
| 2878e9be9d | |||
| 7b419c2b12 | |||
| 0049126a91 | |||
| 80c5194d8b | |||
| 27a6b99453 | |||
| 61982de511 | |||
| efd9883197 | |||
| ce4ae8d90c | |||
| 1d2722f994 | |||
| 7e109e7f2d | |||
| f7a0b305df | |||
| 746ec71a0d | |||
| 41a63a0985 | |||
| 5a149c5492 | |||
| 7ff0becf72 | |||
| 4c2967a733 | |||
| 2b80e5d1c9 | |||
| 4e7ff53214 | |||
| 9ec36e49b7 | |||
| cdfa5ee540 | |||
| 71853f69f3 | |||
| dd13073037 | |||
| 1010df1077 | |||
| 9eaf9cf491 | |||
| 8e96318478 | |||
| bedff1f258 | |||
| 71a10a9a8b | |||
| 54287666e8 | |||
| 6de829163d | |||
| 605aed6f20 | |||
| adb5ed7c30 | |||
| 2b019e57df | |||
| 30c07712e3 | |||
| 82c92501d9 | |||
| c2a871a1a7 | |||
| dfd7bd5889 | |||
| 5aee4b1660 | |||
| 68a7bb67de | |||
| 7ff043d8df | |||
| 31e93428cf | |||
| 5f56b3eeea | |||
| e69b05693a | |||
| 8ae2451340 | |||
| b3b3279345 | |||
| 94a451c79b | |||
| ec7b959028 | |||
| 7061beea6e | |||
| e408162e26 | |||
| 25ae3895cb | |||
| 60296f2a41 | |||
| 3b97844310 | |||
| c904d245d2 | |||
| d315a2a91b | |||
| 0741d05ab3 | |||
| 61aa920362 | |||
| 24b9b17e64 | |||
| 5e3e633c4b | |||
| 8e6d3c34e2 | |||
| f3cff902ba | |||
| 4fe1b4c26f | |||
| cb24a08b06 | |||
| 597a53d11a | |||
| 639deeb502 | |||
| 03e082725e | |||
| 3e3307887e | |||
| d85683aaa8 | |||
| e89ed985fc | |||
| b1fb8bb18c | |||
| 45ebd7c37a | |||
| 3506020e55 | |||
| eca77d684b | |||
| e263a4a21f | |||
| dc4353c682 | |||
| e1d8069967 | |||
| 060726b7a3 | |||
| f72ccb0f0d | |||
| 2ff6bfbdd8 | |||
| d4f0b4772b | |||
| 17d258d094 | |||
| 8288d38257 | |||
| 1cd560c85e | |||
| 9018642992 | |||
| 04e652441e | |||
| ccf5990bc7 | |||
| 3d7c9ee2b8 | |||
| ad35c4006e | |||
| 2ec790e851 | |||
| 8f8cec0785 | |||
| 4a80cd301b | |||
| 63524eceff | |||
| a477b007e1 | |||
| 5b81510325 | |||
| 3bef973326 | |||
| bc5bb271d8 | |||
| 158e3cb073 | |||
| 36fb0eba29 | |||
| 1b37e85418 | |||
| 6a93497c8f | |||
| b8ee3465f8 | |||
| a7dfb36a84 | |||
| 14a7b5bdc8 | |||
| ffbd76539a | |||
| bdcc19e86a | |||
| 7929bac685 | |||
| f590aad42c | |||
| ec23f3dcb7 | |||
| 9644eb4329 | |||
| 7a4c6e4ed4 | |||
| d5ad504104 | |||
| d684f6d7ae | |||
| 1ba6444e72 | |||
| dc08b1170a | |||
| f0d9703587 | |||
| c08efef8ed | |||
| 6d29774744 | |||
| af6bab7703 | |||
| 7033f92d82 | |||
| 128dfa6d81 | |||
| 0b0ec075bb | |||
| cca4d21e7c | |||
| 4f59678ded | |||
| 8a1d2af3cf |
@@ -79,6 +79,9 @@ jobs:
|
|||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- name: Install wasm-opt
|
||||||
|
run: cargo install --version 0.112.0 wasm-opt
|
||||||
|
|
||||||
- name: Build release contracts
|
- name: Build release contracts
|
||||||
run: make wasm
|
run: make wasm
|
||||||
|
|
||||||
@@ -96,9 +99,14 @@ jobs:
|
|||||||
cp target/release/nym-network-statistics $OUTPUT_DIR
|
cp target/release/nym-network-statistics $OUTPUT_DIR
|
||||||
cp target/release/nym-cli $OUTPUT_DIR
|
cp target/release/nym-cli $OUTPUT_DIR
|
||||||
cp target/release/credential $OUTPUT_DIR
|
cp target/release/credential $OUTPUT_DIR
|
||||||
|
cp target/release/explorer-api $OUTPUT_DIR
|
||||||
|
|
||||||
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
|
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
|
||||||
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
|
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
|
||||||
|
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_bandwidth.wasm $OUTPUT_DIR
|
||||||
|
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
|
||||||
|
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
|
||||||
|
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
|
||||||
|
|
||||||
- name: Deploy branch to CI www
|
- name: Deploy branch to CI www
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/nym-contracts-') && github.event_name == 'release' }}
|
if: ${{ (startsWith(github.ref, 'refs/tags/nym-contracts-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||||
runs-on: [self-hosted, custom-runner-linux]
|
runs-on: [self-hosted, custom-runner-linux]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -19,6 +19,9 @@ jobs:
|
|||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- name: Install wasm-opt
|
||||||
|
run: cargo install --version 0.112.0 wasm-opt
|
||||||
|
|
||||||
- name: Build release contracts
|
- name: Build release contracts
|
||||||
run: make wasm
|
run: make wasm
|
||||||
|
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
name: CI for Network Explorer API
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
release:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
env:
|
|
||||||
NETWORK: mainnet
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish-nym:
|
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/nym-explorer-api-') && (github.event_name == 'release' || github.event_name == 'workflow_dispatch') }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
platform: [ubuntu-20.04]
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- 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 libudev-dev squashfs-tools
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Check the release tag starts with `nym-explorer-api-`
|
|
||||||
uses: actions/github-script@v3
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
core.setFailed('Release tag did not start with nym-explorer-api-...')
|
|
||||||
|
|
||||||
- name: Install Rust stable
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
|
|
||||||
- name: Build all explorer-api
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
args: --manifest-path explorer-api/Cargo.toml --workspace --release
|
|
||||||
|
|
||||||
- name: Upload Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: my-artifact
|
|
||||||
path: |
|
|
||||||
target/release/explorer-api
|
|
||||||
retention-days: 30
|
|
||||||
|
|
||||||
- name: Upload to release based on tag name
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
with:
|
|
||||||
files: |
|
|
||||||
target/release/explorer-api
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
name: Publish Nym CLI binaries
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
release:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
env:
|
|
||||||
NETWORK: mainnet
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish-nym-cli:
|
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/nym-cli-') && (github.event_name == 'release' || github.event_name == 'workflow_dispatch') }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
platform: [ubuntu-20.04, windows-latest, macos-latest]
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Check the release tag starts with `nym-cli-`
|
|
||||||
uses: actions/github-script@v3
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
core.setFailed('Release tag did not start with nym-cli-...')
|
|
||||||
|
|
||||||
- name: Install Rust stable
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
|
|
||||||
- name: Build binary
|
|
||||||
run: make build-nym-cli
|
|
||||||
|
|
||||||
- name: Upload Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: nym-cli-${{ matrix.platform }}
|
|
||||||
path: |
|
|
||||||
target/release/nym-cli*
|
|
||||||
retention-days: 30
|
|
||||||
|
|
||||||
- name: Upload to release based on tag name
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
with:
|
|
||||||
files: |
|
|
||||||
target/release/nym-cli
|
|
||||||
@@ -10,7 +10,7 @@ defaults:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-tauri:
|
publish-tauri:
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release' }}
|
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ defaults:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-tauri:
|
publish-tauri:
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release' }}
|
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-nym:
|
publish-nym:
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release' }}
|
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ defaults:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-tauri:
|
publish-tauri:
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release' }}
|
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ defaults:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-tauri:
|
publish-tauri:
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release' }}
|
if: ${{ (startsWith(github.ref, 'refs/tags/nym-wallet-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
@@ -39,7 +39,5 @@ validator-api-config.toml
|
|||||||
dist
|
dist
|
||||||
storybook-static
|
storybook-static
|
||||||
envs/qwerty.env
|
envs/qwerty.env
|
||||||
Cargo.lock
|
|
||||||
nym-connect/Cargo.lock
|
|
||||||
.parcel-cache
|
.parcel-cache
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
|
|||||||
@@ -4,6 +4,74 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [v1.1.14] (2023-04-04)
|
||||||
|
|
||||||
|
- Investigate cause of qwerty validator being in invalid rewarding state ([#3224])
|
||||||
|
- Fix NR config due to changes in #3199 ([#3223])
|
||||||
|
- [Issue] Mixnodes and gateway do not close connections properly ([#3187])
|
||||||
|
- disable sign-ext when using wasm-opt + update wasm-opt ([#3203])
|
||||||
|
- chore: tidy up client `Debug` config section ([#3199])
|
||||||
|
|
||||||
|
[#3224]: https://github.com/nymtech/nym/issues/3224
|
||||||
|
[#3223]: https://github.com/nymtech/nym/issues/3223
|
||||||
|
[#3187]: https://github.com/nymtech/nym/issues/3187
|
||||||
|
[#3203]: https://github.com/nymtech/nym/pull/3203
|
||||||
|
[#3199]: https://github.com/nymtech/nym/pull/3199
|
||||||
|
|
||||||
|
## [v1.1.13] (2023-03-15)
|
||||||
|
|
||||||
|
- NE - instead of throwing a "Mixnode/Gateway not found" error for blacklisted nodes due to bad performance, show their history but tag them as "Having poor performance" ([#2979])
|
||||||
|
- NE - Upgrade Sandbox and make below changes: ([#2332])
|
||||||
|
- Explorer - Updates ([#3168])
|
||||||
|
- Fix contracts and nym-api audit findings ([#3026])
|
||||||
|
- Website v2 - deploy infrastructure for strapi and CI ([#2213])
|
||||||
|
- add blockstream green to sp list ([#3180])
|
||||||
|
- mock-nym-api: fix .storybook lint error ([#3178])
|
||||||
|
- Validating new interval config parameters to prevent division by zero ([#3153])
|
||||||
|
|
||||||
|
[#2979]: https://github.com/nymtech/nym/issues/2979
|
||||||
|
[#2332]: https://github.com/nymtech/nym/issues/2332
|
||||||
|
[#3168]: https://github.com/nymtech/nym/issues/3168
|
||||||
|
[#3026]: https://github.com/nymtech/nym/issues/3026
|
||||||
|
[#2213]: https://github.com/nymtech/nym/issues/2213
|
||||||
|
[#3180]: https://github.com/nymtech/nym/pull/3180
|
||||||
|
[#3178]: https://github.com/nymtech/nym/pull/3178
|
||||||
|
[#3153]: https://github.com/nymtech/nym/pull/3153
|
||||||
|
|
||||||
|
## [v1.1.12] (2023-03-07)
|
||||||
|
|
||||||
|
- Fix generated docs for mixnet and vesting contract on docs.rs ([#3093])
|
||||||
|
- Introduce a way of injecting topology into the client ([#3044])
|
||||||
|
- Update mixnet TypeScript client methods #1 ([#2783])
|
||||||
|
- Update tooltips for routing and average score ([#3133])
|
||||||
|
- update selected service provider description style ([#3128])
|
||||||
|
|
||||||
|
[#3093]: https://github.com/nymtech/nym/issues/3093
|
||||||
|
[#3044]: https://github.com/nymtech/nym/issues/3044
|
||||||
|
[#2783]: https://github.com/nymtech/nym/issues/2783
|
||||||
|
[#3133]: https://github.com/nymtech/nym/pull/3133
|
||||||
|
[#3128]: https://github.com/nymtech/nym/pull/3128
|
||||||
|
|
||||||
|
## [v1.1.11] (2023-02-28)
|
||||||
|
|
||||||
|
- Fix empty dealer set loop ([#3105])
|
||||||
|
- The nym-api db.sqlite is broken when trying to run against it it in `enabled-credentials-mode true` there is an ordering issue with migrations when using the credential binary to purchase bandwidth ([#3100])
|
||||||
|
- Feature/latency based gateway selection ([#3081])
|
||||||
|
- Fix the credential binary to handle transactions to sleep when in non-inProgress epochs ([#3057])
|
||||||
|
- Publish mixnet contract to crates.io ([#1919])
|
||||||
|
- Publish vesting contract to crates.io ([#1920])
|
||||||
|
- Feature/update checker to use master ([#3097])
|
||||||
|
- Feature/improve binary checks ([#3094])
|
||||||
|
|
||||||
|
[#3105]: https://github.com/nymtech/nym/issues/3105
|
||||||
|
[#3100]: https://github.com/nymtech/nym/issues/3100
|
||||||
|
[#3081]: https://github.com/nymtech/nym/pull/3081
|
||||||
|
[#3057]: https://github.com/nymtech/nym/issues/3057
|
||||||
|
[#1919]: https://github.com/nymtech/nym/issues/1919
|
||||||
|
[#1920]: https://github.com/nymtech/nym/issues/1920
|
||||||
|
[#3097]: https://github.com/nymtech/nym/pull/3097
|
||||||
|
[#3094]: https://github.com/nymtech/nym/pull/3094
|
||||||
|
|
||||||
## [v1.1.10] (2023-02-21)
|
## [v1.1.10] (2023-02-21)
|
||||||
|
|
||||||
- Verloc listener causing mixnode unexpected shutdown ([#3038])
|
- Verloc listener causing mixnode unexpected shutdown ([#3038])
|
||||||
|
|||||||
Generated
+668
-854
File diff suppressed because it is too large
Load Diff
+13
-3
@@ -22,7 +22,6 @@ members = [
|
|||||||
"clients/native",
|
"clients/native",
|
||||||
"clients/native/websocket-requests",
|
"clients/native/websocket-requests",
|
||||||
"clients/socks5",
|
"clients/socks5",
|
||||||
"common/bandwidth-claim-contract",
|
|
||||||
"common/bin-common",
|
"common/bin-common",
|
||||||
"common/client-libs/gateway-client",
|
"common/client-libs/gateway-client",
|
||||||
"common/client-libs/mixnet-client",
|
"common/client-libs/mixnet-client",
|
||||||
@@ -105,9 +104,20 @@ edition = "2021"
|
|||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
async-trait = "0.1.63"
|
async-trait = "0.1.64"
|
||||||
|
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
dotenv = "0.15.0"
|
cosmwasm-derive = "=1.0.0"
|
||||||
|
cosmwasm-schema = "=1.0.0"
|
||||||
|
cosmwasm-std = "=1.0.0"
|
||||||
|
cosmwasm-storage = "=1.0.0"
|
||||||
|
cw-utils = "=0.13.4"
|
||||||
|
cw-storage-plus = "=0.13.4"
|
||||||
|
cw2 = { version = "=0.13.4" }
|
||||||
|
cw3 = { version = "=0.13.4" }
|
||||||
|
cw3-fixed-multisig = { version = "=0.13.4" }
|
||||||
|
cw4 = { version = "=0.13.4" }
|
||||||
|
dotenvy = "0.15.6"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
once_cell = "1.7.2"
|
once_cell = "1.7.2"
|
||||||
|
|||||||
@@ -1,132 +1,90 @@
|
|||||||
|
# Default target
|
||||||
|
all: test
|
||||||
|
|
||||||
test: clippy-all cargo-test wasm fmt
|
test: clippy-all cargo-test wasm fmt
|
||||||
|
|
||||||
test-all: test cargo-test-expensive
|
test-all: test cargo-test-expensive
|
||||||
|
|
||||||
no-clippy: build cargo-test wasm fmt
|
no-clippy: build cargo-test wasm fmt
|
||||||
|
|
||||||
happy: fmt clippy-happy test
|
happy: fmt clippy-happy test
|
||||||
clippy-all: clippy-main clippy-main-examples clippy-all-contracts clippy-all-wallet clippy-all-connect clippy-all-connect-mobile clippy-all-wasm-client
|
|
||||||
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect clippy-happy-connect-mobile
|
|
||||||
cargo-test: test-main test-contracts test-wallet test-connect test-connect-mobile
|
|
||||||
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive
|
|
||||||
build: build-contracts build-wallet build-main build-main-examples build-connect build-connect-mobile build-wasm-client
|
|
||||||
fmt: fmt-main fmt-contracts fmt-wallet fmt-connect fmt-connect-mobile fmt-wasm-client
|
|
||||||
|
|
||||||
clippy-happy-main:
|
# -----------------------------------------------------------------------------
|
||||||
cargo clippy
|
# Define targets for a given workspace
|
||||||
|
# $(1): name
|
||||||
|
# $(2): path to workspace
|
||||||
|
# $(3): extra arguments to cargo
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
define add_cargo_workspace
|
||||||
|
|
||||||
clippy-happy-contracts:
|
clippy-happy-$(1):
|
||||||
cargo clippy --manifest-path contracts/Cargo.toml --target wasm32-unknown-unknown
|
cargo clippy --manifest-path $(2)/Cargo.toml $(3)
|
||||||
|
|
||||||
clippy-happy-wallet:
|
clippy-$(1):
|
||||||
cargo clippy --manifest-path nym-wallet/Cargo.toml
|
cargo clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
|
||||||
|
|
||||||
clippy-happy-connect:
|
clippy-$(1)-examples:
|
||||||
cargo clippy --manifest-path nym-connect/desktop/Cargo.toml
|
cargo clippy --manifest-path $(2)/Cargo.toml --workspace --examples -- -D warnings
|
||||||
|
|
||||||
clippy-happy-connect-mobile:
|
test-$(1):
|
||||||
cargo clippy --manifest-path nym-connect/mobile/src-tauri/Cargo.toml
|
cargo test --manifest-path $(2)/Cargo.toml --workspace
|
||||||
|
|
||||||
clippy-main:
|
test-$(1)-expensive:
|
||||||
cargo clippy --workspace -- -D warnings
|
cargo test --manifest-path $(2)/Cargo.toml --workspace -- --ignored
|
||||||
|
|
||||||
clippy-main-examples:
|
build-$(1):
|
||||||
cargo clippy --workspace --examples -- -D warnings
|
cargo build --manifest-path $(2)/Cargo.toml --workspace $(3)
|
||||||
|
|
||||||
clippy-wasm:
|
build-$(1)-examples:
|
||||||
cargo clippy --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --workspace -- -D warnings
|
cargo build --manifest-path $(2)/Cargo.toml --workspace --examples
|
||||||
|
|
||||||
|
fmt-$(1):
|
||||||
|
cargo fmt --manifest-path $(2)/Cargo.toml --all
|
||||||
|
|
||||||
clippy-all-contracts:
|
clippy-happy: clippy-happy-$(1)
|
||||||
cargo clippy --workspace --manifest-path contracts/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings
|
clippy-all: clippy-$(1) clippy-$(1)-examples
|
||||||
|
cargo-test: test-$(1)
|
||||||
|
cargo-test-expensive: test-$(1)-expensive
|
||||||
|
build: build-$(1) build-$(1)-examples
|
||||||
|
fmt: fmt-$(1)
|
||||||
|
|
||||||
clippy-all-wallet:
|
endef
|
||||||
cargo clippy --workspace --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings
|
|
||||||
|
|
||||||
clippy-all-connect:
|
# -----------------------------------------------------------------------------
|
||||||
cargo clippy --workspace --manifest-path nym-connect/desktop/Cargo.toml --all-features -- -D warnings
|
# Rust workspaces
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
clippy-all-connect-mobile:
|
# Generate targets for the various cargo workspaces
|
||||||
cargo clippy --workspace --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all-features -- -D warnings
|
|
||||||
|
|
||||||
clippy-all-wasm-client:
|
$(eval $(call add_cargo_workspace,main,.))
|
||||||
cargo clippy --workspace --manifest-path clients/webassembly/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings
|
$(eval $(call add_cargo_workspace,contracts,contracts,--target wasm32-unknown-unknown))
|
||||||
|
$(eval $(call add_cargo_workspace,wasm-client,clients/webassembly,--target wasm32-unknown-unknown))
|
||||||
|
$(eval $(call add_cargo_workspace,wallet,nym-wallet,))
|
||||||
|
$(eval $(call add_cargo_workspace,connect,nym-connect/desktop))
|
||||||
|
ifndef NYM_NO_MOBILE
|
||||||
|
$(eval $(call add_cargo_workspace,connect-mobile,nym-connect/mobile/src-tauri))
|
||||||
|
endif
|
||||||
|
|
||||||
test-main:
|
# -----------------------------------------------------------------------------
|
||||||
cargo test --workspace
|
# Convenience targets for crates that are already part of the main workspace
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
test-main-expensive:
|
|
||||||
cargo test --workspace -- --ignored
|
|
||||||
|
|
||||||
test-contracts:
|
|
||||||
cargo test --manifest-path contracts/Cargo.toml --all-features
|
|
||||||
|
|
||||||
test-contracts-expensive:
|
|
||||||
cargo test --manifest-path contracts/Cargo.toml --all-features -- --ignored
|
|
||||||
|
|
||||||
test-wallet:
|
|
||||||
cargo test --manifest-path nym-wallet/Cargo.toml --all-features
|
|
||||||
|
|
||||||
test-wallet-expensive:
|
|
||||||
cargo test --manifest-path nym-wallet/Cargo.toml --all-features -- --ignored
|
|
||||||
|
|
||||||
test-connect:
|
|
||||||
cargo test --manifest-path nym-connect/desktop/Cargo.toml --all-features
|
|
||||||
|
|
||||||
test-connect-expensive:
|
|
||||||
cargo test --manifest-path nym-connect/desktop/Cargo.toml --all-features -- --ignored
|
|
||||||
|
|
||||||
test-connect-mobile:
|
|
||||||
cargo test --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all-features
|
|
||||||
|
|
||||||
test-connect-mobile-expensive:
|
|
||||||
cargo test --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all-features -- --ignored
|
|
||||||
|
|
||||||
build-main:
|
|
||||||
cargo build --workspace
|
|
||||||
|
|
||||||
build-main-examples:
|
|
||||||
cargo build --workspace --examples
|
|
||||||
|
|
||||||
build-contracts:
|
|
||||||
cargo build --manifest-path contracts/Cargo.toml --workspace
|
|
||||||
|
|
||||||
build-wallet:
|
|
||||||
cargo build --manifest-path nym-wallet/Cargo.toml --workspace
|
|
||||||
|
|
||||||
build-connect:
|
|
||||||
cargo build --manifest-path nym-connect/desktop/Cargo.toml --workspace
|
|
||||||
|
|
||||||
build-connect-mobile:
|
|
||||||
cargo build --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --workspace
|
|
||||||
|
|
||||||
build-explorer-api:
|
build-explorer-api:
|
||||||
cargo build --manifest-path explorer-api/Cargo.toml --workspace
|
cargo build -p explorer-api
|
||||||
|
|
||||||
build-wasm-client:
|
|
||||||
cargo build --manifest-path clients/webassembly/Cargo.toml --workspace --target wasm32-unknown-unknown
|
|
||||||
|
|
||||||
build-nym-cli:
|
build-nym-cli:
|
||||||
cargo build --release --manifest-path tools/nym-cli/Cargo.toml
|
cargo build -p nym-cli --release
|
||||||
|
|
||||||
fmt-main:
|
# -----------------------------------------------------------------------------
|
||||||
cargo fmt --all
|
# Misc
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
fmt-contracts:
|
|
||||||
cargo fmt --manifest-path contracts/Cargo.toml --all
|
|
||||||
|
|
||||||
fmt-wallet:
|
|
||||||
cargo fmt --manifest-path nym-wallet/Cargo.toml --all
|
|
||||||
|
|
||||||
fmt-connect:
|
|
||||||
cargo fmt --manifest-path nym-connect/desktop/Cargo.toml --all
|
|
||||||
|
|
||||||
fmt-connect-mobile:
|
|
||||||
cargo fmt --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all
|
|
||||||
|
|
||||||
fmt-wasm-client:
|
|
||||||
cargo fmt --manifest-path clients/webassembly/Cargo.toml --all
|
|
||||||
|
|
||||||
wasm:
|
wasm:
|
||||||
RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
|
RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
|
||||||
|
wasm-opt --disable-sign-ext -Os contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
|
||||||
|
wasm-opt --disable-sign-ext -Os contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm -o contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm
|
||||||
|
|
||||||
|
# NOTE: this seems deprecated an not needed anymore?
|
||||||
mixnet-opt: wasm
|
mixnet-opt: wasm
|
||||||
cd contracts/mixnet && make opt
|
cd contracts/mixnet && make opt
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
|||||||
* nym-wallet - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
|
* nym-wallet - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
|
||||||
|
|
||||||
[](https://opensource.org/licenses/Apache-2.0)
|
[](https://opensource.org/licenses/Apache-2.0)
|
||||||
[](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
|
[](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
|
||||||
|
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "client-core"
|
name = "client-core"
|
||||||
version = "1.1.10"
|
version = "1.1.14"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.66"
|
rust-version = "1.66"
|
||||||
@@ -8,7 +8,7 @@ rust-version = "1.66"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = { version = "0.1.58" }
|
async-trait = { workspace = true }
|
||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
dashmap = "5.4.0"
|
dashmap = "5.4.0"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
@@ -20,6 +20,7 @@ serde_json = "1.0.89"
|
|||||||
tap = "1.0.1"
|
tap = "1.0.1"
|
||||||
thiserror = "1.0.34"
|
thiserror = "1.0.34"
|
||||||
url = { version ="2.2", features = ["serde"] }
|
url = { version ="2.2", features = ["serde"] }
|
||||||
|
tungstenite = { version = "0.13.0", default-features = false }
|
||||||
tokio = { version = "1.24.1", features = ["macros"]}
|
tokio = { version = "1.24.1", features = ["macros"]}
|
||||||
time = "0.3.17"
|
time = "0.3.17"
|
||||||
|
|
||||||
@@ -36,6 +37,10 @@ nym-topology = { path = "../../common/topology" }
|
|||||||
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
|
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
|
||||||
nym-task = { path = "../../common/task" }
|
nym-task = { path = "../../common/task" }
|
||||||
|
|
||||||
|
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.validator-client]
|
||||||
|
path = "../../common/client-libs/validator-client"
|
||||||
|
features = ["nyxd-client"]
|
||||||
|
|
||||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
|
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
features = ["time"]
|
features = ["time"]
|
||||||
@@ -44,6 +49,9 @@ features = ["time"]
|
|||||||
version = "1.24.1"
|
version = "1.24.1"
|
||||||
features = ["time"]
|
features = ["time"]
|
||||||
|
|
||||||
|
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||||
|
version = "0.14"
|
||||||
|
|
||||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
|
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
|
||||||
@@ -65,6 +73,7 @@ features = ["futures"]
|
|||||||
|
|
||||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
|
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-utils]
|
||||||
path = "../../common/wasm-utils"
|
path = "../../common/wasm-utils"
|
||||||
|
features = ["websocket"]
|
||||||
|
|
||||||
[target."cfg(target_arch = \"wasm32\")".dependencies.time]
|
[target."cfg(target_arch = \"wasm32\")".dependencies.time]
|
||||||
version = "0.3.17"
|
version = "0.3.17"
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ use crate::{client::replies::reply_storage, config::DebugConfig};
|
|||||||
|
|
||||||
pub fn setup_empty_reply_surb_backend(debug_config: &DebugConfig) -> reply_storage::Empty {
|
pub fn setup_empty_reply_surb_backend(debug_config: &DebugConfig) -> reply_storage::Empty {
|
||||||
reply_storage::Empty {
|
reply_storage::Empty {
|
||||||
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
|
min_surb_threshold: debug_config
|
||||||
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
|
.reply_surbs
|
||||||
|
.minimum_reply_surb_storage_threshold,
|
||||||
|
max_surb_threshold: debug_config
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_storage_threshold,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use crate::client::replies::reply_controller::{ReplyControllerReceiver, ReplyCon
|
|||||||
use crate::client::replies::reply_storage::{
|
use crate::client::replies::reply_storage::{
|
||||||
CombinedReplyStorage, PersistentReplyStorage, ReplyStorageBackend, SentReplyKeys,
|
CombinedReplyStorage, PersistentReplyStorage, ReplyStorageBackend, SentReplyKeys,
|
||||||
};
|
};
|
||||||
|
use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider;
|
||||||
use crate::client::topology_control::{
|
use crate::client::topology_control::{
|
||||||
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
|
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
|
||||||
};
|
};
|
||||||
@@ -34,13 +35,15 @@ use nym_crypto::asymmetric::{encryption, identity};
|
|||||||
use nym_sphinx::acknowledgements::AckKey;
|
use nym_sphinx::acknowledgements::AckKey;
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use nym_sphinx::addressing::nodes::NodeIdentity;
|
use nym_sphinx::addressing::nodes::NodeIdentity;
|
||||||
use nym_sphinx::receiver::ReconstructedMessage;
|
use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
|
||||||
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
|
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
|
||||||
use nym_task::{TaskClient, TaskManager};
|
use nym_task::{TaskClient, TaskManager};
|
||||||
|
use nym_topology::provider_trait::TopologyProvider;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tap::TapFallible;
|
use tap::TapFallible;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use validator_client::nyxd::CosmWasmClient;
|
use validator_client::nyxd::CosmWasmClient;
|
||||||
|
|
||||||
@@ -90,6 +93,7 @@ impl ClientOutput {
|
|||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
pub shared_lane_queue_lengths: LaneQueueLengths,
|
pub shared_lane_queue_lengths: LaneQueueLengths,
|
||||||
pub reply_controller_sender: ReplyControllerSender,
|
pub reply_controller_sender: ReplyControllerSender,
|
||||||
|
pub topology_accessor: TopologyAccessor,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ClientInputStatus {
|
pub enum ClientInputStatus {
|
||||||
@@ -154,6 +158,7 @@ pub struct BaseClientBuilder<'a, B, C: Clone> {
|
|||||||
nym_api_endpoints: Vec<Url>,
|
nym_api_endpoints: Vec<Url>,
|
||||||
reply_storage_backend: B,
|
reply_storage_backend: B,
|
||||||
|
|
||||||
|
custom_topology_provider: Option<Box<dyn TopologyProvider>>,
|
||||||
bandwidth_controller: Option<BandwidthController<C>>,
|
bandwidth_controller: Option<BandwidthController<C>>,
|
||||||
key_manager: KeyManager,
|
key_manager: KeyManager,
|
||||||
}
|
}
|
||||||
@@ -177,6 +182,7 @@ where
|
|||||||
bandwidth_controller,
|
bandwidth_controller,
|
||||||
reply_storage_backend,
|
reply_storage_backend,
|
||||||
key_manager,
|
key_manager,
|
||||||
|
custom_topology_provider: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,11 +201,17 @@ where
|
|||||||
disabled_credentials: credentials_toggle.is_disabled(),
|
disabled_credentials: credentials_toggle.is_disabled(),
|
||||||
nym_api_endpoints,
|
nym_api_endpoints,
|
||||||
reply_storage_backend,
|
reply_storage_backend,
|
||||||
|
custom_topology_provider: None,
|
||||||
bandwidth_controller,
|
bandwidth_controller,
|
||||||
key_manager,
|
key_manager,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_topology_provider(mut self, provider: Box<dyn TopologyProvider>) -> Self {
|
||||||
|
self.custom_topology_provider = Some(provider);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_mix_recipient(&self) -> Recipient {
|
pub fn as_mix_recipient(&self) -> Recipient {
|
||||||
Recipient::new(
|
Recipient::new(
|
||||||
*self.key_manager.identity_keypair().public_key(),
|
*self.key_manager.identity_keypair().public_key(),
|
||||||
@@ -224,15 +236,15 @@ where
|
|||||||
|
|
||||||
let mut stream = LoopCoverTrafficStream::new(
|
let mut stream = LoopCoverTrafficStream::new(
|
||||||
ack_key,
|
ack_key,
|
||||||
debug_config.average_ack_delay,
|
debug_config.acknowledgements.average_ack_delay,
|
||||||
debug_config.average_packet_delay,
|
debug_config.traffic.average_packet_delay,
|
||||||
debug_config.loop_cover_traffic_average_delay,
|
debug_config.cover_traffic.loop_cover_traffic_average_delay,
|
||||||
mix_tx,
|
mix_tx,
|
||||||
self_address,
|
self_address,
|
||||||
topology_accessor,
|
topology_accessor,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(size) = debug_config.use_extended_packet_size {
|
if let Some(size) = debug_config.traffic.use_extended_packet_size {
|
||||||
log::debug!("Setting extended packet size: {:?}", size);
|
log::debug!("Setting extended packet size: {:?}", size);
|
||||||
stream.set_custom_packet_size(size.into());
|
stream.set_custom_packet_size(size.into());
|
||||||
}
|
}
|
||||||
@@ -282,14 +294,15 @@ where
|
|||||||
shutdown: TaskClient,
|
shutdown: TaskClient,
|
||||||
) {
|
) {
|
||||||
info!("Starting received messages buffer controller...");
|
info!("Starting received messages buffer controller...");
|
||||||
ReceivedMessagesBufferController::new(
|
let controller: ReceivedMessagesBufferController<SphinxMessageReceiver> =
|
||||||
local_encryption_keypair,
|
ReceivedMessagesBufferController::new(
|
||||||
query_receiver,
|
local_encryption_keypair,
|
||||||
mixnet_receiver,
|
query_receiver,
|
||||||
reply_key_storage,
|
mixnet_receiver,
|
||||||
reply_controller_sender,
|
reply_key_storage,
|
||||||
)
|
reply_controller_sender,
|
||||||
.start_with_shutdown(shutdown)
|
);
|
||||||
|
controller.start_with_shutdown(shutdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_gateway_client(
|
async fn start_gateway_client(
|
||||||
@@ -304,7 +317,7 @@ where
|
|||||||
}
|
}
|
||||||
let gateway_address = self.gateway_config.gateway_listener.clone();
|
let gateway_address = self.gateway_config.gateway_listener.clone();
|
||||||
if gateway_address.is_empty() {
|
if gateway_address.is_empty() {
|
||||||
return Err(ClientCoreError::GatwayAddressUnknown);
|
return Err(ClientCoreError::GatewayAddressUnknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
||||||
@@ -325,7 +338,9 @@ where
|
|||||||
shared_key,
|
shared_key,
|
||||||
mixnet_message_sender,
|
mixnet_message_sender,
|
||||||
ack_sender,
|
ack_sender,
|
||||||
self.debug_config.gateway_response_timeout,
|
self.debug_config
|
||||||
|
.gateway_connection
|
||||||
|
.gateway_response_timeout,
|
||||||
self.bandwidth_controller.take(),
|
self.bandwidth_controller.take(),
|
||||||
shutdown,
|
shutdown,
|
||||||
);
|
);
|
||||||
@@ -341,25 +356,38 @@ where
|
|||||||
Ok(gateway_client)
|
Ok(gateway_client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn setup_topology_provider(
|
||||||
|
custom_provider: Option<Box<dyn TopologyProvider>>,
|
||||||
|
nym_api_urls: Vec<Url>,
|
||||||
|
) -> Box<dyn TopologyProvider> {
|
||||||
|
// if no custom provider was ... provided ..., create one using nym-api
|
||||||
|
custom_provider.unwrap_or_else(|| {
|
||||||
|
Box::new(NymApiTopologyProvider::new(
|
||||||
|
nym_api_urls,
|
||||||
|
env!("CARGO_PKG_VERSION").to_string(),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// future responsible for periodically polling directory server and updating
|
// future responsible for periodically polling directory server and updating
|
||||||
// the current global view of topology
|
// the current global view of topology
|
||||||
async fn start_topology_refresher(
|
async fn start_topology_refresher(
|
||||||
nym_api_urls: Vec<Url>,
|
topology_provider: Box<dyn TopologyProvider>,
|
||||||
refresh_rate: Duration,
|
refresh_rate: Duration,
|
||||||
topology_accessor: TopologyAccessor,
|
topology_accessor: TopologyAccessor,
|
||||||
shutdown: TaskClient,
|
shutdown: TaskClient,
|
||||||
) -> Result<(), ClientCoreError> {
|
) -> Result<(), ClientCoreError> {
|
||||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
let topology_refresher_config = TopologyRefresherConfig::new(refresh_rate);
|
||||||
nym_api_urls,
|
|
||||||
refresh_rate,
|
let mut topology_refresher = TopologyRefresher::new(
|
||||||
env!("CARGO_PKG_VERSION").to_string(),
|
topology_refresher_config,
|
||||||
|
topology_accessor,
|
||||||
|
topology_provider,
|
||||||
);
|
);
|
||||||
let mut topology_refresher =
|
|
||||||
TopologyRefresher::new(topology_refresher_config, topology_accessor);
|
|
||||||
// before returning, block entire runtime to refresh the current network view so that any
|
// before returning, block entire runtime to refresh the current network view so that any
|
||||||
// components depending on topology would see a non-empty view
|
// components depending on topology would see a non-empty view
|
||||||
info!("Obtaining initial network topology");
|
info!("Obtaining initial network topology");
|
||||||
topology_refresher.refresh().await;
|
topology_refresher.try_refresh().await;
|
||||||
|
|
||||||
if let Err(err) = topology_refresher.ensure_topology_is_routable().await {
|
if let Err(err) = topology_refresher.ensure_topology_is_routable().await {
|
||||||
log::error!(
|
log::error!(
|
||||||
@@ -468,9 +496,13 @@ where
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
let topology_provider = Self::setup_topology_provider(
|
||||||
|
self.custom_topology_provider.take(),
|
||||||
|
self.nym_api_endpoints,
|
||||||
|
);
|
||||||
Self::start_topology_refresher(
|
Self::start_topology_refresher(
|
||||||
self.nym_api_endpoints.clone(),
|
topology_provider,
|
||||||
self.debug_config.topology_refresh_rate,
|
self.debug_config.topology.topology_refresh_rate,
|
||||||
shared_topology_accessor.clone(),
|
shared_topology_accessor.clone(),
|
||||||
task_manager.subscribe(),
|
task_manager.subscribe(),
|
||||||
)
|
)
|
||||||
@@ -506,7 +538,7 @@ where
|
|||||||
self_address,
|
self_address,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(size) = self.debug_config.use_extended_packet_size {
|
if let Some(size) = self.debug_config.traffic.use_extended_packet_size {
|
||||||
log::debug!("Setting extended packet size: {:?}", size);
|
log::debug!("Setting extended packet size: {:?}", size);
|
||||||
controller_config.set_custom_packet_size(size.into());
|
controller_config.set_custom_packet_size(size.into());
|
||||||
}
|
}
|
||||||
@@ -525,12 +557,16 @@ where
|
|||||||
task_manager.subscribe(),
|
task_manager.subscribe(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if !self.debug_config.disable_loop_cover_traffic_stream {
|
if !self
|
||||||
|
.debug_config
|
||||||
|
.cover_traffic
|
||||||
|
.disable_loop_cover_traffic_stream
|
||||||
|
{
|
||||||
Self::start_cover_traffic_stream(
|
Self::start_cover_traffic_stream(
|
||||||
self.debug_config,
|
self.debug_config,
|
||||||
self.key_manager.ack_key(),
|
self.key_manager.ack_key(),
|
||||||
self_address,
|
self_address,
|
||||||
shared_topology_accessor,
|
shared_topology_accessor.clone(),
|
||||||
sphinx_message_sender,
|
sphinx_message_sender,
|
||||||
task_manager.subscribe(),
|
task_manager.subscribe(),
|
||||||
);
|
);
|
||||||
@@ -554,6 +590,7 @@ where
|
|||||||
client_state: ClientState {
|
client_state: ClientState {
|
||||||
shared_lane_queue_lengths,
|
shared_lane_queue_lengths,
|
||||||
reply_controller_sender,
|
reply_controller_sender,
|
||||||
|
topology_accessor: shared_topology_accessor,
|
||||||
},
|
},
|
||||||
task_manager,
|
task_manager,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -30,8 +30,12 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
|
|||||||
// it will only be happening on the very first run and in practice won't incur huge
|
// it will only be happening on the very first run and in practice won't incur huge
|
||||||
// costs since the storage is going to be empty
|
// costs since the storage is going to be empty
|
||||||
let mem_store = CombinedReplyStorage::new(
|
let mem_store = CombinedReplyStorage::new(
|
||||||
debug_config.minimum_reply_surb_storage_threshold,
|
debug_config
|
||||||
debug_config.maximum_reply_surb_storage_threshold,
|
.reply_surbs
|
||||||
|
.minimum_reply_surb_storage_threshold,
|
||||||
|
debug_config
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_storage_threshold,
|
||||||
);
|
);
|
||||||
storage_backend
|
storage_backend
|
||||||
.init_fresh(&mem_store)
|
.init_fresh(&mem_store)
|
||||||
@@ -46,8 +50,12 @@ async fn setup_fresh_backend<P: AsRef<Path>>(
|
|||||||
fn setup_inactive_backend(debug_config: &DebugConfig) -> fs_backend::Backend {
|
fn setup_inactive_backend(debug_config: &DebugConfig) -> fs_backend::Backend {
|
||||||
info!("creating inactive surb database");
|
info!("creating inactive surb database");
|
||||||
fs_backend::Backend::new_inactive(
|
fs_backend::Backend::new_inactive(
|
||||||
debug_config.minimum_reply_surb_storage_threshold,
|
debug_config
|
||||||
debug_config.maximum_reply_surb_storage_threshold,
|
.reply_surbs
|
||||||
|
.minimum_reply_surb_storage_threshold,
|
||||||
|
debug_config
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_storage_threshold,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,25 +153,35 @@ impl Config {
|
|||||||
ack_key,
|
ack_key,
|
||||||
self_recipient,
|
self_recipient,
|
||||||
packet_size: Default::default(),
|
packet_size: Default::default(),
|
||||||
ack_wait_addition: base_client_debug_config.ack_wait_addition,
|
ack_wait_addition: base_client_debug_config.acknowledgements.ack_wait_addition,
|
||||||
ack_wait_multiplier: base_client_debug_config.ack_wait_multiplier,
|
ack_wait_multiplier: base_client_debug_config
|
||||||
average_message_sending_delay: base_client_debug_config.message_sending_average_delay,
|
.acknowledgements
|
||||||
average_packet_delay_duration: base_client_debug_config.average_packet_delay,
|
.ack_wait_multiplier,
|
||||||
average_ack_delay_duration: base_client_debug_config.average_ack_delay,
|
average_message_sending_delay: base_client_debug_config
|
||||||
|
.traffic
|
||||||
|
.message_sending_average_delay,
|
||||||
|
average_packet_delay_duration: base_client_debug_config.traffic.average_packet_delay,
|
||||||
|
average_ack_delay_duration: base_client_debug_config.acknowledgements.average_ack_delay,
|
||||||
disable_main_poisson_packet_distribution: base_client_debug_config
|
disable_main_poisson_packet_distribution: base_client_debug_config
|
||||||
|
.traffic
|
||||||
.disable_main_poisson_packet_distribution,
|
.disable_main_poisson_packet_distribution,
|
||||||
minimum_reply_surb_request_size: base_client_debug_config
|
minimum_reply_surb_request_size: base_client_debug_config
|
||||||
|
.reply_surbs
|
||||||
.minimum_reply_surb_request_size,
|
.minimum_reply_surb_request_size,
|
||||||
maximum_reply_surb_request_size: base_client_debug_config
|
maximum_reply_surb_request_size: base_client_debug_config
|
||||||
|
.reply_surbs
|
||||||
.maximum_reply_surb_request_size,
|
.maximum_reply_surb_request_size,
|
||||||
maximum_allowed_reply_surb_request_size: base_client_debug_config
|
maximum_allowed_reply_surb_request_size: base_client_debug_config
|
||||||
|
.reply_surbs
|
||||||
.maximum_allowed_reply_surb_request_size,
|
.maximum_allowed_reply_surb_request_size,
|
||||||
maximum_reply_surb_rerequest_waiting_period: base_client_debug_config
|
maximum_reply_surb_rerequest_waiting_period: base_client_debug_config
|
||||||
|
.reply_surbs
|
||||||
.maximum_reply_surb_rerequest_waiting_period,
|
.maximum_reply_surb_rerequest_waiting_period,
|
||||||
maximum_reply_surb_drop_waiting_period: base_client_debug_config
|
maximum_reply_surb_drop_waiting_period: base_client_debug_config
|
||||||
|
.reply_surbs
|
||||||
.maximum_reply_surb_drop_waiting_period,
|
.maximum_reply_surb_drop_waiting_period,
|
||||||
maximum_reply_surb_age: base_client_debug_config.maximum_reply_surb_age,
|
maximum_reply_surb_age: base_client_debug_config.reply_surbs.maximum_reply_surb_age,
|
||||||
maximum_reply_key_age: base_client_debug_config.maximum_reply_key_age,
|
maximum_reply_key_age: base_client_debug_config.reply_surbs.maximum_reply_key_age,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,13 +30,13 @@ pub type ReceivedBufferRequestReceiver = mpsc::UnboundedReceiver<ReceivedBufferM
|
|||||||
pub type ReconstructedMessagesSender = mpsc::UnboundedSender<Vec<ReconstructedMessage>>;
|
pub type ReconstructedMessagesSender = mpsc::UnboundedSender<Vec<ReconstructedMessage>>;
|
||||||
pub type ReconstructedMessagesReceiver = mpsc::UnboundedReceiver<Vec<ReconstructedMessage>>;
|
pub type ReconstructedMessagesReceiver = mpsc::UnboundedReceiver<Vec<ReconstructedMessage>>;
|
||||||
|
|
||||||
struct ReceivedMessagesBufferInner {
|
struct ReceivedMessagesBufferInner<R: MessageReceiver> {
|
||||||
messages: Vec<ReconstructedMessage>,
|
messages: Vec<ReconstructedMessage>,
|
||||||
local_encryption_keypair: Arc<encryption::KeyPair>,
|
local_encryption_keypair: Arc<encryption::KeyPair>,
|
||||||
|
|
||||||
// TODO: looking how it 'looks' here, perhaps `MessageReceiver` should be renamed to something
|
// TODO: looking how it 'looks' here, perhaps `MessageReceiver` should be renamed to something
|
||||||
// else instead.
|
// else instead.
|
||||||
message_receiver: MessageReceiver,
|
message_receiver: R,
|
||||||
message_sender: Option<ReconstructedMessagesSender>,
|
message_sender: Option<ReconstructedMessagesSender>,
|
||||||
|
|
||||||
// TODO: this will get cleared upon re-running the client
|
// TODO: this will get cleared upon re-running the client
|
||||||
@@ -45,7 +45,7 @@ struct ReceivedMessagesBufferInner {
|
|||||||
recently_reconstructed: HashSet<i32>,
|
recently_reconstructed: HashSet<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReceivedMessagesBufferInner {
|
impl<R: MessageReceiver> ReceivedMessagesBufferInner<R> {
|
||||||
fn recover_from_fragment(&mut self, fragment_data: &[u8]) -> Option<NymMessage> {
|
fn recover_from_fragment(&mut self, fragment_data: &[u8]) -> Option<NymMessage> {
|
||||||
if nym_sphinx::cover::is_cover(fragment_data) {
|
if nym_sphinx::cover::is_cover(fragment_data) {
|
||||||
trace!("The message was a loop cover message! Skipping it");
|
trace!("The message was a loop cover message! Skipping it");
|
||||||
@@ -102,13 +102,13 @@ impl ReceivedMessagesBufferInner {
|
|||||||
&mut self,
|
&mut self,
|
||||||
reply_ciphertext: &mut [u8],
|
reply_ciphertext: &mut [u8],
|
||||||
reply_key: SurbEncryptionKey,
|
reply_key: SurbEncryptionKey,
|
||||||
) -> Option<NymMessage> {
|
) -> Result<Option<NymMessage>, MessageRecoveryError> {
|
||||||
// note: this performs decryption IN PLACE without extra allocation
|
// note: this performs decryption IN PLACE without extra allocation
|
||||||
self.message_receiver
|
self.message_receiver
|
||||||
.recover_plaintext_from_reply(reply_ciphertext, reply_key);
|
.recover_plaintext_from_reply(reply_ciphertext, reply_key)?;
|
||||||
let fragment_data = reply_ciphertext;
|
let fragment_data = reply_ciphertext;
|
||||||
|
|
||||||
self.recover_from_fragment(fragment_data)
|
Ok(self.recover_from_fragment(fragment_data))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_received_regular_packet(&mut self, mut raw_fragment: Vec<u8>) -> Option<NymMessage> {
|
fn process_received_regular_packet(&mut self, mut raw_fragment: Vec<u8>) -> Option<NymMessage> {
|
||||||
@@ -130,13 +130,13 @@ impl ReceivedMessagesBufferInner {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
// Note: you should NEVER create more than a single instance of this using 'new()'.
|
// Note: you should NEVER create more than a single instance of this using 'new()'.
|
||||||
// You should always use .clone() to create additional instances
|
// You should always use .clone() to create additional instances
|
||||||
struct ReceivedMessagesBuffer {
|
struct ReceivedMessagesBuffer<R: MessageReceiver> {
|
||||||
inner: Arc<Mutex<ReceivedMessagesBufferInner>>,
|
inner: Arc<Mutex<ReceivedMessagesBufferInner<R>>>,
|
||||||
reply_key_storage: SentReplyKeys,
|
reply_key_storage: SentReplyKeys,
|
||||||
reply_controller_sender: ReplyControllerSender,
|
reply_controller_sender: ReplyControllerSender,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReceivedMessagesBuffer {
|
impl<R: MessageReceiver> ReceivedMessagesBuffer<R> {
|
||||||
fn new(
|
fn new(
|
||||||
local_encryption_keypair: Arc<encryption::KeyPair>,
|
local_encryption_keypair: Arc<encryption::KeyPair>,
|
||||||
reply_key_storage: SentReplyKeys,
|
reply_key_storage: SentReplyKeys,
|
||||||
@@ -146,7 +146,7 @@ impl ReceivedMessagesBuffer {
|
|||||||
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
|
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
|
||||||
messages: Vec::new(),
|
messages: Vec::new(),
|
||||||
local_encryption_keypair,
|
local_encryption_keypair,
|
||||||
message_receiver: MessageReceiver::new(),
|
message_receiver: R::new(),
|
||||||
message_sender: None,
|
message_sender: None,
|
||||||
recently_reconstructed: HashSet::new(),
|
recently_reconstructed: HashSet::new(),
|
||||||
})),
|
})),
|
||||||
@@ -328,7 +328,10 @@ impl ReceivedMessagesBuffer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_new_received(&mut self, msgs: Vec<Vec<u8>>) {
|
async fn handle_new_received(
|
||||||
|
&mut self,
|
||||||
|
msgs: Vec<Vec<u8>>,
|
||||||
|
) -> Result<(), MessageRecoveryError> {
|
||||||
trace!(
|
trace!(
|
||||||
"Processing {:?} new message that might get added to the buffer!",
|
"Processing {:?} new message that might get added to the buffer!",
|
||||||
msgs.len()
|
msgs.len()
|
||||||
@@ -344,7 +347,7 @@ impl ReceivedMessagesBuffer {
|
|||||||
// if yes - this is a reply message
|
// if yes - this is a reply message
|
||||||
let completed_message =
|
let completed_message =
|
||||||
if let Some((reply_key, reply_message)) = self.get_reply_key(&mut msg) {
|
if let Some((reply_key, reply_message)) = self.get_reply_key(&mut msg) {
|
||||||
inner_guard.process_received_reply(reply_message, reply_key)
|
inner_guard.process_received_reply(reply_message, reply_key)?
|
||||||
} else {
|
} else {
|
||||||
inner_guard.process_received_regular_packet(msg)
|
inner_guard.process_received_regular_packet(msg)
|
||||||
};
|
};
|
||||||
@@ -360,6 +363,7 @@ impl ReceivedMessagesBuffer {
|
|||||||
if !completed_messages.is_empty() {
|
if !completed_messages.is_empty() {
|
||||||
self.handle_reconstructed_messages(completed_messages).await
|
self.handle_reconstructed_messages(completed_messages).await
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,14 +376,14 @@ pub enum ReceivedBufferMessage {
|
|||||||
ReceiverDisconnect,
|
ReceiverDisconnect,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RequestReceiver {
|
struct RequestReceiver<R: MessageReceiver> {
|
||||||
received_buffer: ReceivedMessagesBuffer,
|
received_buffer: ReceivedMessagesBuffer<R>,
|
||||||
query_receiver: ReceivedBufferRequestReceiver,
|
query_receiver: ReceivedBufferRequestReceiver,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RequestReceiver {
|
impl<R: MessageReceiver> RequestReceiver<R> {
|
||||||
fn new(
|
fn new(
|
||||||
received_buffer: ReceivedMessagesBuffer,
|
received_buffer: ReceivedMessagesBuffer<R>,
|
||||||
query_receiver: ReceivedBufferRequestReceiver,
|
query_receiver: ReceivedBufferRequestReceiver,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
RequestReceiver {
|
RequestReceiver {
|
||||||
@@ -422,14 +426,14 @@ impl RequestReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FragmentedMessageReceiver {
|
struct FragmentedMessageReceiver<R: MessageReceiver> {
|
||||||
received_buffer: ReceivedMessagesBuffer,
|
received_buffer: ReceivedMessagesBuffer<R>,
|
||||||
mixnet_packet_receiver: MixnetMessageReceiver,
|
mixnet_packet_receiver: MixnetMessageReceiver,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FragmentedMessageReceiver {
|
impl<R: MessageReceiver> FragmentedMessageReceiver<R> {
|
||||||
fn new(
|
fn new(
|
||||||
received_buffer: ReceivedMessagesBuffer,
|
received_buffer: ReceivedMessagesBuffer<R>,
|
||||||
mixnet_packet_receiver: MixnetMessageReceiver,
|
mixnet_packet_receiver: MixnetMessageReceiver,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
FragmentedMessageReceiver {
|
FragmentedMessageReceiver {
|
||||||
@@ -438,13 +442,16 @@ impl FragmentedMessageReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_with_shutdown(&mut self, mut shutdown: nym_task::TaskClient) {
|
async fn run_with_shutdown(
|
||||||
|
&mut self,
|
||||||
|
mut shutdown: nym_task::TaskClient,
|
||||||
|
) -> Result<(), MessageRecoveryError> {
|
||||||
debug!("Started FragmentedMessageReceiver with graceful shutdown support");
|
debug!("Started FragmentedMessageReceiver with graceful shutdown support");
|
||||||
while !shutdown.is_shutdown() {
|
while !shutdown.is_shutdown() {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
new_messages = self.mixnet_packet_receiver.next() => {
|
new_messages = self.mixnet_packet_receiver.next() => {
|
||||||
if let Some(new_messages) = new_messages {
|
if let Some(new_messages) = new_messages {
|
||||||
self.received_buffer.handle_new_received(new_messages).await;
|
self.received_buffer.handle_new_received(new_messages).await?;
|
||||||
} else {
|
} else {
|
||||||
log::trace!("FragmentedMessageReceiver: Stopping since channel closed");
|
log::trace!("FragmentedMessageReceiver: Stopping since channel closed");
|
||||||
break;
|
break;
|
||||||
@@ -457,15 +464,16 @@ impl FragmentedMessageReceiver {
|
|||||||
}
|
}
|
||||||
shutdown.recv_timeout().await;
|
shutdown.recv_timeout().await;
|
||||||
log::debug!("FragmentedMessageReceiver: Exiting");
|
log::debug!("FragmentedMessageReceiver: Exiting");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ReceivedMessagesBufferController {
|
pub(crate) struct ReceivedMessagesBufferController<R: MessageReceiver> {
|
||||||
fragmented_message_receiver: FragmentedMessageReceiver,
|
fragmented_message_receiver: FragmentedMessageReceiver<R>,
|
||||||
request_receiver: RequestReceiver,
|
request_receiver: RequestReceiver<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReceivedMessagesBufferController {
|
impl<R: MessageReceiver + Clone + Send + 'static> ReceivedMessagesBufferController<R> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
local_encryption_keypair: Arc<encryption::KeyPair>,
|
local_encryption_keypair: Arc<encryption::KeyPair>,
|
||||||
query_receiver: ReceivedBufferRequestReceiver,
|
query_receiver: ReceivedBufferRequestReceiver,
|
||||||
@@ -494,9 +502,13 @@ impl ReceivedMessagesBufferController {
|
|||||||
|
|
||||||
let shutdown_handle = shutdown.clone();
|
let shutdown_handle = shutdown.clone();
|
||||||
spawn_future(async move {
|
spawn_future(async move {
|
||||||
fragmented_message_receiver
|
match fragmented_message_receiver
|
||||||
.run_with_shutdown(shutdown_handle)
|
.run_with_shutdown(shutdown_handle)
|
||||||
.await;
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => error!("{e}"),
|
||||||
|
}
|
||||||
});
|
});
|
||||||
spawn_future(async move {
|
spawn_future(async move {
|
||||||
request_receiver.run_with_shutdown(shutdown).await;
|
request_receiver.run_with_shutdown(shutdown).await;
|
||||||
|
|||||||
@@ -35,8 +35,12 @@ impl ReplyStorageBackend for Backend {
|
|||||||
) -> Result<Self, Self::StorageError> {
|
) -> Result<Self, Self::StorageError> {
|
||||||
Ok(Backend {
|
Ok(Backend {
|
||||||
empty: Empty {
|
empty: Empty {
|
||||||
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
|
min_surb_threshold: debug_config
|
||||||
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
|
.reply_surbs
|
||||||
|
.minimum_reply_surb_storage_threshold,
|
||||||
|
max_surb_threshold: debug_config
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_storage_threshold,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,18 +155,21 @@ impl Backend {
|
|||||||
// (assuming no key rotation has happened)
|
// (assuming no key rotation has happened)
|
||||||
// but the way it's currently coded, everyone will purge old data
|
// but the way it's currently coded, everyone will purge old data
|
||||||
let since_last_flush = OffsetDateTime::now_utc() - last_flush;
|
let since_last_flush = OffsetDateTime::now_utc() - last_flush;
|
||||||
if since_last_flush.whole_days() > 0 {
|
let days = since_last_flush.whole_days();
|
||||||
info!("it's been over {} days and {} hours since we last used our data store. our reply surbs are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours());
|
let hours = since_last_flush.whole_hours() % 24;
|
||||||
|
|
||||||
|
if days > 0 {
|
||||||
|
info!("it's been over {days} days and {hours} hours since we last used our data store. our reply surbs are already outdated - we're going to purge them now.");
|
||||||
manager.delete_all_reply_surb_data().await?;
|
manager.delete_all_reply_surb_data().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if since_last_flush.whole_days() > 1 {
|
if days > 1 {
|
||||||
info!("it's been over {} days and {} hours since we last used our data store. our reply keys are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours());
|
info!("it's been over {days} days and {hours} hours since we last used our data store. our reply keys are already outdated - we're going to purge them now.");
|
||||||
manager.delete_all_reply_keys().await?;
|
manager.delete_all_reply_keys().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if since_last_flush.whole_days() > 2 {
|
if days > 2 {
|
||||||
info!("it's been over {} days and {} hours since we last used our data store. our used sender tags are already outdated - we're going to purge them now.", since_last_flush.whole_days(), since_last_flush.whole_hours());
|
info!("it's been over {days} days and {hours} hours since we last used our data store. our used sender tags are already outdated - we're going to purge them now.");
|
||||||
manager.delete_all_tags().await?;
|
manager.delete_all_tags().await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,12 @@ impl ReplyStorageBackend for Empty {
|
|||||||
_db_path: Option<PathBuf>,
|
_db_path: Option<PathBuf>,
|
||||||
) -> Result<Self, Self::StorageError> {
|
) -> Result<Self, Self::StorageError> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
|
min_surb_threshold: debug_config
|
||||||
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
|
.reply_surbs
|
||||||
|
.minimum_reply_surb_storage_threshold,
|
||||||
|
max_surb_threshold: debug_config
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_storage_threshold,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,336 +0,0 @@
|
|||||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::spawn_future;
|
|
||||||
use futures::StreamExt;
|
|
||||||
use log::*;
|
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
|
||||||
use nym_sphinx::params::DEFAULT_NUM_MIX_HOPS;
|
|
||||||
use nym_topology::{nym_topology_from_detailed, NymTopology, NymTopologyError};
|
|
||||||
use rand::seq::SliceRandom;
|
|
||||||
use rand::thread_rng;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
// I'm extremely curious why compiler NEVER complained about lack of Debug here before
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TopologyAccessorInner(Option<NymTopology>);
|
|
||||||
|
|
||||||
impl AsRef<Option<NymTopology>> for TopologyAccessorInner {
|
|
||||||
fn as_ref(&self) -> &Option<NymTopology> {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TopologyAccessorInner {
|
|
||||||
fn new() -> Self {
|
|
||||||
TopologyAccessorInner(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, new: Option<NymTopology>) {
|
|
||||||
self.0 = new;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TopologyReadPermit<'a> {
|
|
||||||
permit: RwLockReadGuard<'a, TopologyAccessorInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Deref for TopologyReadPermit<'a> {
|
|
||||||
type Target = TopologyAccessorInner;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.permit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TopologyReadPermit<'a> {
|
|
||||||
/// Using provided topology read permit, tries to get an immutable reference to the underlying
|
|
||||||
/// topology. For obvious reasons the lifetime of the topology reference is bound to the permit.
|
|
||||||
pub(super) fn try_get_valid_topology_ref(
|
|
||||||
&'a self,
|
|
||||||
ack_recipient: &Recipient,
|
|
||||||
packet_recipient: Option<&Recipient>,
|
|
||||||
) -> Result<&'a NymTopology, NymTopologyError> {
|
|
||||||
// 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through?
|
|
||||||
let topology = self
|
|
||||||
.permit
|
|
||||||
.as_ref()
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
|
|
||||||
|
|
||||||
// 2. does it have any mixnode at all?
|
|
||||||
// 3. does it have any gateways at all?
|
|
||||||
// 4. does it have a mixnode on each layer?
|
|
||||||
topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS)?;
|
|
||||||
|
|
||||||
// 5. does it contain OUR gateway (so that we could create an ack packet)?
|
|
||||||
if !topology.gateway_exists(ack_recipient.gateway()) {
|
|
||||||
return Err(NymTopologyError::NonExistentGatewayError {
|
|
||||||
identity_key: ack_recipient.gateway().to_base58_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. for our target recipient, does it contain THEIR gateway (so that we could create
|
|
||||||
if let Some(recipient) = packet_recipient {
|
|
||||||
if !topology.gateway_exists(recipient.gateway()) {
|
|
||||||
return Err(NymTopologyError::NonExistentGatewayError {
|
|
||||||
identity_key: recipient.gateway().to_base58_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(topology)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<RwLockReadGuard<'a, TopologyAccessorInner>> for TopologyReadPermit<'a> {
|
|
||||||
fn from(read_permit: RwLockReadGuard<'a, TopologyAccessorInner>) -> Self {
|
|
||||||
TopologyReadPermit {
|
|
||||||
permit: read_permit,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct TopologyAccessor {
|
|
||||||
// `RwLock` *seems to* be the better approach for this as write access is only requested every
|
|
||||||
// few seconds, while reads are needed every single packet generated.
|
|
||||||
// However, proper benchmarks will be needed to determine if `RwLock` is indeed a better
|
|
||||||
// approach than a `Mutex`
|
|
||||||
inner: Arc<RwLock<TopologyAccessorInner>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TopologyAccessor {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
TopologyAccessor {
|
|
||||||
inner: Arc::new(RwLock::new(TopologyAccessorInner::new())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_read_permit(&self) -> TopologyReadPermit<'_> {
|
|
||||||
self.inner.read().await.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update_global_topology(&self, new_topology: Option<NymTopology>) {
|
|
||||||
self.inner.write().await.update(new_topology);
|
|
||||||
}
|
|
||||||
|
|
||||||
// only used by the client at startup to get a slightly more reasonable error message
|
|
||||||
// (currently displays as unused because health checker is disabled due to required changes)
|
|
||||||
pub async fn ensure_is_routable(&self) -> Result<(), NymTopologyError> {
|
|
||||||
match &self.inner.read().await.0 {
|
|
||||||
None => Err(NymTopologyError::EmptyNetworkTopology),
|
|
||||||
Some(ref topology) => topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TopologyAccessor {
|
|
||||||
fn default() -> Self {
|
|
||||||
TopologyAccessor::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TopologyRefresherConfig {
|
|
||||||
nym_api_urls: Vec<Url>,
|
|
||||||
refresh_rate: Duration,
|
|
||||||
client_version: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TopologyRefresherConfig {
|
|
||||||
pub fn new(nym_api_urls: Vec<Url>, refresh_rate: Duration, client_version: String) -> Self {
|
|
||||||
TopologyRefresherConfig {
|
|
||||||
nym_api_urls,
|
|
||||||
refresh_rate,
|
|
||||||
client_version,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TopologyRefresher {
|
|
||||||
validator_client: validator_client::client::NymApiClient,
|
|
||||||
client_version: String,
|
|
||||||
|
|
||||||
nym_api_urls: Vec<Url>,
|
|
||||||
topology_accessor: TopologyAccessor,
|
|
||||||
refresh_rate: Duration,
|
|
||||||
|
|
||||||
currently_used_api: usize,
|
|
||||||
was_latest_valid: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TopologyRefresher {
|
|
||||||
pub fn new(mut cfg: TopologyRefresherConfig, topology_accessor: TopologyAccessor) -> Self {
|
|
||||||
cfg.nym_api_urls.shuffle(&mut thread_rng());
|
|
||||||
|
|
||||||
TopologyRefresher {
|
|
||||||
validator_client: validator_client::client::NymApiClient::new(
|
|
||||||
cfg.nym_api_urls[0].clone(),
|
|
||||||
),
|
|
||||||
client_version: cfg.client_version,
|
|
||||||
nym_api_urls: cfg.nym_api_urls,
|
|
||||||
topology_accessor,
|
|
||||||
refresh_rate: cfg.refresh_rate,
|
|
||||||
currently_used_api: 0,
|
|
||||||
was_latest_valid: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn use_next_nym_api(&mut self) {
|
|
||||||
if self.nym_api_urls.len() == 1 {
|
|
||||||
warn!("There's only a single nym API available - it won't be possible to use a different one");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len();
|
|
||||||
self.validator_client
|
|
||||||
.change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verifies whether nodes a reasonably distributed among all mix layers.
|
|
||||||
///
|
|
||||||
/// In ideal world we would have 33% nodes on layer 1, 33% on layer 2 and 33% on layer 3.
|
|
||||||
/// However, this is a rather unrealistic expectation, instead we check whether there exists
|
|
||||||
/// a layer with more than 66% of nodes or with fewer than 15% and if so, we trigger a failure.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `topology`: active topology constructed from validator api data
|
|
||||||
fn check_layer_distribution(&self, active_topology: &NymTopology) -> bool {
|
|
||||||
let mixes = active_topology.mixes();
|
|
||||||
let mixnodes_count = active_topology.num_mixnodes();
|
|
||||||
|
|
||||||
if active_topology.gateways().is_empty() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// trivial check to see if have at least a single node on each layer (regardless of active set size)
|
|
||||||
if mixes.get(&1).is_none() || mixes.get(&2).is_none() || mixes.get(&3).is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let upper_bound = (mixnodes_count as f32 * 0.66) as usize;
|
|
||||||
let lower_bound = (mixnodes_count as f32 * 0.15) as usize;
|
|
||||||
|
|
||||||
let layer1 = mixes.get(&1).unwrap().len();
|
|
||||||
let layer2 = mixes.get(&2).unwrap().len();
|
|
||||||
let layer3 = mixes.get(&3).unwrap().len();
|
|
||||||
|
|
||||||
if layer1 < lower_bound || layer1 > upper_bound {
|
|
||||||
warn!(
|
|
||||||
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
|
|
||||||
mixnodes_count, layer1, layer2, layer3
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if layer2 < lower_bound || layer2 > upper_bound {
|
|
||||||
warn!(
|
|
||||||
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
|
|
||||||
mixnodes_count, layer1, layer2, layer3
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if layer3 < lower_bound || layer3 > upper_bound {
|
|
||||||
warn!(
|
|
||||||
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
|
|
||||||
mixnodes_count, layer1, layer2, layer3
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_current_compatible_topology(&self) -> Option<NymTopology> {
|
|
||||||
// TODO: optimization for the future:
|
|
||||||
// only refresh mixnodes on timer and refresh gateways only when
|
|
||||||
// we have to send to a new, unknown, gateway
|
|
||||||
|
|
||||||
let mixnodes = match self.validator_client.get_cached_active_mixnodes().await {
|
|
||||||
Err(err) => {
|
|
||||||
error!("failed to get network mixnodes - {err}");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Ok(mixes) => mixes,
|
|
||||||
};
|
|
||||||
|
|
||||||
let gateways = match self.validator_client.get_cached_gateways().await {
|
|
||||||
Err(err) => {
|
|
||||||
error!("failed to get network gateways - {err}");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Ok(gateways) => gateways,
|
|
||||||
};
|
|
||||||
|
|
||||||
let topology = nym_topology_from_detailed(mixnodes, gateways)
|
|
||||||
.filter_system_version(&self.client_version);
|
|
||||||
|
|
||||||
if !self.check_layer_distribution(&topology) {
|
|
||||||
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used.");
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(topology)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn refresh(&mut self) {
|
|
||||||
trace!("Refreshing the topology");
|
|
||||||
let new_topology = self.get_current_compatible_topology().await;
|
|
||||||
|
|
||||||
if new_topology.is_none() {
|
|
||||||
self.use_next_nym_api();
|
|
||||||
}
|
|
||||||
|
|
||||||
if new_topology.is_none() && self.was_latest_valid {
|
|
||||||
// if we failed to grab this topology, but the one before it was alright, let's assume
|
|
||||||
// validator had a tiny hiccup and use the old data
|
|
||||||
warn!("we're going to keep on using the old topology for this iteration");
|
|
||||||
self.was_latest_valid = false;
|
|
||||||
return;
|
|
||||||
} else if new_topology.is_some() {
|
|
||||||
self.was_latest_valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.topology_accessor
|
|
||||||
.update_global_topology(new_topology)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn ensure_topology_is_routable(&self) -> Result<(), NymTopologyError> {
|
|
||||||
self.topology_accessor.ensure_is_routable().await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
|
|
||||||
spawn_future(async move {
|
|
||||||
debug!("Started TopologyRefresher with graceful shutdown support");
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let mut interval = tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(
|
|
||||||
self.refresh_rate,
|
|
||||||
));
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
let mut interval =
|
|
||||||
gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32);
|
|
||||||
|
|
||||||
while !shutdown.is_shutdown() {
|
|
||||||
tokio::select! {
|
|
||||||
_ = interval.next() => {
|
|
||||||
self.refresh().await;
|
|
||||||
},
|
|
||||||
_ = shutdown.recv() => {
|
|
||||||
log::trace!("TopologyRefresher: Received shutdown");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shutdown.recv_timeout().await;
|
|
||||||
log::debug!("TopologyRefresher: Exiting");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
|
use nym_sphinx::params::DEFAULT_NUM_MIX_HOPS;
|
||||||
|
use nym_topology::{NymTopology, NymTopologyError};
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::{Notify, RwLock, RwLockReadGuard};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TopologyAccessorInner {
|
||||||
|
controlled_manually: AtomicBool,
|
||||||
|
released_manual_control: Notify,
|
||||||
|
// `RwLock` *seems to* be the better approach for this as write access is only requested every
|
||||||
|
// few seconds, while reads are needed every single packet generated.
|
||||||
|
// However, proper benchmarks will be needed to determine if `RwLock` is indeed a better
|
||||||
|
// approach than a `Mutex`
|
||||||
|
topology: RwLock<Option<NymTopology>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TopologyAccessorInner {
|
||||||
|
fn new() -> Self {
|
||||||
|
TopologyAccessorInner {
|
||||||
|
controlled_manually: AtomicBool::new(false),
|
||||||
|
released_manual_control: Notify::new(),
|
||||||
|
topology: RwLock::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update(&self, new: Option<NymTopology>) {
|
||||||
|
*self.topology.write().await = new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TopologyReadPermit<'a> {
|
||||||
|
permit: RwLockReadGuard<'a, Option<NymTopology>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for TopologyReadPermit<'a> {
|
||||||
|
type Target = Option<NymTopology>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.permit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TopologyReadPermit<'a> {
|
||||||
|
/// Using provided topology read permit, tries to get an immutable reference to the underlying
|
||||||
|
/// topology. For obvious reasons the lifetime of the topology reference is bound to the permit.
|
||||||
|
pub(crate) fn try_get_valid_topology_ref(
|
||||||
|
&'a self,
|
||||||
|
ack_recipient: &Recipient,
|
||||||
|
packet_recipient: Option<&Recipient>,
|
||||||
|
) -> Result<&'a NymTopology, NymTopologyError> {
|
||||||
|
// 1. Have we managed to get anything from the refresher, i.e. have the nym-api queries gone through?
|
||||||
|
let topology = self
|
||||||
|
.permit
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(NymTopologyError::EmptyNetworkTopology)?;
|
||||||
|
|
||||||
|
// 2. does it have any mixnode at all?
|
||||||
|
// 3. does it have any gateways at all?
|
||||||
|
// 4. does it have a mixnode on each layer?
|
||||||
|
topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS)?;
|
||||||
|
|
||||||
|
// 5. does it contain OUR gateway (so that we could create an ack packet)?
|
||||||
|
if !topology.gateway_exists(ack_recipient.gateway()) {
|
||||||
|
return Err(NymTopologyError::NonExistentGatewayError {
|
||||||
|
identity_key: ack_recipient.gateway().to_base58_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. for our target recipient, does it contain THEIR gateway (so that we could create
|
||||||
|
if let Some(recipient) = packet_recipient {
|
||||||
|
if !topology.gateway_exists(recipient.gateway()) {
|
||||||
|
return Err(NymTopologyError::NonExistentGatewayError {
|
||||||
|
identity_key: recipient.gateway().to_base58_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(topology)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<RwLockReadGuard<'a, Option<NymTopology>>> for TopologyReadPermit<'a> {
|
||||||
|
fn from(read_permit: RwLockReadGuard<'a, Option<NymTopology>>) -> Self {
|
||||||
|
TopologyReadPermit {
|
||||||
|
permit: read_permit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct TopologyAccessor {
|
||||||
|
inner: Arc<TopologyAccessorInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TopologyAccessor {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
TopologyAccessor {
|
||||||
|
inner: Arc::new(TopologyAccessorInner::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn controlled_manually(&self) -> bool {
|
||||||
|
self.inner.controlled_manually.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_read_permit(&self) -> TopologyReadPermit<'_> {
|
||||||
|
self.inner.topology.read().await.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn update_global_topology(&self, new_topology: Option<NymTopology>) {
|
||||||
|
self.inner.update(new_topology).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn wait_for_released_manual_control(&self) {
|
||||||
|
self.inner.released_manual_control.notified().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn current_topology(&self) -> Option<NymTopology> {
|
||||||
|
self.inner.topology.read().await.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn manually_change_topology(&self, new_topology: NymTopology) {
|
||||||
|
self.inner.controlled_manually.store(true, Ordering::SeqCst);
|
||||||
|
self.inner.update(Some(new_topology)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release_manual_control(&self) {
|
||||||
|
self.inner
|
||||||
|
.controlled_manually
|
||||||
|
.store(false, Ordering::SeqCst);
|
||||||
|
self.inner.released_manual_control.notify_waiters();
|
||||||
|
}
|
||||||
|
|
||||||
|
// only used by the client at startup to get a slightly more reasonable error message
|
||||||
|
// (currently displays as unused because health checker is disabled due to required changes)
|
||||||
|
pub async fn ensure_is_routable(&self) -> Result<(), NymTopologyError> {
|
||||||
|
match self.inner.topology.read().await.deref() {
|
||||||
|
None => Err(NymTopologyError::EmptyNetworkTopology),
|
||||||
|
Some(ref topology) => topology.ensure_can_construct_path_through(DEFAULT_NUM_MIX_HOPS),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TopologyAccessor {
|
||||||
|
fn default() -> Self {
|
||||||
|
TopologyAccessor::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::spawn_future;
|
||||||
|
pub(crate) use accessor::{TopologyAccessor, TopologyReadPermit};
|
||||||
|
use futures::StreamExt;
|
||||||
|
use log::*;
|
||||||
|
use nym_topology::provider_trait::TopologyProvider;
|
||||||
|
use nym_topology::NymTopologyError;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
mod accessor;
|
||||||
|
pub(crate) mod nym_api_provider;
|
||||||
|
|
||||||
|
// TODO: move it to config later
|
||||||
|
const MAX_FAILURE_COUNT: usize = 10;
|
||||||
|
|
||||||
|
pub struct TopologyRefresherConfig {
|
||||||
|
refresh_rate: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TopologyRefresherConfig {
|
||||||
|
pub fn new(refresh_rate: Duration) -> Self {
|
||||||
|
TopologyRefresherConfig { refresh_rate }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TopologyRefresher {
|
||||||
|
topology_provider: Box<dyn TopologyProvider>,
|
||||||
|
topology_accessor: TopologyAccessor,
|
||||||
|
|
||||||
|
refresh_rate: Duration,
|
||||||
|
consecutive_failure_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TopologyRefresher {
|
||||||
|
pub fn new(
|
||||||
|
cfg: TopologyRefresherConfig,
|
||||||
|
topology_accessor: TopologyAccessor,
|
||||||
|
topology_provider: Box<dyn TopologyProvider>,
|
||||||
|
) -> Self {
|
||||||
|
TopologyRefresher {
|
||||||
|
topology_provider,
|
||||||
|
topology_accessor,
|
||||||
|
refresh_rate: cfg.refresh_rate,
|
||||||
|
consecutive_failure_count: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_topology_provider(&mut self, provider: Box<dyn TopologyProvider>) {
|
||||||
|
self.topology_provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn try_refresh(&mut self) {
|
||||||
|
trace!("Refreshing the topology");
|
||||||
|
|
||||||
|
if self.topology_accessor.controlled_manually() {
|
||||||
|
info!("topology is being controlled manually - we're going to wait until the control is released...");
|
||||||
|
self.topology_accessor
|
||||||
|
.wait_for_released_manual_control()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_topology = self.topology_provider.get_new_topology().await;
|
||||||
|
if new_topology.is_none() {
|
||||||
|
warn!("failed to obtain new network topology");
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_topology.is_none() && self.consecutive_failure_count < MAX_FAILURE_COUNT {
|
||||||
|
// if we failed to grab this topology, but the one before it was alright, let's assume
|
||||||
|
// validator had a tiny hiccup and use the old data
|
||||||
|
warn!("we're going to keep on using the old topology for this iteration");
|
||||||
|
self.consecutive_failure_count += 1;
|
||||||
|
return;
|
||||||
|
} else if new_topology.is_some() {
|
||||||
|
self.consecutive_failure_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.topology_accessor
|
||||||
|
.update_global_topology(new_topology)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn ensure_topology_is_routable(&self) -> Result<(), NymTopologyError> {
|
||||||
|
self.topology_accessor.ensure_is_routable().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_with_shutdown(mut self, mut shutdown: nym_task::TaskClient) {
|
||||||
|
spawn_future(async move {
|
||||||
|
debug!("Started TopologyRefresher with graceful shutdown support");
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
let mut interval = tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(
|
||||||
|
self.refresh_rate,
|
||||||
|
));
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let mut interval =
|
||||||
|
gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32);
|
||||||
|
|
||||||
|
while !shutdown.is_shutdown() {
|
||||||
|
tokio::select! {
|
||||||
|
_ = interval.next() => {
|
||||||
|
self.try_refresh().await;
|
||||||
|
},
|
||||||
|
_ = shutdown.recv() => {
|
||||||
|
log::trace!("TopologyRefresher: Received shutdown");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shutdown.recv_timeout().await;
|
||||||
|
log::debug!("TopologyRefresher: Exiting");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use log::{error, warn};
|
||||||
|
use nym_topology::provider_trait::TopologyProvider;
|
||||||
|
use nym_topology::{nym_topology_from_detailed, NymTopology, NymTopologyError};
|
||||||
|
use rand::prelude::SliceRandom;
|
||||||
|
use rand::thread_rng;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
pub(crate) struct NymApiTopologyProvider {
|
||||||
|
validator_client: validator_client::client::NymApiClient,
|
||||||
|
nym_api_urls: Vec<Url>,
|
||||||
|
|
||||||
|
client_version: String,
|
||||||
|
currently_used_api: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NymApiTopologyProvider {
|
||||||
|
pub(crate) fn new(mut nym_api_urls: Vec<Url>, client_version: String) -> Self {
|
||||||
|
nym_api_urls.shuffle(&mut thread_rng());
|
||||||
|
|
||||||
|
NymApiTopologyProvider {
|
||||||
|
validator_client: validator_client::client::NymApiClient::new(nym_api_urls[0].clone()),
|
||||||
|
nym_api_urls,
|
||||||
|
client_version,
|
||||||
|
currently_used_api: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn use_next_nym_api(&mut self) {
|
||||||
|
if self.nym_api_urls.len() == 1 {
|
||||||
|
warn!("There's only a single nym API available - it won't be possible to use a different one");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currently_used_api = (self.currently_used_api + 1) % self.nym_api_urls.len();
|
||||||
|
self.validator_client
|
||||||
|
.change_nym_api(self.nym_api_urls[self.currently_used_api].clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies whether nodes a reasonably distributed among all mix layers.
|
||||||
|
///
|
||||||
|
/// In ideal world we would have 33% nodes on layer 1, 33% on layer 2 and 33% on layer 3.
|
||||||
|
/// However, this is a rather unrealistic expectation, instead we check whether there exists
|
||||||
|
/// a layer with more than 66% of nodes or with fewer than 15% and if so, we trigger a failure.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `topology`: active topology constructed from validator api data
|
||||||
|
fn check_layer_distribution(
|
||||||
|
&self,
|
||||||
|
active_topology: &NymTopology,
|
||||||
|
) -> Result<(), NymTopologyError> {
|
||||||
|
let lower_threshold = 0.15;
|
||||||
|
let upper_threshold = 0.66;
|
||||||
|
active_topology.ensure_even_layer_distribution(lower_threshold, upper_threshold)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
|
||||||
|
let mixnodes = match self.validator_client.get_cached_active_mixnodes().await {
|
||||||
|
Err(err) => {
|
||||||
|
error!("failed to get network mixnodes - {err}");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Ok(mixes) => mixes,
|
||||||
|
};
|
||||||
|
|
||||||
|
let gateways = match self.validator_client.get_cached_gateways().await {
|
||||||
|
Err(err) => {
|
||||||
|
error!("failed to get network gateways - {err}");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Ok(gateways) => gateways,
|
||||||
|
};
|
||||||
|
|
||||||
|
let topology = nym_topology_from_detailed(mixnodes, gateways)
|
||||||
|
.filter_system_version(&self.client_version);
|
||||||
|
|
||||||
|
if let Err(err) = self.check_layer_distribution(&topology) {
|
||||||
|
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used: {err}");
|
||||||
|
self.use_next_nym_api();
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(topology)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hehe, wasm
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
#[async_trait]
|
||||||
|
impl TopologyProvider for NymApiTopologyProvider {
|
||||||
|
async fn get_new_topology(&mut self) -> Option<NymTopology> {
|
||||||
|
self.get_current_compatible_topology().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl TopologyProvider for NymApiTopologyProvider {
|
||||||
|
async fn get_new_topology(&mut self) -> Option<NymTopology> {
|
||||||
|
self.get_current_compatible_topology().await
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use nym_config::defaults::NymNetworkDetails;
|
use nym_config::defaults::NymNetworkDetails;
|
||||||
use nym_config::{NymConfig, OptionalSet, DB_FILE_NAME};
|
use nym_config::{NymConfig, OptionalSet, CRED_DB_FILE_NAME};
|
||||||
use nym_sphinx::params::PacketSize;
|
use nym_sphinx::params::PacketSize;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@@ -13,6 +13,7 @@ use url::Url;
|
|||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
pub mod old_config_v1_1_13;
|
||||||
pub mod persistence;
|
pub mod persistence;
|
||||||
|
|
||||||
pub const MISSING_VALUE: &str = "MISSING VALUE";
|
pub const MISSING_VALUE: &str = "MISSING VALUE";
|
||||||
@@ -210,11 +211,12 @@ impl<T> Config<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_high_default_traffic_volume(&mut self) {
|
pub fn set_high_default_traffic_volume(&mut self) {
|
||||||
self.debug.average_packet_delay = Duration::from_millis(10);
|
self.debug.traffic.average_packet_delay = Duration::from_millis(10);
|
||||||
// basically don't really send cover messages
|
// basically don't really send cover messages
|
||||||
self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2_000_000);
|
self.debug.cover_traffic.loop_cover_traffic_average_delay =
|
||||||
|
Duration::from_millis(2_000_000);
|
||||||
// 250 "real" messages / s
|
// 250 "real" messages / s
|
||||||
self.debug.message_sending_average_delay = Duration::from_millis(4);
|
self.debug.traffic.message_sending_average_delay = Duration::from_millis(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_disabled_cover_traffic(mut self, disabled: bool) -> Self {
|
pub fn with_disabled_cover_traffic(mut self, disabled: bool) -> Self {
|
||||||
@@ -225,8 +227,8 @@ impl<T> Config<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_no_cover_traffic(&mut self) {
|
pub fn set_no_cover_traffic(&mut self) {
|
||||||
self.debug.disable_loop_cover_traffic_stream = true;
|
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
|
||||||
self.debug.disable_main_poisson_packet_distribution = true;
|
self.debug.traffic.disable_main_poisson_packet_distribution = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_custom_version(&mut self, version: &str) {
|
pub fn set_custom_version(&mut self, version: &str) {
|
||||||
@@ -311,87 +313,93 @@ impl<T> Config<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_average_packet_delay(&self) -> Duration {
|
pub fn get_average_packet_delay(&self) -> Duration {
|
||||||
self.debug.average_packet_delay
|
self.debug.traffic.average_packet_delay
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_average_ack_delay(&self) -> Duration {
|
pub fn get_average_ack_delay(&self) -> Duration {
|
||||||
self.debug.average_ack_delay
|
self.debug.acknowledgements.average_ack_delay
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ack_wait_multiplier(&self) -> f64 {
|
pub fn get_ack_wait_multiplier(&self) -> f64 {
|
||||||
self.debug.ack_wait_multiplier
|
self.debug.acknowledgements.ack_wait_multiplier
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ack_wait_addition(&self) -> Duration {
|
pub fn get_ack_wait_addition(&self) -> Duration {
|
||||||
self.debug.ack_wait_addition
|
self.debug.acknowledgements.ack_wait_addition
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_loop_cover_traffic_average_delay(&self) -> Duration {
|
pub fn get_loop_cover_traffic_average_delay(&self) -> Duration {
|
||||||
self.debug.loop_cover_traffic_average_delay
|
self.debug.cover_traffic.loop_cover_traffic_average_delay
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_message_sending_average_delay(&self) -> Duration {
|
pub fn get_message_sending_average_delay(&self) -> Duration {
|
||||||
self.debug.message_sending_average_delay
|
self.debug.traffic.message_sending_average_delay
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_gateway_response_timeout(&self) -> Duration {
|
pub fn get_gateway_response_timeout(&self) -> Duration {
|
||||||
self.debug.gateway_response_timeout
|
self.debug.gateway_connection.gateway_response_timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_topology_refresh_rate(&self) -> Duration {
|
pub fn get_topology_refresh_rate(&self) -> Duration {
|
||||||
self.debug.topology_refresh_rate
|
self.debug.topology.topology_refresh_rate
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_topology_resolution_timeout(&self) -> Duration {
|
pub fn get_topology_resolution_timeout(&self) -> Duration {
|
||||||
self.debug.topology_resolution_timeout
|
self.debug.topology.topology_resolution_timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_disabled_loop_cover_traffic_stream(&self) -> bool {
|
pub fn get_disabled_loop_cover_traffic_stream(&self) -> bool {
|
||||||
self.debug.disable_loop_cover_traffic_stream
|
self.debug.cover_traffic.disable_loop_cover_traffic_stream
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_disabled_main_poisson_packet_distribution(&self) -> bool {
|
pub fn get_disabled_main_poisson_packet_distribution(&self) -> bool {
|
||||||
self.debug.disable_main_poisson_packet_distribution
|
self.debug.traffic.disable_main_poisson_packet_distribution
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_use_extended_packet_size(&self) -> Option<ExtendedPacketSize> {
|
pub fn get_use_extended_packet_size(&self) -> Option<ExtendedPacketSize> {
|
||||||
self.debug.use_extended_packet_size
|
self.debug.traffic.use_extended_packet_size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_minimum_reply_surb_storage_threshold(&self) -> usize {
|
pub fn get_minimum_reply_surb_storage_threshold(&self) -> usize {
|
||||||
self.debug.minimum_reply_surb_storage_threshold
|
self.debug.reply_surbs.minimum_reply_surb_storage_threshold
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_maximum_reply_surb_storage_threshold(&self) -> usize {
|
pub fn get_maximum_reply_surb_storage_threshold(&self) -> usize {
|
||||||
self.debug.maximum_reply_surb_storage_threshold
|
self.debug.reply_surbs.maximum_reply_surb_storage_threshold
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_minimum_reply_surb_request_size(&self) -> u32 {
|
pub fn get_minimum_reply_surb_request_size(&self) -> u32 {
|
||||||
self.debug.minimum_reply_surb_request_size
|
self.debug.reply_surbs.minimum_reply_surb_request_size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_maximum_reply_surb_request_size(&self) -> u32 {
|
pub fn get_maximum_reply_surb_request_size(&self) -> u32 {
|
||||||
self.debug.maximum_reply_surb_request_size
|
self.debug.reply_surbs.maximum_reply_surb_request_size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_maximum_allowed_reply_surb_request_size(&self) -> u32 {
|
pub fn get_maximum_allowed_reply_surb_request_size(&self) -> u32 {
|
||||||
self.debug.maximum_allowed_reply_surb_request_size
|
self.debug
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_allowed_reply_surb_request_size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_maximum_reply_surb_rerequest_waiting_period(&self) -> Duration {
|
pub fn get_maximum_reply_surb_rerequest_waiting_period(&self) -> Duration {
|
||||||
self.debug.maximum_reply_surb_rerequest_waiting_period
|
self.debug
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_rerequest_waiting_period
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_maximum_reply_surb_drop_waiting_period(&self) -> Duration {
|
pub fn get_maximum_reply_surb_drop_waiting_period(&self) -> Duration {
|
||||||
self.debug.maximum_reply_surb_drop_waiting_period
|
self.debug
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_drop_waiting_period
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_maximum_reply_surb_age(&self) -> Duration {
|
pub fn get_maximum_reply_surb_age(&self) -> Duration {
|
||||||
self.debug.maximum_reply_surb_age
|
self.debug.reply_surbs.maximum_reply_surb_age
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_maximum_reply_key_age(&self) -> Duration {
|
pub fn get_maximum_reply_key_age(&self) -> Duration {
|
||||||
self.debug.maximum_reply_key_age
|
self.debug.reply_surbs.maximum_reply_key_age
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,63 +458,63 @@ impl From<nym_topology::gateway::Node> for GatewayEndpointConfig {
|
|||||||
pub struct Client<T> {
|
pub struct Client<T> {
|
||||||
/// Version of the client for which this configuration was created.
|
/// Version of the client for which this configuration was created.
|
||||||
#[serde(default = "missing_string_value")]
|
#[serde(default = "missing_string_value")]
|
||||||
version: String,
|
pub version: String,
|
||||||
|
|
||||||
/// ID specifies the human readable ID of this particular client.
|
/// ID specifies the human readable ID of this particular client.
|
||||||
id: String,
|
pub id: String,
|
||||||
|
|
||||||
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
|
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
|
||||||
/// to claim bandwidth without presenting bandwidth credentials.
|
/// to claim bandwidth without presenting bandwidth credentials.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
disabled_credentials_mode: bool,
|
pub disabled_credentials_mode: bool,
|
||||||
|
|
||||||
/// Addresses to nyxd validators via which the client can communicate with the chain.
|
/// Addresses to nyxd validators via which the client can communicate with the chain.
|
||||||
#[serde(alias = "validator_urls")]
|
#[serde(alias = "validator_urls")]
|
||||||
nyxd_urls: Vec<Url>,
|
pub nyxd_urls: Vec<Url>,
|
||||||
|
|
||||||
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
||||||
#[serde(alias = "validator_api_urls")]
|
#[serde(alias = "validator_api_urls")]
|
||||||
nym_api_urls: Vec<Url>,
|
pub nym_api_urls: Vec<Url>,
|
||||||
|
|
||||||
/// Path to file containing private identity key.
|
/// Path to file containing private identity key.
|
||||||
private_identity_key_file: PathBuf,
|
pub private_identity_key_file: PathBuf,
|
||||||
|
|
||||||
/// Path to file containing public identity key.
|
/// Path to file containing public identity key.
|
||||||
public_identity_key_file: PathBuf,
|
pub public_identity_key_file: PathBuf,
|
||||||
|
|
||||||
/// Path to file containing private encryption key.
|
/// Path to file containing private encryption key.
|
||||||
private_encryption_key_file: PathBuf,
|
pub private_encryption_key_file: PathBuf,
|
||||||
|
|
||||||
/// Path to file containing public encryption key.
|
/// Path to file containing public encryption key.
|
||||||
public_encryption_key_file: PathBuf,
|
pub public_encryption_key_file: PathBuf,
|
||||||
|
|
||||||
/// Path to file containing shared key derived with the specified gateway that is used
|
/// Path to file containing shared key derived with the specified gateway that is used
|
||||||
/// for all communication with it.
|
/// for all communication with it.
|
||||||
gateway_shared_key_file: PathBuf,
|
pub gateway_shared_key_file: PathBuf,
|
||||||
|
|
||||||
/// Path to file containing key used for encrypting and decrypting the content of an
|
/// Path to file containing key used for encrypting and decrypting the content of an
|
||||||
/// acknowledgement so that nobody besides the client knows which packet it refers to.
|
/// acknowledgement so that nobody besides the client knows which packet it refers to.
|
||||||
ack_key_file: PathBuf,
|
pub ack_key_file: PathBuf,
|
||||||
|
|
||||||
/// Information regarding how the client should send data to gateway.
|
/// Information regarding how the client should send data to gateway.
|
||||||
gateway_endpoint: GatewayEndpointConfig,
|
pub gateway_endpoint: GatewayEndpointConfig,
|
||||||
|
|
||||||
/// Path to the database containing bandwidth credentials of this client.
|
/// Path to the database containing bandwidth credentials of this client.
|
||||||
database_path: PathBuf,
|
pub database_path: PathBuf,
|
||||||
|
|
||||||
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
|
/// Path to the persistent store for received reply surbs, unused encryption keys and used sender tags.
|
||||||
// this was set to use #[serde(default)] for the purposes of compatibility for multi-surbs introduced in 1.1.4.
|
// this was set to use #[serde(default)] for the purposes of compatibility for multi-surbs introduced in 1.1.4.
|
||||||
// if you're reading this message and we have already introduced some breaking changes, feel free
|
// if you're reading this message and we have already introduced some breaking changes, feel free
|
||||||
// to remove that attribute since at this point the client configs should have gotten regenerated
|
// to remove that attribute since at this point the client configs should have gotten regenerated
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
reply_surb_database_path: PathBuf,
|
pub reply_surb_database_path: PathBuf,
|
||||||
|
|
||||||
/// nym_home_directory specifies absolute path to the home nym Clients directory.
|
/// nym_home_directory specifies absolute path to the home nym Clients directory.
|
||||||
/// It is expected to use default value and hence .toml file should not redefine this field.
|
/// It is expected to use default value and hence .toml file should not redefine this field.
|
||||||
nym_root_directory: PathBuf,
|
pub nym_root_directory: PathBuf,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
super_struct: PhantomData<T>,
|
pub super_struct: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: NymConfig> Default for Client<T> {
|
impl<T: NymConfig> Default for Client<T> {
|
||||||
@@ -579,7 +587,7 @@ impl<T: NymConfig> Client<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_database_path(id: &str) -> PathBuf {
|
fn default_database_path(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(id).join(DB_FILE_NAME)
|
T::default_data_directory(id).join(CRED_DB_FILE_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,9 +595,9 @@ impl<T: NymConfig> Client<T> {
|
|||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Logging {}
|
pub struct Logging {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||||
#[serde(default, deny_unknown_fields)]
|
#[serde(default, deny_unknown_fields)]
|
||||||
pub struct DebugConfig {
|
pub struct Traffic {
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
/// sent packet is going to be delayed at any given mix node.
|
/// sent packet is going to be delayed at any given mix node.
|
||||||
/// So for a packet going through three mix nodes, on average, it will take three times this value
|
/// So for a packet going through three mix nodes, on average, it will take three times this value
|
||||||
@@ -597,6 +605,74 @@ pub struct DebugConfig {
|
|||||||
#[serde(with = "humantime_serde")]
|
#[serde(with = "humantime_serde")]
|
||||||
pub average_packet_delay: Duration,
|
pub average_packet_delay: Duration,
|
||||||
|
|
||||||
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
|
/// it is going to take another 'real traffic stream' message to be sent.
|
||||||
|
/// If no real packets are available and cover traffic is enabled,
|
||||||
|
/// a loop cover message is sent instead in order to preserve the rate.
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub message_sending_average_delay: Duration,
|
||||||
|
|
||||||
|
/// Controls whether the main packet stream constantly produces packets according to the predefined
|
||||||
|
/// poisson distribution.
|
||||||
|
pub disable_main_poisson_packet_distribution: bool,
|
||||||
|
|
||||||
|
/// Controls whether the sent sphinx packet use a NON-DEFAULT bigger size.
|
||||||
|
pub use_extended_packet_size: Option<ExtendedPacketSize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Traffic {
|
||||||
|
fn default() -> Self {
|
||||||
|
Traffic {
|
||||||
|
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||||
|
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
|
||||||
|
disable_main_poisson_packet_distribution: false,
|
||||||
|
use_extended_packet_size: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(default, deny_unknown_fields)]
|
||||||
|
pub struct CoverTraffic {
|
||||||
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
|
/// it is going to take for another loop cover traffic message to be sent.
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub loop_cover_traffic_average_delay: Duration,
|
||||||
|
|
||||||
|
/// Controls whether the dedicated loop cover traffic stream should be enabled.
|
||||||
|
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
|
||||||
|
pub disable_loop_cover_traffic_stream: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CoverTraffic {
|
||||||
|
fn default() -> Self {
|
||||||
|
CoverTraffic {
|
||||||
|
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
|
||||||
|
disable_loop_cover_traffic_stream: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(default, deny_unknown_fields)]
|
||||||
|
pub struct GatewayConnection {
|
||||||
|
/// How long we're willing to wait for a response to a message sent to the gateway,
|
||||||
|
/// before giving up on it.
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub gateway_response_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GatewayConnection {
|
||||||
|
fn default() -> Self {
|
||||||
|
GatewayConnection {
|
||||||
|
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(default, deny_unknown_fields)]
|
||||||
|
pub struct Acknowledgements {
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
/// sent acknowledgement is going to be delayed at any given mix node.
|
/// sent acknowledgement is going to be delayed at any given mix node.
|
||||||
/// So for an ack going through three mix nodes, on average, it will take three times this value
|
/// So for an ack going through three mix nodes, on average, it will take three times this value
|
||||||
@@ -614,24 +690,21 @@ pub struct DebugConfig {
|
|||||||
/// In an ideal network with 0 latency, this value would have been 0.
|
/// In an ideal network with 0 latency, this value would have been 0.
|
||||||
#[serde(with = "humantime_serde")]
|
#[serde(with = "humantime_serde")]
|
||||||
pub ack_wait_addition: Duration,
|
pub ack_wait_addition: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
impl Default for Acknowledgements {
|
||||||
/// it is going to take for another loop cover traffic message to be sent.
|
fn default() -> Self {
|
||||||
#[serde(with = "humantime_serde")]
|
Acknowledgements {
|
||||||
pub loop_cover_traffic_average_delay: Duration,
|
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||||
|
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
|
||||||
/// it is going to take another 'real traffic stream' message to be sent.
|
}
|
||||||
/// If no real packets are available and cover traffic is enabled,
|
}
|
||||||
/// a loop cover message is sent instead in order to preserve the rate.
|
}
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub message_sending_average_delay: Duration,
|
|
||||||
|
|
||||||
/// How long we're willing to wait for a response to a message sent to the gateway,
|
|
||||||
/// before giving up on it.
|
|
||||||
#[serde(with = "humantime_serde")]
|
|
||||||
pub gateway_response_timeout: Duration,
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(default, deny_unknown_fields)]
|
||||||
|
pub struct Topology {
|
||||||
/// The uniform delay every which clients are querying the directory server
|
/// The uniform delay every which clients are querying the directory server
|
||||||
/// to try to obtain a compatible network topology to send sphinx packets through.
|
/// to try to obtain a compatible network topology to send sphinx packets through.
|
||||||
#[serde(with = "humantime_serde")]
|
#[serde(with = "humantime_serde")]
|
||||||
@@ -642,18 +715,20 @@ pub struct DebugConfig {
|
|||||||
/// did not reach its destination.
|
/// did not reach its destination.
|
||||||
#[serde(with = "humantime_serde")]
|
#[serde(with = "humantime_serde")]
|
||||||
pub topology_resolution_timeout: Duration,
|
pub topology_resolution_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
/// Controls whether the dedicated loop cover traffic stream should be enabled.
|
impl Default for Topology {
|
||||||
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
|
fn default() -> Self {
|
||||||
pub disable_loop_cover_traffic_stream: bool,
|
Topology {
|
||||||
|
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
|
||||||
/// Controls whether the main packet stream constantly produces packets according to the predefined
|
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
||||||
/// poisson distribution.
|
}
|
||||||
pub disable_main_poisson_packet_distribution: bool,
|
}
|
||||||
|
}
|
||||||
/// Controls whether the sent sphinx packet use a NON-DEFAULT bigger size.
|
|
||||||
pub use_extended_packet_size: Option<ExtendedPacketSize>,
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(default, deny_unknown_fields)]
|
||||||
|
pub struct ReplySurbs {
|
||||||
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
|
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
|
||||||
/// It can only allow to go below that value if its to request additional reply surbs.
|
/// It can only allow to go below that value if its to request additional reply surbs.
|
||||||
pub minimum_reply_surb_storage_threshold: usize,
|
pub minimum_reply_surb_storage_threshold: usize,
|
||||||
@@ -691,29 +766,9 @@ pub struct DebugConfig {
|
|||||||
pub maximum_reply_key_age: Duration,
|
pub maximum_reply_key_age: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
impl Default for ReplySurbs {
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum ExtendedPacketSize {
|
|
||||||
Extended8,
|
|
||||||
Extended16,
|
|
||||||
Extended32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for DebugConfig {
|
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
DebugConfig {
|
ReplySurbs {
|
||||||
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
|
||||||
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
|
||||||
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
|
|
||||||
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
|
|
||||||
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
|
|
||||||
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
|
|
||||||
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
|
||||||
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
|
|
||||||
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
|
||||||
disable_loop_cover_traffic_stream: false,
|
|
||||||
disable_main_poisson_packet_distribution: false,
|
|
||||||
use_extended_packet_size: None,
|
|
||||||
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
||||||
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
||||||
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
|
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
|
||||||
@@ -728,6 +783,52 @@ impl Default for DebugConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(default, deny_unknown_fields)]
|
||||||
|
pub struct DebugConfig {
|
||||||
|
/// Defines all configuration options related to traffic streams.
|
||||||
|
pub traffic: Traffic,
|
||||||
|
|
||||||
|
/// Defines all configuration options related to cover traffic stream(s).
|
||||||
|
pub cover_traffic: CoverTraffic,
|
||||||
|
|
||||||
|
/// Defines all configuration options related to the gateway connection.
|
||||||
|
pub gateway_connection: GatewayConnection,
|
||||||
|
|
||||||
|
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
|
||||||
|
pub acknowledgements: Acknowledgements,
|
||||||
|
|
||||||
|
/// Defines all configuration options related topology, such as refresh rates or timeouts.
|
||||||
|
pub topology: Topology,
|
||||||
|
|
||||||
|
/// Defines all configuration options related to reply SURBs.
|
||||||
|
pub reply_surbs: ReplySurbs,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum ExtendedPacketSize {
|
||||||
|
Extended8,
|
||||||
|
Extended16,
|
||||||
|
Extended32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// it could be derived, sure, but I'd rather have an explicit implementation in case we had to change
|
||||||
|
// something manually at some point
|
||||||
|
#[allow(clippy::derivable_impls)]
|
||||||
|
impl Default for DebugConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
DebugConfig {
|
||||||
|
traffic: Default::default(),
|
||||||
|
cover_traffic: Default::default(),
|
||||||
|
gateway_connection: Default::default(),
|
||||||
|
acknowledgements: Default::default(),
|
||||||
|
topology: Default::default(),
|
||||||
|
reply_surbs: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ExtendedPacketSize> for PacketSize {
|
impl From<ExtendedPacketSize> for PacketSize {
|
||||||
fn from(size: ExtendedPacketSize) -> PacketSize {
|
fn from(size: ExtendedPacketSize) -> PacketSize {
|
||||||
match size {
|
match size {
|
||||||
|
|||||||
@@ -0,0 +1,198 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::config::{
|
||||||
|
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, ExtendedPacketSize,
|
||||||
|
GatewayConnection, Logging, ReplySurbs, Topology, Traffic, DEFAULT_ACK_WAIT_ADDITION,
|
||||||
|
DEFAULT_ACK_WAIT_MULTIPLIER, DEFAULT_AVERAGE_PACKET_DELAY, DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||||
|
DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
|
||||||
|
DEFAULT_MAXIMUM_REPLY_KEY_AGE, DEFAULT_MAXIMUM_REPLY_SURB_AGE,
|
||||||
|
DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD, DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
|
||||||
|
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
|
||||||
|
DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD, DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
|
||||||
|
DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE, DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
||||||
|
DEFAULT_TOPOLOGY_REFRESH_RATE, DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
||||||
|
};
|
||||||
|
use nym_config::NymConfig;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct OldConfigV1_1_13<T> {
|
||||||
|
pub client: Client<T>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
logging: Logging,
|
||||||
|
#[serde(default)]
|
||||||
|
debug: OldDebugConfigV1_1_13,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: NymConfig> Default for OldConfigV1_1_13<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
OldConfigV1_1_13 {
|
||||||
|
client: Client::<T>::default(),
|
||||||
|
logging: Default::default(),
|
||||||
|
debug: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(default, deny_unknown_fields)]
|
||||||
|
pub struct OldDebugConfigV1_1_13 {
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub average_packet_delay: Duration,
|
||||||
|
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub average_ack_delay: Duration,
|
||||||
|
|
||||||
|
pub ack_wait_multiplier: f64,
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub ack_wait_addition: Duration,
|
||||||
|
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub loop_cover_traffic_average_delay: Duration,
|
||||||
|
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub message_sending_average_delay: Duration,
|
||||||
|
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub gateway_response_timeout: Duration,
|
||||||
|
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub topology_refresh_rate: Duration,
|
||||||
|
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub topology_resolution_timeout: Duration,
|
||||||
|
|
||||||
|
pub disable_loop_cover_traffic_stream: bool,
|
||||||
|
|
||||||
|
pub disable_main_poisson_packet_distribution: bool,
|
||||||
|
|
||||||
|
pub use_extended_packet_size: Option<ExtendedPacketSize>,
|
||||||
|
|
||||||
|
pub minimum_reply_surb_storage_threshold: usize,
|
||||||
|
|
||||||
|
pub maximum_reply_surb_storage_threshold: usize,
|
||||||
|
|
||||||
|
pub minimum_reply_surb_request_size: u32,
|
||||||
|
|
||||||
|
pub maximum_reply_surb_request_size: u32,
|
||||||
|
|
||||||
|
pub maximum_allowed_reply_surb_request_size: u32,
|
||||||
|
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub maximum_reply_surb_rerequest_waiting_period: Duration,
|
||||||
|
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub maximum_reply_surb_drop_waiting_period: Duration,
|
||||||
|
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub maximum_reply_surb_age: Duration,
|
||||||
|
|
||||||
|
#[serde(with = "humantime_serde")]
|
||||||
|
pub maximum_reply_key_age: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OldDebugConfigV1_1_13> for DebugConfig {
|
||||||
|
fn from(value: OldDebugConfigV1_1_13) -> Self {
|
||||||
|
DebugConfig {
|
||||||
|
traffic: Traffic {
|
||||||
|
average_packet_delay: value.average_packet_delay,
|
||||||
|
message_sending_average_delay: value.message_sending_average_delay,
|
||||||
|
disable_main_poisson_packet_distribution: value
|
||||||
|
.disable_main_poisson_packet_distribution,
|
||||||
|
use_extended_packet_size: value.use_extended_packet_size,
|
||||||
|
},
|
||||||
|
cover_traffic: CoverTraffic {
|
||||||
|
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
|
||||||
|
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
|
||||||
|
},
|
||||||
|
gateway_connection: GatewayConnection {
|
||||||
|
gateway_response_timeout: value.gateway_response_timeout,
|
||||||
|
},
|
||||||
|
acknowledgements: Acknowledgements {
|
||||||
|
average_ack_delay: value.average_ack_delay,
|
||||||
|
ack_wait_multiplier: value.ack_wait_multiplier,
|
||||||
|
ack_wait_addition: value.ack_wait_addition,
|
||||||
|
},
|
||||||
|
topology: Topology {
|
||||||
|
topology_refresh_rate: value.topology_refresh_rate,
|
||||||
|
topology_resolution_timeout: value.topology_resolution_timeout,
|
||||||
|
},
|
||||||
|
reply_surbs: ReplySurbs {
|
||||||
|
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
|
||||||
|
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
|
||||||
|
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
|
||||||
|
maximum_reply_surb_request_size: value.maximum_reply_surb_request_size,
|
||||||
|
maximum_allowed_reply_surb_request_size: value
|
||||||
|
.maximum_allowed_reply_surb_request_size,
|
||||||
|
maximum_reply_surb_rerequest_waiting_period: value
|
||||||
|
.maximum_reply_surb_rerequest_waiting_period,
|
||||||
|
maximum_reply_surb_drop_waiting_period: value
|
||||||
|
.maximum_reply_surb_drop_waiting_period,
|
||||||
|
maximum_reply_surb_age: value.maximum_reply_surb_age,
|
||||||
|
maximum_reply_key_age: value.maximum_reply_key_age,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OldDebugConfigV1_1_13 {
|
||||||
|
fn default() -> Self {
|
||||||
|
OldDebugConfigV1_1_13 {
|
||||||
|
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||||
|
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||||
|
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
|
||||||
|
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
|
||||||
|
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
|
||||||
|
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
|
||||||
|
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||||
|
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
|
||||||
|
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
||||||
|
disable_loop_cover_traffic_stream: false,
|
||||||
|
disable_main_poisson_packet_distribution: false,
|
||||||
|
use_extended_packet_size: None,
|
||||||
|
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
||||||
|
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
||||||
|
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
|
||||||
|
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
|
||||||
|
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
|
||||||
|
maximum_reply_surb_rerequest_waiting_period:
|
||||||
|
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
|
||||||
|
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
|
||||||
|
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
|
||||||
|
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> From<OldConfigV1_1_13<T>> for Config<U> {
|
||||||
|
fn from(value: OldConfigV1_1_13<T>) -> Self {
|
||||||
|
Config {
|
||||||
|
client: Client {
|
||||||
|
version: value.client.version,
|
||||||
|
id: value.client.id,
|
||||||
|
disabled_credentials_mode: value.client.disabled_credentials_mode,
|
||||||
|
nyxd_urls: value.client.nyxd_urls,
|
||||||
|
nym_api_urls: value.client.nym_api_urls,
|
||||||
|
private_identity_key_file: value.client.private_identity_key_file,
|
||||||
|
public_identity_key_file: value.client.public_identity_key_file,
|
||||||
|
private_encryption_key_file: value.client.private_encryption_key_file,
|
||||||
|
public_encryption_key_file: value.client.public_encryption_key_file,
|
||||||
|
gateway_shared_key_file: value.client.gateway_shared_key_file,
|
||||||
|
ack_key_file: value.client.ack_key_file,
|
||||||
|
gateway_endpoint: value.client.gateway_endpoint,
|
||||||
|
database_path: value.client.database_path,
|
||||||
|
reply_surb_database_path: value.client.reply_surb_database_path,
|
||||||
|
nym_root_directory: value.client.nym_root_directory,
|
||||||
|
|
||||||
|
super_struct: PhantomData,
|
||||||
|
},
|
||||||
|
logging: value.logging,
|
||||||
|
debug: value.debug.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
use gateway_client::error::GatewayClientError;
|
use gateway_client::error::GatewayClientError;
|
||||||
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
||||||
|
use nym_topology::gateway::GatewayConversionError;
|
||||||
use nym_topology::NymTopologyError;
|
use nym_topology::NymTopologyError;
|
||||||
use validator_client::ValidatorClientError;
|
use validator_client::ValidatorClientError;
|
||||||
|
|
||||||
@@ -53,7 +54,32 @@ pub enum ClientCoreError {
|
|||||||
GatewayOwnerUnknown,
|
GatewayOwnerUnknown,
|
||||||
|
|
||||||
#[error("The address of the gateway is unknown - did you run init?")]
|
#[error("The address of the gateway is unknown - did you run init?")]
|
||||||
GatwayAddressUnknown,
|
GatewayAddressUnknown,
|
||||||
|
|
||||||
|
#[error("The gateway is malformed: {source}")]
|
||||||
|
MalformedGateway {
|
||||||
|
#[from]
|
||||||
|
source: GatewayConversionError,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("failed to establish connection to gateway: {source}")]
|
||||||
|
GatewayConnectionFailure {
|
||||||
|
#[from]
|
||||||
|
source: tungstenite::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
#[error("failed to establish gateway connection (wasm)")]
|
||||||
|
GatewayJsConnectionFailure,
|
||||||
|
|
||||||
|
#[error("Gateway connection was abruptly closed")]
|
||||||
|
GatewayConnectionAbruptlyClosed,
|
||||||
|
|
||||||
|
#[error("Timed out while trying to establish gateway connection")]
|
||||||
|
GatewayConnectionTimeout,
|
||||||
|
|
||||||
|
#[error("No ping measurements for the gateway ({identity}) performed")]
|
||||||
|
NoGatewayMeasurements { identity: String },
|
||||||
|
|
||||||
#[error("failed to register receiver for reconstructed mixnet messages")]
|
#[error("failed to register receiver for reconstructed mixnet messages")]
|
||||||
FailedToRegisterReceiver,
|
FailedToRegisterReceiver,
|
||||||
|
|||||||
@@ -6,52 +6,223 @@ use crate::{
|
|||||||
config::{persistence::key_pathfinder::ClientKeyPathfinder, Config},
|
config::{persistence::key_pathfinder::ClientKeyPathfinder, Config},
|
||||||
error::ClientCoreError,
|
error::ClientCoreError,
|
||||||
};
|
};
|
||||||
#[cfg(target_arch = "wasm32")]
|
use futures::{SinkExt, StreamExt};
|
||||||
use gateway_client::wasm_mockups::SigningNyxdClient;
|
|
||||||
use gateway_client::GatewayClient;
|
use gateway_client::GatewayClient;
|
||||||
use gateway_requests::registration::handshake::SharedKeys;
|
use gateway_requests::registration::handshake::SharedKeys;
|
||||||
|
use log::{debug, info, trace, warn};
|
||||||
use nym_config::NymConfig;
|
use nym_config::NymConfig;
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_crypto::asymmetric::identity;
|
||||||
use nym_topology::{filter::VersionFilterable, gateway};
|
use nym_topology::{filter::VersionFilterable, gateway};
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng, Rng};
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
use tap::TapFallible;
|
use tap::TapFallible;
|
||||||
|
use tungstenite::Message;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use tokio::time::Instant;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use tokio_tungstenite::connect_async;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use validator_client::nyxd::SigningNyxdClient;
|
use validator_client::nyxd::SigningNyxdClient;
|
||||||
|
|
||||||
pub(super) async fn query_gateway_details(
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
validator_servers: Vec<Url>,
|
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
|
||||||
chosen_gateway_id: Option<identity::PublicKey>,
|
|
||||||
) -> Result<gateway::Node, ClientCoreError> {
|
#[cfg(target_arch = "wasm32")]
|
||||||
let nym_api = validator_servers
|
use gateway_client::wasm_mockups::SigningNyxdClient;
|
||||||
.choose(&mut thread_rng())
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use wasm_timer::Instant;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use wasm_utils::websocket::JSWebsocket;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
type WsConn = JSWebsocket;
|
||||||
|
|
||||||
|
const MEASUREMENTS: usize = 3;
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
const CONN_TIMEOUT: Duration = Duration::from_millis(1500);
|
||||||
|
const PING_TIMEOUT: Duration = Duration::from_millis(1000);
|
||||||
|
|
||||||
|
struct GatewayWithLatency {
|
||||||
|
gateway: gateway::Node,
|
||||||
|
latency: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GatewayWithLatency {
|
||||||
|
fn new(gateway: gateway::Node, latency: Duration) -> Self {
|
||||||
|
GatewayWithLatency { gateway, latency }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn current_gateways<R: Rng>(
|
||||||
|
rng: &mut R,
|
||||||
|
nym_apis: Vec<Url>,
|
||||||
|
) -> Result<Vec<gateway::Node>, ClientCoreError> {
|
||||||
|
let nym_api = nym_apis
|
||||||
|
.choose(rng)
|
||||||
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
.ok_or(ClientCoreError::ListOfNymApisIsEmpty)?;
|
||||||
let validator_client = validator_client::client::NymApiClient::new(nym_api.clone());
|
let client = validator_client::client::NymApiClient::new(nym_api.clone());
|
||||||
|
|
||||||
log::trace!("Fetching list of gateways from: {}", nym_api);
|
log::trace!("Fetching list of gateways from: {}", nym_api);
|
||||||
let gateways = validator_client.get_cached_gateways().await?;
|
|
||||||
|
let gateways = client.get_cached_gateways().await?;
|
||||||
let valid_gateways = gateways
|
let valid_gateways = gateways
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|gateway| gateway.try_into().ok())
|
.filter_map(|gateway| gateway.try_into().ok())
|
||||||
.collect::<Vec<gateway::Node>>();
|
.collect::<Vec<gateway::Node>>();
|
||||||
|
|
||||||
|
// we were always filtering by version so I'm not removing that 'feature'
|
||||||
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
|
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
|
||||||
|
Ok(filtered_gateways)
|
||||||
|
}
|
||||||
|
|
||||||
// if we have chosen particular gateway - use it, otherwise choose a random one.
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
// (remember that in active topology all gateways have at least 100 reputation so should
|
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
|
||||||
// be working correctly)
|
match tokio::time::timeout(CONN_TIMEOUT, connect_async(endpoint)).await {
|
||||||
if let Some(gateway_id) = chosen_gateway_id {
|
Err(_elapsed) => Err(ClientCoreError::GatewayConnectionTimeout),
|
||||||
filtered_gateways
|
Ok(Err(conn_failure)) => Err(conn_failure.into()),
|
||||||
.iter()
|
Ok(Ok((stream, _))) => Ok(stream),
|
||||||
.find(|gateway| gateway.identity_key == gateway_id)
|
}
|
||||||
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_id.to_string()))
|
}
|
||||||
.cloned()
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
async fn connect(endpoint: &str) -> Result<WsConn, ClientCoreError> {
|
||||||
|
JSWebsocket::new(endpoint).map_err(|_| ClientCoreError::GatewayJsConnectionFailure)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn measure_latency(gateway: gateway::Node) -> Result<GatewayWithLatency, ClientCoreError> {
|
||||||
|
let addr = gateway.clients_address();
|
||||||
|
trace!(
|
||||||
|
"establishing connection to {} ({addr})...",
|
||||||
|
gateway.identity_key,
|
||||||
|
);
|
||||||
|
let mut stream = connect(&addr).await?;
|
||||||
|
|
||||||
|
let mut results = Vec::new();
|
||||||
|
for _ in 0..MEASUREMENTS {
|
||||||
|
let measurement_future = async {
|
||||||
|
let ping_content = vec![1, 2, 3];
|
||||||
|
let start = Instant::now();
|
||||||
|
stream.send(Message::Ping(ping_content.clone())).await?;
|
||||||
|
|
||||||
|
match stream.next().await {
|
||||||
|
Some(Ok(Message::Pong(content))) => {
|
||||||
|
if content == ping_content {
|
||||||
|
let elapsed = Instant::now().duration_since(start);
|
||||||
|
trace!("current ping time: {elapsed:?}");
|
||||||
|
results.push(elapsed);
|
||||||
|
} else {
|
||||||
|
warn!("received a pong message with different content? wtf.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Ok(_)) => warn!("received a message that's not a pong!"),
|
||||||
|
Some(Err(err)) => return Err(err.into()),
|
||||||
|
None => return Err(ClientCoreError::GatewayConnectionAbruptlyClosed),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok::<(), ClientCoreError>(())
|
||||||
|
};
|
||||||
|
|
||||||
|
// thanks to wasm we can't use tokio::time::timeout : (
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
let timeout = tokio::time::sleep(PING_TIMEOUT);
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
tokio::pin!(timeout);
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let mut timeout = wasm_timer::Delay::new(PING_TIMEOUT);
|
||||||
|
|
||||||
|
tokio::select! {
|
||||||
|
_ = &mut timeout => {
|
||||||
|
warn!("timed out while trying to perform measurement...")
|
||||||
|
}
|
||||||
|
res = measurement_future => res?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = results.len() as u64;
|
||||||
|
if count == 0 {
|
||||||
|
return Err(ClientCoreError::NoGatewayMeasurements {
|
||||||
|
identity: gateway.identity_key.to_base58_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let sum: Duration = results.into_iter().sum();
|
||||||
|
let avg = Duration::from_nanos(sum.as_nanos() as u64 / count);
|
||||||
|
|
||||||
|
Ok(GatewayWithLatency::new(gateway, avg))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn choose_gateway_by_latency<R: Rng>(
|
||||||
|
rng: &mut R,
|
||||||
|
gateways: Vec<gateway::Node>,
|
||||||
|
) -> Result<gateway::Node, ClientCoreError> {
|
||||||
|
info!("choosing gateway by latency...");
|
||||||
|
|
||||||
|
let mut gateways_with_latency = Vec::new();
|
||||||
|
for gateway in gateways {
|
||||||
|
let id = *gateway.identity();
|
||||||
|
trace!("measuring latency to {id}...");
|
||||||
|
let with_latency = match measure_latency(gateway).await {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => {
|
||||||
|
warn!("failed to measure {id}: {err}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug!(
|
||||||
|
"{id} ({}): {:?}",
|
||||||
|
with_latency.gateway.location, with_latency.latency
|
||||||
|
);
|
||||||
|
gateways_with_latency.push(with_latency)
|
||||||
|
}
|
||||||
|
|
||||||
|
let chosen = gateways_with_latency
|
||||||
|
.choose_weighted(rng, |item| 1. / item.latency.as_secs_f32())
|
||||||
|
.expect("invalid selection weight!");
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"chose gateway {} (located at {}) with average latency of {:?}",
|
||||||
|
chosen.gateway.identity_key, chosen.gateway.location, chosen.latency
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(chosen.gateway.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn uniformly_random_gateway<R: Rng>(
|
||||||
|
rng: &mut R,
|
||||||
|
gateways: Vec<gateway::Node>,
|
||||||
|
) -> Result<gateway::Node, ClientCoreError> {
|
||||||
|
gateways
|
||||||
|
.choose(rng)
|
||||||
|
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn query_gateway_details(
|
||||||
|
validator_servers: Vec<Url>,
|
||||||
|
chosen_gateway_id: Option<identity::PublicKey>,
|
||||||
|
by_latency: bool,
|
||||||
|
) -> Result<gateway::Node, ClientCoreError> {
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
let gateways = current_gateways(&mut rng, validator_servers).await?;
|
||||||
|
|
||||||
|
// if we set an explicit gateway, use that one and nothing else
|
||||||
|
if let Some(explicitly_chosen) = chosen_gateway_id {
|
||||||
|
gateways
|
||||||
|
.into_iter()
|
||||||
|
.find(|gateway| gateway.identity_key == explicitly_chosen)
|
||||||
|
.ok_or_else(|| ClientCoreError::NoGatewayWithId(explicitly_chosen.to_string()))
|
||||||
|
} else if by_latency {
|
||||||
|
choose_gateway_by_latency(&mut rng, gateways).await
|
||||||
} else {
|
} else {
|
||||||
filtered_gateways
|
uniformly_random_gateway(&mut rng, gateways)
|
||||||
.choose(&mut rand::thread_rng())
|
|
||||||
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
|
|
||||||
.cloned()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,9 +77,11 @@ pub async fn register_with_gateway(
|
|||||||
key_manager: &mut KeyManager,
|
key_manager: &mut KeyManager,
|
||||||
nym_api_endpoints: Vec<Url>,
|
nym_api_endpoints: Vec<Url>,
|
||||||
chosen_gateway_id: Option<identity::PublicKey>,
|
chosen_gateway_id: Option<identity::PublicKey>,
|
||||||
|
by_latency: bool,
|
||||||
) -> Result<GatewayEndpointConfig, ClientCoreError> {
|
) -> Result<GatewayEndpointConfig, ClientCoreError> {
|
||||||
// Get the gateway details of the gateway we will use
|
// Get the gateway details of the gateway we will use
|
||||||
let gateway = helpers::query_gateway_details(nym_api_endpoints, chosen_gateway_id).await?;
|
let gateway =
|
||||||
|
helpers::query_gateway_details(nym_api_endpoints, chosen_gateway_id, by_latency).await?;
|
||||||
log::debug!("Querying gateway gives: {}", gateway);
|
log::debug!("Querying gateway gives: {}", gateway);
|
||||||
|
|
||||||
let our_identity = key_manager.identity_keypair();
|
let our_identity = key_manager.identity_keypair();
|
||||||
@@ -102,6 +104,7 @@ pub async fn setup_gateway_from_config<C, T>(
|
|||||||
register_gateway: bool,
|
register_gateway: bool,
|
||||||
user_chosen_gateway_id: Option<identity::PublicKey>,
|
user_chosen_gateway_id: Option<identity::PublicKey>,
|
||||||
config: &Config<T>,
|
config: &Config<T>,
|
||||||
|
by_latency: bool,
|
||||||
) -> Result<GatewayEndpointConfig, ClientCoreError>
|
) -> Result<GatewayEndpointConfig, ClientCoreError>
|
||||||
where
|
where
|
||||||
C: NymConfig + ClientCoreConfigTrait,
|
C: NymConfig + ClientCoreConfigTrait,
|
||||||
@@ -109,17 +112,20 @@ where
|
|||||||
{
|
{
|
||||||
let id = config.get_id();
|
let id = config.get_id();
|
||||||
|
|
||||||
// If we are not going to register gateway, and an explicitly chosed gateway is not passed in,
|
// If we are not going to register gateway, and an explicitly chosen gateway is not passed in,
|
||||||
// load the existing configuration file
|
// load the existing configuration file
|
||||||
if !register_gateway && user_chosen_gateway_id.is_none() {
|
if !register_gateway && user_chosen_gateway_id.is_none() {
|
||||||
println!("Not registering gateway, will reuse existing config and keys");
|
println!("Not registering gateway, will reuse existing config and keys");
|
||||||
return load_existing_gateway_config::<C>(&id);
|
return load_existing_gateway_config::<C>(&id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Else, we preceed by querying the nym-api
|
// Else, we proceed by querying the nym-api
|
||||||
let gateway =
|
let gateway = helpers::query_gateway_details(
|
||||||
helpers::query_gateway_details(config.get_nym_api_endpoints(), user_chosen_gateway_id)
|
config.get_nym_api_endpoints(),
|
||||||
.await?;
|
user_chosen_gateway_id,
|
||||||
|
by_latency,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
log::debug!("Querying gateway gives: {}", gateway);
|
log::debug!("Querying gateway gives: {}", gateway);
|
||||||
|
|
||||||
// If we are not registering, just return this and assume the caller has the keys already and
|
// If we are not registering, just return this and assume the caller has the keys already and
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bip39 = "1.0.1"
|
bip39 = { workspace = true }
|
||||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
rand = "0.7.3"
|
rand = "0.7.3"
|
||||||
@@ -15,10 +15,10 @@ thiserror = "1.0"
|
|||||||
url = "2.2"
|
url = "2.2"
|
||||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
|
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
|
||||||
|
|
||||||
coconut-interface = { path = "../../common/coconut-interface" }
|
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||||
nym-config = { path = "../../common/config" }
|
nym-config = { path = "../../common/config" }
|
||||||
credentials = { path = "../../common/credentials" }
|
nym-credentials = { path = "../../common/credentials" }
|
||||||
credential-storage = { path = "../../common/credential-storage" }
|
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||||
nym-crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
nym-crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
||||||
nym-bin-common = { path = "../../common/bin-common"}
|
nym-bin-common = { path = "../../common/bin-common"}
|
||||||
nym-network-defaults = { path = "../../common/network-defaults" }
|
nym-network-defaults = { path = "../../common/network-defaults" }
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ use nym_bin_common::completions::ArgShell;
|
|||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use coconut_interface::{Base58, Parameters};
|
use nym_coconut_interface::{Base58, Parameters};
|
||||||
use credential_storage::storage::Storage;
|
use nym_credential_storage::storage::Storage;
|
||||||
use credential_storage::PersistentStorage;
|
use nym_credential_storage::PersistentStorage;
|
||||||
use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
|
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
|
||||||
use credentials::coconut::utils::obtain_aggregate_signature;
|
use nym_credentials::coconut::utils::obtain_aggregate_signature;
|
||||||
use nym_crypto::asymmetric::{encryption, identity};
|
use nym_crypto::asymmetric::{encryption, identity};
|
||||||
use nym_network_defaults::VOUCHER_INFO;
|
use nym_network_defaults::VOUCHER_INFO;
|
||||||
use validator_client::nyxd::traits::DkgQueryClient;
|
use validator_client::nyxd::traits::DkgQueryClient;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
use std::time::SystemTimeError;
|
use std::time::SystemTimeError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use credential_storage::error::StorageError;
|
use nym_credential_storage::error::StorageError;
|
||||||
use credentials::error::Error as CredentialError;
|
use nym_credentials::error::Error as CredentialError;
|
||||||
use nym_crypto::asymmetric::encryption::KeyRecoveryError;
|
use nym_crypto::asymmetric::encryption::KeyRecoveryError;
|
||||||
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
||||||
use validator_client::nyxd::error::NyxdError;
|
use validator_client::nyxd::error::NyxdError;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use commands::*;
|
|||||||
use error::Result;
|
use error::Result;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nym_bin_common::completions::fig_generate;
|
use nym_bin_common::completions::fig_generate;
|
||||||
use nym_config::{DATA_DIR, DB_FILE_NAME};
|
use nym_config::{CRED_DB_FILE_NAME, DATA_DIR};
|
||||||
use nym_network_defaults::{setup_env, NymNetworkDetails};
|
use nym_network_defaults::{setup_env, NymNetworkDetails};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
@@ -51,8 +51,11 @@ async fn block_until_coconut_is_available<C: Clone + CosmWasmClient + Send + Syn
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// Use 20 additional seconds to avoid the exact moment of going into the final epoch state
|
// Use 1 additional second to not start the next iteration immediately and spam get_current_epoch queries
|
||||||
let secs_until_final = epoch.final_timestamp_secs() + 20 - current_timestamp_secs;
|
let secs_until_final = epoch
|
||||||
|
.final_timestamp_secs()
|
||||||
|
.saturating_sub(current_timestamp_secs)
|
||||||
|
+ 1;
|
||||||
info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final);
|
info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final);
|
||||||
std::thread::sleep(Duration::from_secs(secs_until_final));
|
std::thread::sleep(Duration::from_secs(secs_until_final));
|
||||||
}
|
}
|
||||||
@@ -70,8 +73,11 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
match args.command {
|
match args.command {
|
||||||
Command::Run(r) => {
|
Command::Run(r) => {
|
||||||
let db_path = r.client_home_directory.join(DATA_DIR).join(DB_FILE_NAME);
|
let db_path = r
|
||||||
let shared_storage = credential_storage::initialise_storage(db_path).await;
|
.client_home_directory
|
||||||
|
.join(DATA_DIR)
|
||||||
|
.join(CRED_DB_FILE_NAME);
|
||||||
|
let shared_storage = nym_credential_storage::initialise_storage(db_path).await;
|
||||||
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
|
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
|
||||||
|
|
||||||
let network_details = NymNetworkDetails::new_from_env();
|
let network_details = NymNetworkDetails::new_from_env();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use credentials::coconut::bandwidth::BandwidthVoucher;
|
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
|
||||||
use std::fs::{create_dir_all, read_dir, File};
|
use std::fs::{create_dir_all, read_dir, File};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use coconut_interface::Parameters;
|
use nym_coconut_interface::Parameters;
|
||||||
use credentials::coconut::bandwidth::BandwidthVoucher;
|
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
|
||||||
|
|
||||||
use nym_crypto::asymmetric::{encryption, identity};
|
use nym_crypto::asymmetric::{encryption, identity};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client"
|
name = "nym-client"
|
||||||
version = "1.1.10"
|
version = "1.1.14"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||||
description = "Implementation of the Nym Client"
|
description = "Implementation of the Nym Client"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -36,10 +36,10 @@ tokio-tungstenite = "0.14" # websocket
|
|||||||
## internal
|
## internal
|
||||||
nym-bin-common = { path = "../../common/bin-common" }
|
nym-bin-common = { path = "../../common/bin-common" }
|
||||||
client-core = { path = "../client-core", features = ["fs-surb-storage"] }
|
client-core = { path = "../client-core", features = ["fs-surb-storage"] }
|
||||||
coconut-interface = { path = "../../common/coconut-interface" }
|
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||||
nym-config = { path = "../../common/config" }
|
nym-config = { path = "../../common/config" }
|
||||||
credential-storage = { path = "../../common/credential-storage" }
|
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||||
credentials = { path = "../../common/credentials" }
|
nym-credentials = { path = "../../common/credentials" }
|
||||||
nym-crypto = { path = "../../common/crypto" }
|
nym-crypto = { path = "../../common/crypto" }
|
||||||
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
||||||
gateway-requests = { path = "../../gateway/gateway-requests" }
|
gateway-requests = { path = "../../gateway/gateway-requests" }
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ pub use client_core::config::Config as BaseConfig;
|
|||||||
pub use client_core::config::MISSING_VALUE;
|
pub use client_core::config::MISSING_VALUE;
|
||||||
pub use client_core::config::{DebugConfig, GatewayEndpointConfig};
|
pub use client_core::config::{DebugConfig, GatewayEndpointConfig};
|
||||||
|
|
||||||
|
pub mod old_config_v1_1_13;
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
|
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::client::config::{Config, Socket};
|
||||||
|
use client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
|
||||||
|
use nym_config::NymConfig;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct OldConfigV1_1_13 {
|
||||||
|
#[serde(flatten)]
|
||||||
|
base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
|
||||||
|
|
||||||
|
socket: Socket,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NymConfig for OldConfigV1_1_13 {
|
||||||
|
fn template() -> &'static str {
|
||||||
|
// not intended to be used
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_root_directory() -> PathBuf {
|
||||||
|
dirs::home_dir()
|
||||||
|
.expect("Failed to evaluate $HOME value")
|
||||||
|
.join(".nym")
|
||||||
|
.join("clients")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_default_root_directory() -> Option<PathBuf> {
|
||||||
|
dirs::home_dir().map(|path| path.join(".nym").join("clients"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root_directory(&self) -> PathBuf {
|
||||||
|
self.base.client.nym_root_directory.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_directory(&self) -> PathBuf {
|
||||||
|
self.root_directory()
|
||||||
|
.join(&self.base.client.id)
|
||||||
|
.join("config")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_directory(&self) -> PathBuf {
|
||||||
|
self.root_directory()
|
||||||
|
.join(&self.base.client.id)
|
||||||
|
.join("data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OldConfigV1_1_13> for Config {
|
||||||
|
fn from(value: OldConfigV1_1_13) -> Self {
|
||||||
|
Config {
|
||||||
|
base: value.base.into(),
|
||||||
|
socket: value.socket,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -110,10 +110,15 @@ host = '{{ socket.host }}'
|
|||||||
|
|
||||||
[debug]
|
[debug]
|
||||||
|
|
||||||
average_packet_delay = '{{ debug.average_packet_delay }}'
|
[debug.traffic]
|
||||||
average_ack_delay = '{{ debug.average_ack_delay }}'
|
average_packet_delay = '{{ debug.traffic.average_packet_delay }}'
|
||||||
loop_cover_traffic_average_delay = '{{ debug.loop_cover_traffic_average_delay }}'
|
message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}'
|
||||||
message_sending_average_delay = '{{ debug.message_sending_average_delay }}'
|
|
||||||
|
[debug.acknowledgements]
|
||||||
|
average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
|
||||||
|
|
||||||
|
[debug.cover_traffic]
|
||||||
|
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
|
||||||
|
|
||||||
"#
|
"#
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ impl SocketClient {
|
|||||||
let client = validator_client::Client::new_query(client_config)
|
let client = validator_client::Client::new_query(client_config)
|
||||||
.expect("Could not construct query client");
|
.expect("Could not construct query client");
|
||||||
BandwidthController::new(
|
BandwidthController::new(
|
||||||
credential_storage::initialise_storage(config.get_base().get_database_path()).await,
|
nym_credential_storage::initialise_storage(config.get_base().get_database_path()).await,
|
||||||
client,
|
client,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -101,6 +101,7 @@ impl SocketClient {
|
|||||||
let ClientState {
|
let ClientState {
|
||||||
shared_lane_queue_lengths,
|
shared_lane_queue_lengths,
|
||||||
reply_controller_sender,
|
reply_controller_sender,
|
||||||
|
..
|
||||||
} = client_state;
|
} = client_state;
|
||||||
|
|
||||||
let websocket_handler = websocket::HandlerBuilder::new(
|
let websocket_handler = websocket::HandlerBuilder::new(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::commands::try_upgrade_v1_1_13_config;
|
||||||
use crate::{
|
use crate::{
|
||||||
client::config::Config,
|
client::config::Config,
|
||||||
commands::{override_config, OverrideConfig},
|
commands::{override_config, OverrideConfig},
|
||||||
@@ -25,6 +26,11 @@ pub(crate) struct Init {
|
|||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
gateway: Option<identity::PublicKey>,
|
gateway: Option<identity::PublicKey>,
|
||||||
|
|
||||||
|
/// Specifies whether the new gateway should be determined based by latency as opposed to being chosen
|
||||||
|
/// uniformly.
|
||||||
|
#[clap(long, conflicts_with = "gateway")]
|
||||||
|
latency_based_selection: bool,
|
||||||
|
|
||||||
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
|
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
|
||||||
/// potentially causing loss of access.
|
/// potentially causing loss of access.
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
@@ -116,6 +122,9 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
|||||||
|
|
||||||
let already_init = Config::default_config_file_path(id).exists();
|
let already_init = Config::default_config_file_path(id).exists();
|
||||||
if already_init {
|
if already_init {
|
||||||
|
// in case we're using old config, try to upgrade it
|
||||||
|
// (if we're using the current version, it's a no-op)
|
||||||
|
try_upgrade_v1_1_13_config(id)?;
|
||||||
println!("Client \"{id}\" was already initialised before");
|
println!("Client \"{id}\" was already initialised before");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +152,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
|||||||
register_gateway,
|
register_gateway,
|
||||||
user_chosen_gateway_id,
|
user_chosen_gateway_id,
|
||||||
config.get_base(),
|
config.get_base(),
|
||||||
|
args.latency_based_selection,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
|
||||||
use crate::client::config::{BaseConfig, Config};
|
use crate::client::config::{BaseConfig, Config};
|
||||||
use clap::CommandFactory;
|
use clap::CommandFactory;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use log::info;
|
||||||
use nym_bin_common::build_information::BinaryBuildInformation;
|
use nym_bin_common::build_information::BinaryBuildInformation;
|
||||||
use nym_bin_common::completions::{fig_generate, ArgShell};
|
use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||||
use nym_config::OptionalSet;
|
use nym_config::{NymConfig, OptionalSet};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
@@ -102,6 +104,20 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
|
||||||
|
// explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
|
||||||
|
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
|
||||||
|
// if we failed to load it, there might have been nothing to upgrade
|
||||||
|
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
info!("It seems the client is using <= v1.1.13 config template.");
|
||||||
|
info!("It is going to get updated to the current specification.");
|
||||||
|
|
||||||
|
let updated: Config = old_config.into();
|
||||||
|
updated.save_to_file(None)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
|
use crate::commands::try_upgrade_v1_1_13_config;
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{config::Config, SocketClient},
|
client::{config::Config, SocketClient},
|
||||||
commands::{override_config, OverrideConfig},
|
commands::{override_config, OverrideConfig},
|
||||||
error::ClientError,
|
error::ClientError,
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nym_bin_common::version_checker::is_minor_version_compatible;
|
use nym_bin_common::version_checker::is_minor_version_compatible;
|
||||||
@@ -100,6 +100,10 @@ fn version_check(cfg: &Config) -> bool {
|
|||||||
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
|
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
|
// in case we're using old config, try to upgrade it
|
||||||
|
// (if we're using the current version, it's a no-op)
|
||||||
|
try_upgrade_v1_1_13_config(id)?;
|
||||||
|
|
||||||
let mut config = match Config::load_from_file(id) {
|
let mut config = match Config::load_from_file(id) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.1.10"
|
version = "1.1.14"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -29,26 +29,26 @@ url = "2.2"
|
|||||||
# internal
|
# internal
|
||||||
nym-bin-common = { path = "../../common/bin-common" }
|
nym-bin-common = { path = "../../common/bin-common" }
|
||||||
client-core = { path = "../client-core", features = ["fs-surb-storage"] }
|
client-core = { path = "../client-core", features = ["fs-surb-storage"] }
|
||||||
coconut-interface = { path = "../../common/coconut-interface" }
|
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||||
nym-config = { path = "../../common/config" }
|
nym-config = { path = "../../common/config" }
|
||||||
credential-storage = { path = "../../common/credential-storage", optional = true }
|
nym-credential-storage = { path = "../../common/credential-storage", optional = true }
|
||||||
mobile-storage = { path = "../../common/mobile-storage", optional = true }
|
mobile-storage = { path = "../../common/mobile-storage", optional = true }
|
||||||
credentials = { path = "../../common/credentials" }
|
nym-credentials = { path = "../../common/credentials" }
|
||||||
nym-crypto = { path = "../../common/crypto" }
|
nym-crypto = { path = "../../common/crypto" }
|
||||||
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
||||||
gateway-requests = { path = "../../gateway/gateway-requests" }
|
gateway-requests = { path = "../../gateway/gateway-requests" }
|
||||||
nym-network-defaults = { path = "../../common/network-defaults" }
|
nym-network-defaults = { path = "../../common/network-defaults" }
|
||||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||||
ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
|
nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
|
||||||
nym-pemstore = { path = "../../common/pemstore" }
|
nym-pemstore = { path = "../../common/pemstore" }
|
||||||
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
nym-socks5-proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
||||||
service-providers-common = { path = "../../service-providers/common" }
|
nym-service-providers-common = { path = "../../service-providers/common" }
|
||||||
socks5-requests = { path = "../../common/socks5/requests" }
|
nym-socks5-requests = { path = "../../common/socks5/requests" }
|
||||||
nym-task = { path = "../../common/task" }
|
nym-task = { path = "../../common/task" }
|
||||||
nym-topology = { path = "../../common/topology" }
|
nym-topology = { path = "../../common/topology" }
|
||||||
validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
|
validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["credential-storage"]
|
default = ["nym-credential-storage"]
|
||||||
eth = []
|
eth = []
|
||||||
mobile = ["mobile-storage", "gateway-client/mobile"]
|
mobile = ["mobile-storage", "gateway-client/mobile"]
|
||||||
|
|||||||
@@ -7,14 +7,15 @@ pub use client_core::config::MISSING_VALUE;
|
|||||||
use client_core::config::{ClientCoreConfigTrait, DebugConfig};
|
use client_core::config::{ClientCoreConfigTrait, DebugConfig};
|
||||||
use nym_config::defaults::DEFAULT_SOCKS5_LISTENING_PORT;
|
use nym_config::defaults::DEFAULT_SOCKS5_LISTENING_PORT;
|
||||||
use nym_config::{NymConfig, OptionalSet};
|
use nym_config::{NymConfig, OptionalSet};
|
||||||
|
use nym_service_providers_common::interface::ProviderInterfaceVersion;
|
||||||
|
use nym_socks5_requests::Socks5ProtocolVersion;
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use service_providers_common::interface::ProviderInterfaceVersion;
|
|
||||||
use socks5_requests::Socks5ProtocolVersion;
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub mod old_config_v1_1_13;
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
|
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::client::config::{Config, Socks5, Socks5Debug};
|
||||||
|
use client_core::config::old_config_v1_1_13::OldConfigV1_1_13 as OldBaseConfigV1_1_13;
|
||||||
|
use nym_config::NymConfig;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct OldConfigV1_1_13 {
|
||||||
|
#[serde(flatten)]
|
||||||
|
base: OldBaseConfigV1_1_13<OldConfigV1_1_13>,
|
||||||
|
|
||||||
|
socks5: Socks5,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
socks5_debug: Socks5Debug,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NymConfig for OldConfigV1_1_13 {
|
||||||
|
fn template() -> &'static str {
|
||||||
|
// not intended to be used
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_root_directory() -> PathBuf {
|
||||||
|
#[cfg(not(feature = "mobile"))]
|
||||||
|
let base_dir = dirs::home_dir().expect("Failed to evaluate $HOME value");
|
||||||
|
#[cfg(feature = "mobile")]
|
||||||
|
let base_dir = PathBuf::from("/tmp");
|
||||||
|
|
||||||
|
base_dir.join(".nym").join("socks5-clients")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_default_root_directory() -> Option<PathBuf> {
|
||||||
|
dirs::home_dir().map(|path| path.join(".nym").join("socks5-clients"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root_directory(&self) -> PathBuf {
|
||||||
|
self.base.client.nym_root_directory.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_directory(&self) -> PathBuf {
|
||||||
|
self.root_directory()
|
||||||
|
.join(&self.base.client.id)
|
||||||
|
.join("config")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_directory(&self) -> PathBuf {
|
||||||
|
self.root_directory()
|
||||||
|
.join(&self.base.client.id)
|
||||||
|
.join("data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OldConfigV1_1_13> for Config {
|
||||||
|
fn from(value: OldConfigV1_1_13) -> Self {
|
||||||
|
Config {
|
||||||
|
base: value.base.into(),
|
||||||
|
socks5: value.socks5,
|
||||||
|
socks5_debug: value.socks5_debug,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -114,10 +114,15 @@ send_anonymously = {{ socks5.send_anonymously }}
|
|||||||
|
|
||||||
[debug]
|
[debug]
|
||||||
|
|
||||||
average_packet_delay = '{{ debug.average_packet_delay }}'
|
[debug.traffic]
|
||||||
average_ack_delay = '{{ debug.average_ack_delay }}'
|
average_packet_delay = '{{ debug.traffic.average_packet_delay }}'
|
||||||
loop_cover_traffic_average_delay = '{{ debug.loop_cover_traffic_average_delay }}'
|
message_sending_average_delay = '{{ debug.traffic.message_sending_average_delay }}'
|
||||||
message_sending_average_delay = '{{ debug.message_sending_average_delay }}'
|
|
||||||
|
[debug.acknowledgements]
|
||||||
|
average_ack_delay = '{{ debug.acknowledgements.average_ack_delay }}'
|
||||||
|
|
||||||
|
[debug.cover_traffic]
|
||||||
|
loop_cover_traffic_average_delay = '{{ debug.cover_traffic.loop_cover_traffic_average_delay }}'
|
||||||
|
|
||||||
"#
|
"#
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ impl NymClient {
|
|||||||
|
|
||||||
#[cfg(not(feature = "mobile"))]
|
#[cfg(not(feature = "mobile"))]
|
||||||
let storage =
|
let storage =
|
||||||
credential_storage::initialise_storage(config.get_base().get_database_path()).await;
|
nym_credential_storage::initialise_storage(config.get_base().get_database_path()).await;
|
||||||
|
|
||||||
#[cfg(feature = "mobile")]
|
#[cfg(feature = "mobile")]
|
||||||
let storage = mobile_storage::PersistentStorage {};
|
let storage = mobile_storage::PersistentStorage {};
|
||||||
@@ -123,7 +123,7 @@ impl NymClient {
|
|||||||
|
|
||||||
let ClientState {
|
let ClientState {
|
||||||
shared_lane_queue_lengths,
|
shared_lane_queue_lengths,
|
||||||
reply_controller_sender: _,
|
..
|
||||||
} = client_status;
|
} = client_status;
|
||||||
|
|
||||||
let authenticator = Authenticator::new(auth_methods, allowed_users);
|
let authenticator = Authenticator::new(auth_methods, allowed_users);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::commands::try_upgrade_v1_1_13_config;
|
||||||
use crate::{
|
use crate::{
|
||||||
client::config::Config,
|
client::config::Config,
|
||||||
commands::{override_config, OverrideConfig},
|
commands::{override_config, OverrideConfig},
|
||||||
@@ -37,6 +38,11 @@ pub(crate) struct Init {
|
|||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
gateway: Option<identity::PublicKey>,
|
gateway: Option<identity::PublicKey>,
|
||||||
|
|
||||||
|
/// Specifies whether the new gateway should be determined based by latency as opposed to being chosen
|
||||||
|
/// uniformly.
|
||||||
|
#[clap(long, conflicts_with = "gateway")]
|
||||||
|
latency_based_selection: bool,
|
||||||
|
|
||||||
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
|
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
|
||||||
/// potentially causing loss of access.
|
/// potentially causing loss of access.
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
@@ -119,6 +125,9 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
|||||||
|
|
||||||
let already_init = Config::default_config_file_path(id).exists();
|
let already_init = Config::default_config_file_path(id).exists();
|
||||||
if already_init {
|
if already_init {
|
||||||
|
// in case we're using old config, try to upgrade it
|
||||||
|
// (if we're using the current version, it's a no-op)
|
||||||
|
try_upgrade_v1_1_13_config(id)?;
|
||||||
println!("SOCKS5 client \"{id}\" was already initialised before");
|
println!("SOCKS5 client \"{id}\" was already initialised before");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +158,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
|||||||
register_gateway,
|
register_gateway,
|
||||||
user_chosen_gateway_id,
|
user_chosen_gateway_id,
|
||||||
config.get_base(),
|
config.get_base(),
|
||||||
|
args.latency_based_selection,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::client::config::old_config_v1_1_13::OldConfigV1_1_13;
|
||||||
use crate::client::config::{BaseConfig, Config};
|
use crate::client::config::{BaseConfig, Config};
|
||||||
use clap::CommandFactory;
|
use clap::CommandFactory;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use log::info;
|
||||||
use nym_bin_common::build_information::BinaryBuildInformation;
|
use nym_bin_common::build_information::BinaryBuildInformation;
|
||||||
use nym_bin_common::completions::{fig_generate, ArgShell};
|
use nym_bin_common::completions::{fig_generate, ArgShell};
|
||||||
use nym_config::OptionalSet;
|
use nym_config::{NymConfig, OptionalSet};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
pub mod init;
|
pub mod init;
|
||||||
@@ -101,6 +103,20 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_upgrade_v1_1_13_config(id: &str) -> std::io::Result<()> {
|
||||||
|
// explicitly load it as v1.1.13 (which is incompatible with the current, i.e. 1.1.14+)
|
||||||
|
let Ok(old_config) = OldConfigV1_1_13::load_from_file(id) else {
|
||||||
|
// if we failed to load it, there might have been nothing to upgrade
|
||||||
|
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
info!("It seems the client is using <= v1.1.13 config template.");
|
||||||
|
info!("It is going to get updated to the current specification.");
|
||||||
|
|
||||||
|
let updated: Config = old_config.into();
|
||||||
|
updated.save_to_file(None)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::commands::try_upgrade_v1_1_13_config;
|
||||||
use crate::{
|
use crate::{
|
||||||
client::{config::Config, NymClient},
|
client::{config::Config, NymClient},
|
||||||
commands::{override_config, OverrideConfig},
|
commands::{override_config, OverrideConfig},
|
||||||
error::Socks5ClientError,
|
error::Socks5ClientError,
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nym_bin_common::version_checker::is_minor_version_compatible;
|
use nym_bin_common::version_checker::is_minor_version_compatible;
|
||||||
@@ -108,6 +108,10 @@ fn version_check(cfg: &Config) -> bool {
|
|||||||
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
pub(crate) async fn execute(args: &Run) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let id = &args.id;
|
let id = &args.id;
|
||||||
|
|
||||||
|
// in case we're using old config, try to upgrade it
|
||||||
|
// (if we're using the current version, it's a no-op)
|
||||||
|
try_upgrade_v1_1_13_config(id)?;
|
||||||
|
|
||||||
let mut config = match Config::load_from_file(id) {
|
let mut config = match Config::load_from_file(id) {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::socks::types::SocksProxyError;
|
use crate::socks::types::SocksProxyError;
|
||||||
use client_core::error::ClientCoreError;
|
use client_core::error::ClientCoreError;
|
||||||
use socks5_requests::{ConnectionError, ConnectionId};
|
use nym_socks5_requests::{ConnectionError, ConnectionId};
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum Socks5ClientError {
|
pub enum Socks5ClientError {
|
||||||
|
|||||||
@@ -8,19 +8,19 @@ use client_core::client::inbound_messages::{InputMessage, InputMessageSender};
|
|||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use futures::task::{Context, Poll};
|
use futures::task::{Context, Poll};
|
||||||
use log::*;
|
use log::*;
|
||||||
|
use nym_service_providers_common::interface::{ProviderInterfaceVersion, RequestVersion};
|
||||||
|
use nym_socks5_proxy_helpers::connection_controller::{
|
||||||
|
ConnectionReceiver, ControllerCommand, ControllerSender,
|
||||||
|
};
|
||||||
|
use nym_socks5_proxy_helpers::proxy_runner::ProxyRunner;
|
||||||
|
use nym_socks5_requests::{
|
||||||
|
ConnectionId, RemoteAddress, Socks5ProtocolVersion, Socks5ProviderRequest, Socks5Request,
|
||||||
|
};
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use nym_task::connections::{LaneQueueLengths, TransmissionLane};
|
use nym_task::connections::{LaneQueueLengths, TransmissionLane};
|
||||||
use nym_task::TaskClient;
|
use nym_task::TaskClient;
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
use proxy_helpers::connection_controller::{
|
|
||||||
ConnectionReceiver, ControllerCommand, ControllerSender,
|
|
||||||
};
|
|
||||||
use proxy_helpers::proxy_runner::ProxyRunner;
|
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use service_providers_common::interface::{ProviderInterfaceVersion, RequestVersion};
|
|
||||||
use socks5_requests::{
|
|
||||||
ConnectionId, RemoteAddress, Socks5ProtocolVersion, Socks5ProviderRequest, Socks5Request,
|
|
||||||
};
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ use log::*;
|
|||||||
|
|
||||||
use client_core::client::received_buffer::ReconstructedMessagesReceiver;
|
use client_core::client::received_buffer::ReconstructedMessagesReceiver;
|
||||||
use client_core::client::received_buffer::{ReceivedBufferMessage, ReceivedBufferRequestSender};
|
use client_core::client::received_buffer::{ReceivedBufferMessage, ReceivedBufferRequestSender};
|
||||||
|
use nym_service_providers_common::interface::{ControlResponse, ResponseContent};
|
||||||
|
use nym_socks5_proxy_helpers::connection_controller::ControllerSender;
|
||||||
|
use nym_socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent};
|
||||||
use nym_sphinx::receiver::ReconstructedMessage;
|
use nym_sphinx::receiver::ReconstructedMessage;
|
||||||
use nym_task::TaskClient;
|
use nym_task::TaskClient;
|
||||||
use proxy_helpers::connection_controller::ControllerSender;
|
|
||||||
use service_providers_common::interface::{ControlResponse, ResponseContent};
|
|
||||||
use socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent};
|
|
||||||
|
|
||||||
use crate::error::Socks5ClientError;
|
use crate::error::Socks5ClientError;
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ use client_core::client::{
|
|||||||
inbound_messages::InputMessageSender, received_buffer::ReceivedBufferRequestSender,
|
inbound_messages::InputMessageSender, received_buffer::ReceivedBufferRequestSender,
|
||||||
};
|
};
|
||||||
use log::*;
|
use log::*;
|
||||||
|
use nym_socks5_proxy_helpers::connection_controller::Controller;
|
||||||
use nym_sphinx::addressing::clients::Recipient;
|
use nym_sphinx::addressing::clients::Recipient;
|
||||||
use nym_task::connections::{ConnectionCommandSender, LaneQueueLengths};
|
use nym_task::connections::{ConnectionCommandSender, LaneQueueLengths};
|
||||||
use nym_task::TaskClient;
|
use nym_task::TaskClient;
|
||||||
use proxy_helpers::connection_controller::Controller;
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use tap::TapFallible;
|
use tap::TapFallible;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use socks5_requests::Socks5RequestError;
|
use nym_socks5_requests::Socks5RequestError;
|
||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|||||||
@@ -1,83 +1,71 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2019,
|
"ecmaVersion": 2019,
|
||||||
"sourceType": "module"
|
"sourceType": "module"
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"Atomics": "readonly",
|
"Atomics": "readonly",
|
||||||
"SharedArrayBuffer": "readonly"
|
"SharedArrayBuffer": "readonly"
|
||||||
},
|
},
|
||||||
"plugins": ["prettier", "mocha"],
|
"plugins": ["prettier", "mocha"],
|
||||||
"extends": [
|
"extends": ["airbnb-base", "airbnb-typescript/base", "prettier"],
|
||||||
"airbnb-base",
|
"rules": {
|
||||||
"airbnb-typescript/base",
|
"prettier/prettier": "error",
|
||||||
"prettier"],
|
"import/prefer-default-export": "off",
|
||||||
"rules": {
|
"import/no-extraneous-dependencies": [
|
||||||
"prettier/prettier": "error",
|
"error",
|
||||||
"import/prefer-default-export": "off",
|
{
|
||||||
"import/no-extraneous-dependencies": [
|
"devDependencies": ["**/*.test.[jt]s", "**/*.spec.[jt]s"]
|
||||||
"error",
|
}
|
||||||
{
|
],
|
||||||
"devDependencies": [
|
"import/extensions": [
|
||||||
"**/*.test.[jt]s",
|
"error",
|
||||||
"**/*.spec.[jt]s"
|
"ignorePackages",
|
||||||
]
|
{
|
||||||
}
|
"ts": "never",
|
||||||
],
|
"js": "never"
|
||||||
"import/extensions": [
|
}
|
||||||
"error",
|
|
||||||
"ignorePackages",
|
|
||||||
{
|
|
||||||
"ts": "never",
|
|
||||||
"js": "never"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "**/*.ts",
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"project": "./tsconfig.json"
|
|
||||||
},
|
|
||||||
"plugins": ["@typescript-eslint/eslint-plugin"],
|
|
||||||
"extends": [
|
|
||||||
"plugin:@typescript-eslint/eslint-recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/explicit-function-return-type": "off",
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"no-use-before-define": [0],
|
|
||||||
"@typescript-eslint/no-use-before-define": [1],
|
|
||||||
"import/no-unresolved": 0,
|
|
||||||
"import/no-extraneous-dependencies": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"devDependencies": [
|
|
||||||
"**/*.test.ts",
|
|
||||||
"**/*.spec.ts"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"quotes": "off",
|
|
||||||
"@typescript-eslint/quotes": [
|
|
||||||
2,
|
|
||||||
"single",
|
|
||||||
{
|
|
||||||
"avoidEscape": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-unused-vars": [2, { "argsIgnorePattern": "^_" }]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "**/*.ts",
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"project": "./tsconfig.json"
|
||||||
|
},
|
||||||
|
"plugins": ["@typescript-eslint/eslint-plugin"],
|
||||||
|
"extends": ["plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "prettier"],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
|
"no-use-before-define": [0],
|
||||||
|
"@typescript-eslint/no-use-before-define": [1],
|
||||||
|
"import/no-unresolved": 0,
|
||||||
|
"import/no-extraneous-dependencies": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"devDependencies": ["**/*.test.ts", "**/*.spec.ts"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"quotes": "off",
|
||||||
|
"@typescript-eslint/quotes": [
|
||||||
|
2,
|
||||||
|
"single",
|
||||||
|
{
|
||||||
|
"avoidEscape": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-unused-vars": [2, { "argsIgnorePattern": "^_" }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ignorePatterns": ["tsconfig.json", "*.d.ts", "dist/**/*", "dist", "node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ coverage
|
|||||||
dist
|
dist
|
||||||
docs
|
docs
|
||||||
examples/accounts
|
examples/accounts
|
||||||
node_modules
|
node_modules
|
||||||
|
.env
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
coverage
|
coverage
|
||||||
node_modules
|
node_modules
|
||||||
tests
|
tests
|
||||||
|
src
|
||||||
|
type
|
||||||
|
|||||||
+24
-25
@@ -1,40 +1,39 @@
|
|||||||
Nym Validator Client
|
# Nym Validator Client (Typescript)
|
||||||
====================
|
|
||||||
|
|
||||||
A TypeScript client for interacting with CosmWasm smart contracts in Nym validators.
|
A TypeScript client for interacting with CosmWasm smart contracts in Nym validators.
|
||||||
|
|
||||||
Running examples
|
Include the Nym Validator in your project:
|
||||||
-----------------
|
|
||||||
|
|
||||||
With the code checked out, `cd examples`. This folder contains runnable example code that will set up a blockchain and allow you to interact with it through the client.
|
|
||||||
|
|
||||||
Running tests
|
|
||||||
-------------
|
|
||||||
|
|
||||||
```
|
```
|
||||||
npm test
|
yarn add @nymproject/nym-validator-client
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also trigger test execution with a test watcher. I don't have the centuries of life left to me that are needed to fight through the arcana of wiring up a working TypeScript mocha triggered execution setup, so for now my Cargo-based hack is:
|
Connect to validator and make queries
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo watch -s "cd clients/validator && npm test"
|
import Validator from '@nymproject/nym-validator-client'
|
||||||
|
|
||||||
|
const main = async () => {
|
||||||
|
|
||||||
|
const client = await Validator.connectForQuery(rpcAddress, validatorAddress, prefix, mixnetContractAddress, vestingContractAddress, denom)
|
||||||
|
|
||||||
|
client.getBalance(address)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
It's ugly but works fine if you have Cargo installed. TypeScript setup help happily accepted here.
|
Connect to validator for performing actions
|
||||||
|
|
||||||
Generating Documentation
|
```
|
||||||
------------------------
|
import Validator from '@nymproject/nym-validator-client'
|
||||||
|
|
||||||
You can generate docs by running `npm run docs`. Generated output will appear in the `docs` directory.
|
const main = async () => {
|
||||||
|
|
||||||
Packaging
|
const client = await Validator.connect(mnemonic, rpcAddress, validatorAddress, prefix, mixnetContractAddress, vestingContractAddress, denom)
|
||||||
------------------------
|
|
||||||
|
|
||||||
If you're a Nym platform developer who's made changes to the client and wants to re-publish the package to NPM, here's how you do it:
|
const res = await client.send(address, [{ amount: '10000000', denom: 'unym' }]);
|
||||||
|
|
||||||
1. Bump the version number (use SemVer)
|
}
|
||||||
1. `npm run build`
|
|
||||||
1. `npm login` (if you haven't already)
|
```
|
||||||
1. `npm publish`
|
|
||||||
|
|||||||
@@ -4,15 +4,23 @@
|
|||||||
"description": "A TypeScript client for interacting with smart contracts in Nym validators",
|
"description": "A TypeScript client for interacting with smart contracts in Nym validators",
|
||||||
"repository": "https://github.com/nymtech/nym",
|
"repository": "https://github.com/nymtech/nym",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "rollup -c ./rollup.config.mjs",
|
||||||
"test": "ts-mocha tests/**/*.test.ts",
|
"build:types": "rollup-type-bundler --dist ./dist/nym-validator-client",
|
||||||
|
"build:prod": "sh ./scripts/build-prod.sh",
|
||||||
|
"test": "ts-mocha -p ./tsconfig.test.json ./src/tests/**/*.test.ts",
|
||||||
|
"testmock": "ts-mocha -p ./tsconfig.test.json ./src/tests/mock/*.test.ts",
|
||||||
"coverage": "nyc npm test",
|
"coverage": "nyc npm test",
|
||||||
"lint": "eslint src",
|
"clean": "rm -rf ./dist",
|
||||||
"lint:fix": "eslint src --fix",
|
"lint": "eslint",
|
||||||
|
"lint:fix": "eslint --fix",
|
||||||
|
"lint:tsc": "tsc --noEmit",
|
||||||
"docs": "typedoc --out docs src/index.ts"
|
"docs": "typedoc --out docs src/index.ts"
|
||||||
},
|
},
|
||||||
|
"files": [
|
||||||
|
"./dist/*"
|
||||||
|
],
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Nym Technologies SA (https://nymtech.net)",
|
"author": "Nym Technologies SA (https://nymtech.net)",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
@@ -21,6 +29,14 @@
|
|||||||
],
|
],
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@favware/rollup-type-bundler": "^2.0.0",
|
||||||
|
"@nymproject/types": "^1.0.0",
|
||||||
|
"@rollup/plugin-commonjs": "^24.0.1",
|
||||||
|
"@rollup/plugin-json": "^6.0.0",
|
||||||
|
"@rollup/plugin-typescript": "^11.0.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
|
"rollup": "^3.17.2",
|
||||||
|
"rollup-plugin-dts": "^5.2.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.7.0",
|
"@typescript-eslint/eslint-plugin": "^5.7.0",
|
||||||
"@typescript-eslint/parser": "^5.7.0",
|
"@typescript-eslint/parser": "^5.7.0",
|
||||||
"eslint": "^7.18.0",
|
"eslint": "^7.18.0",
|
||||||
@@ -31,21 +47,21 @@
|
|||||||
"eslint-plugin-import": "^2.25.4",
|
"eslint-plugin-import": "^2.25.4",
|
||||||
"eslint-plugin-mocha": "^10.0.3",
|
"eslint-plugin-mocha": "^10.0.3",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"expect": "^28.1.3",
|
|
||||||
"mocha": "^10.0.0",
|
"mocha": "^10.0.0",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"typedoc": "^0.22.13",
|
|
||||||
"ts-mocha": "^10.0.0",
|
"ts-mocha": "^10.0.0",
|
||||||
"typescript": "^4.6.2"
|
"typedoc": "^0.22.13",
|
||||||
},
|
"typescript": "^4.6.2",
|
||||||
"dependencies": {
|
"cosmjs-types": "^0.4.1",
|
||||||
"@cosmjs/cosmwasm-stargate": "^0.28.0",
|
"dotenv": "^16.0.3",
|
||||||
"@cosmjs/crypto": "^0.28.0",
|
"expect": "^28.1.3",
|
||||||
"@cosmjs/math": "^0.28.0",
|
"moq.ts": "^7.3.4",
|
||||||
"@cosmjs/proto-signing": "^0.28.0",
|
"@cosmjs/cosmwasm-stargate": "^0.29.5",
|
||||||
"@cosmjs/stargate": "^0.28.0",
|
"@cosmjs/crypto": "^0.29.5",
|
||||||
"@cosmjs/tendermint-rpc": "^0.28.0",
|
"@cosmjs/math": "^0.29.5",
|
||||||
"axios": "^0.26.1",
|
"@cosmjs/proto-signing": "^0.29.5",
|
||||||
"cosmjs-types": "^0.4.1"
|
"@cosmjs/stargate": "^0.29.5",
|
||||||
|
"@cosmjs/tendermint-rpc": "^0.29.5",
|
||||||
|
"axios": "^1.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import typescript from '@rollup/plugin-typescript';
|
||||||
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
import json from '@rollup/plugin-json';
|
||||||
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
input: './src/index.ts',
|
||||||
|
output: {
|
||||||
|
dir: 'dist/nym-validator-client',
|
||||||
|
format: 'cjs',
|
||||||
|
},
|
||||||
|
plugins: [resolve(), typescript(), commonjs(), json()],
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
rm -rf ./dist || true
|
||||||
|
rm -rf ../../dist || true
|
||||||
|
|
||||||
|
|
||||||
|
# Bundle application
|
||||||
|
|
||||||
|
yarn build
|
||||||
|
|
||||||
|
# Bundle types
|
||||||
|
|
||||||
|
yarn build:types
|
||||||
|
|
||||||
|
# Build package.json for bundle
|
||||||
|
|
||||||
|
node ./scripts/buildPackageJson.mjs
|
||||||
|
|
||||||
|
# Copy README
|
||||||
|
|
||||||
|
cp README.md dist/nym-validator-client
|
||||||
|
|
||||||
|
# move the output outside of the yarn/npm workspaces
|
||||||
|
|
||||||
|
mv ./dist ../../
|
||||||
|
|
||||||
|
echo "Output can be found in:"
|
||||||
|
realpath ../../dist
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
// parse the package.json from the SDK, so we can keep fields like the name and version
|
||||||
|
const json = JSON.parse(fs.readFileSync('./package.json').toString());
|
||||||
|
|
||||||
|
// defaults (NB: these are in the output file locations)
|
||||||
|
const main = 'index.js';
|
||||||
|
const types = 'index.d.ts';
|
||||||
|
|
||||||
|
// make a package.json for the bundle
|
||||||
|
const packageJson = {
|
||||||
|
name: json.name,
|
||||||
|
version: json.version,
|
||||||
|
license: json.license,
|
||||||
|
author: json.author,
|
||||||
|
main,
|
||||||
|
types,
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.writeFileSync('./dist/nym-validator-client/package.json', JSON.stringify(packageJson, null, 2));
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Decimal } from '@cosmjs/math';
|
import { Decimal } from '@cosmjs/math';
|
||||||
import { Coin } from '@cosmjs/stargate';
|
import { Coin } from '@cosmjs/stargate';
|
||||||
|
import { CoinMap } from './types/shared';
|
||||||
|
|
||||||
// NARROW NO-BREAK SPACE (U+202F)
|
// NARROW NO-BREAK SPACE (U+202F)
|
||||||
const thinSpace = '\u202F';
|
const thinSpace = '\u202F';
|
||||||
@@ -34,15 +35,6 @@ export function nativeToPrintable(nativeValue: string): string {
|
|||||||
return Decimal.fromAtomics(nativeValue, 6).toString();
|
return Decimal.fromAtomics(nativeValue, 6).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MappedCoin {
|
|
||||||
readonly denom: string;
|
|
||||||
readonly fractionalDigits: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CoinMap {
|
|
||||||
readonly [key: string]: MappedCoin;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function nativeCoinToDisplay(coin: Coin, coinMap: CoinMap): Coin {
|
export function nativeCoinToDisplay(coin: Coin, coinMap: CoinMap): Coin {
|
||||||
if (!coinMap) return coin;
|
if (!coinMap) return coin;
|
||||||
|
|
||||||
|
|||||||
+133
-75
@@ -1,6 +1,3 @@
|
|||||||
import { Bip39, Random } from '@cosmjs/crypto';
|
|
||||||
import { DirectSecp256k1HdWallet, EncodeObject } from '@cosmjs/proto-signing';
|
|
||||||
import { coin as cosmosCoin, Coin, DeliverTxResponse, isDeliverTxFailure, StdFee } from '@cosmjs/stargate';
|
|
||||||
import {
|
import {
|
||||||
ExecuteResult,
|
ExecuteResult,
|
||||||
InstantiateOptions,
|
InstantiateOptions,
|
||||||
@@ -8,45 +5,37 @@ import {
|
|||||||
MigrateResult,
|
MigrateResult,
|
||||||
UploadResult,
|
UploadResult,
|
||||||
} from '@cosmjs/cosmwasm-stargate';
|
} from '@cosmjs/cosmwasm-stargate';
|
||||||
import SigningClient, { ISigningClient } from './signing-client';
|
import { Bip39, Random } from '@cosmjs/crypto';
|
||||||
|
import { DirectSecp256k1HdWallet, EncodeObject } from '@cosmjs/proto-signing';
|
||||||
|
import { Coin, coin as cosmosCoin, DeliverTxResponse, isDeliverTxFailure, StdFee } from '@cosmjs/stargate';
|
||||||
import {
|
import {
|
||||||
ContractStateParams,
|
ContractStateParams,
|
||||||
Delegation,
|
Delegation,
|
||||||
Gateway,
|
Gateway,
|
||||||
GatewayBond,
|
GatewayBond,
|
||||||
|
GatewayOwnershipResponse,
|
||||||
|
LayerDistribution,
|
||||||
MixnetContractVersion,
|
MixnetContractVersion,
|
||||||
MixNode,
|
MixNode,
|
||||||
MixNodeBond,
|
MixNodeBond,
|
||||||
|
MixNodeCostParams,
|
||||||
|
MixNodeDetails,
|
||||||
|
MixNodeRewarding,
|
||||||
|
MixOwnershipResponse,
|
||||||
PagedAllDelegationsResponse,
|
PagedAllDelegationsResponse,
|
||||||
PagedDelegatorDelegationsResponse,
|
PagedDelegatorDelegationsResponse,
|
||||||
PagedGatewayResponse,
|
PagedGatewayResponse,
|
||||||
PagedMixDelegationsResponse,
|
PagedMixDelegationsResponse,
|
||||||
PagedMixnodeResponse,
|
PagedMixNodeBondResponse,
|
||||||
} from './types';
|
PagedMixNodeDetailsResponse,
|
||||||
import {
|
PagedUnbondedMixnodesResponse,
|
||||||
CoinMap,
|
RewardingParams,
|
||||||
displayAmountToNative,
|
StakeSaturationResponse,
|
||||||
MappedCoin,
|
UnbondedMixnodeResponse,
|
||||||
nativeCoinToDisplay,
|
} from '@nymproject/types';
|
||||||
nativeToPrintable,
|
|
||||||
printableBalance,
|
|
||||||
printableCoin,
|
|
||||||
} from './currency';
|
|
||||||
import QueryClient from './query-client';
|
import QueryClient from './query-client';
|
||||||
import { nymGasPrice } from './stargate-helper';
|
import SigningClient, { ISigningClient } from './signing-client';
|
||||||
|
import { ContractState } from './types/shared';
|
||||||
export { coins, coin } from '@cosmjs/stargate';
|
|
||||||
export { Coin };
|
|
||||||
export {
|
|
||||||
displayAmountToNative,
|
|
||||||
nativeCoinToDisplay,
|
|
||||||
printableCoin,
|
|
||||||
printableBalance,
|
|
||||||
nativeToPrintable,
|
|
||||||
MappedCoin,
|
|
||||||
CoinMap,
|
|
||||||
};
|
|
||||||
export { nymGasPrice };
|
|
||||||
|
|
||||||
export interface INymClient {
|
export interface INymClient {
|
||||||
readonly mixnetContract: string;
|
readonly mixnetContract: string;
|
||||||
@@ -147,7 +136,7 @@ export default class ValidatorClient implements INymClient {
|
|||||||
return DirectSecp256k1HdWallet.fromMnemonic(mnemonic, signerOptions);
|
return DirectSecp256k1HdWallet.fromMnemonic(mnemonic, signerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBalance(address: string): Promise<Coin> {
|
async getBalance(address: string): Promise<Coin> {
|
||||||
return this.client.getBalance(address, this.denom);
|
return this.client.getBalance(address, this.denom);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,15 +148,39 @@ export default class ValidatorClient implements INymClient {
|
|||||||
return this.client.getCachedMixnodes();
|
return this.client.getCachedMixnodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getActiveMixnodes(): Promise<MixNodeBond[]> {
|
async getStakeSaturation(mixId: number): Promise<StakeSaturationResponse> {
|
||||||
|
return this.client.getStakeSaturation(this.mixnetContract, mixId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getActiveMixnodes(): Promise<MixNodeDetails[]> {
|
||||||
return this.client.getActiveMixnodes();
|
return this.client.getActiveMixnodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getUnbondedMixNodeInformation(mixId: number): Promise<UnbondedMixnodeResponse> {
|
||||||
|
return this.client.getUnbondedMixNodeInformation(this.mixnetContract, mixId);
|
||||||
|
}
|
||||||
|
|
||||||
async getRewardedMixnodes(): Promise<MixNodeBond[]> {
|
async getRewardedMixnodes(): Promise<MixNodeBond[]> {
|
||||||
return this.client.getRewardedMixnodes();
|
return this.client.getRewardedMixnodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getMixnetContractSettings(): Promise<ContractStateParams> {
|
async getMixnodeRewardingDetails(mixId: number): Promise<MixNodeRewarding> {
|
||||||
|
return this.client.getMixnodeRewardingDetails(this.mixnetContract, mixId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOwnedMixnode(address: string): Promise<MixOwnershipResponse> {
|
||||||
|
return this.client.getOwnedMixnode(this.mixnetContract, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ownsGateway(address: string): Promise<GatewayOwnershipResponse> {
|
||||||
|
return this.client.ownsGateway(this.mixnetContract, address);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLayerDistribution(): Promise<LayerDistribution> {
|
||||||
|
return this.client.getLayerDistribution(this.mixnetContract);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getMixnetContractSettings(): Promise<ContractState> {
|
||||||
return this.client.getStateParams(this.mixnetContract);
|
return this.client.getStateParams(this.mixnetContract);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,29 +188,74 @@ export default class ValidatorClient implements INymClient {
|
|||||||
return this.client.getContractVersion(this.mixnetContract);
|
return this.client.getContractVersion(this.mixnetContract);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getRewardPool(): Promise<string> {
|
public async getVestingContractVersion(): Promise<MixnetContractVersion> {
|
||||||
return this.client.getRewardPool(this.mixnetContract);
|
return this.client.getContractVersion(this.vestingContract);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getCirculatingSupply(): Promise<string> {
|
public async getSpendableCoins(vestingAccountAddress: string): Promise<MixnetContractVersion> {
|
||||||
return this.client.getCirculatingSupply(this.mixnetContract);
|
return this.client.getSpendableCoins(this.vestingContract, vestingAccountAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getSybilResistancePercent(): Promise<number> {
|
public async getRewardParams(): Promise<RewardingParams> {
|
||||||
return this.client.getSybilResistancePercent(this.mixnetContract);
|
return this.client.getRewardParams(this.mixnetContract);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getIntervalRewardPercent(): Promise<number> {
|
async getUnbondedMixNodes(): Promise<UnbondedMixnodeResponse[]> {
|
||||||
return this.client.getIntervalRewardPercent(this.mixnetContract);
|
let mixNodes: UnbondedMixnodeResponse[] = [];
|
||||||
|
const limit = 50;
|
||||||
|
let startAfter;
|
||||||
|
for (;;) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const pagedResponse: PagedUnbondedMixnodesResponse = await this.client.getUnbondedMixNodes(
|
||||||
|
this.mixnetContract,
|
||||||
|
limit,
|
||||||
|
startAfter,
|
||||||
|
);
|
||||||
|
|
||||||
|
mixNodes = mixNodes.concat(pagedResponse.nodes);
|
||||||
|
startAfter = pagedResponse.start_next_after;
|
||||||
|
// if `start_next_after` is not set, we're done
|
||||||
|
if (!startAfter) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mixNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getAllNyxdMixnodes(): Promise<MixNodeBond[]> {
|
public async getMixNodeBonds(): Promise<MixNodeBond[]> {
|
||||||
let mixNodes: MixNodeBond[] = [];
|
let mixNodes: MixNodeBond[] = [];
|
||||||
const limit = 50;
|
const limit = 50;
|
||||||
let startAfter;
|
let startAfter;
|
||||||
for (; ;) {
|
for (;;) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const pagedResponse: PagedMixnodeResponse = await this.client.getMixNodesPaged(this.mixnetContract, limit);
|
const pagedResponse: PagedMixNodeBondResponse = await this.client.getMixNodeBonds(
|
||||||
|
this.mixnetContract,
|
||||||
|
limit,
|
||||||
|
startAfter,
|
||||||
|
);
|
||||||
|
mixNodes = mixNodes.concat(pagedResponse.nodes);
|
||||||
|
startAfter = pagedResponse.start_next_after;
|
||||||
|
// if `start_next_after` is not set, we're done
|
||||||
|
if (!startAfter) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mixNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getMixNodesDetailed(): Promise<MixNodeDetails[]> {
|
||||||
|
let mixNodes: MixNodeDetails[] = [];
|
||||||
|
const limit = 50;
|
||||||
|
let startAfter;
|
||||||
|
for (;;) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const pagedResponse: PagedMixNodeDetailsResponse = await this.client.getMixNodesDetailed(
|
||||||
|
this.mixnetContract,
|
||||||
|
limit,
|
||||||
|
startAfter,
|
||||||
|
);
|
||||||
mixNodes = mixNodes.concat(pagedResponse.nodes);
|
mixNodes = mixNodes.concat(pagedResponse.nodes);
|
||||||
startAfter = pagedResponse.start_next_after;
|
startAfter = pagedResponse.start_next_after;
|
||||||
// if `start_next_after` is not set, we're done
|
// if `start_next_after` is not set, we're done
|
||||||
@@ -210,37 +268,24 @@ export default class ValidatorClient implements INymClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async getAllNyxdGateways(): Promise<GatewayBond[]> {
|
public async getAllNyxdGateways(): Promise<GatewayBond[]> {
|
||||||
let gateways: GatewayBond[] = [];
|
const pagedResponse: PagedGatewayResponse = await this.client.getGatewaysPaged(this.mixnetContract);
|
||||||
const limit = 50;
|
return pagedResponse.nodes;
|
||||||
let startAfter;
|
|
||||||
for (; ;) {
|
|
||||||
// eslint-disable-next-line no-await-in-loop
|
|
||||||
const pagedResponse: PagedGatewayResponse = await this.client.getGatewaysPaged(this.mixnetContract, limit);
|
|
||||||
gateways = gateways.concat(pagedResponse.nodes);
|
|
||||||
startAfter = pagedResponse.start_next_after;
|
|
||||||
// if `start_next_after` is not set, we're done
|
|
||||||
if (!startAfter) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return gateways;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets list of all delegations towards particular mixnode.
|
* Gets list of all delegations towards particular mixnode.
|
||||||
*
|
*
|
||||||
* @param mixIdentity identity of the node to which the delegation was sent
|
* @param mix_id identity of the node to which the delegation was sent
|
||||||
*/
|
*/
|
||||||
public async getAllNyxdSingleMixnodeDelegations(mixIdentity: string): Promise<Delegation[]> {
|
public async getAllNyxdSingleMixnodeDelegations(mix_id: number): Promise<Delegation[]> {
|
||||||
let delegations: Delegation[] = [];
|
let delegations: Delegation[] = [];
|
||||||
const limit = 250;
|
const limit = 250;
|
||||||
let startAfter;
|
let startAfter;
|
||||||
for (; ;) {
|
for (;;) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const pagedResponse: PagedMixDelegationsResponse = await this.client.getMixNodeDelegationsPaged(
|
const pagedResponse: PagedMixDelegationsResponse = await this.client.getMixNodeDelegationsPaged(
|
||||||
this.mixnetContract,
|
this.mixnetContract,
|
||||||
mixIdentity,
|
mix_id,
|
||||||
limit,
|
limit,
|
||||||
startAfter,
|
startAfter,
|
||||||
);
|
);
|
||||||
@@ -259,7 +304,7 @@ export default class ValidatorClient implements INymClient {
|
|||||||
let delegations: Delegation[] = [];
|
let delegations: Delegation[] = [];
|
||||||
const limit = 250;
|
const limit = 250;
|
||||||
let startAfter;
|
let startAfter;
|
||||||
for (; ;) {
|
for (;;) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const pagedResponse: PagedDelegatorDelegationsResponse = await this.client.getDelegatorDelegationsPaged(
|
const pagedResponse: PagedDelegatorDelegationsResponse = await this.client.getDelegatorDelegationsPaged(
|
||||||
this.mixnetContract,
|
this.mixnetContract,
|
||||||
@@ -278,13 +323,13 @@ export default class ValidatorClient implements INymClient {
|
|||||||
return delegations;
|
return delegations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getAllNyxdNetworkDelegations(): Promise<Delegation[]> {
|
public async getAllNyxdDelegations(): Promise<Delegation[]> {
|
||||||
let delegations: Delegation[] = [];
|
let delegations: Delegation[] = [];
|
||||||
const limit = 250;
|
const limit = 250;
|
||||||
let startAfter;
|
let startAfter;
|
||||||
for (; ;) {
|
for (;;) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllNetworkDelegationsPaged(
|
const pagedResponse: PagedAllDelegationsResponse = await this.client.getAllDelegationsPaged(
|
||||||
this.mixnetContract,
|
this.mixnetContract,
|
||||||
limit,
|
limit,
|
||||||
startAfter,
|
startAfter,
|
||||||
@@ -300,6 +345,10 @@ export default class ValidatorClient implements INymClient {
|
|||||||
return delegations;
|
return delegations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getDelegationDetails(mix_id: number, delegator: string): Promise<Delegation> {
|
||||||
|
return this.client.getDelegationDetails(this.mixnetContract, mix_id, delegator);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a minimum gateway bond required to create a fresh mixnode.
|
* Generate a minimum gateway bond required to create a fresh mixnode.
|
||||||
*
|
*
|
||||||
@@ -308,7 +357,7 @@ export default class ValidatorClient implements INymClient {
|
|||||||
public async minimumMixnodePledge(): Promise<Coin> {
|
public async minimumMixnodePledge(): Promise<Coin> {
|
||||||
const stateParams = await this.getMixnetContractSettings();
|
const stateParams = await this.getMixnetContractSettings();
|
||||||
// we trust the contract to return a valid number
|
// we trust the contract to return a valid number
|
||||||
return cosmosCoin(stateParams.minimum_mixnode_pledge, this.prefix);
|
return cosmosCoin(stateParams.params.minimum_mixnode_pledge, this.prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -319,7 +368,7 @@ export default class ValidatorClient implements INymClient {
|
|||||||
public async minimumGatewayPledge(): Promise<Coin> {
|
public async minimumGatewayPledge(): Promise<Coin> {
|
||||||
const stateParams = await this.getMixnetContractSettings();
|
const stateParams = await this.getMixnetContractSettings();
|
||||||
// we trust the contract to return a valid number
|
// we trust the contract to return a valid number
|
||||||
return cosmosCoin(stateParams.minimum_gateway_pledge, this.prefix);
|
return cosmosCoin(stateParams.params.minimum_gateway_pledge, this.prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async send(
|
public async send(
|
||||||
@@ -393,12 +442,21 @@ export default class ValidatorClient implements INymClient {
|
|||||||
public async bondMixNode(
|
public async bondMixNode(
|
||||||
mixNode: MixNode,
|
mixNode: MixNode,
|
||||||
ownerSignature: string,
|
ownerSignature: string,
|
||||||
|
costParams: MixNodeCostParams,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee?: StdFee | 'auto' | number,
|
fee?: StdFee | 'auto' | number,
|
||||||
memo?: string,
|
memo?: string,
|
||||||
): Promise<ExecuteResult> {
|
): Promise<ExecuteResult> {
|
||||||
this.assertSigning();
|
this.assertSigning();
|
||||||
return (this.client as ISigningClient).bondMixNode(this.mixnetContract, mixNode, ownerSignature, pledge, fee, memo);
|
return (this.client as ISigningClient).bondMixNode(
|
||||||
|
this.mixnetContract,
|
||||||
|
mixNode,
|
||||||
|
costParams,
|
||||||
|
ownerSignature,
|
||||||
|
pledge,
|
||||||
|
fee,
|
||||||
|
memo,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async unbondMixNode(fee?: StdFee | 'auto' | number, memo?: string): Promise<ExecuteResult> {
|
public async unbondMixNode(fee?: StdFee | 'auto' | number, memo?: string): Promise<ExecuteResult> {
|
||||||
@@ -423,29 +481,29 @@ export default class ValidatorClient implements INymClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async delegateToMixNode(
|
public async delegateToMixNode(
|
||||||
mixIdentity: string,
|
mixId: number,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee?: StdFee | 'auto' | number,
|
fee?: StdFee | 'auto' | number,
|
||||||
memo?: string,
|
memo?: string,
|
||||||
): Promise<ExecuteResult> {
|
): Promise<ExecuteResult> {
|
||||||
this.assertSigning();
|
this.assertSigning();
|
||||||
return (this.client as ISigningClient).delegateToMixNode(this.mixnetContract, mixIdentity, amount, fee, memo);
|
return (this.client as ISigningClient).delegateToMixNode(this.mixnetContract, mixId, amount, fee, memo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async undelegateFromMixNode(
|
public async undelegateFromMixNode(
|
||||||
mixIdentity: string,
|
mixId: number,
|
||||||
fee?: StdFee | 'auto' | number,
|
fee?: StdFee | 'auto' | number,
|
||||||
memo?: string,
|
memo?: string,
|
||||||
): Promise<ExecuteResult> {
|
): Promise<ExecuteResult> {
|
||||||
return (this.client as ISigningClient).undelegateFromMixNode(this.mixnetContract, mixIdentity, fee, memo);
|
return (this.client as ISigningClient).undelegateFromMixNode(this.mixnetContract, mixId, fee, memo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateMixnodeConfig(
|
public async updateMixnodeConfig(
|
||||||
mixIdentity: string,
|
mixId: number,
|
||||||
fee: StdFee | 'auto' | number,
|
fee: StdFee | 'auto' | number,
|
||||||
profitPercentage: number,
|
profitPercentage: number,
|
||||||
): Promise<ExecuteResult> {
|
): Promise<ExecuteResult> {
|
||||||
return (this.client as ISigningClient).updateMixnodeConfig(this.mixnetContract, mixIdentity, profitPercentage, fee);
|
return (this.client as ISigningClient).updateMixnodeConfig(this.mixnetContract, mixId, profitPercentage, fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateContractStateParams(
|
public async updateContractStateParams(
|
||||||
|
|||||||
@@ -2,9 +2,8 @@
|
|||||||
* Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
* Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { GatewayBond, MixNodeBond } from './types';
|
import { GatewayBond, MixNodeBond, MixNodeDetails } from '@nymproject/types';
|
||||||
|
|
||||||
export const NYM_API_VERSION = '/v1';
|
export const NYM_API_VERSION = '/v1';
|
||||||
export const NYM_API_GATEWAYS_PATH = `${NYM_API_VERSION}/gateways`;
|
export const NYM_API_GATEWAYS_PATH = `${NYM_API_VERSION}/gateways`;
|
||||||
@@ -17,7 +16,7 @@ export interface INymApiQuery {
|
|||||||
|
|
||||||
getCachedGateways(): Promise<GatewayBond[]>;
|
getCachedGateways(): Promise<GatewayBond[]>;
|
||||||
|
|
||||||
getActiveMixnodes(): Promise<MixNodeBond[]>;
|
getActiveMixnodes(): Promise<MixNodeDetails[]>;
|
||||||
|
|
||||||
getRewardedMixnodes(): Promise<MixNodeBond[]>;
|
getRewardedMixnodes(): Promise<MixNodeBond[]>;
|
||||||
}
|
}
|
||||||
@@ -51,7 +50,7 @@ export default class NymApiQuerier implements INymApiQuery {
|
|||||||
throw new Error('None of the provided validator APIs seem to be alive');
|
throw new Error('None of the provided validator APIs seem to be alive');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getActiveMixnodes(): Promise<MixNodeBond[]> {
|
async getActiveMixnodes(): Promise<MixNodeDetails[]> {
|
||||||
const url = new URL(this.nymApiUrl);
|
const url = new URL(this.nymApiUrl);
|
||||||
url.pathname += NYM_API_ACTIVE_MIXNODES_PATH;
|
url.pathname += NYM_API_ACTIVE_MIXNODES_PATH;
|
||||||
|
|
||||||
|
|||||||
@@ -2,28 +2,24 @@
|
|||||||
* Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
* Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { JsonObject } from '@cosmjs/cosmwasm-stargate';
|
|
||||||
// eslint-disable-next-line import/no-cycle
|
// eslint-disable-next-line import/no-cycle
|
||||||
import { INyxdQuery } from './query-client';
|
import { INyxdQuery } from './query-client';
|
||||||
|
import { Delegation, RewardingParams, StakeSaturationResponse } from '@nymproject/types';
|
||||||
import {
|
import {
|
||||||
ContractStateParams,
|
UnbondedMixnodeResponse,
|
||||||
Delegation,
|
|
||||||
GatewayOwnershipResponse,
|
GatewayOwnershipResponse,
|
||||||
LayerDistribution,
|
|
||||||
MixnetContractVersion,
|
MixnetContractVersion,
|
||||||
MixOwnershipResponse,
|
MixOwnershipResponse,
|
||||||
PagedAllDelegationsResponse,
|
PagedAllDelegationsResponse,
|
||||||
PagedDelegatorDelegationsResponse,
|
PagedDelegatorDelegationsResponse,
|
||||||
PagedGatewayResponse,
|
PagedGatewayResponse,
|
||||||
PagedMixDelegationsResponse,
|
PagedMixDelegationsResponse,
|
||||||
PagedMixnodeResponse,
|
PagedMixNodeBondResponse,
|
||||||
RewardingStatus,
|
PagedMixNodeDetailsResponse,
|
||||||
} from './types';
|
PagedUnbondedMixnodesResponse,
|
||||||
|
LayerDistribution,
|
||||||
interface SmartContractQuery {
|
} from '@nymproject/types';
|
||||||
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
|
import { ContractState, SmartContractQuery } from './types/shared';
|
||||||
}
|
|
||||||
|
|
||||||
export default class NyxdQuerier implements INyxdQuery {
|
export default class NyxdQuerier implements INyxdQuery {
|
||||||
client: SmartContractQuery;
|
client: SmartContractQuery;
|
||||||
@@ -38,15 +34,44 @@ export default class NyxdQuerier implements INyxdQuery {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
|
getMixNodeBonds(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedMixNodeBondResponse> {
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
get_mix_nodes: {
|
get_mix_node_bonds: {
|
||||||
limit,
|
limit,
|
||||||
start_after: startAfter,
|
start_after: startAfter,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMixNodesDetailed(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedMixNodeDetailsResponse> {
|
||||||
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
|
get_mix_nodes_detailed: {
|
||||||
|
limit,
|
||||||
|
start_after: startAfter,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse> {
|
||||||
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
|
get_stake_saturation: { mix_id: mixId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<any> {
|
||||||
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
|
get_mixnode_rewarding_details: { mix_id: mixId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
|
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
get_gateways: {
|
get_gateways: {
|
||||||
@@ -56,35 +81,51 @@ export default class NyxdQuerier implements INyxdQuery {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
owns_mixnode: {
|
get_owned_mixnode: {
|
||||||
address,
|
address,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUnbondedMixNodes(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedUnbondedMixnodesResponse> {
|
||||||
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
|
get_unbonded_mix_nodes: { limit, start_after: startAfter },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse> {
|
||||||
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
|
get_unbonded_mix_node_information: { mix_id: mixId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
owns_gateway: {
|
get_owned_gateway: {
|
||||||
address,
|
address,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
|
getStateParams(mixnetContractAddress: string): Promise<ContractState> {
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
state_params: {},
|
get_state: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllNetworkDelegationsPaged(
|
getAllDelegationsPaged(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
limit?: number,
|
limit?: number,
|
||||||
startAfter?: [string, string],
|
startAfter?: [string, string],
|
||||||
): Promise<PagedAllDelegationsResponse> {
|
): Promise<PagedAllDelegationsResponse> {
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
get_all_network_delegations: {
|
get_all_delegations: {
|
||||||
start_after: startAfter,
|
start_after: startAfter,
|
||||||
limit,
|
limit,
|
||||||
},
|
},
|
||||||
@@ -93,13 +134,13 @@ export default class NyxdQuerier implements INyxdQuery {
|
|||||||
|
|
||||||
getMixNodeDelegationsPaged(
|
getMixNodeDelegationsPaged(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixIdentity: string,
|
mix_id: number,
|
||||||
limit?: number,
|
limit?: number,
|
||||||
startAfter?: string,
|
startAfter?: string,
|
||||||
): Promise<PagedMixDelegationsResponse> {
|
): Promise<PagedMixDelegationsResponse> {
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
get_mixnode_delegations: {
|
get_mixnode_delegations: {
|
||||||
mix_identity: mixIdentity,
|
mix_id: mix_id,
|
||||||
start_after: startAfter,
|
start_after: startAfter,
|
||||||
limit,
|
limit,
|
||||||
},
|
},
|
||||||
@@ -121,10 +162,10 @@ export default class NyxdQuerier implements INyxdQuery {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
|
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation> {
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
get_delegation_details: {
|
get_delegation_details: {
|
||||||
mix_identity: mixIdentity,
|
mix_id: mix_id,
|
||||||
delegator,
|
delegator,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -132,44 +173,19 @@ export default class NyxdQuerier implements INyxdQuery {
|
|||||||
|
|
||||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
|
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
layer_distribution: {},
|
get_layer_distribution: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getRewardPool(mixnetContractAddress: string): Promise<string> {
|
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams> {
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||||
get_reward_pool: {},
|
get_rewarding_params: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
|
getSpendableCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<any> {
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
return this.client.queryContractSmart(vestingContractAddress, {
|
||||||
get_circulating_supply: {},
|
vesting_account_address: vestingAccountAddress,
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_interval_reward_percent: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_sybil_resistance_percent: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getRewardingStatus(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
rewardingIntervalNonce: number,
|
|
||||||
): Promise<RewardingStatus> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_rewarding_status: {
|
|
||||||
mix_identity: mixIdentity,
|
|
||||||
rewarding_interval_nonce: rewardingIntervalNonce,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,75 +1,56 @@
|
|||||||
import { CosmWasmClient, JsonObject } from '@cosmjs/cosmwasm-stargate';
|
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate';
|
||||||
import { Tendermint34Client } from '@cosmjs/tendermint-rpc';
|
|
||||||
import {
|
|
||||||
Account,
|
|
||||||
Block,
|
|
||||||
Coin,
|
|
||||||
DeliverTxResponse,
|
|
||||||
IndexedTx,
|
|
||||||
SearchTxFilter,
|
|
||||||
SearchTxQuery,
|
|
||||||
SequenceResponse,
|
|
||||||
} from '@cosmjs/stargate';
|
|
||||||
import { Code, CodeDetails, Contract, ContractCodeHistoryEntry } from '@cosmjs/cosmwasm-stargate/build/cosmwasmclient';
|
|
||||||
// eslint-disable-next-line import/no-cycle
|
// eslint-disable-next-line import/no-cycle
|
||||||
import NyxdQuerier from './nyxd-querier';
|
import NyxdQuerier from './nyxd-querier';
|
||||||
import {
|
import {
|
||||||
ContractStateParams,
|
|
||||||
Delegation,
|
Delegation,
|
||||||
GatewayBond,
|
GatewayBond,
|
||||||
GatewayOwnershipResponse,
|
GatewayOwnershipResponse,
|
||||||
LayerDistribution,
|
LayerDistribution,
|
||||||
MixnetContractVersion,
|
MixnetContractVersion,
|
||||||
MixNodeBond,
|
MixNodeDetails,
|
||||||
MixOwnershipResponse,
|
MixOwnershipResponse,
|
||||||
PagedAllDelegationsResponse,
|
PagedAllDelegationsResponse,
|
||||||
PagedDelegatorDelegationsResponse,
|
PagedDelegatorDelegationsResponse,
|
||||||
PagedGatewayResponse,
|
PagedGatewayResponse,
|
||||||
PagedMixDelegationsResponse,
|
PagedMixDelegationsResponse,
|
||||||
PagedMixnodeResponse,
|
PagedMixNodeBondResponse,
|
||||||
RewardingStatus,
|
PagedMixNodeDetailsResponse,
|
||||||
} from './types';
|
PagedUnbondedMixnodesResponse,
|
||||||
import NymApiQuerier, { INymApiQuery as INymApiQuery } from './nym-api-querier';
|
StakeSaturationResponse,
|
||||||
|
UnbondedMixnodeResponse,
|
||||||
export interface ICosmWasmQuery {
|
MixNodeBond,
|
||||||
// methods exposed by `CosmWasmClient`
|
MixNodeRewarding,
|
||||||
getChainId(): Promise<string>;
|
} from '@nymproject/types';
|
||||||
getHeight(): Promise<number>;
|
import NymApiQuerier, { INymApiQuery } from './nym-api-querier';
|
||||||
getAccount(searchAddress: string): Promise<Account | null>;
|
import { ContractState, ICosmWasmQuery } from './types/shared';
|
||||||
getSequence(address: string): Promise<SequenceResponse>;
|
import { RewardingParams } from '@nymproject/types';
|
||||||
getBlock(height?: number): Promise<Block>;
|
import { Tendermint34Client } from '@cosmjs/tendermint-rpc';
|
||||||
getBalance(address: string, searchDenom: string): Promise<Coin>;
|
|
||||||
getTx(id: string): Promise<IndexedTx | null>;
|
|
||||||
searchTx(query: SearchTxQuery, filter?: SearchTxFilter): Promise<readonly IndexedTx[]>;
|
|
||||||
disconnect(): void;
|
|
||||||
broadcastTx(tx: Uint8Array, timeoutMs?: number, pollIntervalMs?: number): Promise<DeliverTxResponse>;
|
|
||||||
getCodes(): Promise<readonly Code[]>;
|
|
||||||
getCodeDetails(codeId: number): Promise<CodeDetails>;
|
|
||||||
getContracts(codeId: number): Promise<readonly string[]>;
|
|
||||||
getContract(address: string): Promise<Contract>;
|
|
||||||
getContractCodeHistory(address: string): Promise<readonly ContractCodeHistoryEntry[]>;
|
|
||||||
queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null>;
|
|
||||||
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface INyxdQuery {
|
export interface INyxdQuery {
|
||||||
// nym-specific implemented inside NymQuerier
|
// nym-specific implemented inside NymQuerier
|
||||||
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
|
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
|
||||||
|
getMixNodeBonds(
|
||||||
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse>;
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedMixNodeBondResponse>;
|
||||||
|
getMixNodesDetailed(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedMixNodeDetailsResponse>;
|
||||||
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse>;
|
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse>;
|
||||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
|
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
|
||||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
|
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
|
||||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams>;
|
getStateParams(mixnetContractAddress: string): Promise<ContractState>;
|
||||||
|
getAllDelegationsPaged(
|
||||||
getAllNetworkDelegationsPaged(
|
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
limit?: number,
|
limit?: number,
|
||||||
startAfter?: [string, string],
|
startAfter?: [string, string],
|
||||||
): Promise<PagedAllDelegationsResponse>;
|
): Promise<PagedAllDelegationsResponse>;
|
||||||
getMixNodeDelegationsPaged(
|
getMixNodeDelegationsPaged(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixIdentity: string,
|
mix_id: number,
|
||||||
limit?: number,
|
limit?: number,
|
||||||
startAfter?: string,
|
startAfter?: string,
|
||||||
): Promise<PagedMixDelegationsResponse>;
|
): Promise<PagedMixDelegationsResponse>;
|
||||||
@@ -79,21 +60,15 @@ export interface INyxdQuery {
|
|||||||
limit?: number,
|
limit?: number,
|
||||||
startAfter?: string,
|
startAfter?: string,
|
||||||
): Promise<PagedDelegatorDelegationsResponse>;
|
): Promise<PagedDelegatorDelegationsResponse>;
|
||||||
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation>;
|
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation>;
|
||||||
|
|
||||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution>;
|
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution>;
|
||||||
getRewardPool(mixnetContractAddress: string): Promise<string>;
|
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams>;
|
||||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string>;
|
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse>;
|
||||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number>;
|
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse>;
|
||||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number>;
|
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding>;
|
||||||
getRewardingStatus(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
rewardingIntervalNonce: number,
|
|
||||||
): Promise<RewardingStatus>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IQueryClient extends ICosmWasmQuery, INyxdQuery, INymApiQuery { }
|
export interface IQueryClient extends ICosmWasmQuery, INyxdQuery, INymApiQuery {}
|
||||||
|
|
||||||
export default class QueryClient extends CosmWasmClient implements IQueryClient {
|
export default class QueryClient extends CosmWasmClient implements IQueryClient {
|
||||||
private nyxdQuerier: NyxdQuerier;
|
private nyxdQuerier: NyxdQuerier;
|
||||||
@@ -115,41 +90,73 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
|||||||
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
|
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
|
getMixNodeBonds(
|
||||||
return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedMixNodeBondResponse> {
|
||||||
|
return this.nyxdQuerier.getMixNodeBonds(mixnetContractAddress, limit, startAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMixNodesDetailed(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedMixNodeDetailsResponse> {
|
||||||
|
return this.nyxdQuerier.getMixNodesDetailed(mixnetContractAddress, limit, startAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse> {
|
||||||
|
return this.nyxdQuerier.getStakeSaturation(mixnetContractAddress, mixId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding> {
|
||||||
|
return this.nyxdQuerier.getMixnodeRewardingDetails(mixnetContractAddress, mixId);
|
||||||
}
|
}
|
||||||
|
|
||||||
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
|
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
|
||||||
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
|
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
||||||
return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address);
|
return this.nyxdQuerier.getOwnedMixnode(mixnetContractAddress, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
||||||
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
|
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
|
getUnbondedMixNodes(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedUnbondedMixnodesResponse> {
|
||||||
|
return this.nyxdQuerier.getUnbondedMixNodes(mixnetContractAddress, limit, startAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse> {
|
||||||
|
return this.nyxdQuerier.getUnbondedMixNodeInformation(mixnetContractAddress, mixId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStateParams(mixnetContractAddress: string): Promise<ContractState> {
|
||||||
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
|
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllNetworkDelegationsPaged(
|
getAllDelegationsPaged(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
limit?: number,
|
limit?: number,
|
||||||
startAfter?: [string, string],
|
startAfter?: [string, string],
|
||||||
): Promise<PagedAllDelegationsResponse> {
|
): Promise<PagedAllDelegationsResponse> {
|
||||||
return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
|
return this.nyxdQuerier.getAllDelegationsPaged(mixnetContractAddress, limit, startAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMixNodeDelegationsPaged(
|
getMixNodeDelegationsPaged(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixIdentity: string,
|
mix_id: number,
|
||||||
limit?: number,
|
limit?: number,
|
||||||
startAfter?: string,
|
startAfter?: string,
|
||||||
): Promise<PagedMixDelegationsResponse> {
|
): Promise<PagedMixDelegationsResponse> {
|
||||||
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
|
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mix_id, limit, startAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDelegatorDelegationsPaged(
|
getDelegatorDelegationsPaged(
|
||||||
@@ -161,36 +168,16 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
|||||||
return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
|
return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
|
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation> {
|
||||||
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
|
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mix_id, delegator);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
|
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
|
||||||
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
|
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRewardPool(mixnetContractAddress: string): Promise<string> {
|
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams> {
|
||||||
return this.nyxdQuerier.getRewardPool(mixnetContractAddress);
|
return this.nyxdQuerier.getRewardParams(mixnetContractAddress);
|
||||||
}
|
|
||||||
|
|
||||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
|
|
||||||
return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getRewardingStatus(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
rewardingIntervalNonce: number,
|
|
||||||
): Promise<RewardingStatus> {
|
|
||||||
return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCachedGateways(): Promise<GatewayBond[]> {
|
getCachedGateways(): Promise<GatewayBond[]> {
|
||||||
@@ -201,11 +188,15 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
|||||||
return this.nymApiQuerier.getCachedMixnodes();
|
return this.nymApiQuerier.getCachedMixnodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
getActiveMixnodes(): Promise<MixNodeBond[]> {
|
getActiveMixnodes(): Promise<MixNodeDetails[]> {
|
||||||
return this.nymApiQuerier.getActiveMixnodes();
|
return this.nymApiQuerier.getActiveMixnodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
getRewardedMixnodes(): Promise<MixNodeBond[]> {
|
getRewardedMixnodes(): Promise<MixNodeBond[]> {
|
||||||
return this.nymApiQuerier.getRewardedMixnodes();
|
return this.nymApiQuerier.getRewardedMixnodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSpendableCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<any> {
|
||||||
|
return this.nyxdQuerier.getSpendableCoins(vestingContractAddress, vestingAccountAddress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,15 +25,22 @@ import {
|
|||||||
MixnetContractVersion,
|
MixnetContractVersion,
|
||||||
MixNode,
|
MixNode,
|
||||||
MixNodeBond,
|
MixNodeBond,
|
||||||
|
MixNodeCostParams,
|
||||||
|
MixNodeDetails,
|
||||||
|
MixNodeRewarding,
|
||||||
MixOwnershipResponse,
|
MixOwnershipResponse,
|
||||||
PagedAllDelegationsResponse,
|
PagedAllDelegationsResponse,
|
||||||
PagedDelegatorDelegationsResponse,
|
PagedDelegatorDelegationsResponse,
|
||||||
PagedGatewayResponse,
|
PagedGatewayResponse,
|
||||||
PagedMixDelegationsResponse,
|
PagedMixDelegationsResponse,
|
||||||
PagedMixnodeResponse,
|
PagedMixNodeBondResponse,
|
||||||
RewardingStatus,
|
PagedMixNodeDetailsResponse,
|
||||||
} from './types';
|
PagedUnbondedMixnodesResponse,
|
||||||
|
RewardingParams,
|
||||||
|
UnbondedMixnodeResponse,
|
||||||
|
} from '@nymproject/types';
|
||||||
import NymApiQuerier from './nym-api-querier';
|
import NymApiQuerier from './nym-api-querier';
|
||||||
|
import { ContractState } from './types/shared';
|
||||||
|
|
||||||
// methods exposed by `SigningCosmWasmClient`
|
// methods exposed by `SigningCosmWasmClient`
|
||||||
export interface ICosmWasmSigning {
|
export interface ICosmWasmSigning {
|
||||||
@@ -143,6 +150,7 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
|
|||||||
bondMixNode(
|
bondMixNode(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixNode: MixNode,
|
mixNode: MixNode,
|
||||||
|
costParams: MixNodeCostParams,
|
||||||
ownerSignature: string,
|
ownerSignature: string,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee?: StdFee | 'auto' | number,
|
fee?: StdFee | 'auto' | number,
|
||||||
@@ -164,7 +172,7 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
|
|||||||
|
|
||||||
delegateToMixNode(
|
delegateToMixNode(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixIdentity: string,
|
mixId: number,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee?: StdFee | 'auto' | number,
|
fee?: StdFee | 'auto' | number,
|
||||||
memo?: string,
|
memo?: string,
|
||||||
@@ -172,14 +180,14 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
|
|||||||
|
|
||||||
undelegateFromMixNode(
|
undelegateFromMixNode(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixIdentity: string,
|
mixId: number,
|
||||||
fee?: StdFee | 'auto' | number,
|
fee?: StdFee | 'auto' | number,
|
||||||
memo?: string,
|
memo?: string,
|
||||||
): Promise<ExecuteResult>;
|
): Promise<ExecuteResult>;
|
||||||
|
|
||||||
updateMixnodeConfig(
|
updateMixnodeConfig(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixIdentity: string,
|
mixId: number,
|
||||||
profitMarginPercent: number,
|
profitMarginPercent: number,
|
||||||
fee: StdFee | 'auto' | number,
|
fee: StdFee | 'auto' | number,
|
||||||
): Promise<ExecuteResult>;
|
): Promise<ExecuteResult>;
|
||||||
@@ -235,44 +243,76 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
// query related:
|
// query related:
|
||||||
|
|
||||||
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
|
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
|
||||||
return this.nyxdQuerier.getContractVersion(mixnetContractAddress);
|
return this.getContractVersion(mixnetContractAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
|
getMixNodeBonds(
|
||||||
return this.nyxdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedMixNodeBondResponse> {
|
||||||
|
return this.nyxdQuerier.getMixNodeBonds(mixnetContractAddress, limit, startAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMixNodesDetailed(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedMixNodeDetailsResponse> {
|
||||||
|
return this.nyxdQuerier.getMixNodesDetailed(mixnetContractAddress, limit, startAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStakeSaturation(mixnetContractAddress: string, mixId: number) {
|
||||||
|
return this.nyxdQuerier.getStakeSaturation(mixnetContractAddress, mixId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse> {
|
||||||
|
return this.nyxdQuerier.getUnbondedMixNodeInformation(mixnetContractAddress, mixId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding> {
|
||||||
|
return this.nyxdQuerier.getMixnodeRewardingDetails(mixnetContractAddress, mixId);
|
||||||
}
|
}
|
||||||
|
|
||||||
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
|
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
|
||||||
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
|
return this.nyxdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
||||||
return this.nyxdQuerier.ownsMixNode(mixnetContractAddress, address);
|
return this.nyxdQuerier.getOwnedMixnode(mixnetContractAddress, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
||||||
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
|
return this.nyxdQuerier.ownsGateway(mixnetContractAddress, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
|
getStateParams(mixnetContractAddress: string): Promise<ContractState> {
|
||||||
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
|
return this.nyxdQuerier.getStateParams(mixnetContractAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllNetworkDelegationsPaged(
|
getAllDelegationsPaged(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
limit?: number,
|
limit?: number,
|
||||||
startAfter?: [string, string],
|
startAfter?: [string, string],
|
||||||
): Promise<PagedAllDelegationsResponse> {
|
): Promise<PagedAllDelegationsResponse> {
|
||||||
return this.nyxdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
|
return this.getAllDelegationsPaged(mixnetContractAddress, limit, startAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnbondedMixNodes(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedUnbondedMixnodesResponse> {
|
||||||
|
return this.nyxdQuerier.getUnbondedMixNodes(mixnetContractAddress, limit, startAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMixNodeDelegationsPaged(
|
getMixNodeDelegationsPaged(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixIdentity: string,
|
mix_id: number,
|
||||||
limit?: number,
|
limit?: number,
|
||||||
startAfter?: string,
|
startAfter?: string,
|
||||||
): Promise<PagedMixDelegationsResponse> {
|
): Promise<PagedMixDelegationsResponse> {
|
||||||
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
|
return this.nyxdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mix_id, limit, startAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDelegatorDelegationsPaged(
|
getDelegatorDelegationsPaged(
|
||||||
@@ -284,36 +324,16 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
|
return this.nyxdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
|
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation> {
|
||||||
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
|
return this.nyxdQuerier.getDelegationDetails(mixnetContractAddress, mix_id, delegator);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
|
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
|
||||||
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
|
return this.nyxdQuerier.getLayerDistribution(mixnetContractAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRewardPool(mixnetContractAddress: string): Promise<string> {
|
getRewardParams(mixnetContractAddress: string): Promise<RewardingParams> {
|
||||||
return this.nyxdQuerier.getRewardPool(mixnetContractAddress);
|
return this.nyxdQuerier.getRewardParams(mixnetContractAddress);
|
||||||
}
|
|
||||||
|
|
||||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
|
|
||||||
return this.nyxdQuerier.getCirculatingSupply(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.nyxdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.nyxdQuerier.getSybilResistancePercent(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getRewardingStatus(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
rewardingIntervalNonce: number,
|
|
||||||
): Promise<RewardingStatus> {
|
|
||||||
return this.nyxdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getCachedGateways(): Promise<GatewayBond[]> {
|
getCachedGateways(): Promise<GatewayBond[]> {
|
||||||
@@ -324,7 +344,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
return this.nymApiQuerier.getCachedMixnodes();
|
return this.nymApiQuerier.getCachedMixnodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
getActiveMixnodes(): Promise<MixNodeBond[]> {
|
getActiveMixnodes(): Promise<MixNodeDetails[]> {
|
||||||
return this.nymApiQuerier.getActiveMixnodes();
|
return this.nymApiQuerier.getActiveMixnodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,11 +352,16 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
return this.nymApiQuerier.getRewardedMixnodes();
|
return this.nymApiQuerier.getRewardedMixnodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSpendableCoins(vestingContractAddress: string, vestingAccountAddress: string): Promise<any> {
|
||||||
|
return this.nyxdQuerier.getSpendableCoins(vestingContractAddress, vestingAccountAddress);
|
||||||
|
}
|
||||||
|
|
||||||
// signing related:
|
// signing related:
|
||||||
|
|
||||||
bondMixNode(
|
bondMixNode(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixNode: MixNode,
|
mixNode: MixNode,
|
||||||
|
costParams: MixNodeCostParams,
|
||||||
ownerSignature: string,
|
ownerSignature: string,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: StdFee | 'auto' | number = 'auto',
|
fee: StdFee | 'auto' | number = 'auto',
|
||||||
@@ -348,6 +373,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
{
|
{
|
||||||
bond_mixnode: {
|
bond_mixnode: {
|
||||||
mix_node: mixNode,
|
mix_node: mixNode,
|
||||||
|
cost_params: costParams,
|
||||||
owner_signature: ownerSignature,
|
owner_signature: ownerSignature,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -414,7 +440,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
|
|
||||||
delegateToMixNode(
|
delegateToMixNode(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixIdentity: string,
|
mixId: number,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee: StdFee | 'auto' | number = 'auto',
|
fee: StdFee | 'auto' | number = 'auto',
|
||||||
memo = 'Default MixNode Delegation from Typescript',
|
memo = 'Default MixNode Delegation from Typescript',
|
||||||
@@ -424,7 +450,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
mixnetContractAddress,
|
mixnetContractAddress,
|
||||||
{
|
{
|
||||||
delegate_to_mixnode: {
|
delegate_to_mixnode: {
|
||||||
mix_identity: mixIdentity,
|
mix_id: mixId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fee,
|
fee,
|
||||||
@@ -435,7 +461,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
|
|
||||||
undelegateFromMixNode(
|
undelegateFromMixNode(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixIdentity: string,
|
mixId: number,
|
||||||
fee: StdFee | 'auto' | number = 'auto',
|
fee: StdFee | 'auto' | number = 'auto',
|
||||||
memo = 'Default MixNode Undelegation from Typescript',
|
memo = 'Default MixNode Undelegation from Typescript',
|
||||||
): Promise<ExecuteResult> {
|
): Promise<ExecuteResult> {
|
||||||
@@ -444,7 +470,7 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
mixnetContractAddress,
|
mixnetContractAddress,
|
||||||
{
|
{
|
||||||
undelegate_from_mixnode: {
|
undelegate_from_mixnode: {
|
||||||
mix_identity: mixIdentity,
|
mix_id: mixId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fee,
|
fee,
|
||||||
@@ -454,14 +480,14 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
|||||||
|
|
||||||
updateMixnodeConfig(
|
updateMixnodeConfig(
|
||||||
mixnetContractAddress: string,
|
mixnetContractAddress: string,
|
||||||
mixIdentity: string,
|
mixId: number,
|
||||||
profitMarginPercent: number,
|
profitMarginPercent: number,
|
||||||
fee: StdFee | 'auto' | number,
|
fee: StdFee | 'auto' | number,
|
||||||
): Promise<ExecuteResult> {
|
): Promise<ExecuteResult> {
|
||||||
return this.execute(
|
return this.execute(
|
||||||
this.clientAddress,
|
this.clientAddress,
|
||||||
mixnetContractAddress,
|
mixnetContractAddress,
|
||||||
{ update_mixnode_config: { profit_margin_percent: profitMarginPercent, mix_identity: mixIdentity } },
|
{ update_mixnode_config: { profit_margin_percent: profitMarginPercent, mix_id: mixId } },
|
||||||
fee,
|
fee,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
import expect from 'expect';
|
||||||
|
|
||||||
|
export const amountDemon = {
|
||||||
|
amount: expect.any(String),
|
||||||
|
denom: expect.any(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const delegation = {
|
||||||
|
owner: expect.any(String),
|
||||||
|
mix_id: expect.any(Number),
|
||||||
|
cumulative_reward_ratio: expect.any(String),
|
||||||
|
amount: amountDemon,
|
||||||
|
height: expect.any(Number || BigInt),
|
||||||
|
proxy: expect.any(String || null)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const detailedDelegation = {
|
||||||
|
delegation: delegation,
|
||||||
|
mixnode_still_bonded: expect.any(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const gateway = {
|
||||||
|
pledge_amount: amountDemon,
|
||||||
|
owner: expect.any(String),
|
||||||
|
block_height: expect.any(Number || BigInt),
|
||||||
|
gateway: {
|
||||||
|
host: expect.any(String),
|
||||||
|
mix_port: expect.any(Number),
|
||||||
|
clients_port: expect.any(Number),
|
||||||
|
location: expect.any(String),
|
||||||
|
sphinx_key: expect.any(String),
|
||||||
|
identity_key: expect.any(String),
|
||||||
|
version: expect.any(String),
|
||||||
|
},
|
||||||
|
proxy: expect.any(String || null)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pagedGateway = {
|
||||||
|
nodes: gateway,
|
||||||
|
per_page: expect.any(Number),
|
||||||
|
start_next_after: expect.any(Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ownGateway = {
|
||||||
|
address: expect.any(String),
|
||||||
|
gateway: gateway
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rewardingdetails = {
|
||||||
|
cost_params: {
|
||||||
|
profit_margin_percent: expect.any(String),
|
||||||
|
interval_operating_cost: {
|
||||||
|
denom: expect.any(String),
|
||||||
|
amount: expect.any(String)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
operator: expect.any(String),
|
||||||
|
delegates: expect.any(String),
|
||||||
|
total_unit_reward: expect.any(String),
|
||||||
|
unit_delegation: expect.any(String),
|
||||||
|
last_rewarded_epoch: expect.any(Number),
|
||||||
|
unique_delegations: expect.any(Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mix_node = {
|
||||||
|
host: expect.any(String),
|
||||||
|
mix_port: expect.any(Number),
|
||||||
|
verloc_port: expect.any(Number),
|
||||||
|
http_api_port: expect.any(Number),
|
||||||
|
sphinx_key: expect.any(String),
|
||||||
|
identity_key: expect.any(String),
|
||||||
|
version: expect.any(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mixnodebond = {
|
||||||
|
mix_id: expect.any(Number),
|
||||||
|
owner: expect.any(String),
|
||||||
|
original_pledge: {
|
||||||
|
denom: expect.any(String),
|
||||||
|
amount: expect.any(String)
|
||||||
|
},
|
||||||
|
layer: expect.any(String),
|
||||||
|
mix_node: mix_node,
|
||||||
|
proxy: expect.any(String) || null,
|
||||||
|
bonding_height: expect.any(Number || BigInt),
|
||||||
|
is_unbonding: expect.any(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mixnode = {
|
||||||
|
bond_information: mixnodebond,
|
||||||
|
rewarding_details: rewardingdetails
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ownedNode = {
|
||||||
|
address: expect.any(String),
|
||||||
|
mixnode_details: {
|
||||||
|
bond_information: mixnodebond,
|
||||||
|
rewarding_details: rewardingdetails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saturation = {
|
||||||
|
mix_id: expect.any(Number),
|
||||||
|
current_saturation: expect.any(String),
|
||||||
|
uncapped_saturation: expect.any(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const contractVersion = {
|
||||||
|
build_timestamp: expect.any(String),
|
||||||
|
build_version: expect.any(String),
|
||||||
|
commit_sha: expect.any(String),
|
||||||
|
commit_timestamp: expect.any(String),
|
||||||
|
commit_branch: expect.any(String),
|
||||||
|
rustc_version: expect.any(String)
|
||||||
|
};
|
||||||
|
|
||||||
|
export const stateParams = {
|
||||||
|
minimum_gateway_pledge: amountDemon,
|
||||||
|
minimum_mixnode_pledge: expect.any(String),
|
||||||
|
mixnode_rewarded_set_size: expect.any(Number),
|
||||||
|
mixnode_active_set_size: expect.any(Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const contract = {
|
||||||
|
owner: expect.any(Number),
|
||||||
|
rewarding_validator_address: expect.any(Number),
|
||||||
|
vesting_contract_address: expect.any(Number),
|
||||||
|
rewarding_denom: expect.any(String),
|
||||||
|
params: stateParams
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rewardingnode = {
|
||||||
|
mix_id: expect.any(Number),
|
||||||
|
rewarding_details: rewardingdetails
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unbondednode = {
|
||||||
|
mix_id: expect.any(Number),
|
||||||
|
unbonded_info: {
|
||||||
|
identity_key: expect.any(String),
|
||||||
|
owner: expect.any(String),
|
||||||
|
proxy: expect.any(String) || null,
|
||||||
|
unbonding_height: expect.any(Number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const allunbondednodes = [
|
||||||
|
expect.any(Number), {
|
||||||
|
identity_key: expect.any(String),
|
||||||
|
owner: expect.any(String),
|
||||||
|
proxy: expect.any(String) || null,
|
||||||
|
unbonding_height: expect.any(Number)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export const layerDistribution = {
|
||||||
|
layer1: expect.any(Number),
|
||||||
|
layer2: expect.any(Number),
|
||||||
|
layer3: expect.any(Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const intervalRewardParams = {
|
||||||
|
reward_pool: expect.any(Number),
|
||||||
|
staking_supply: expect.any(Number),
|
||||||
|
staking_supply_scale_factor: expect.any(Number),
|
||||||
|
epoch_reward_budget: expect.any(Number),
|
||||||
|
stake_saturation_point: expect.any(Number),
|
||||||
|
sybil_resistance: expect.any(Number),
|
||||||
|
active_set_work_factor: expect.any(Number),
|
||||||
|
interval_pool_emission: expect.any(Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rewardingParams = {
|
||||||
|
interval: intervalRewardParams,
|
||||||
|
rewarded_set_size: expect.any(Number),
|
||||||
|
active_set_size: expect.any(Number)
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Mock, Times } from 'moq.ts';
|
||||||
|
import expect from 'expect';
|
||||||
|
import { INyxdQuery } from '../../query-client';
|
||||||
|
|
||||||
|
export class TestHelper {
|
||||||
|
buildMethod = async <T>(methodName: string, args: any[], expectedResult: any): Promise<T> => {
|
||||||
|
const client = new Mock<INyxdQuery>()
|
||||||
|
.setup((nym) => nym[methodName](...args))
|
||||||
|
.returns(Promise.resolve(expectedResult));
|
||||||
|
const obj = client.object();
|
||||||
|
const actualDetails = await obj[methodName](...args);
|
||||||
|
|
||||||
|
client.verify((nym) => nym[methodName](...args), Times.Exactly(1));
|
||||||
|
expect(Object.keys([actualDetails])).toEqual(Object.keys(expectedResult));
|
||||||
|
expect(actualDetails).toBeDefined();
|
||||||
|
|
||||||
|
return actualDetails;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import expect from 'expect';
|
||||||
|
import { Delegation } from '@nymproject/types';
|
||||||
|
import { PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse } from '../../types/shared-types';
|
||||||
|
import { TestHelper } from './client';
|
||||||
|
import { mixnet, mixnodeowneraddress, mix_id } from './testData';
|
||||||
|
|
||||||
|
describe('Delegation mock tests', () => {
|
||||||
|
const testHelper = new TestHelper();
|
||||||
|
|
||||||
|
it('get Delegation Details', () => {
|
||||||
|
const execute = testHelper.buildMethod('getDelegationDetails', [mixnet, mix_id, mixnodeowneraddress], <Delegation>{
|
||||||
|
owner: mixnodeowneraddress,
|
||||||
|
mix_id: 0,
|
||||||
|
cumulative_reward_ratio: '',
|
||||||
|
amount: {
|
||||||
|
denom: 'nym',
|
||||||
|
amount: '10',
|
||||||
|
},
|
||||||
|
height: 1314134144132n,
|
||||||
|
proxy: 'null',
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get All Delegations Paged', () => {
|
||||||
|
const execute = testHelper.buildMethod('getAllDelegationsPaged', [mixnet], <PagedAllDelegationsResponse>{
|
||||||
|
delegations: [],
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get Delegator Delegations Paged', () => {
|
||||||
|
const execute = testHelper.buildMethod('getDelegatorDelegationsPaged', [mixnet, mixnodeowneraddress], <
|
||||||
|
PagedDelegatorDelegationsResponse
|
||||||
|
>{
|
||||||
|
delegations: [],
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import expect from 'expect';
|
||||||
|
import { GatewayOwnershipResponse, PagedGatewayResponse } from '../../types/shared-types';
|
||||||
|
import { TestHelper } from './client';
|
||||||
|
import { gatewayowneraddress, mixnet } from './testData';
|
||||||
|
|
||||||
|
describe('Gateway mock tests', () => {
|
||||||
|
const testHelper = new TestHelper();
|
||||||
|
|
||||||
|
it('get Gateways Paged', () => {
|
||||||
|
const execute = testHelper.buildMethod('getGatewaysPaged', [mixnet], <PagedGatewayResponse>{
|
||||||
|
nodes: [],
|
||||||
|
per_page: 25,
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('owns Gateway', () => {
|
||||||
|
const execute = testHelper.buildMethod('ownsGateway', [mixnet, gatewayowneraddress], <GatewayOwnershipResponse>{
|
||||||
|
address: gatewayowneraddress,
|
||||||
|
gateway: {},
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import expect from 'expect';
|
||||||
|
import { LayerDistribution, MixnetContractVersion, StakeSaturationResponse } from '@nymproject/types';
|
||||||
|
import { TestHelper } from './client';
|
||||||
|
import { mixnet, mix_id } from './testData';
|
||||||
|
import { RewardingParams } from '@nymproject/types';
|
||||||
|
import { ContractState } from '../../types/shared';
|
||||||
|
|
||||||
|
describe('Mixnet mock tests', () => {
|
||||||
|
const testHelper = new TestHelper();
|
||||||
|
|
||||||
|
it('get Layer Distribution', () => {
|
||||||
|
const execute = testHelper.buildMethod('getLayerDistribution', [mixnet], <LayerDistribution>{
|
||||||
|
layer1: 2,
|
||||||
|
layer2: 2,
|
||||||
|
layer3: 5,
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get Reward Params', () => {
|
||||||
|
const execute = testHelper.buildMethod('getRewardParams', [mixnet], <RewardingParams>{
|
||||||
|
interval: {},
|
||||||
|
rewarded_set_size: 0,
|
||||||
|
active_set_size: 0,
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get Stake Saturation', () => {
|
||||||
|
const execute = testHelper.buildMethod('getStakeSaturation', [mixnet, mix_id], <StakeSaturationResponse>{
|
||||||
|
mix_id: 0,
|
||||||
|
current_saturation: '',
|
||||||
|
uncapped_saturation: '',
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get State Params', () => {
|
||||||
|
const execute = testHelper.buildMethod('getStateParams', [mixnet], <ContractState>{
|
||||||
|
owner: '',
|
||||||
|
rewarding_validator_address: '',
|
||||||
|
vesting_contract_address: '',
|
||||||
|
rewarding_denom: 'unym',
|
||||||
|
params: {
|
||||||
|
minimum_mixnode_pledge: '',
|
||||||
|
minimum_gateway_pledge: '',
|
||||||
|
mixnode_rewarded_set_size: 240,
|
||||||
|
mixnode_active_set_size: 240,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get Contract Version', () => {
|
||||||
|
const execute = testHelper.buildMethod('getContractVersion', [mixnet], <MixnetContractVersion>{
|
||||||
|
build_timestamp: 'test',
|
||||||
|
commit_branch: 'test',
|
||||||
|
build_version: 'test',
|
||||||
|
rustc_version: 'test',
|
||||||
|
commit_sha: 'test',
|
||||||
|
commit_timestamp: 'test',
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import expect from 'expect';
|
||||||
|
import {
|
||||||
|
MixNodeRewarding,
|
||||||
|
MixOwnershipResponse,
|
||||||
|
PagedMixDelegationsResponse,
|
||||||
|
PagedMixNodeBondResponse,
|
||||||
|
PagedMixNodeDetailsResponse,
|
||||||
|
UnbondedMixnodeResponse,
|
||||||
|
} from '@nymproject/types';
|
||||||
|
import { TestHelper } from './client';
|
||||||
|
import { mixnet, mixnodeowneraddress, mix_id } from './testData';
|
||||||
|
|
||||||
|
describe('Mixnode mock tests', () => {
|
||||||
|
const testHelper = new TestHelper();
|
||||||
|
|
||||||
|
it('get Mixnode Bonds', () => {
|
||||||
|
const execute = testHelper.buildMethod('getMixNodeBonds', [mixnet], <PagedMixNodeBondResponse>{
|
||||||
|
nodes: [],
|
||||||
|
per_page: 25,
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get Mixnode Delegations Paged', () => {
|
||||||
|
const execute = testHelper.buildMethod('getMixNodeDelegationsPaged', [mixnet, mix_id], <
|
||||||
|
PagedMixDelegationsResponse
|
||||||
|
>{
|
||||||
|
delegations: [],
|
||||||
|
per_page: 25,
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get Mixnodes Detailed', () => {
|
||||||
|
const execute = testHelper.buildMethod('getMixNodesDetailed', [mixnet], <PagedMixNodeDetailsResponse>{
|
||||||
|
nodes: [],
|
||||||
|
per_page: 25,
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get Mixnode Rewarding Details', () => {
|
||||||
|
const execute = testHelper.buildMethod('getMixnodeRewardingDetails', [mixnet, mix_id], <MixNodeRewarding>{
|
||||||
|
cost_params: {},
|
||||||
|
operator: '',
|
||||||
|
delegates: '',
|
||||||
|
total_unit_reward: '',
|
||||||
|
unit_delegation: '',
|
||||||
|
last_rewarded_epoch: 1,
|
||||||
|
unique_delegations: 1,
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get Owned Mixnode', () => {
|
||||||
|
const execute = testHelper.buildMethod('getOwnedMixnode', [mixnet, mixnodeowneraddress], <MixOwnershipResponse>{
|
||||||
|
address: '',
|
||||||
|
mixnode: {},
|
||||||
|
});
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('get Unbonded Mixnode Information', () => {
|
||||||
|
const execute = testHelper.buildMethod(
|
||||||
|
'getUnbondedMixNodeInformation',
|
||||||
|
[mixnet, mix_id],
|
||||||
|
<UnbondedMixnodeResponse>{},
|
||||||
|
);
|
||||||
|
expect(execute).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export const mixnet = 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g';
|
||||||
|
export const gatewayowneraddress = 'n16evnn8glr0sham3matj8rg2s24m6x56ayk87ts';
|
||||||
|
// export const mixId = 436207616;
|
||||||
|
export const mix_id = 26;
|
||||||
|
export const nodeIdentityKey = 'ATmVJknZarDF6Yj53M7h8NS9LLCUvWuToXpk3pDvYUH1';
|
||||||
|
export const mixnodeowneraddress = 'n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47';
|
||||||
|
export const delegatorAddress = 'n1lemst75va9700tsrxq58adzujrh6h9s5x60h9h';
|
||||||
|
export const rewardingIntervalNonce = 1;
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import expect from 'expect';
|
||||||
|
import ValidatorClient from '../../index';
|
||||||
|
|
||||||
|
import {
|
||||||
|
allunbondednodes,
|
||||||
|
contract,
|
||||||
|
delegation,
|
||||||
|
gateway,
|
||||||
|
layerDistribution,
|
||||||
|
mixnode,
|
||||||
|
mixnodebond,
|
||||||
|
ownedNode,
|
||||||
|
ownGateway,
|
||||||
|
rewardingnode,
|
||||||
|
saturation,
|
||||||
|
unbondednode,
|
||||||
|
} from '../expectedResponses';
|
||||||
|
import { delegatorAddress, gatewayowneraddress } from '../mock/testData';
|
||||||
|
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
describe('Mixnet queries', () => {
|
||||||
|
let client: ValidatorClient;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
client = await ValidatorClient.connectForQuery(
|
||||||
|
process.env.rpcAddress || '',
|
||||||
|
process.env.validatorAddress || '',
|
||||||
|
process.env.prefix || '',
|
||||||
|
process.env.mixnetContractAddress || '',
|
||||||
|
process.env.vestingContractAddress || '',
|
||||||
|
process.env.denom || '',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// CONTRACT
|
||||||
|
//
|
||||||
|
|
||||||
|
it('can query for an account balance', async () => {
|
||||||
|
const balance = await client.getBalance('n1ptg680vnmef2cd8l0s9uyc4f0hgf3x8sed6w77');
|
||||||
|
expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for stake saturation', async () => {
|
||||||
|
const stakeSaturation = await client.getStakeSaturation(7);
|
||||||
|
expect(Object.keys(stakeSaturation)).toEqual(Object.keys(saturation));
|
||||||
|
expect(stakeSaturation).toBeTruthy();
|
||||||
|
expect(stakeSaturation?.current_saturation).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for contract version', async () => {
|
||||||
|
const contractV = await client.getMixnetContractVersion();
|
||||||
|
expect(contractV).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for mixnet contract settings', async () => {
|
||||||
|
const settings = await client.getMixnetContractSettings();
|
||||||
|
expect(Object.keys(settings)).toEqual(Object.keys(contract));
|
||||||
|
expect(settings).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for reward pool', async () => {
|
||||||
|
const rewardPool = await client.getRewardParams();
|
||||||
|
// TODO add velidation here
|
||||||
|
expect(rewardPool).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for layer distribution', async () => {
|
||||||
|
const layer = await client.getLayerDistribution();
|
||||||
|
expect(Object.keys(layer)).toEqual(Object.keys(layerDistribution));
|
||||||
|
expect(layer).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// MIXNODES
|
||||||
|
//
|
||||||
|
|
||||||
|
it('can query for unbonded mixnodes', async () => {
|
||||||
|
const unbondedNodes = await client.getUnbondedMixNodes();
|
||||||
|
for (let i = 0; i < unbondedNodes.length; i++) {
|
||||||
|
expect(Object.keys(unbondedNodes[0])).toEqual(Object.keys(allunbondednodes));
|
||||||
|
expect(unbondedNodes).toBeTruthy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for unbonded mixnode information', async () => {
|
||||||
|
const unbondedMixnodeInfo = await client.getUnbondedMixNodeInformation(1);
|
||||||
|
expect(Object.keys(unbondedMixnodeInfo)).toEqual(Object.keys(unbondednode));
|
||||||
|
expect(unbondedMixnodeInfo).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for mixnode rewarding details', async () => {
|
||||||
|
const rewardingDetails = await client.getMixnodeRewardingDetails(1);
|
||||||
|
expect(Object.keys(rewardingDetails)).toEqual(Object.keys(rewardingnode));
|
||||||
|
expect(rewardingDetails).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for owned mixnode', async () => {
|
||||||
|
const ownedMixnode = await client.getOwnedMixnode('n1ptg680vnmef2cd8l0s9uyc4f0hgf3x8sed6w77');
|
||||||
|
expect(Object.keys(ownedMixnode)).toEqual(Object.keys(ownedNode));
|
||||||
|
expect(ownedMixnode).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for all mixnode bonds', async () => {
|
||||||
|
const mixnodeBonds = await client.getMixNodeBonds();
|
||||||
|
expect(Object.keys(mixnodeBonds[0])).toEqual(Object.keys(mixnodebond));
|
||||||
|
expect(mixnodeBonds).toBeTruthy();
|
||||||
|
expect(Array.isArray(mixnodeBonds)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for all mixnode details', async () => {
|
||||||
|
const mixnodeDetails = await client.getMixNodesDetailed();
|
||||||
|
expect(Object.keys(mixnodeDetails[0])).toEqual(Object.keys(mixnode));
|
||||||
|
expect(mixnodeDetails).toBeTruthy();
|
||||||
|
expect(Array.isArray(mixnodeDetails)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for all active mixnodes', async () => {
|
||||||
|
const activeNodes = await client.getActiveMixnodes();
|
||||||
|
expect(Object.keys(activeNodes[0])).toEqual(Object.keys(mixnode));
|
||||||
|
expect(activeNodes).toBeTruthy();
|
||||||
|
expect(Array.isArray(activeNodes)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for rewarded mixnodes', async () => {
|
||||||
|
const rewardNodes = await client.getRewardedMixnodes();
|
||||||
|
expect(Object.keys(rewardNodes[0])).toEqual(Object.keys(mixnode));
|
||||||
|
expect(rewardNodes).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// DELEGATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
it('can query for account delegations', async () => {
|
||||||
|
const delegations = await client.getAllNyxdDelegatorDelegations('n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47');
|
||||||
|
expect(Object.keys(delegations[0])).toEqual(Object.keys(delegation));
|
||||||
|
expect(delegations).toBeTruthy();
|
||||||
|
expect(Array.isArray(delegations)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for all delegations', async () => {
|
||||||
|
const allDelegations = await client.getAllNyxdDelegations();
|
||||||
|
expect(Object.keys(allDelegations[0])).toEqual(Object.keys(delegation));
|
||||||
|
expect(allDelegations).toBeTruthy();
|
||||||
|
expect(Array.isArray(allDelegations)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for all delegations on a node', async () => {
|
||||||
|
const mixnodeDelegations = await client.getAllNyxdSingleMixnodeDelegations(1);
|
||||||
|
expect(Object.keys(mixnodeDelegations[0])).toEqual(Object.keys(delegation));
|
||||||
|
expect(mixnodeDelegations).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for detailed delegations', async () => {
|
||||||
|
const detailedDelegation = await client.getDelegationDetails(7, delegatorAddress);
|
||||||
|
expect(Object.keys(detailedDelegation)).toEqual(Object.keys(detailedDelegation));
|
||||||
|
expect(detailedDelegation).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// GATEWAYS
|
||||||
|
//
|
||||||
|
|
||||||
|
it('can query for all gateways', async () => {
|
||||||
|
const gateways = await client.getAllNyxdGateways();
|
||||||
|
expect(Object.keys(gateways[0])).toEqual(Object.keys(gateway));
|
||||||
|
expect(gateways).toBeTruthy();
|
||||||
|
expect(Array.isArray(gateways)).toBeTruthy();
|
||||||
|
}).timeout(10000);
|
||||||
|
|
||||||
|
it('can query for owned gateway', async () => {
|
||||||
|
const gateway = await client.ownsGateway(gatewayowneraddress);
|
||||||
|
expect(Object.keys(gateway)).toEqual(Object.keys(ownGateway));
|
||||||
|
expect(gateway).toBeTruthy();
|
||||||
|
}).timeout(10000);
|
||||||
|
});
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import expect from 'expect';
|
||||||
|
import ValidatorClient from '../../index';
|
||||||
|
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
describe('Vesting queries', () => {
|
||||||
|
let client: ValidatorClient;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
client = await ValidatorClient.connectForQuery(
|
||||||
|
process.env.rpcAddress || '',
|
||||||
|
process.env.validatorAddress || '',
|
||||||
|
process.env.prefix || '',
|
||||||
|
process.env.mixnetContractAddress || '',
|
||||||
|
process.env.vestingContractAddress || '',
|
||||||
|
process.env.denom || '',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can query for contract version', async () => {
|
||||||
|
const contract = await client.getVestingContractVersion();
|
||||||
|
expect(contract).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can get the balance on the account', () => {});
|
||||||
|
});
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import expect from 'expect';
|
||||||
|
import ValidatorClient from '../../';
|
||||||
|
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
// TODO: implement for QA with .env for mnemonics
|
||||||
|
describe('Mixnet actions', () => {
|
||||||
|
let client: ValidatorClient;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
client = await ValidatorClient.connect(
|
||||||
|
process.env.mnemonic || '',
|
||||||
|
process.env.rpcAddress || '',
|
||||||
|
process.env.validatorAddress || '',
|
||||||
|
process.env.prefix || '',
|
||||||
|
process.env.mixnetContractAddress || '',
|
||||||
|
process.env.vestingContractAddress || '',
|
||||||
|
process.env.denom || '',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can send tokens', async () => {
|
||||||
|
const res = await client.send(client.address, [{ amount: '10000000', denom: 'unym' }]);
|
||||||
|
expect(res.transactionHash).toBeDefined();
|
||||||
|
}).timeout(10000);
|
||||||
|
|
||||||
|
it.skip('can delegate tokens', async () => {
|
||||||
|
const [_, secondMixnode] = await client.getActiveMixnodes();
|
||||||
|
|
||||||
|
if (secondMixnode) {
|
||||||
|
const res = await client.delegateToMixNode(
|
||||||
|
secondMixnode.bond_information.mix_id,
|
||||||
|
{
|
||||||
|
amount: '15000000',
|
||||||
|
denom: 'unym',
|
||||||
|
},
|
||||||
|
{ gas: '1000000', amount: [{ amount: '100000', denom: 'unym' }] },
|
||||||
|
);
|
||||||
|
expect(res.transactionHash).toBeDefined();
|
||||||
|
}
|
||||||
|
}).timeout(10000);
|
||||||
|
|
||||||
|
// Need to provide a mix id that can be undelegated from
|
||||||
|
it.skip('can undelegate from a mixnode', async () => {
|
||||||
|
const mixId = 8;
|
||||||
|
const res = await client.undelegateFromMixNode(mixId);
|
||||||
|
expect(res.transactionHash).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('Can unbond a mixnode', async () => {
|
||||||
|
const res = await client.unbondMixNode();
|
||||||
|
expect(res.transactionHash).toBeDefined();
|
||||||
|
}).timeout(10000);
|
||||||
|
|
||||||
|
it.skip('Can bond a mixnode', async () => {
|
||||||
|
const res = await client.bondMixNode(
|
||||||
|
{
|
||||||
|
identity_key: '3P6pAcF2p3pYMqWdpHqhbavu3ifyaBs5Qw5UmmCGwimx',
|
||||||
|
sphinx_key: 'GQMQKwUThaggatA6oZteSWTsCQoUfmLtamJ7o9YkP9aE',
|
||||||
|
host: '109.74.195.67',
|
||||||
|
mix_port: 1789,
|
||||||
|
verloc_port: 1790,
|
||||||
|
http_api_port: 8000,
|
||||||
|
version: '1.1.4',
|
||||||
|
},
|
||||||
|
'3rXWCQBUj5JQB3UBUkZcXhCk9Zh3cjduMF8aFHPTG7KTkkhZzDJTNmE2p2Ph1g6iQW5vRGTpQzjXF33WDwvhzHk6',
|
||||||
|
{ profit_margin_percent: '0.1', interval_operating_cost: { amount: '40', denom: 'nym' } },
|
||||||
|
{ amount: '100_000_000', denom: 'unym' },
|
||||||
|
{ gas: '1000000', amount: [{ amount: '100000', denom: 'unym' }] },
|
||||||
|
);
|
||||||
|
expect(res.transactionHash).toBeDefined();
|
||||||
|
}).timeout(10000);
|
||||||
|
|
||||||
|
it.skip('can unbond a gateway', async () => {
|
||||||
|
const res = await client.unbondGateway();
|
||||||
|
expect(res.transactionHash).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('can bond a gateway', async () => {
|
||||||
|
const res = await client.bondGateway(
|
||||||
|
{
|
||||||
|
identity_key: '36vfvEyBzo5cWEFbnP7fqgY39kFw9PQhvwzbispeNaxL',
|
||||||
|
sphinx_key: 'G65Fwc2JNAotuHQFqmDKhQNQL5rn3r9pupUdmxMygNUZ',
|
||||||
|
host: '151.236.220.82',
|
||||||
|
version: '1.1.4',
|
||||||
|
mix_port: 1789,
|
||||||
|
clients_port: 9000,
|
||||||
|
location: 'Cuba',
|
||||||
|
},
|
||||||
|
'3ipSJksWHehZm1YfuH5Ahtg7b22NnrP9hEs6iEDXfUS5uiUhpWmCjGR3b3NDHuxeGjpZYJNYJ52D8WCPK5ZR7Szj',
|
||||||
|
{ amount: '100_000_000', denom: 'unym' },
|
||||||
|
);
|
||||||
|
expect(res.transactionHash).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('can update contract state params', async () => {
|
||||||
|
const res = await client.updateContractStateParams({
|
||||||
|
minimum_mixnode_pledge: '',
|
||||||
|
minimum_gateway_pledge: '',
|
||||||
|
mixnode_rewarded_set_size: 2,
|
||||||
|
mixnode_active_set_size: 2,
|
||||||
|
});
|
||||||
|
expect(res.transactionHash).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es6",
|
|
||||||
"module": "commonjs",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"strict": true,
|
|
||||||
"declaration": true,
|
|
||||||
"outDir": "./dist"
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"tests",
|
|
||||||
"dist",
|
|
||||||
"node_modules"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
import { Coin } from '@cosmjs/stargate';
|
|
||||||
|
|
||||||
// TODO: ideally we'd have re-exported those using that fancy crate that builds ts types from rust
|
|
||||||
|
|
||||||
export type MixnetContractVersion = {
|
|
||||||
build_timestamp: string;
|
|
||||||
build_version: string;
|
|
||||||
commit_sha: string;
|
|
||||||
commit_timestamp: string;
|
|
||||||
commit_branch: string;
|
|
||||||
rustc_version: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PagedMixnodeResponse = {
|
|
||||||
nodes: MixNodeBond[];
|
|
||||||
per_page: number;
|
|
||||||
start_next_after?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PagedGatewayResponse = {
|
|
||||||
nodes: GatewayBond[];
|
|
||||||
per_page: number;
|
|
||||||
start_next_after?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type MixOwnershipResponse = {
|
|
||||||
address: string;
|
|
||||||
mixnode?: MixNodeBond;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GatewayOwnershipResponse = {
|
|
||||||
address: string;
|
|
||||||
gateway?: GatewayBond;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ContractStateParams = {
|
|
||||||
// ideally I'd want to define those as `number` rather than `string`, but
|
|
||||||
// rust-side they are defined as Uint128 and that don't have
|
|
||||||
// native javascript representations and therefore are interpreted as strings after deserialization
|
|
||||||
minimum_mixnode_pledge: string;
|
|
||||||
minimum_gateway_pledge: string;
|
|
||||||
mixnode_rewarded_set_size: number;
|
|
||||||
mixnode_active_set_size: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type LayerDistribution = {
|
|
||||||
gateways: number;
|
|
||||||
layer1: number;
|
|
||||||
layer2: number;
|
|
||||||
layer3: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Delegation = {
|
|
||||||
owner: string;
|
|
||||||
node_identity: string;
|
|
||||||
amount: Coin;
|
|
||||||
block_height: number;
|
|
||||||
proxy?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PagedMixDelegationsResponse = {
|
|
||||||
delegations: Delegation[];
|
|
||||||
start_next_after?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PagedDelegatorDelegationsResponse = {
|
|
||||||
delegations: Delegation[];
|
|
||||||
start_next_after?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PagedAllDelegationsResponse = {
|
|
||||||
delegations: Delegation[];
|
|
||||||
start_next_after?: [string, string];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RewardingResult = {
|
|
||||||
operator_reward: string;
|
|
||||||
total_delegator_reward: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type NodeRewardParams = {
|
|
||||||
period_reward_pool: string;
|
|
||||||
k: string;
|
|
||||||
reward_blockstamp: number;
|
|
||||||
circulating_supply: string;
|
|
||||||
uptime: string;
|
|
||||||
sybil_resistance_percent: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type DelegatorRewardParams = {
|
|
||||||
node_reward_params: NodeRewardParams;
|
|
||||||
sigma: number;
|
|
||||||
profit_margin: number;
|
|
||||||
node_profit: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PendingDelegatorRewarding = {
|
|
||||||
running_results: RewardingResult;
|
|
||||||
next_start: string;
|
|
||||||
rewarding_params: DelegatorRewardParams;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RewardingStatus = { Complete: RewardingResult } | { PendingNextDelegatorPage: PendingDelegatorRewarding };
|
|
||||||
|
|
||||||
export type MixnodeRewardingStatusResponse = {
|
|
||||||
status?: RewardingStatus;
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum Layer {
|
|
||||||
Gateway,
|
|
||||||
One,
|
|
||||||
Two,
|
|
||||||
Three,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MixNodeBond = {
|
|
||||||
owner: string;
|
|
||||||
mix_node: MixNode;
|
|
||||||
layer: Layer;
|
|
||||||
bond_amount: Coin;
|
|
||||||
total_delegation: Coin;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type MixNode = {
|
|
||||||
host: string;
|
|
||||||
mix_port: number;
|
|
||||||
verloc_port: number;
|
|
||||||
http_api_port: number;
|
|
||||||
sphinx_key: string;
|
|
||||||
identity_key: string;
|
|
||||||
version: string;
|
|
||||||
profit_margin_percent: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GatewayBond = {
|
|
||||||
owner: string;
|
|
||||||
gateway: Gateway;
|
|
||||||
|
|
||||||
bond_amount: Coin;
|
|
||||||
total_delegation: Coin;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Gateway = {
|
|
||||||
host: string;
|
|
||||||
mix_port: number;
|
|
||||||
clients_port: number;
|
|
||||||
location: string;
|
|
||||||
sphinx_key: string;
|
|
||||||
identity_key: string;
|
|
||||||
version: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SendRequest = {
|
|
||||||
senderAddress: string;
|
|
||||||
recipientAddress: string;
|
|
||||||
transferAmount: readonly Coin[];
|
|
||||||
};
|
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
declare namespace NodeJS {
|
||||||
|
interface ProcessEnv {
|
||||||
|
rpcAddress: string;
|
||||||
|
validatorAddress: string;
|
||||||
|
prefix: string;
|
||||||
|
mixnetContractAddress: string;
|
||||||
|
vestingContractAddress: string;
|
||||||
|
denom: string;
|
||||||
|
mnemonic: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
import { JsonObject } from '@cosmjs/cosmwasm-stargate';
|
||||||
|
import { Code, CodeDetails, Contract, ContractCodeHistoryEntry } from '@cosmjs/cosmwasm-stargate/build/cosmwasmclient';
|
||||||
|
import {
|
||||||
|
Account,
|
||||||
|
Block,
|
||||||
|
Coin,
|
||||||
|
DeliverTxResponse,
|
||||||
|
IndexedTx,
|
||||||
|
SearchTxFilter,
|
||||||
|
SearchTxQuery,
|
||||||
|
SequenceResponse,
|
||||||
|
} from '@cosmjs/stargate';
|
||||||
|
import {
|
||||||
|
MixNodeRewarding,
|
||||||
|
PagedMixNodeBondResponse,
|
||||||
|
PagedMixNodeDetailsResponse,
|
||||||
|
StakeSaturationResponse,
|
||||||
|
UnbondedMixnodeResponse,
|
||||||
|
} from '@nymproject/types';
|
||||||
|
|
||||||
|
export type MixnetContractVersion = {
|
||||||
|
build_timestamp: string;
|
||||||
|
build_version: string;
|
||||||
|
commit_sha: string;
|
||||||
|
commit_timestamp: string;
|
||||||
|
commit_branch: string;
|
||||||
|
rustc_version: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PagedMixnodeResponse = {
|
||||||
|
nodes: MixNodeBond[];
|
||||||
|
per_page: number;
|
||||||
|
start_next_after?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PagedGatewayResponse = {
|
||||||
|
nodes: GatewayBond[];
|
||||||
|
per_page: number;
|
||||||
|
start_next_after?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MixOwnershipResponse = {
|
||||||
|
address: string;
|
||||||
|
mixnode?: MixNodeBond;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GatewayOwnershipResponse = {
|
||||||
|
address: string;
|
||||||
|
gateway?: GatewayBond;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ContractStateParams = {
|
||||||
|
// ideally I'd want to define those as `number` rather than `string`, but
|
||||||
|
// rust-side they are defined as Uint128 and that don't have
|
||||||
|
// native javascript representations and therefore are interpreted as strings after deserialization
|
||||||
|
minimum_mixnode_pledge: string;
|
||||||
|
minimum_gateway_pledge: string;
|
||||||
|
mixnode_rewarded_set_size: number;
|
||||||
|
mixnode_active_set_size: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LayerDistribution = {
|
||||||
|
gateways: number;
|
||||||
|
layer1: number;
|
||||||
|
layer2: number;
|
||||||
|
layer3: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Delegation = {
|
||||||
|
owner: string;
|
||||||
|
node_identity: string;
|
||||||
|
amount: Coin;
|
||||||
|
block_height: number;
|
||||||
|
proxy?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PagedMixDelegationsResponse = {
|
||||||
|
delegations: Delegation[];
|
||||||
|
start_next_after?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PagedDelegatorDelegationsResponse = {
|
||||||
|
delegations: Delegation[];
|
||||||
|
start_next_after?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PagedAllDelegationsResponse = {
|
||||||
|
delegations: Delegation[];
|
||||||
|
start_next_after?: [string, string];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RewardingResult = {
|
||||||
|
operator_reward: string;
|
||||||
|
total_delegator_reward: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type NodeRewardParams = {
|
||||||
|
period_reward_pool: string;
|
||||||
|
k: string;
|
||||||
|
reward_blockstamp: number;
|
||||||
|
circulating_supply: string;
|
||||||
|
uptime: string;
|
||||||
|
sybil_resistance_percent: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DelegatorRewardParams = {
|
||||||
|
node_reward_params: NodeRewardParams;
|
||||||
|
sigma: number;
|
||||||
|
profit_margin: number;
|
||||||
|
node_profit: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PendingDelegatorRewarding = {
|
||||||
|
running_results: RewardingResult;
|
||||||
|
next_start: string;
|
||||||
|
rewarding_params: DelegatorRewardParams;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RewardingStatus = { Complete: RewardingResult } | { PendingNextDelegatorPage: PendingDelegatorRewarding };
|
||||||
|
|
||||||
|
export type MixnodeRewardingStatusResponse = {
|
||||||
|
status?: RewardingStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum Layer {
|
||||||
|
Gateway,
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
Three,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MixNodeBond = {
|
||||||
|
owner: string;
|
||||||
|
mix_node: MixNode;
|
||||||
|
layer: Layer;
|
||||||
|
bond_amount: Coin;
|
||||||
|
total_delegation: Coin;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MixNode = {
|
||||||
|
host: string;
|
||||||
|
mix_port: number;
|
||||||
|
verloc_port: number;
|
||||||
|
http_api_port: number;
|
||||||
|
sphinx_key: string;
|
||||||
|
identity_key: string;
|
||||||
|
version: string;
|
||||||
|
profit_margin_percent: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GatewayBond = {
|
||||||
|
owner: string;
|
||||||
|
gateway: Gateway;
|
||||||
|
|
||||||
|
bond_amount: Coin;
|
||||||
|
total_delegation: Coin;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Gateway = {
|
||||||
|
host: string;
|
||||||
|
mix_port: number;
|
||||||
|
clients_port: number;
|
||||||
|
location: string;
|
||||||
|
sphinx_key: string;
|
||||||
|
identity_key: string;
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SendRequest = {
|
||||||
|
senderAddress: string;
|
||||||
|
recipientAddress: string;
|
||||||
|
transferAmount: readonly Coin[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface SmartContractQuery {
|
||||||
|
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
|
||||||
|
}
|
||||||
|
export interface ICosmWasmQuery {
|
||||||
|
// methods exposed by `CosmWasmClient`
|
||||||
|
getChainId(): Promise<string>;
|
||||||
|
getHeight(): Promise<number>;
|
||||||
|
getAccount(searchAddress: string): Promise<Account | null>;
|
||||||
|
getSequence(address: string): Promise<SequenceResponse>;
|
||||||
|
getBlock(height?: number): Promise<Block>;
|
||||||
|
getBalance(address: string, searchDenom: string): Promise<Coin>;
|
||||||
|
getTx(id: string): Promise<IndexedTx | null>;
|
||||||
|
searchTx(query: SearchTxQuery, filter?: SearchTxFilter): Promise<readonly IndexedTx[]>;
|
||||||
|
disconnect(): void;
|
||||||
|
broadcastTx(tx: Uint8Array, timeoutMs?: number, pollIntervalMs?: number): Promise<DeliverTxResponse>;
|
||||||
|
getCodes(): Promise<readonly Code[]>;
|
||||||
|
getCodeDetails(codeId: number): Promise<CodeDetails>;
|
||||||
|
getContracts(codeId: number): Promise<readonly string[]>;
|
||||||
|
getContract(address: string): Promise<Contract>;
|
||||||
|
getContractCodeHistory(address: string): Promise<readonly ContractCodeHistoryEntry[]>;
|
||||||
|
queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null>;
|
||||||
|
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface INymdQuery {
|
||||||
|
// nym-specific implemented inside NymQuerier
|
||||||
|
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
|
||||||
|
getMixNodeBonds(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedMixNodeBondResponse>;
|
||||||
|
getMixNodesDetailed(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedMixNodeDetailsResponse>;
|
||||||
|
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse>;
|
||||||
|
getOwnedMixnode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
|
||||||
|
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
|
||||||
|
getStateParams(mixnetContractAddress: string): Promise<ContractState>;
|
||||||
|
getAllNetworkDelegationsPaged(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: [string, string],
|
||||||
|
): Promise<PagedAllDelegationsResponse>;
|
||||||
|
getMixNodeDelegationsPaged(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
mix_id: number,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedMixDelegationsResponse>;
|
||||||
|
getDelegatorDelegationsPaged(
|
||||||
|
mixnetContractAddress: string,
|
||||||
|
delegator: string,
|
||||||
|
limit?: number,
|
||||||
|
startAfter?: string,
|
||||||
|
): Promise<PagedDelegatorDelegationsResponse>;
|
||||||
|
getDelegationDetails(mixnetContractAddress: string, mix_id: number, delegator: string): Promise<Delegation>;
|
||||||
|
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution>;
|
||||||
|
getStakeSaturation(mixnetContractAddress: string, mixId: number): Promise<StakeSaturationResponse>;
|
||||||
|
getUnbondedMixNodeInformation(mixnetContractAddress: string, mixId: number): Promise<UnbondedMixnodeResponse>;
|
||||||
|
getMixnodeRewardingDetails(mixnetContractAddress: string, mixId: number): Promise<MixNodeRewarding>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVestingQuerier {
|
||||||
|
getVestingContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MappedCoin {
|
||||||
|
readonly denom: string;
|
||||||
|
readonly fractionalDigits: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CoinMap {
|
||||||
|
readonly [key: string]: MappedCoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContractState {
|
||||||
|
owner: string;
|
||||||
|
rewarding_validator_address: string;
|
||||||
|
vesting_contract_address: string;
|
||||||
|
rewarding_denom: string;
|
||||||
|
params: ContractStateParams;
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import ValidatorClient from '../../validator/index';
|
|
||||||
import expect from 'expect';
|
|
||||||
|
|
||||||
describe('Query: balances', () => {
|
|
||||||
it('can query for an account balance', async () => {
|
|
||||||
const client = await ValidatorClient.connectForQuery(
|
|
||||||
'https://rpc.nymtech.net/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
|
|
||||||
const balance = await client.getBalance('n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy');
|
|
||||||
expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
|
|
||||||
}).timeout(5000);
|
|
||||||
})
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import ValidatorClient from '../../dist';
|
|
||||||
import expect from 'expect';
|
|
||||||
|
|
||||||
// TODO: implement for QA with .env for mnemonics
|
|
||||||
// describe('Sign: send', () => {
|
|
||||||
// it('can send tokens', async () => {
|
|
||||||
// const client = await ValidatorClient.connect(
|
|
||||||
// '<ADD MNEMONIC HERE>',
|
|
||||||
// 'https://rpc.nyx.nodes.guru/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
|
|
||||||
// await client.send('<ADD ADDRESS HERE>')
|
|
||||||
// const balance = await client.getBalance('n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy');
|
|
||||||
// expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
|
|
||||||
// }).timeout(5000);
|
|
||||||
// })
|
|
||||||
@@ -1,23 +1,22 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es6",
|
"outDir": "./dist/nym-validator-client",
|
||||||
"module": "commonjs",
|
"module": "ES2020",
|
||||||
"esModuleInterop": true,
|
"target": "es2021",
|
||||||
"strict": true,
|
"allowJs": false,
|
||||||
"declaration": true,
|
"strict": true,
|
||||||
"outDir": "./dist",
|
"lib": ["es2021", "dom", "dom.iterable", "esnext"],
|
||||||
"skipLibCheck": true
|
"moduleResolution": "node",
|
||||||
},
|
"skipDefaultLibCheck": true,
|
||||||
"typedocOptions": {
|
"esModuleInterop": true,
|
||||||
"entryPoints": [
|
"declaration": true,
|
||||||
"src/index.ts"
|
"skipLibCheck": true,
|
||||||
],
|
"noImplicitAny": true,
|
||||||
"out": "docs"
|
"typeRoots": ["./src/types/global.d.ts"]
|
||||||
},
|
},
|
||||||
"exclude": [
|
"typedocOptions": {
|
||||||
"dist",
|
"entryPoints": ["./src/index.ts"],
|
||||||
"examples",
|
"out": "docs"
|
||||||
"tests",
|
},
|
||||||
"node_modules"
|
"exclude": ["dist", "./src/tests/**/*", "node_module"]
|
||||||
]
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "CommonJS"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,8 +31,8 @@ wasm-bindgen-futures = "0.4"
|
|||||||
|
|
||||||
# internal
|
# internal
|
||||||
client-core = { path = "../client-core", default-features = false, features = ["wasm"] }
|
client-core = { path = "../client-core", default-features = false, features = ["wasm"] }
|
||||||
coconut-interface = { path = "../../common/coconut-interface" }
|
nym-coconut-interface = { path = "../../common/coconut-interface" }
|
||||||
credentials = { path = "../../common/credentials" }
|
nym-credentials = { path = "../../common/credentials" }
|
||||||
nym-crypto = { path = "../../common/crypto" }
|
nym-crypto = { path = "../../common/crypto" }
|
||||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||||
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
|
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
|
||||||
|
|||||||
@@ -3,8 +3,15 @@
|
|||||||
|
|
||||||
// due to expansion of #[wasm_bindgen] macro on `Debug` Config struct
|
// due to expansion of #[wasm_bindgen] macro on `Debug` Config struct
|
||||||
#![allow(clippy::drop_non_drop)]
|
#![allow(clippy::drop_non_drop)]
|
||||||
|
// another issue due to #[wasm_bindgen] and `Copy` trait
|
||||||
|
#![allow(clippy::drop_copy)]
|
||||||
|
|
||||||
use client_core::config::{DebugConfig as ConfigDebug, ExtendedPacketSize, GatewayEndpointConfig};
|
use client_core::config::{
|
||||||
|
Acknowledgements as ConfigAcknowledgements, CoverTraffic as ConfigCoverTraffic,
|
||||||
|
DebugConfig as ConfigDebug, ExtendedPacketSize, GatewayConnection as ConfigGatewayConnection,
|
||||||
|
GatewayEndpointConfig, ReplySurbs as ConfigReplySurbs, Topology as ConfigTopology,
|
||||||
|
Traffic as ConfigTraffic,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@@ -48,15 +55,124 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// just a helper structure to more easily pass through the JS boundary
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub struct Debug {
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Traffic {
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
/// sent packet is going to be delayed at any given mix node.
|
/// sent packet is going to be delayed at any given mix node.
|
||||||
/// So for a packet going through three mix nodes, on average, it will take three times this value
|
/// So for a packet going through three mix nodes, on average, it will take three times this value
|
||||||
/// until the packet reaches its destination.
|
/// until the packet reaches its destination.
|
||||||
pub average_packet_delay_ms: u64,
|
pub average_packet_delay_ms: u64,
|
||||||
|
|
||||||
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
|
/// it is going to take another 'real traffic stream' message to be sent.
|
||||||
|
/// If no real packets are available and cover traffic is enabled,
|
||||||
|
/// a loop cover message is sent instead in order to preserve the rate.
|
||||||
|
pub message_sending_average_delay_ms: u64,
|
||||||
|
|
||||||
|
/// Controls whether the main packet stream constantly produces packets according to the predefined
|
||||||
|
/// poisson distribution.
|
||||||
|
pub disable_main_poisson_packet_distribution: bool,
|
||||||
|
|
||||||
|
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
|
||||||
|
pub use_extended_packet_size: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Traffic> for ConfigTraffic {
|
||||||
|
fn from(traffic: Traffic) -> Self {
|
||||||
|
let use_extended_packet_size = traffic
|
||||||
|
.use_extended_packet_size
|
||||||
|
.then(|| ExtendedPacketSize::Extended32);
|
||||||
|
|
||||||
|
ConfigTraffic {
|
||||||
|
average_packet_delay: Duration::from_millis(traffic.average_packet_delay_ms),
|
||||||
|
message_sending_average_delay: Duration::from_millis(
|
||||||
|
traffic.message_sending_average_delay_ms,
|
||||||
|
),
|
||||||
|
disable_main_poisson_packet_distribution: traffic
|
||||||
|
.disable_main_poisson_packet_distribution,
|
||||||
|
use_extended_packet_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConfigTraffic> for Traffic {
|
||||||
|
fn from(traffic: ConfigTraffic) -> Self {
|
||||||
|
Traffic {
|
||||||
|
average_packet_delay_ms: traffic.average_packet_delay.as_millis() as u64,
|
||||||
|
message_sending_average_delay_ms: traffic.message_sending_average_delay.as_millis()
|
||||||
|
as u64,
|
||||||
|
disable_main_poisson_packet_distribution: traffic
|
||||||
|
.disable_main_poisson_packet_distribution,
|
||||||
|
use_extended_packet_size: traffic.use_extended_packet_size.is_some(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct CoverTraffic {
|
||||||
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
|
/// it is going to take for another loop cover traffic message to be sent.
|
||||||
|
pub loop_cover_traffic_average_delay_ms: u64,
|
||||||
|
|
||||||
|
/// Controls whether the dedicated loop cover traffic stream should be enabled.
|
||||||
|
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
|
||||||
|
pub disable_loop_cover_traffic_stream: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CoverTraffic> for ConfigCoverTraffic {
|
||||||
|
fn from(cover_traffic: CoverTraffic) -> Self {
|
||||||
|
ConfigCoverTraffic {
|
||||||
|
loop_cover_traffic_average_delay: Duration::from_millis(
|
||||||
|
cover_traffic.loop_cover_traffic_average_delay_ms,
|
||||||
|
),
|
||||||
|
disable_loop_cover_traffic_stream: cover_traffic.disable_loop_cover_traffic_stream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConfigCoverTraffic> for CoverTraffic {
|
||||||
|
fn from(cover_traffic: ConfigCoverTraffic) -> Self {
|
||||||
|
CoverTraffic {
|
||||||
|
loop_cover_traffic_average_delay_ms: cover_traffic
|
||||||
|
.loop_cover_traffic_average_delay
|
||||||
|
.as_millis() as u64,
|
||||||
|
disable_loop_cover_traffic_stream: cover_traffic.disable_loop_cover_traffic_stream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct GatewayConnection {
|
||||||
|
/// How long we're willing to wait for a response to a message sent to the gateway,
|
||||||
|
/// before giving up on it.
|
||||||
|
pub gateway_response_timeout_ms: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<GatewayConnection> for ConfigGatewayConnection {
|
||||||
|
fn from(gateway_connection: GatewayConnection) -> Self {
|
||||||
|
ConfigGatewayConnection {
|
||||||
|
gateway_response_timeout: Duration::from_millis(
|
||||||
|
gateway_connection.gateway_response_timeout_ms,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConfigGatewayConnection> for GatewayConnection {
|
||||||
|
fn from(gateway_connection: ConfigGatewayConnection) -> Self {
|
||||||
|
GatewayConnection {
|
||||||
|
gateway_response_timeout_ms: gateway_connection.gateway_response_timeout.as_millis()
|
||||||
|
as u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Acknowledgements {
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
/// sent acknowledgement is going to be delayed at any given mix node.
|
/// sent acknowledgement is going to be delayed at any given mix node.
|
||||||
/// So for an ack going through three mix nodes, on average, it will take three times this value
|
/// So for an ack going through three mix nodes, on average, it will take three times this value
|
||||||
@@ -72,21 +188,31 @@ pub struct Debug {
|
|||||||
/// it is assumed it was lost and retransmission of the data packet happens.
|
/// it is assumed it was lost and retransmission of the data packet happens.
|
||||||
/// In an ideal network with 0 latency, this value would have been 0.
|
/// In an ideal network with 0 latency, this value would have been 0.
|
||||||
pub ack_wait_addition_ms: u64,
|
pub ack_wait_addition_ms: u64,
|
||||||
|
}
|
||||||
|
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
impl From<Acknowledgements> for ConfigAcknowledgements {
|
||||||
/// it is going to take for another loop cover traffic message to be sent.
|
fn from(acknowledgements: Acknowledgements) -> Self {
|
||||||
pub loop_cover_traffic_average_delay_ms: u64,
|
ConfigAcknowledgements {
|
||||||
|
average_ack_delay: Duration::from_millis(acknowledgements.average_ack_delay_ms),
|
||||||
|
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
|
||||||
|
ack_wait_addition: Duration::from_millis(acknowledgements.ack_wait_addition_ms),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
impl From<ConfigAcknowledgements> for Acknowledgements {
|
||||||
/// it is going to take another 'real traffic stream' message to be sent.
|
fn from(acknowledgements: ConfigAcknowledgements) -> Self {
|
||||||
/// If no real packets are available and cover traffic is enabled,
|
Acknowledgements {
|
||||||
/// a loop cover message is sent instead in order to preserve the rate.
|
average_ack_delay_ms: acknowledgements.average_ack_delay.as_millis() as u64,
|
||||||
pub message_sending_average_delay_ms: u64,
|
ack_wait_multiplier: acknowledgements.ack_wait_multiplier,
|
||||||
|
ack_wait_addition_ms: acknowledgements.ack_wait_addition.as_millis() as u64,
|
||||||
/// How long we're willing to wait for a response to a message sent to the gateway,
|
}
|
||||||
/// before giving up on it.
|
}
|
||||||
pub gateway_response_timeout_ms: u64,
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Topology {
|
||||||
/// The uniform delay every which clients are querying the directory server
|
/// The uniform delay every which clients are querying the directory server
|
||||||
/// to try to obtain a compatible network topology to send sphinx packets through.
|
/// to try to obtain a compatible network topology to send sphinx packets through.
|
||||||
pub topology_refresh_rate_ms: u64,
|
pub topology_refresh_rate_ms: u64,
|
||||||
@@ -95,18 +221,31 @@ pub struct Debug {
|
|||||||
/// path. This timeout determines waiting period until it is decided that the packet
|
/// path. This timeout determines waiting period until it is decided that the packet
|
||||||
/// did not reach its destination.
|
/// did not reach its destination.
|
||||||
pub topology_resolution_timeout_ms: u64,
|
pub topology_resolution_timeout_ms: u64,
|
||||||
|
}
|
||||||
|
|
||||||
/// Controls whether the dedicated loop cover traffic stream should be enabled.
|
impl From<Topology> for ConfigTopology {
|
||||||
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay_ms])
|
fn from(topology: Topology) -> Self {
|
||||||
pub disable_loop_cover_traffic_stream: bool,
|
ConfigTopology {
|
||||||
|
topology_refresh_rate: Duration::from_millis(topology.topology_refresh_rate_ms),
|
||||||
|
topology_resolution_timeout: Duration::from_millis(
|
||||||
|
topology.topology_resolution_timeout_ms,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Controls whether the main packet stream constantly produces packets according to the predefined
|
impl From<ConfigTopology> for Topology {
|
||||||
/// poisson distribution.
|
fn from(topology: ConfigTopology) -> Self {
|
||||||
pub disable_main_poisson_packet_distribution: bool,
|
Topology {
|
||||||
|
topology_refresh_rate_ms: topology.topology_refresh_rate.as_millis() as u64,
|
||||||
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
|
topology_resolution_timeout_ms: topology.topology_resolution_timeout.as_millis() as u64,
|
||||||
pub use_extended_packet_size: bool,
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct ReplySurbs {
|
||||||
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
|
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
|
||||||
/// It can only allow to go below that value if its to request additional reply surbs.
|
/// It can only allow to go below that value if its to request additional reply surbs.
|
||||||
pub minimum_reply_surb_storage_threshold: usize,
|
pub minimum_reply_surb_storage_threshold: usize,
|
||||||
@@ -140,46 +279,80 @@ pub struct Debug {
|
|||||||
pub maximum_reply_key_age_ms: u64,
|
pub maximum_reply_key_age_ms: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Debug> for ConfigDebug {
|
impl From<ReplySurbs> for ConfigReplySurbs {
|
||||||
fn from(debug: Debug) -> Self {
|
fn from(reply_surbs: ReplySurbs) -> Self {
|
||||||
// For now we just always use the (older) 32kb extended size
|
ConfigReplySurbs {
|
||||||
let use_extended_packet_size = debug
|
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
|
||||||
.use_extended_packet_size
|
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
|
||||||
.then(|| ExtendedPacketSize::Extended32);
|
minimum_reply_surb_request_size: reply_surbs.minimum_reply_surb_request_size,
|
||||||
|
maximum_reply_surb_request_size: reply_surbs.maximum_reply_surb_request_size,
|
||||||
ConfigDebug {
|
maximum_allowed_reply_surb_request_size: reply_surbs
|
||||||
average_packet_delay: Duration::from_millis(debug.average_packet_delay_ms),
|
.maximum_allowed_reply_surb_request_size,
|
||||||
average_ack_delay: Duration::from_millis(debug.average_ack_delay_ms),
|
|
||||||
ack_wait_multiplier: debug.ack_wait_multiplier,
|
|
||||||
ack_wait_addition: Duration::from_millis(debug.ack_wait_addition_ms),
|
|
||||||
loop_cover_traffic_average_delay: Duration::from_millis(
|
|
||||||
debug.loop_cover_traffic_average_delay_ms,
|
|
||||||
),
|
|
||||||
message_sending_average_delay: Duration::from_millis(
|
|
||||||
debug.message_sending_average_delay_ms,
|
|
||||||
),
|
|
||||||
gateway_response_timeout: Duration::from_millis(debug.gateway_response_timeout_ms),
|
|
||||||
topology_refresh_rate: Duration::from_millis(debug.topology_refresh_rate_ms),
|
|
||||||
topology_resolution_timeout: Duration::from_millis(
|
|
||||||
debug.topology_resolution_timeout_ms,
|
|
||||||
),
|
|
||||||
disable_loop_cover_traffic_stream: debug.disable_loop_cover_traffic_stream,
|
|
||||||
disable_main_poisson_packet_distribution: debug
|
|
||||||
.disable_main_poisson_packet_distribution,
|
|
||||||
use_extended_packet_size,
|
|
||||||
minimum_reply_surb_storage_threshold: debug.minimum_reply_surb_storage_threshold,
|
|
||||||
maximum_reply_surb_storage_threshold: debug.maximum_reply_surb_storage_threshold,
|
|
||||||
minimum_reply_surb_request_size: debug.minimum_reply_surb_request_size,
|
|
||||||
maximum_reply_surb_request_size: debug.maximum_reply_surb_request_size,
|
|
||||||
maximum_allowed_reply_surb_request_size: debug.maximum_allowed_reply_surb_request_size,
|
|
||||||
maximum_reply_surb_rerequest_waiting_period: Duration::from_millis(
|
maximum_reply_surb_rerequest_waiting_period: Duration::from_millis(
|
||||||
debug.maximum_reply_surb_rerequest_waiting_period_ms,
|
reply_surbs.maximum_reply_surb_rerequest_waiting_period_ms,
|
||||||
),
|
),
|
||||||
maximum_reply_surb_drop_waiting_period: Duration::from_millis(
|
maximum_reply_surb_drop_waiting_period: Duration::from_millis(
|
||||||
debug.maximum_reply_surb_drop_waiting_period_ms,
|
reply_surbs.maximum_reply_surb_drop_waiting_period_ms,
|
||||||
),
|
),
|
||||||
maximum_reply_surb_age: Duration::from_millis(debug.maximum_reply_surb_age_ms),
|
maximum_reply_surb_age: Duration::from_millis(reply_surbs.maximum_reply_surb_age_ms),
|
||||||
maximum_reply_key_age: Duration::from_millis(debug.maximum_reply_key_age_ms),
|
maximum_reply_key_age: Duration::from_millis(reply_surbs.maximum_reply_key_age_ms),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConfigReplySurbs> for ReplySurbs {
|
||||||
|
fn from(reply_surbs: ConfigReplySurbs) -> Self {
|
||||||
|
ReplySurbs {
|
||||||
|
minimum_reply_surb_storage_threshold: reply_surbs.minimum_reply_surb_storage_threshold,
|
||||||
|
maximum_reply_surb_storage_threshold: reply_surbs.maximum_reply_surb_storage_threshold,
|
||||||
|
minimum_reply_surb_request_size: reply_surbs.minimum_reply_surb_request_size,
|
||||||
|
maximum_reply_surb_request_size: reply_surbs.maximum_reply_surb_request_size,
|
||||||
|
maximum_allowed_reply_surb_request_size: reply_surbs
|
||||||
|
.maximum_allowed_reply_surb_request_size,
|
||||||
|
maximum_reply_surb_rerequest_waiting_period_ms: reply_surbs
|
||||||
|
.maximum_reply_surb_rerequest_waiting_period
|
||||||
|
.as_millis() as u64,
|
||||||
|
maximum_reply_surb_drop_waiting_period_ms: reply_surbs
|
||||||
|
.maximum_reply_surb_drop_waiting_period
|
||||||
|
.as_millis() as u64,
|
||||||
|
maximum_reply_surb_age_ms: reply_surbs.maximum_reply_surb_age.as_millis() as u64,
|
||||||
|
maximum_reply_key_age_ms: reply_surbs.maximum_reply_key_age.as_millis() as u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// just a helper structure to more easily pass through the JS boundary
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Debug {
|
||||||
|
/// Defines all configuration options related to traffic streams.
|
||||||
|
pub traffic: Traffic,
|
||||||
|
|
||||||
|
/// Defines all configuration options related to cover traffic stream(s).
|
||||||
|
pub cover_traffic: CoverTraffic,
|
||||||
|
|
||||||
|
/// Defines all configuration options related to the gateway connection.
|
||||||
|
pub gateway_connection: GatewayConnection,
|
||||||
|
|
||||||
|
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
|
||||||
|
pub acknowledgements: Acknowledgements,
|
||||||
|
|
||||||
|
/// Defines all configuration options related topology, such as refresh rates or timeouts.
|
||||||
|
pub topology: Topology,
|
||||||
|
|
||||||
|
/// Defines all configuration options related to reply SURBs.
|
||||||
|
pub reply_surbs: ReplySurbs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Debug> for ConfigDebug {
|
||||||
|
fn from(debug: Debug) -> Self {
|
||||||
|
ConfigDebug {
|
||||||
|
traffic: debug.traffic.into(),
|
||||||
|
cover_traffic: debug.cover_traffic.into(),
|
||||||
|
gateway_connection: debug.gateway_connection.into(),
|
||||||
|
acknowledgements: debug.acknowledgements.into(),
|
||||||
|
topology: debug.topology.into(),
|
||||||
|
reply_surbs: debug.reply_surbs.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,34 +360,12 @@ impl From<Debug> for ConfigDebug {
|
|||||||
impl From<ConfigDebug> for Debug {
|
impl From<ConfigDebug> for Debug {
|
||||||
fn from(debug: ConfigDebug) -> Self {
|
fn from(debug: ConfigDebug) -> Self {
|
||||||
Debug {
|
Debug {
|
||||||
average_packet_delay_ms: debug.average_packet_delay.as_millis() as u64,
|
traffic: debug.traffic.into(),
|
||||||
average_ack_delay_ms: debug.average_ack_delay.as_millis() as u64,
|
cover_traffic: debug.cover_traffic.into(),
|
||||||
ack_wait_multiplier: debug.ack_wait_multiplier,
|
gateway_connection: debug.gateway_connection.into(),
|
||||||
ack_wait_addition_ms: debug.ack_wait_addition.as_millis() as u64,
|
acknowledgements: debug.acknowledgements.into(),
|
||||||
loop_cover_traffic_average_delay_ms: debug.loop_cover_traffic_average_delay.as_millis()
|
topology: debug.topology.into(),
|
||||||
as u64,
|
reply_surbs: debug.reply_surbs.into(),
|
||||||
message_sending_average_delay_ms: debug.message_sending_average_delay.as_millis()
|
|
||||||
as u64,
|
|
||||||
gateway_response_timeout_ms: debug.gateway_response_timeout.as_millis() as u64,
|
|
||||||
topology_refresh_rate_ms: debug.topology_refresh_rate.as_millis() as u64,
|
|
||||||
topology_resolution_timeout_ms: debug.topology_resolution_timeout.as_millis() as u64,
|
|
||||||
disable_loop_cover_traffic_stream: debug.disable_loop_cover_traffic_stream,
|
|
||||||
disable_main_poisson_packet_distribution: debug
|
|
||||||
.disable_main_poisson_packet_distribution,
|
|
||||||
use_extended_packet_size: debug.use_extended_packet_size.is_some(),
|
|
||||||
minimum_reply_surb_storage_threshold: debug.minimum_reply_surb_storage_threshold,
|
|
||||||
maximum_reply_surb_storage_threshold: debug.maximum_reply_surb_storage_threshold,
|
|
||||||
minimum_reply_surb_request_size: debug.minimum_reply_surb_request_size,
|
|
||||||
maximum_reply_surb_request_size: debug.maximum_reply_surb_request_size,
|
|
||||||
maximum_allowed_reply_surb_request_size: debug.maximum_allowed_reply_surb_request_size,
|
|
||||||
maximum_reply_surb_rerequest_waiting_period_ms: debug
|
|
||||||
.maximum_reply_surb_rerequest_waiting_period
|
|
||||||
.as_millis() as u64,
|
|
||||||
maximum_reply_surb_drop_waiting_period_ms: debug
|
|
||||||
.maximum_reply_surb_drop_waiting_period
|
|
||||||
.as_millis() as u64,
|
|
||||||
maximum_reply_surb_age_ms: debug.maximum_reply_surb_age.as_millis() as u64,
|
|
||||||
maximum_reply_key_age_ms: debug.maximum_reply_key_age.as_millis() as u64,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,8 +82,14 @@ impl NymClientBuilder {
|
|||||||
// with no persistence
|
// with no persistence
|
||||||
fn setup_reply_surb_storage_backend(config: &Config) -> browser_backend::Backend {
|
fn setup_reply_surb_storage_backend(config: &Config) -> browser_backend::Backend {
|
||||||
browser_backend::Backend::new(
|
browser_backend::Backend::new(
|
||||||
config.debug.minimum_reply_surb_storage_threshold,
|
config
|
||||||
config.debug.maximum_reply_surb_storage_threshold,
|
.debug
|
||||||
|
.reply_surbs
|
||||||
|
.minimum_reply_surb_storage_threshold,
|
||||||
|
config
|
||||||
|
.debug
|
||||||
|
.reply_surbs
|
||||||
|
.maximum_reply_surb_storage_threshold,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "bandwidth-claim-contract"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
schemars = "0.8"
|
|
||||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
// Serializable structures for what we find in common/crypto
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
|
||||||
pub struct PublicKey([u8; 32]);
|
|
||||||
|
|
||||||
impl PublicKey {
|
|
||||||
pub fn new(bytes: [u8; 32]) -> Self {
|
|
||||||
PublicKey(bytes)
|
|
||||||
}
|
|
||||||
pub fn to_bytes(&self) -> [u8; 32] {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<[u8]> for PublicKey {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &[u8] {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
|
||||||
pub struct Signature([u8; 32], [u8; 32]);
|
|
||||||
|
|
||||||
impl Signature {
|
|
||||||
pub fn new(bytes: [u8; 64]) -> Self {
|
|
||||||
let mut sig1 = [0u8; 32];
|
|
||||||
let mut sig2 = [0u8; 32];
|
|
||||||
sig1.copy_from_slice(&bytes[..32]);
|
|
||||||
sig2.copy_from_slice(&bytes[32..]);
|
|
||||||
|
|
||||||
Signature(sig1, sig2)
|
|
||||||
}
|
|
||||||
pub fn to_bytes(&self) -> [u8; 64] {
|
|
||||||
let mut res = [0u8; 64];
|
|
||||||
res[..32].copy_from_slice(&self.0);
|
|
||||||
res[32..].copy_from_slice(&self.1);
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
pub mod keys;
|
|
||||||
pub mod msg;
|
|
||||||
pub mod payment;
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::keys::PublicKey;
|
|
||||||
use crate::payment::LinkPaymentData;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
|
||||||
pub struct InstantiateMsg {}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum ExecuteMsg {
|
|
||||||
LinkPayment { data: LinkPaymentData },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum QueryMsg {
|
|
||||||
GetPayments {
|
|
||||||
limit: Option<u32>,
|
|
||||||
start_after: Option<PublicKey>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub struct MigrateMsg {}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::keys::{PublicKey, Signature};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
|
||||||
pub struct Payment {
|
|
||||||
verification_key: PublicKey,
|
|
||||||
gateway_identity: PublicKey,
|
|
||||||
bandwidth: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Payment {
|
|
||||||
pub fn new(verification_key: PublicKey, gateway_identity: PublicKey, bandwidth: u64) -> Self {
|
|
||||||
Payment {
|
|
||||||
verification_key,
|
|
||||||
gateway_identity,
|
|
||||||
bandwidth,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verification_key(&self) -> PublicKey {
|
|
||||||
self.verification_key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
|
||||||
pub struct LinkPaymentData {
|
|
||||||
pub verification_key: PublicKey,
|
|
||||||
pub gateway_identity: PublicKey,
|
|
||||||
pub bandwidth: u64,
|
|
||||||
pub signature: Signature,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LinkPaymentData {
|
|
||||||
pub fn new(
|
|
||||||
verification_key: [u8; 32],
|
|
||||||
gateway_identity: [u8; 32],
|
|
||||||
bandwidth: u64,
|
|
||||||
signature: [u8; 64],
|
|
||||||
) -> Self {
|
|
||||||
LinkPaymentData {
|
|
||||||
verification_key: PublicKey::new(verification_key),
|
|
||||||
gateway_identity: PublicKey::new(gateway_identity),
|
|
||||||
bandwidth,
|
|
||||||
signature: Signature::new(signature),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
|
||||||
pub struct PagedPaymentResponse {
|
|
||||||
pub payments: Vec<Payment>,
|
|
||||||
pub per_page: usize,
|
|
||||||
pub start_next_after: Option<PublicKey>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PagedPaymentResponse {
|
|
||||||
pub fn new(
|
|
||||||
payments: Vec<Payment>,
|
|
||||||
per_page: usize,
|
|
||||||
start_next_after: Option<PublicKey>,
|
|
||||||
) -> Self {
|
|
||||||
PagedPaymentResponse {
|
|
||||||
payments,
|
|
||||||
per_page,
|
|
||||||
start_next_after,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user