Compare commits
142 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bdda08c0b4 | |||
| 0e48ff5e9e | |||
| 68d6ece6c2 | |||
| 493f036687 | |||
| 1f4f312e8e | |||
| d829837275 | |||
| 36253e3f36 | |||
| 7743554bb8 | |||
| de05e32845 | |||
| 136f4a8473 | |||
| 57e2628928 | |||
| 879e324696 | |||
| 351552b863 | |||
| 847f3a0ecc | |||
| 58958a2449 | |||
| 3051d84e8d | |||
| 2d7003dfae | |||
| a91c997aed | |||
| 5787653210 | |||
| adbeeb3e5f | |||
| 4899ccf4ef | |||
| 9e0bcc025f | |||
| 16d6444169 | |||
| 730f03de30 | |||
| 2e366a094d | |||
| b134334ec7 | |||
| f95b9b7f4a | |||
| 11ed3b3e45 | |||
| a71e228a25 | |||
| e025564678 | |||
| cf6188d794 | |||
| dd7308d92f | |||
| 73ca14aae2 | |||
| 0fee189a89 | |||
| a0c6efafd2 | |||
| fcc5398aab | |||
| 9f1f765f73 | |||
| 7bbb10e52c | |||
| 1aad69a527 | |||
| 4ad02d5328 | |||
| 45b74008f1 | |||
| 2ddf9646ec | |||
| b258cd8733 | |||
| 7a1a7c003e | |||
| 2953837f25 | |||
| ee98820bb4 | |||
| 0f41dda013 | |||
| efd8ba5978 | |||
| 0c7181a211 | |||
| f47a111640 | |||
| ae5a9ccc50 | |||
| 40465665f0 | |||
| 8c437ac964 | |||
| f3b17ad2f8 | |||
| 9121078576 | |||
| c505a00fac | |||
| 09bad9c6b4 | |||
| 597f5e9545 | |||
| 54cb7be1e1 | |||
| 431c98e591 | |||
| 35f2e71202 | |||
| 0134030341 | |||
| 97c775bc68 | |||
| 62ba6b30ae | |||
| 24354275d3 | |||
| 76335e9adc | |||
| bab8eb746e | |||
| f298f5d4fa | |||
| 85b078a3e8 | |||
| 0115b02be3 | |||
| a4ffd135e7 | |||
| bbce67902b | |||
| e6930046c4 | |||
| 0c9402503a | |||
| 81e133b789 | |||
| 7be07c29c1 | |||
| 31bc439f65 | |||
| 6479480cf7 | |||
| 4af70ef255 | |||
| eba58f6451 | |||
| 35206655e0 | |||
| e14db00fc2 | |||
| 72b92784cc | |||
| 38b95c2673 | |||
| f0f9899f82 | |||
| 09c46e3403 | |||
| 9fbab5aaad | |||
| 2032b3bdae | |||
| 8f57919571 | |||
| 3748ab77a1 | |||
| 3cdca0ad8d | |||
| 9cbb3dfa0e | |||
| 4071d30f3c | |||
| 4c13d91bfb | |||
| 8355e6ce5e | |||
| dd33052f0e | |||
| a76a51e823 | |||
| 4caa9390cf | |||
| b31d3c003f | |||
| 21e11c9221 | |||
| c9dcde0ee0 | |||
| 3fcc16090a | |||
| bbb1e5e15a | |||
| 6d30e7ea8e | |||
| 9288f71c5f | |||
| 79f5983c76 | |||
| 335453b63c | |||
| 4292a55614 | |||
| 92e902c81e | |||
| c47bc174bc | |||
| f97f0475e9 | |||
| 67a945a15f | |||
| d1a28826d5 | |||
| 8f026ab6c6 | |||
| bdcdcf7f8b | |||
| 182e147a86 | |||
| 687b437ea0 | |||
| f2c5dbb696 | |||
| a8bf690c17 | |||
| 1328ba35be | |||
| 8b046d4139 | |||
| f4cd372808 | |||
| 7228331db6 | |||
| 45f3f3ec01 | |||
| 5f9e54c83c | |||
| f8c2f90502 | |||
| 96e8bdfea4 | |||
| 31b7921c68 | |||
| 71fb6a1ba1 | |||
| 892653cd96 | |||
| a7471ef324 | |||
| 403141c1f5 | |||
| d8c82bf6d0 | |||
| dd86ba36dd | |||
| 0320220219 | |||
| e32ee2ccf3 | |||
| b8ca1762c2 | |||
| d1e9fcf03a | |||
| 303a774378 | |||
| c9ca71f47b | |||
| 92faf1e3d5 | |||
| e509989ac3 |
@@ -38,7 +38,7 @@ jobs:
|
||||
- name: install npm
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- name: Matrix - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
- name: Build
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
node-version: 18
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
node-version: 18
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
node-version: 18
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16"
|
||||
node-version: 18
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- name: Install Yarn
|
||||
run: npm install -g yarn
|
||||
- run: yarn
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
name: Linting for Network Explorer (eslint/prettier)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'explorer/**'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: explorer
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
- name: Run ESLint
|
||||
# GitHub should automatically annotate the PR
|
||||
run: yarn && yarn lint
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
continue-on-error: true
|
||||
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- name: Matrix - Node Install
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
run: npm install
|
||||
|
||||
@@ -167,7 +167,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- name: Matrix - Node Install
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
run: npm install
|
||||
|
||||
@@ -167,7 +167,7 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- name: Matrix - Node Install
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
run: npm install
|
||||
|
||||
@@ -20,10 +20,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Node v16
|
||||
- name: Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -51,11 +51,15 @@ jobs:
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
uses: timheuer/base64-to-file@v1.2
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Install project dependencies
|
||||
shell: bash
|
||||
run: cd .. && yarn --network-timeout 100000
|
||||
|
||||
- name: Install app dependencies and build it
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -73,7 +77,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-connect_1.0.0_x64.dmg
|
||||
path: nym-connect/desktop/target/release/bundle/dmg/nym-connect_1.0.0_x64.dmg
|
||||
path: nym-connect/desktop/target/release/bundle/dmg/nym-connect_1*_x64.dmg
|
||||
retention-days: 30
|
||||
|
||||
- name: Clean up keychain
|
||||
|
||||
@@ -26,19 +26,25 @@ jobs:
|
||||
sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev
|
||||
continue-on-error: true
|
||||
|
||||
- name: Node v16
|
||||
- name: Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install project dependencies
|
||||
shell: bash
|
||||
run: cd .. && yarn --network-timeout 100000
|
||||
|
||||
- name: Install app dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
uses: timheuer/base64-to-file@v1.2
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
@@ -53,7 +59,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-connect.AppImage.tar.gz
|
||||
path: nym-connect/desktop/target/release/bundle/appimage/nym-connect_1.0.0_amd64.AppImage
|
||||
path: nym-connect/desktop/target/release/bundle/appimage/nym-connect_1*_amd64.AppImage
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
|
||||
@@ -39,10 +39,10 @@ jobs:
|
||||
Remove-Item -path certificate -include tempCert.txt
|
||||
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
|
||||
|
||||
- name: Node v16
|
||||
- name: Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -50,15 +50,21 @@ jobs:
|
||||
toolchain: stable
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
uses: timheuer/base64-to-file@v1.2
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Install project dependencies
|
||||
shell: bash
|
||||
run: cd .. && yarn --network-timeout 100000
|
||||
|
||||
- name: Install app dependencies
|
||||
shell: bash
|
||||
run: yarn --network-timeout 100000
|
||||
|
||||
- name: Build and sign it
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||
@@ -72,7 +78,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-connect_1.0.0_x64_en-US.msi
|
||||
path: nym-connect/desktop/target/release/bundle/msi/nym-connect_1.0.0_x64_en-US.msi
|
||||
path: nym-connect/desktop/target/release/bundle/msi/nym-connect_1*_x64_en-US.msi
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
|
||||
@@ -20,10 +20,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Node v16
|
||||
- name: Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -51,11 +51,15 @@ jobs:
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
uses: timheuer/base64-to-file@v1.2
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Install project dependencies
|
||||
shell: bash
|
||||
run: cd .. && yarn --network-timeout 100000
|
||||
|
||||
- name: Install app dependencies and build it
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
name: Publish Nym Wallet (Ubuntu)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
@@ -25,19 +26,24 @@ jobs:
|
||||
sudo apt-get install -y webkit2gtk-4.0
|
||||
continue-on-error: true
|
||||
|
||||
- name: Node v16
|
||||
- name: Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install project dependencies
|
||||
shell: bash
|
||||
run: cd .. && yarn --network-timeout 100000
|
||||
|
||||
- name: Install app dependencies
|
||||
run: yarn
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
uses: timheuer/base64-to-file@v1.2
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
@@ -47,8 +53,17 @@ jobs:
|
||||
env:
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-wallet_1.0.0_amd64.AppImage.tar.gz
|
||||
path: nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
nym-wallet/target/release/bundle/appimage/*.AppImage
|
||||
|
||||
@@ -39,10 +39,10 @@ jobs:
|
||||
Remove-Item -path certificate -include tempCert.txt
|
||||
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
|
||||
|
||||
- name: Node v16
|
||||
- name: Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -50,15 +50,21 @@ jobs:
|
||||
toolchain: stable
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
uses: timheuer/base64-to-file@v1.2
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Install project dependencies
|
||||
shell: bash
|
||||
run: cd .. && yarn --network-timeout 100000
|
||||
|
||||
- name: Install app dependencies
|
||||
shell: bash
|
||||
run: yarn --network-timeout 100000
|
||||
|
||||
- name: Build and sign it
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
- name: Build dependencies
|
||||
|
||||
@@ -34,10 +34,10 @@ jobs:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
|
||||
- name: Node v16
|
||||
- name: Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- name: Install yarn for building application
|
||||
run: yarn install
|
||||
|
||||
@@ -68,8 +68,10 @@ jobs:
|
||||
working-directory: nym-connect/native/android
|
||||
env:
|
||||
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.NYMS5_ANDROID_SENTRY_AUTH_TOKEN }}
|
||||
# build for arm64 and x86_64
|
||||
run: |
|
||||
echo "auth.token=$SENTRY_AUTH_TOKEN" | tee -a sentry.properties
|
||||
./gradlew :app:assembleArch64Debug
|
||||
./gradlew :app:assembleArch64Release
|
||||
|
||||
@@ -107,4 +109,3 @@ jobs:
|
||||
files: |
|
||||
apk/nyms5-arch64-debug.apk
|
||||
apk/nyms5-arch64-release.apk
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Publish SDK to NPM
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: [custom-runner-linux]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Build and publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
|
||||
working-directory: ./sdk/typescript/packages/sdk
|
||||
run: scripts/publish.sh
|
||||
@@ -1 +1 @@
|
||||
16
|
||||
18
|
||||
|
||||
@@ -11,6 +11,7 @@ on:
|
||||
- 'nym-connect/mobile/package.json'
|
||||
- 'nym-wallet/src/**'
|
||||
- 'nym-wallet/package.json'
|
||||
- 'explorer/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'ts-packages/**'
|
||||
@@ -21,6 +22,7 @@ on:
|
||||
- 'nym-connect/mobile/package.json'
|
||||
- 'nym-wallet/src/**'
|
||||
- 'nym-wallet/package.json'
|
||||
- 'explorer/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -33,7 +35,7 @@ jobs:
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
- name: Install
|
||||
|
||||
@@ -4,6 +4,51 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v1.1.23] (2023-07-04)
|
||||
|
||||
- nym-cli: add client identity key signing support ([#3576])
|
||||
- Feature/node tester package ([#3634])
|
||||
- Add medium toggle to socks5 client ([#3615])
|
||||
- Don't fully turn off background task when cover traffic is disabled ([#3596])
|
||||
|
||||
[#3576]: https://github.com/nymtech/nym/issues/3576
|
||||
[#3634]: https://github.com/nymtech/nym/pull/3634
|
||||
[#3615]: https://github.com/nymtech/nym/pull/3615
|
||||
[#3596]: https://github.com/nymtech/nym/pull/3596
|
||||
|
||||
## [v1.1.22] (2023-06-20)
|
||||
|
||||
- CLI tool for querying network-requesters ([#3539])
|
||||
- Statically link OpenSSL ([#3510])
|
||||
- NymConnect - add sentry.io reporting ([#3421])
|
||||
- init command does not change version number in config.toml ([#3336])
|
||||
- [Bug] Config version does not correspond to binary version ([#3434])
|
||||
|
||||
[#3539]: https://github.com/nymtech/nym/issues/3539
|
||||
[#3510]: https://github.com/nymtech/nym/issues/3510
|
||||
[#3421]: https://github.com/nymtech/nym/issues/3421
|
||||
[#3336]: https://github.com/nymtech/nym/issues/3336
|
||||
[#3434]: https://github.com/nymtech/nym/issues/3434
|
||||
|
||||
## [v1.1.21] (2023-06-13)
|
||||
|
||||
- mixFetch: Change socks5 `SendRequest` to include OrderedMessage index as a field rather than making it serialized inside the `data` field
|
||||
([#3534])
|
||||
- Explorer - add more data columns to the Service Provider section: ([#3474])
|
||||
- network-requester: support report if they run an open proxy using `ControlRequest` API ([#3461])
|
||||
- Refactor client configs (London discussion) ([#3444])
|
||||
- Increase `DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE` to 2000 to improve reliability ([#3433])
|
||||
- socks5: sender waits for lanes to clear even though the connection is closed ([#3366])
|
||||
- version bump for variables ([#3545])
|
||||
|
||||
[#3534]: https://github.com/nymtech/nym/issues/3534
|
||||
[#3474]: https://github.com/nymtech/nym/issues/3474
|
||||
[#3461]: https://github.com/nymtech/nym/issues/3461
|
||||
[#3444]: https://github.com/nymtech/nym/issues/3444
|
||||
[#3433]: https://github.com/nymtech/nym/issues/3433
|
||||
[#3366]: https://github.com/nymtech/nym/issues/3366
|
||||
[#3545]: https://github.com/nymtech/nym/pull/3545
|
||||
|
||||
## [v1.1.20] (2023-06-06)
|
||||
|
||||
- Explorer - Fix SP supported apps list ([#3458])
|
||||
|
||||
Generated
+30
-25
@@ -68,7 +68,7 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
@@ -946,7 +946,7 @@ dependencies = [
|
||||
"cosmos-sdk-proto",
|
||||
"ecdsa",
|
||||
"eyre",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"k256",
|
||||
"prost 0.10.4",
|
||||
"prost-types 0.10.1",
|
||||
@@ -1732,7 +1732,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.20"
|
||||
version = "1.1.23"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.2.7",
|
||||
@@ -2117,9 +2117,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.9"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -3322,7 +3322,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.21"
|
||||
version = "1.1.24"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3423,7 +3423,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-bin-common"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap 4.2.7",
|
||||
@@ -3459,7 +3459,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.20"
|
||||
version = "1.1.23"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.13.1",
|
||||
@@ -3507,8 +3507,10 @@ dependencies = [
|
||||
"nym-multisig-contract-common",
|
||||
"nym-name-service-common",
|
||||
"nym-network-defaults",
|
||||
"nym-pemstore",
|
||||
"nym-service-provider-directory-common",
|
||||
"nym-sphinx",
|
||||
"nym-types",
|
||||
"nym-validator-client",
|
||||
"nym-vesting-contract-common",
|
||||
"rand 0.6.5",
|
||||
@@ -3523,7 +3525,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.20"
|
||||
version = "1.1.23"
|
||||
dependencies = [
|
||||
"clap 4.2.7",
|
||||
"dirs 4.0.0",
|
||||
@@ -3559,7 +3561,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client-core"
|
||||
version = "1.1.14"
|
||||
version = "1.1.15"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.2",
|
||||
@@ -3622,7 +3624,7 @@ dependencies = [
|
||||
"digest 0.9.0",
|
||||
"doc-comment",
|
||||
"ff 0.11.1",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"group 0.11.0",
|
||||
"itertools",
|
||||
"nym-dkg",
|
||||
@@ -3797,7 +3799,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.20"
|
||||
version = "1.1.23"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3850,7 +3852,7 @@ name = "nym-gateway-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"log",
|
||||
"nym-bandwidth-controller",
|
||||
"nym-coconut-interface",
|
||||
@@ -3948,7 +3950,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.21"
|
||||
version = "1.1.24"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58",
|
||||
@@ -4058,7 +4060,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.20"
|
||||
version = "1.1.23"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-file-watcher",
|
||||
@@ -4104,7 +4106,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-statistics"
|
||||
version = "1.1.20"
|
||||
version = "1.1.23"
|
||||
dependencies = [
|
||||
"dirs 4.0.0",
|
||||
"log",
|
||||
@@ -4162,7 +4164,6 @@ dependencies = [
|
||||
"nym-sdk",
|
||||
"nym-service-providers-common",
|
||||
"nym-socks5-requests",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"tokio",
|
||||
]
|
||||
@@ -4185,7 +4186,7 @@ dependencies = [
|
||||
"criterion",
|
||||
"curve25519-dalek",
|
||||
"fastrand",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"rayon",
|
||||
@@ -4264,7 +4265,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.20"
|
||||
version = "1.1.23"
|
||||
dependencies = [
|
||||
"clap 4.2.7",
|
||||
"lazy_static",
|
||||
@@ -4403,6 +4404,7 @@ dependencies = [
|
||||
"nym-pemstore",
|
||||
"nym-sphinx-addressing",
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-routing",
|
||||
"nym-sphinx-types",
|
||||
"nym-topology",
|
||||
"rand 0.7.3",
|
||||
@@ -4430,6 +4432,7 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-sphinx-addressing",
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-routing",
|
||||
"nym-sphinx-types",
|
||||
"nym-topology",
|
||||
"rand 0.7.3",
|
||||
@@ -4461,6 +4464,7 @@ dependencies = [
|
||||
"nym-sphinx-chunking",
|
||||
"nym-sphinx-forwarding",
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-routing",
|
||||
"nym-sphinx-types",
|
||||
"nym-topology",
|
||||
"rand 0.7.3",
|
||||
@@ -4538,7 +4542,7 @@ dependencies = [
|
||||
"aes-gcm",
|
||||
"argon2",
|
||||
"generic-array 0.14.7",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4632,6 +4636,7 @@ dependencies = [
|
||||
"nym-service-provider-directory-common",
|
||||
"nym-vesting-contract",
|
||||
"nym-vesting-contract-common",
|
||||
"openssl",
|
||||
"prost 0.10.4",
|
||||
"reqwest",
|
||||
"serde",
|
||||
@@ -5517,7 +5522,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5674,7 +5679,7 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"redox_syscall 0.2.16",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -6964,7 +6969,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"flex-error",
|
||||
"futures",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"http",
|
||||
"hyper",
|
||||
"hyper-proxy",
|
||||
@@ -7734,7 +7739,7 @@ version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
|
||||
dependencies = [
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -7902,7 +7907,7 @@ name = "wasm-utils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"indexed_db_futures",
|
||||
"js-sys",
|
||||
"nym-store-cipher",
|
||||
|
||||
+2
-1
@@ -113,8 +113,8 @@ edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
async-trait = "0.1.64"
|
||||
anyhow = "1.0.71"
|
||||
async-trait = "0.1.64"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
cfg-if = "1.0.0"
|
||||
cosmwasm-derive = "=1.0.0"
|
||||
@@ -130,6 +130,7 @@ cw3-fixed-multisig = { version = "=0.13.4" }
|
||||
cw4 = { version = "=0.13.4" }
|
||||
dotenvy = "0.15.6"
|
||||
generic-array = "0.14.7"
|
||||
getrandom = "0.2.10"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
once_cell = "1.7.2"
|
||||
|
||||
@@ -13,7 +13,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
||||
* nym-gateway - acts sort of like a mailbox for mixnet messages, which removes the need for direct delivery to potentially offline or firewalled devices.
|
||||
* nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
|
||||
* nym-explorer - a (projected) block explorer and (existing) mixnet viewer.
|
||||
* 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://github.com/nymtech/nym/actions?query=branch%3Adevelop)
|
||||
@@ -21,7 +21,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
||||
|
||||
### Building
|
||||
|
||||
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/build-nym.html).
|
||||
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/pre-built-binaries.html).
|
||||
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/wallet/desktop-wallet.html).
|
||||
|
||||
### Developing
|
||||
@@ -32,9 +32,9 @@ For Typescript components, please see [ts-packages](./ts-packages).
|
||||
|
||||
### Developer chat
|
||||
|
||||
> We used to use Keybase for developer chats, but we have since migrated to Matrix and Discord. We no longer check the old **nymtech.friends** Keybase team.
|
||||
> We used to use Keybase for developer chats, but we have since migrated to Matrix and Discord. We no longer check the old **nymtech.friends** Keybase team.
|
||||
|
||||
You can chat to us in two places:
|
||||
You can chat to us in two places:
|
||||
* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat)
|
||||
* The various developer channels on [Discord](https://discord.gg/nym)
|
||||
|
||||
@@ -84,4 +84,3 @@ where `s'` is stake `s` scaled over total token circulating supply.
|
||||
### Licensing and copyright information
|
||||
|
||||
This program is available as open source under the terms of the Apache 2.0 license. However, some elements are being licensed under CC0-1.0 and MIT. For accurate information, please check individual files.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.20"
|
||||
version = "1.1.23"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -75,7 +75,7 @@ impl NymConfigTemplate for Config {
|
||||
impl Config {
|
||||
pub fn new<S: AsRef<str>>(id: S) -> Self {
|
||||
Config {
|
||||
base: BaseClientConfig::new(id.as_ref()),
|
||||
base: BaseClientConfig::new(id.as_ref(), env!("CARGO_PKG_VERSION")),
|
||||
storage_paths: ClientPaths::new_default(default_data_directory(id.as_ref())),
|
||||
logging: Default::default(),
|
||||
socket: Default::default(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// all variable size data is always prefixed with u64 length
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// all variable size data is always prefixed with u64 length
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::ErrorKind;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.20"
|
||||
version = "1.1.23"
|
||||
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"
|
||||
edition = "2021"
|
||||
|
||||
@@ -94,6 +94,7 @@ impl From<Init> for OverrideConfig {
|
||||
use_anonymous_replies: init_config.use_reply_surbs,
|
||||
fastmode: init_config.fastmode,
|
||||
no_cover: init_config.no_cover,
|
||||
medium_toggle: false,
|
||||
nyxd_urls: init_config.nyxd_urls,
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
outfox: false,
|
||||
|
||||
@@ -19,7 +19,7 @@ use nym_client_core::client::key_manager::persistence::OnDiskKeys;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_client_core::error::ClientCoreError;
|
||||
use nym_config::OptionalSet;
|
||||
use nym_sphinx::params::PacketType;
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use std::error::Error;
|
||||
|
||||
pub mod init;
|
||||
@@ -72,6 +72,7 @@ pub(crate) struct OverrideConfig {
|
||||
use_anonymous_replies: Option<bool>,
|
||||
fastmode: bool,
|
||||
no_cover: bool,
|
||||
medium_toggle: bool,
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
outfox: bool,
|
||||
@@ -91,6 +92,10 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Syn
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
let disable_cover_traffic_with_keepalive = args.medium_toggle;
|
||||
let secondary_packet_size = args.medium_toggle.then_some(PacketSize::ExtendedPacket16);
|
||||
let no_per_hop_delays = args.medium_toggle;
|
||||
|
||||
let packet_type = if args.outfox {
|
||||
PacketType::Outfox
|
||||
} else {
|
||||
@@ -101,6 +106,17 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
BaseClientConfig::with_high_default_traffic_volume,
|
||||
args.fastmode,
|
||||
)
|
||||
.with_base(
|
||||
// NOTE: This interacts with disabling cover traffic fully, so we want to this to be set before
|
||||
BaseClientConfig::with_disabled_cover_traffic_with_keepalive,
|
||||
disable_cover_traffic_with_keepalive,
|
||||
)
|
||||
.with_base(
|
||||
BaseClientConfig::with_secondary_packet_size,
|
||||
secondary_packet_size,
|
||||
)
|
||||
.with_base(BaseClientConfig::with_no_per_hop_delays, no_per_hop_delays)
|
||||
// NOTE: see comment above about the order of the other disble cover traffic config
|
||||
.with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover)
|
||||
.with_base(BaseClientConfig::with_packet_type, packet_type)
|
||||
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
|
||||
|
||||
@@ -60,6 +60,11 @@ pub(crate) struct Run {
|
||||
#[clap(long, hide = true)]
|
||||
no_cover: bool,
|
||||
|
||||
/// Enable medium mixnet traffic, for experiments only.
|
||||
/// This includes things like disabling cover traffic, no per hop delays, etc.
|
||||
#[clap(long, hide = true)]
|
||||
medium_toggle: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long, hide = true)]
|
||||
@@ -77,6 +82,7 @@ impl From<Run> for OverrideConfig {
|
||||
use_anonymous_replies: run_config.use_anonymous_replies,
|
||||
fastmode: run_config.fastmode,
|
||||
no_cover: run_config.no_cover,
|
||||
medium_toggle: run_config.medium_toggle,
|
||||
nyxd_urls: run_config.nyxd_urls,
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
outfox: run_config.outfox,
|
||||
|
||||
@@ -70,7 +70,11 @@ impl NymConfigTemplate for Config {
|
||||
impl Config {
|
||||
pub fn new<S: AsRef<str>>(id: S, provider_mix_address: S) -> Self {
|
||||
Config {
|
||||
core: CoreConfig::new(id.as_ref(), provider_mix_address.as_ref()),
|
||||
core: CoreConfig::new(
|
||||
id.as_ref(),
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
provider_mix_address.as_ref(),
|
||||
),
|
||||
storage_paths: SocksClientPaths::new_default(default_data_directory(id.as_ref())),
|
||||
logging: Default::default(),
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
16
|
||||
18
|
||||
Generated
+30
-16
@@ -62,7 +62,7 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
@@ -622,7 +622,7 @@ dependencies = [
|
||||
"cosmos-sdk-proto",
|
||||
"ecdsa",
|
||||
"eyre",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"k256",
|
||||
"prost",
|
||||
"prost-types",
|
||||
@@ -1442,9 +1442,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.9"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
@@ -2240,7 +2240,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-bin-common"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"clap",
|
||||
@@ -2255,7 +2255,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client-core"
|
||||
version = "1.1.14"
|
||||
version = "1.1.15"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.2",
|
||||
@@ -2300,7 +2300,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client-wasm"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -2345,7 +2345,7 @@ dependencies = [
|
||||
"bs58",
|
||||
"digest 0.9.0",
|
||||
"ff 0.11.1",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"group 0.11.0",
|
||||
"itertools",
|
||||
"nym-dkg",
|
||||
@@ -2489,7 +2489,7 @@ name = "nym-gateway-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"log",
|
||||
"nym-bandwidth-controller",
|
||||
"nym-coconut-interface",
|
||||
@@ -2636,7 +2636,7 @@ dependencies = [
|
||||
"chacha20",
|
||||
"chacha20poly1305",
|
||||
"curve25519-dalek",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"rayon",
|
||||
@@ -2697,6 +2697,7 @@ dependencies = [
|
||||
"nym-pemstore",
|
||||
"nym-sphinx-addressing",
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-routing",
|
||||
"nym-sphinx-types",
|
||||
"nym-topology",
|
||||
"rand 0.7.3",
|
||||
@@ -2723,6 +2724,7 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-sphinx-addressing",
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-routing",
|
||||
"nym-sphinx-types",
|
||||
"nym-topology",
|
||||
"rand 0.7.3",
|
||||
@@ -2753,6 +2755,7 @@ dependencies = [
|
||||
"nym-sphinx-chunking",
|
||||
"nym-sphinx-forwarding",
|
||||
"nym-sphinx-params",
|
||||
"nym-sphinx-routing",
|
||||
"nym-sphinx-types",
|
||||
"nym-topology",
|
||||
"rand 0.7.3",
|
||||
@@ -2816,7 +2819,7 @@ dependencies = [
|
||||
"aes-gcm",
|
||||
"argon2",
|
||||
"generic-array 0.14.7",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2884,6 +2887,7 @@ dependencies = [
|
||||
"nym-service-provider-directory-common",
|
||||
"nym-vesting-contract",
|
||||
"nym-vesting-contract-common",
|
||||
"openssl",
|
||||
"prost",
|
||||
"reqwest",
|
||||
"serde",
|
||||
@@ -2974,6 +2978,15 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.26.0+1.1.1u"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.87"
|
||||
@@ -2982,6 +2995,7 @@ checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
@@ -3402,7 +3416,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3470,7 +3484,7 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"redox_syscall 0.2.16",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -4388,7 +4402,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"flex-error",
|
||||
"futures",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"http",
|
||||
"hyper",
|
||||
"hyper-proxy",
|
||||
@@ -4788,7 +4802,7 @@ version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
|
||||
dependencies = [
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -4964,7 +4978,7 @@ name = "wasm-utils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"getrandom 0.2.9",
|
||||
"getrandom 0.2.10",
|
||||
"indexed_db_futures",
|
||||
"js-sys",
|
||||
"nym-store-cipher",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nym-client-wasm"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
edition = "2021"
|
||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -3,3 +3,6 @@ clippy:
|
||||
|
||||
test:
|
||||
wasm-pack test --node
|
||||
|
||||
wasm-build:
|
||||
wasm-pack build
|
||||
@@ -8,6 +8,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"build:wasm": "cd ../ && make wasm-build",
|
||||
"start": "webpack-dev-server --port 8001"
|
||||
},
|
||||
"repository": {
|
||||
@@ -36,4 +37,4 @@
|
||||
"dependencies": {
|
||||
"@nymproject/nym-client-wasm": "file:../pkg"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,333 +12,371 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
importScripts('nym_client_wasm.js');
|
||||
importScripts("nym_client_wasm.js");
|
||||
|
||||
console.log('Initializing worker');
|
||||
console.log("Initializing worker");
|
||||
|
||||
// wasm_bindgen creates a global variable (with the exports attached) that is in scope after `importScripts`
|
||||
const {
|
||||
NymNodeTester,
|
||||
WasmGateway,
|
||||
WasmMixNode,
|
||||
WasmNymTopology,
|
||||
default_debug,
|
||||
NymClientBuilder,
|
||||
NymClient,
|
||||
set_panic_hook,
|
||||
Config,
|
||||
GatewayEndpointConfig,
|
||||
ClientStorage,
|
||||
current_network_topology,
|
||||
make_key,
|
||||
make_key2
|
||||
NymNodeTester,
|
||||
WasmGateway,
|
||||
WasmMixNode,
|
||||
WasmNymTopology,
|
||||
default_debug,
|
||||
NymClientBuilder,
|
||||
NymClient,
|
||||
set_panic_hook,
|
||||
Config,
|
||||
GatewayEndpointConfig,
|
||||
ClientStorage,
|
||||
current_network_topology,
|
||||
make_key,
|
||||
make_key2,
|
||||
} = wasm_bindgen;
|
||||
|
||||
let client = null;
|
||||
let tester = null;
|
||||
|
||||
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
|
||||
|
||||
function dummyTopology() {
|
||||
const l1Mixnode = new WasmMixNode(
|
||||
1,
|
||||
'n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47',
|
||||
'178.79.143.65',
|
||||
1789,
|
||||
'4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz',
|
||||
'8ndjk5oZ6HxUZNScLJJ7hk39XtUqGexdKgW7hSX6kpWG',
|
||||
1,
|
||||
'1.10.0',
|
||||
);
|
||||
const l2Mixnode = new WasmMixNode(
|
||||
2,
|
||||
'n1z93z44vf8ssvdhujjvxcj4rd5e3lz0l60wdk70',
|
||||
'109.74.197.180',
|
||||
1789,
|
||||
'7sVjiMrPYZrDWRujku9QLxgE8noT7NTgBAqizCsu7AoK',
|
||||
'GepXwRnKZDd8x2nBWAajGGBVvF3mrpVMQBkgfrGuqRCN',
|
||||
2,
|
||||
'1.10.0',
|
||||
);
|
||||
const l3Mixnode = new WasmMixNode(
|
||||
3,
|
||||
'n1ptg680vnmef2cd8l0s9uyc4f0hgf3x8sed6w77',
|
||||
'176.58.101.80',
|
||||
1789,
|
||||
'FoM5Mx9Pxk1g3zEqkS3APgtBeTtTo3M8k7Yu4bV6kK1R',
|
||||
'DeYjrDC2AcQRVFshiKnbUo6bRvPyZ33QGYR2DLeFJ9qD',
|
||||
3,
|
||||
'1.10.0',
|
||||
);
|
||||
const l1Mixnode = new WasmMixNode(
|
||||
1,
|
||||
"n1fzv4jc7fanl9s0qj02ge2ezk3kts545kjtek47",
|
||||
"178.79.143.65",
|
||||
1789,
|
||||
"4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz",
|
||||
"8ndjk5oZ6HxUZNScLJJ7hk39XtUqGexdKgW7hSX6kpWG",
|
||||
1,
|
||||
"1.10.0"
|
||||
);
|
||||
const l2Mixnode = new WasmMixNode(
|
||||
2,
|
||||
"n1z93z44vf8ssvdhujjvxcj4rd5e3lz0l60wdk70",
|
||||
"109.74.197.180",
|
||||
1789,
|
||||
"7sVjiMrPYZrDWRujku9QLxgE8noT7NTgBAqizCsu7AoK",
|
||||
"GepXwRnKZDd8x2nBWAajGGBVvF3mrpVMQBkgfrGuqRCN",
|
||||
2,
|
||||
"1.10.0"
|
||||
);
|
||||
const l3Mixnode = new WasmMixNode(
|
||||
3,
|
||||
"n1ptg680vnmef2cd8l0s9uyc4f0hgf3x8sed6w77",
|
||||
"176.58.101.80",
|
||||
1789,
|
||||
"FoM5Mx9Pxk1g3zEqkS3APgtBeTtTo3M8k7Yu4bV6kK1R",
|
||||
"DeYjrDC2AcQRVFshiKnbUo6bRvPyZ33QGYR2DLeFJ9qD",
|
||||
3,
|
||||
"1.10.0"
|
||||
);
|
||||
|
||||
const gateway = new WasmGateway(
|
||||
'n16evnn8glr0sham3matj8rg2s24m6x56ayk87ts',
|
||||
'85.159.212.96',
|
||||
1789,
|
||||
9000,
|
||||
'336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9',
|
||||
'BtYjoWihiuFihGKQypmpSspbhmWDPxzqeTVSd8ciCpWL',
|
||||
'1.10.1',
|
||||
);
|
||||
const gateway = new WasmGateway(
|
||||
"n16evnn8glr0sham3matj8rg2s24m6x56ayk87ts",
|
||||
"85.159.212.96",
|
||||
1789,
|
||||
9000,
|
||||
"336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9",
|
||||
"BtYjoWihiuFihGKQypmpSspbhmWDPxzqeTVSd8ciCpWL",
|
||||
"1.10.1"
|
||||
);
|
||||
|
||||
const mixnodes = new Map();
|
||||
mixnodes.set(1, [l1Mixnode]);
|
||||
mixnodes.set(2, [l2Mixnode]);
|
||||
mixnodes.set(3, [l3Mixnode]);
|
||||
const mixnodes = new Map();
|
||||
mixnodes.set(1, [l1Mixnode]);
|
||||
mixnodes.set(2, [l2Mixnode]);
|
||||
mixnodes.set(3, [l3Mixnode]);
|
||||
|
||||
const gateways = [gateway];
|
||||
|
||||
const gateways = [gateway];
|
||||
|
||||
return new WasmNymTopology(mixnodes, gateways)
|
||||
return new WasmNymTopology(mixnodes, gateways);
|
||||
}
|
||||
|
||||
function printAndDisplayTestResult(result) {
|
||||
result.log_details();
|
||||
result.log_details();
|
||||
|
||||
self.postMessage({
|
||||
kind: 'DisplayTesterResults',
|
||||
args: {
|
||||
score: result.score(),
|
||||
sentPackets: result.sent_packets,
|
||||
receivedPackets: result.received_packets,
|
||||
receivedAcks: result.received_acks,
|
||||
duplicatePackets: result.duplicate_packets,
|
||||
duplicateAcks: result.duplicate_acks,
|
||||
},
|
||||
});
|
||||
self.postMessage({
|
||||
kind: "DisplayTesterResults",
|
||||
args: {
|
||||
score: result.score(),
|
||||
sentPackets: result.sent_packets,
|
||||
receivedPackets: result.received_packets,
|
||||
receivedAcks: result.received_acks,
|
||||
duplicatePackets: result.duplicate_packets,
|
||||
duplicateAcks: result.duplicate_acks,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function testWithTester() {
|
||||
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
|
||||
// A) construct with hardcoded topology
|
||||
const topology = dummyTopology();
|
||||
const nodeTester = await new NymNodeTester(topology, preferredGateway);
|
||||
|
||||
// A) construct with hardcoded topology
|
||||
const topology = dummyTopology()
|
||||
const nodeTester = await new NymNodeTester(topology, preferredGateway);
|
||||
// B) first get topology directly from nym-api
|
||||
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||
// const topology = await current_network_topology(validator)
|
||||
// const nodeTester = await new NymNodeTester(topology, preferredGateway);
|
||||
//
|
||||
// C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers)
|
||||
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||
// const nodeTester = await NymNodeTester.new_with_api(validator, preferredGateway)
|
||||
|
||||
// B) first get topology directly from nym-api
|
||||
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||
// const topology = await current_network_topology(validator)
|
||||
// const nodeTester = await new NymNodeTester(topology, preferredGateway);
|
||||
//
|
||||
// C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers)
|
||||
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||
// const nodeTester = await NymNodeTester.new_with_api(validator, preferredGateway)
|
||||
// B) first get topology directly from nym-api
|
||||
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||
// const topology = await current_network_topology(validator)
|
||||
// const nodeTester = await new NymNodeTester(topology, undefined, preferredGateway);
|
||||
//
|
||||
// C) use nym-api in the constructor (note: it does no filtering for 'good' nodes on other layers)
|
||||
// const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||
// const nodeTester = await NymNodeTester.new_with_api(validator, undefined, preferredGateway)
|
||||
// D, E, F) you also don't have to specify the gateway. if you don't, a random one (from your topology) will be used
|
||||
// const topology = dummyTopology()
|
||||
// const nodeTester = await new NymNodeTester(topology);
|
||||
|
||||
// D, E, F) you also don't have to specify the gateway. if you don't, a random one (from your topology) will be used
|
||||
// const topology = dummyTopology()
|
||||
// const nodeTester = await new NymNodeTester(topology);
|
||||
self.onmessage = async (event) => {
|
||||
if (event.data && event.data.kind) {
|
||||
switch (event.data.kind) {
|
||||
case "TestPacket": {
|
||||
const { mixnodeIdentity } = event.data.args;
|
||||
console.log("starting node test...");
|
||||
|
||||
self.onmessage = async event => {
|
||||
if (event.data && event.data.kind) {
|
||||
switch (event.data.kind) {
|
||||
case 'TestPacket': {
|
||||
const {mixnodeIdentity} = event.data.args;
|
||||
console.log("starting node test...");
|
||||
|
||||
let result = await nodeTester.test_node(mixnodeIdentity);
|
||||
printAndDisplayTestResult(result)
|
||||
}
|
||||
}
|
||||
let result = await nodeTester.test_node(mixnodeIdentity);
|
||||
printAndDisplayTestResult(result);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function testerReconnection() {
|
||||
const validator = "https://qwerty-validator-api.qa.nymte.ch/api";
|
||||
const nodeTester = await NymNodeTester.new_with_api(validator);
|
||||
|
||||
self.onmessage = async (event) => {
|
||||
if (event.data && event.data.kind) {
|
||||
switch (event.data.kind) {
|
||||
case "TestPacket": {
|
||||
const { mixnodeIdentity } = event.data.args;
|
||||
console.log("starting node test...");
|
||||
|
||||
let result1 = await nodeTester.test_node(mixnodeIdentity);
|
||||
console.log("sleeping for 5s");
|
||||
await new Promise((r) => setTimeout(r, 5000));
|
||||
await nodeTester.disconnect_from_gateway();
|
||||
|
||||
console.log("sleeping for 5s");
|
||||
await new Promise((r) => setTimeout(r, 5000));
|
||||
|
||||
await nodeTester.reconnect_to_gateway();
|
||||
let result2 = await nodeTester.test_node(mixnodeIdentity);
|
||||
|
||||
printAndDisplayTestResult(result1);
|
||||
printAndDisplayTestResult(result2);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function testWithNymClient() {
|
||||
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
|
||||
const topology = dummyTopology()
|
||||
const topology = dummyTopology();
|
||||
|
||||
let received = 0
|
||||
let received = 0;
|
||||
|
||||
const onMessageHandler = (message) => {
|
||||
received += 1;
|
||||
self.postMessage({
|
||||
kind: 'ReceiveMessage',
|
||||
args: {
|
||||
message,
|
||||
senderTag: undefined,
|
||||
isTestPacket: true,
|
||||
},
|
||||
});
|
||||
|
||||
// it's really up to the user to create proper callback here...
|
||||
console.log(`received ${received} packets so far`)
|
||||
};
|
||||
|
||||
console.log('Instantiating WASM client...');
|
||||
|
||||
let clientBuilder = NymClientBuilder.new_tester(topology, onMessageHandler, preferredGateway)
|
||||
console.log('Web worker creating WASM client...');
|
||||
let local_client = await clientBuilder.start_client();
|
||||
console.log('WASM client running!');
|
||||
|
||||
const selfAddress = local_client.self_address();
|
||||
|
||||
// set the global (I guess we don't have to anymore?)
|
||||
client = local_client;
|
||||
|
||||
console.log(`Client address is ${selfAddress}`);
|
||||
const onMessageHandler = (message) => {
|
||||
received += 1;
|
||||
self.postMessage({
|
||||
kind: 'Ready',
|
||||
args: {
|
||||
selfAddress,
|
||||
},
|
||||
kind: "ReceiveMessage",
|
||||
args: {
|
||||
message,
|
||||
senderTag: undefined,
|
||||
isTestPacket: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Set callback to handle messages passed to the worker.
|
||||
self.onmessage = async event => {
|
||||
console.log(event)
|
||||
if (event.data && event.data.kind) {
|
||||
switch (event.data.kind) {
|
||||
case 'SendMessage': {
|
||||
const {message, recipient} = event.data.args;
|
||||
let uint8Array = new TextEncoder().encode(message);
|
||||
await client.send_regular_message(uint8Array, recipient);
|
||||
break;
|
||||
}
|
||||
case 'TestPacket': {
|
||||
const {mixnodeIdentity} = event.data.args;
|
||||
const req = await client.try_construct_test_packet_request(mixnodeIdentity);
|
||||
await client.change_hardcoded_topology(req.injectable_topology());
|
||||
await client.try_send_test_packets(req);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// it's really up to the user to create proper callback here...
|
||||
console.log(`received ${received} packets so far`);
|
||||
};
|
||||
|
||||
console.log("Instantiating WASM client...");
|
||||
|
||||
let clientBuilder = NymClientBuilder.new_tester(
|
||||
topology,
|
||||
onMessageHandler,
|
||||
preferredGateway
|
||||
);
|
||||
|
||||
console.log("Web worker creating WASM client...");
|
||||
let local_client = await clientBuilder.start_client();
|
||||
console.log("WASM client running!");
|
||||
|
||||
const selfAddress = local_client.self_address();
|
||||
|
||||
// set the global (I guess we don't have to anymore?)
|
||||
client = local_client;
|
||||
|
||||
console.log(`Client address is ${selfAddress}`);
|
||||
self.postMessage({
|
||||
kind: "Ready",
|
||||
args: {
|
||||
selfAddress,
|
||||
},
|
||||
});
|
||||
|
||||
// Set callback to handle messages passed to the worker.
|
||||
self.onmessage = async (event) => {
|
||||
console.log(event);
|
||||
if (event.data && event.data.kind) {
|
||||
switch (event.data.kind) {
|
||||
case "SendMessage": {
|
||||
const { message, recipient } = event.data.args;
|
||||
let uint8Array = new TextEncoder().encode(message);
|
||||
await client.send_regular_message(uint8Array, recipient);
|
||||
break;
|
||||
}
|
||||
};
|
||||
case "TestPacket": {
|
||||
const { mixnodeIdentity } = event.data.args;
|
||||
const req = await client.try_construct_test_packet_request(
|
||||
mixnodeIdentity
|
||||
);
|
||||
await client.change_hardcoded_topology(req.injectable_topology());
|
||||
await client.try_send_test_packets(req);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function normalNymClientUsage() {
|
||||
self.postMessage({kind: 'DisableMagicTestButton'});
|
||||
self.postMessage({ kind: "DisableMagicTestButton" });
|
||||
|
||||
// only really useful if you want to adjust some settings like traffic rate
|
||||
// (if not needed you can just pass a null)
|
||||
const debug = default_debug();
|
||||
// only really useful if you want to adjust some settings like traffic rate
|
||||
// (if not needed you can just pass a null)
|
||||
const debug = default_debug();
|
||||
|
||||
debug.disable_main_poisson_packet_distribution = true;
|
||||
debug.disable_loop_cover_traffic_stream = true;
|
||||
debug.use_extended_packet_size = false;
|
||||
// debug.average_packet_delay_ms = BigInt(10);
|
||||
// debug.average_ack_delay_ms = BigInt(10);
|
||||
// debug.ack_wait_addition_ms = BigInt(3000);
|
||||
// debug.ack_wait_multiplier = 10;
|
||||
debug.disable_main_poisson_packet_distribution = true;
|
||||
debug.disable_loop_cover_traffic_stream = true;
|
||||
debug.use_extended_packet_size = false;
|
||||
// debug.average_packet_delay_ms = BigInt(10);
|
||||
// debug.average_ack_delay_ms = BigInt(10);
|
||||
// debug.ack_wait_addition_ms = BigInt(3000);
|
||||
// debug.ack_wait_multiplier = 10;
|
||||
|
||||
debug.topology_refresh_rate_ms = BigInt(60000)
|
||||
debug.topology_refresh_rate_ms = BigInt(60000);
|
||||
|
||||
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
|
||||
const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||
const preferredGateway = "336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9";
|
||||
const validator = "https://qwerty-validator-api.qa.nymte.ch/api";
|
||||
|
||||
const config = new Config('my-awesome-wasm-client', validator, debug);
|
||||
const config = new Config("my-awesome-wasm-client", validator, debug);
|
||||
|
||||
const onMessageHandler = (message) => {
|
||||
console.log(message);
|
||||
self.postMessage({
|
||||
kind: 'ReceiveMessage',
|
||||
args: {
|
||||
message,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
console.log('Instantiating WASM client...');
|
||||
|
||||
let localClient = await new NymClient(config, onMessageHandler)
|
||||
console.log('WASM client running!');
|
||||
|
||||
const selfAddress = localClient.self_address();
|
||||
|
||||
// set the global (I guess we don't have to anymore?)
|
||||
client = localClient;
|
||||
|
||||
console.log(`Client address is ${selfAddress}`);
|
||||
const onMessageHandler = (message) => {
|
||||
console.log(message);
|
||||
self.postMessage({
|
||||
kind: 'Ready',
|
||||
args: {
|
||||
selfAddress,
|
||||
},
|
||||
kind: "ReceiveMessage",
|
||||
args: {
|
||||
message,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Set callback to handle messages passed to the worker.
|
||||
self.onmessage = async event => {
|
||||
console.log(event)
|
||||
if (event.data && event.data.kind) {
|
||||
switch (event.data.kind) {
|
||||
case 'SendMessage': {
|
||||
const {message, recipient} = event.data.args;
|
||||
let uint8Array = new TextEncoder().encode(message);
|
||||
await client.send_regular_message(uint8Array, recipient);
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log("Instantiating WASM client...");
|
||||
|
||||
let localClient = await new NymClient(config, onMessageHandler);
|
||||
console.log("WASM client running!");
|
||||
|
||||
const selfAddress = localClient.self_address();
|
||||
|
||||
// set the global (I guess we don't have to anymore?)
|
||||
client = localClient;
|
||||
|
||||
console.log(`Client address is ${selfAddress}`);
|
||||
self.postMessage({
|
||||
kind: "Ready",
|
||||
args: {
|
||||
selfAddress,
|
||||
},
|
||||
});
|
||||
|
||||
// Set callback to handle messages passed to the worker.
|
||||
self.onmessage = async (event) => {
|
||||
console.log(event);
|
||||
if (event.data && event.data.kind) {
|
||||
switch (event.data.kind) {
|
||||
case "SendMessage": {
|
||||
const { message, recipient } = event.data.args;
|
||||
let uint8Array = new TextEncoder().encode(message);
|
||||
await client.send_regular_message(uint8Array, recipient);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function messWithStorage() {
|
||||
self.onmessage = async event => {
|
||||
if (event.data && event.data.kind) {
|
||||
switch (event.data.kind) {
|
||||
case 'TestPacket': {
|
||||
const { mixnodeIdentity } = event.data.args;
|
||||
console.log("button clicked...", mixnodeIdentity);
|
||||
self.onmessage = async (event) => {
|
||||
if (event.data && event.data.kind) {
|
||||
switch (event.data.kind) {
|
||||
case "TestPacket": {
|
||||
const { mixnodeIdentity } = event.data.args;
|
||||
console.log("button clicked...", mixnodeIdentity);
|
||||
|
||||
let id1 = "one";
|
||||
let id2 = "two";
|
||||
let id1 = "one";
|
||||
let id2 = "two";
|
||||
|
||||
console.log("making store1 NO-ENC");
|
||||
let _storage1 = await ClientStorage.new_unencrypted(id1);
|
||||
console.log("making store1 NO-ENC");
|
||||
let _storage1 = await ClientStorage.new_unencrypted(id1);
|
||||
|
||||
console.log("making store2 ENC")
|
||||
let _storage2 = await new ClientStorage(id2, "my-secret-password");
|
||||
//
|
||||
//
|
||||
//
|
||||
// console.log("attempting to use store1 WITH PASSWORD")
|
||||
// let _storage1_alt = await new ClientStorage(id1, "password");
|
||||
//
|
||||
//
|
||||
//
|
||||
// console.log("attempting to use store2 WITHOUT PASSWORD")
|
||||
// let _storage2_alt = await ClientStorage.new_unencrypted(id2);
|
||||
//
|
||||
//
|
||||
//
|
||||
// console.log("attempting to use store2 with WRONG PASSWORD")
|
||||
// let _storage2_bad = await new ClientStorage(id2, "bad-password")
|
||||
console.log("making store2 ENC");
|
||||
let _storage2 = await new ClientStorage(id2, "my-secret-password");
|
||||
//
|
||||
//
|
||||
//
|
||||
// console.log("attempting to use store1 WITH PASSWORD")
|
||||
// let _storage1_alt = await new ClientStorage(id1, "password");
|
||||
//
|
||||
//
|
||||
//
|
||||
// console.log("attempting to use store2 WITHOUT PASSWORD")
|
||||
// let _storage2_alt = await ClientStorage.new_unencrypted(id2);
|
||||
//
|
||||
//
|
||||
//
|
||||
// console.log("attempting to use store2 with WRONG PASSWORD")
|
||||
// let _storage2_bad = await new ClientStorage(id2, "bad-password")
|
||||
|
||||
|
||||
//
|
||||
// console.log("read1: ", await storage1.read());
|
||||
// console.log("read2: ", await storage2.read());
|
||||
//
|
||||
// console.log("store1: ", await storage1.store("FOOMP"));
|
||||
//
|
||||
// console.log("read1: ", await storage1.read());
|
||||
// console.log("read2: ", await storage2.read());
|
||||
}
|
||||
}
|
||||
//
|
||||
// console.log("read1: ", await storage1.read());
|
||||
// console.log("read2: ", await storage2.read());
|
||||
//
|
||||
// console.log("store1: ", await storage1.store("FOOMP"));
|
||||
//
|
||||
// console.log("read1: ", await storage1.read());
|
||||
// console.log("read2: ", await storage2.read());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// load WASM package
|
||||
await wasm_bindgen('nym_client_wasm_bg.wasm');
|
||||
console.log('Loaded WASM');
|
||||
// load WASM package
|
||||
await wasm_bindgen("nym_client_wasm_bg.wasm");
|
||||
console.log("Loaded WASM");
|
||||
|
||||
// sets up better stack traces in case of in-rust panics
|
||||
set_panic_hook();
|
||||
// sets up better stack traces in case of in-rust panics
|
||||
set_panic_hook();
|
||||
|
||||
// run test on simplified and dedicated tester:
|
||||
// await testWithTester()
|
||||
// show reconnection capabilities
|
||||
// await testerReconnection()
|
||||
|
||||
// hook-up the whole client for testing
|
||||
// await testWithNymClient()
|
||||
// run test on simplified and dedicated tester:
|
||||
await testWithTester();
|
||||
|
||||
// 'Normal' client setup (to send 'normal' messages)
|
||||
await normalNymClientUsage()
|
||||
// 'Normal' client setup (to send 'normal' messages)
|
||||
// await normalNymClientUsage()
|
||||
}
|
||||
|
||||
// Let's get started!
|
||||
main();
|
||||
main();
|
||||
|
||||
@@ -29,7 +29,7 @@ impl Config {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(id: String, validator_server: String, debug: Option<DebugWasm>) -> Self {
|
||||
Config {
|
||||
base: BaseClientConfig::new(id)
|
||||
base: BaseClientConfig::new(id, env!("CARGO_PKG_VERSION").to_string())
|
||||
.with_custom_nyxd(vec![validator_server
|
||||
.parse()
|
||||
.expect("provided url was malformed")])
|
||||
@@ -39,7 +39,7 @@ impl Config {
|
||||
|
||||
pub(crate) fn new_tester_config<S: Into<String>>(id: S) -> Self {
|
||||
Config {
|
||||
base: BaseClientConfig::new(id)
|
||||
base: BaseClientConfig::new(id.into(), env!("CARGO_PKG_VERSION").to_string())
|
||||
.with_disabled_credentials(true)
|
||||
.with_disabled_cover_traffic(true)
|
||||
.with_disabled_topology_refresh(true),
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
// due to expansion of #[wasm_bindgen] macro on NodeTestResult
|
||||
#![allow(clippy::drop_non_drop)]
|
||||
|
||||
use crate::error::WasmClientError;
|
||||
use crate::tester::LockedGatewayClient;
|
||||
use js_sys::Promise;
|
||||
use nym_node_tester_utils::processor::Received;
|
||||
use nym_node_tester_utils::receiver::ReceivedReceiver;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -12,6 +15,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{Mutex as AsyncMutex, MutexGuard as AsyncMutexGuard};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
use wasm_utils::{console_log, console_warn};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -107,3 +111,37 @@ impl Drop for TestMarker {
|
||||
self.value.store(false, Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait GatewayReconnection {
|
||||
fn disconnect_from_gateway(&self) -> Promise;
|
||||
|
||||
fn reconnect_to_gateway(&self) -> Promise;
|
||||
}
|
||||
|
||||
impl GatewayReconnection for LockedGatewayClient {
|
||||
fn disconnect_from_gateway(&self) -> Promise {
|
||||
let this = self.clone();
|
||||
|
||||
future_to_promise(async move {
|
||||
let mut guard = this.lock().await;
|
||||
guard
|
||||
.disconnect()
|
||||
.await
|
||||
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
|
||||
Ok(JsValue::undefined())
|
||||
})
|
||||
}
|
||||
|
||||
fn reconnect_to_gateway(&self) -> Promise {
|
||||
let this = self.clone();
|
||||
|
||||
future_to_promise(async move {
|
||||
let mut guard = this.lock().await;
|
||||
guard
|
||||
.try_reconnect()
|
||||
.await
|
||||
.map_err(|err| JsValue::from(WasmClientError::from(err)))?;
|
||||
Ok(JsValue::undefined())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::helpers::{current_network_topology_async, setup_from_topology};
|
||||
use crate::storage::ClientStorage;
|
||||
use crate::tester::ephemeral_receiver::EphemeralTestReceiver;
|
||||
use crate::tester::helpers::{
|
||||
NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt,
|
||||
GatewayReconnection, NodeTestResult, ReceivedReceiverWrapper, TestMarker, WasmTestMessageExt,
|
||||
};
|
||||
use crate::topology::WasmNymTopology;
|
||||
use futures::channel::mpsc;
|
||||
@@ -73,6 +73,7 @@ pub struct NymNodeTester {
|
||||
#[wasm_bindgen]
|
||||
pub struct NymNodeTesterBuilder {
|
||||
gateway: Option<IdentityKey>,
|
||||
id: Option<String>,
|
||||
|
||||
base_topology: NymTopology,
|
||||
|
||||
@@ -94,9 +95,11 @@ impl NymNodeTesterBuilder {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(
|
||||
base_topology: WasmNymTopology,
|
||||
id: Option<String>,
|
||||
gateway: Option<IdentityKey>,
|
||||
) -> NymNodeTesterBuilder {
|
||||
NymNodeTesterBuilder {
|
||||
id,
|
||||
gateway,
|
||||
base_topology: base_topology.into(),
|
||||
bandwidth_controller: None,
|
||||
@@ -105,15 +108,20 @@ impl NymNodeTesterBuilder {
|
||||
|
||||
async fn _new_with_api(
|
||||
api_url: String,
|
||||
id: Option<String>,
|
||||
gateway: Option<IdentityKey>,
|
||||
) -> Result<Self, WasmClientError> {
|
||||
let topology = current_network_topology_async(api_url).await?;
|
||||
Ok(NymNodeTesterBuilder::new(topology, gateway))
|
||||
Ok(NymNodeTesterBuilder::new(topology, id, gateway))
|
||||
}
|
||||
|
||||
pub fn new_with_api(gateway: Option<IdentityKey>, api_url: String) -> Promise {
|
||||
pub fn new_with_api(
|
||||
api_url: String,
|
||||
id: Option<String>,
|
||||
gateway: Option<IdentityKey>,
|
||||
) -> Promise {
|
||||
future_to_promise(async move {
|
||||
Self::_new_with_api(api_url, gateway)
|
||||
Self::_new_with_api(api_url, id, gateway)
|
||||
.await
|
||||
.into_promise_result()
|
||||
})
|
||||
@@ -133,7 +141,13 @@ impl NymNodeTesterBuilder {
|
||||
async fn _setup_client(mut self) -> Result<NymNodeTester, WasmClientError> {
|
||||
let task_manager = TaskManager::default();
|
||||
|
||||
let client_store = ClientStorage::new_async(NODE_TESTER_ID, None).await?;
|
||||
let storage_id = if let Some(client_id) = &self.id {
|
||||
format!("{NODE_TESTER_ID}-{client_id}")
|
||||
} else {
|
||||
NODE_TESTER_ID.to_owned()
|
||||
};
|
||||
|
||||
let client_store = ClientStorage::new_async(&storage_id, None).await?;
|
||||
|
||||
let init_details = self.gateway_info(&client_store).await?;
|
||||
let gateway_endpoint = init_details.gateway_details;
|
||||
@@ -234,29 +248,46 @@ async fn test_mixnode(
|
||||
impl NymNodeTester {
|
||||
#[wasm_bindgen(constructor)]
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(topology: WasmNymTopology, gateway: Option<IdentityKey>) -> Promise {
|
||||
pub fn new(
|
||||
topology: WasmNymTopology,
|
||||
id: Option<String>,
|
||||
gateway: Option<IdentityKey>,
|
||||
) -> Promise {
|
||||
console_log!("constructing node tester!");
|
||||
NymNodeTesterBuilder::new(topology, gateway).setup_client()
|
||||
NymNodeTesterBuilder::new(topology, id, gateway).setup_client()
|
||||
}
|
||||
|
||||
async fn _new_with_api(
|
||||
api_url: String,
|
||||
id: Option<String>,
|
||||
gateway: Option<IdentityKey>,
|
||||
) -> Result<Self, WasmClientError> {
|
||||
NymNodeTesterBuilder::_new_with_api(api_url, gateway)
|
||||
NymNodeTesterBuilder::_new_with_api(api_url, id, gateway)
|
||||
.await?
|
||||
._setup_client()
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn new_with_api(api_url: String, gateway: Option<IdentityKey>) -> Promise {
|
||||
pub fn new_with_api(
|
||||
api_url: String,
|
||||
id: Option<String>,
|
||||
gateway: Option<IdentityKey>,
|
||||
) -> Promise {
|
||||
future_to_promise(async move {
|
||||
Self::_new_with_api(api_url, gateway)
|
||||
Self::_new_with_api(api_url, id, gateway)
|
||||
.await
|
||||
.into_promise_result()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn disconnect_from_gateway(&self) -> Promise {
|
||||
self.gateway_client.disconnect_from_gateway()
|
||||
}
|
||||
|
||||
pub fn reconnect_to_gateway(&self) -> Promise {
|
||||
self.gateway_client.reconnect_to_gateway()
|
||||
}
|
||||
|
||||
fn prepare_test_packets(
|
||||
&self,
|
||||
mixnode_identity: String,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-bin-common"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
description = "Common code for nym binaries"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client-core"
|
||||
version = "1.1.14"
|
||||
version = "1.1.15"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.66"
|
||||
|
||||
@@ -10,7 +10,7 @@ use log::*;
|
||||
use nym_sphinx::acknowledgements::AckKey;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::cover::generate_loop_cover_packet;
|
||||
use nym_sphinx::params::PacketSize;
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use nym_sphinx::utils::sample_poisson_duration;
|
||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||
use std::pin::Pin;
|
||||
@@ -63,6 +63,8 @@ where
|
||||
|
||||
/// Optional secondary predefined packet size used for the loop cover messages.
|
||||
secondary_packet_size: Option<PacketSize>,
|
||||
|
||||
packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl<R> Stream for LoopCoverTrafficStream<R>
|
||||
@@ -135,6 +137,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
topology_access,
|
||||
primary_packet_size: traffic_config.primary_packet_size,
|
||||
secondary_packet_size: traffic_config.secondary_packet_size,
|
||||
packet_type: traffic_config.packet_type,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +197,7 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
self.average_ack_delay,
|
||||
self.cover_traffic.loop_cover_traffic_average_delay,
|
||||
cover_traffic_packet_size,
|
||||
nym_sphinx::params::PacketType::Mix,
|
||||
self.packet_type,
|
||||
)
|
||||
.expect("Somehow failed to generate a loop cover message with a valid topology");
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver};
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{
|
||||
|
||||
@@ -66,9 +66,9 @@ pub struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new<S: Into<String>>(id: S) -> Self {
|
||||
pub fn new<S: Into<String>>(id: S, version: S) -> Self {
|
||||
Config {
|
||||
client: Client::new_default(id),
|
||||
client: Client::new_default(id, version),
|
||||
debug: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -137,14 +137,46 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_no_cover_traffic(&mut self) {
|
||||
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
|
||||
self.debug.traffic.disable_main_poisson_packet_distribution = true;
|
||||
}
|
||||
|
||||
pub fn with_disabled_cover_traffic_with_keepalive(mut self, disabled: bool) -> Self {
|
||||
if disabled {
|
||||
self.set_no_cover_traffic_with_keepalive()
|
||||
}
|
||||
self
|
||||
}
|
||||
pub fn set_no_cover_traffic_with_keepalive(&mut self) {
|
||||
self.debug.traffic.disable_main_poisson_packet_distribution = true;
|
||||
self.debug.cover_traffic.loop_cover_traffic_average_delay = Duration::from_secs(5);
|
||||
}
|
||||
|
||||
pub fn with_disabled_topology_refresh(mut self, disable_topology_refresh: bool) -> Self {
|
||||
self.debug.topology.disable_refreshing = disable_topology_refresh;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_no_cover_traffic(&mut self) {
|
||||
self.debug.cover_traffic.disable_loop_cover_traffic_stream = true;
|
||||
self.debug.traffic.disable_main_poisson_packet_distribution = true;
|
||||
pub fn with_no_per_hop_delays(mut self, no_per_hop_delays: bool) -> Self {
|
||||
if no_per_hop_delays {
|
||||
self.set_no_per_hop_delays()
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_no_per_hop_delays(&mut self) {
|
||||
self.debug.traffic.average_packet_delay = Duration::ZERO;
|
||||
self.debug.acknowledgements.average_ack_delay = Duration::ZERO;
|
||||
}
|
||||
|
||||
pub fn with_secondary_packet_size(mut self, secondary_packet_size: Option<PacketSize>) -> Self {
|
||||
self.set_secondary_packet_size(secondary_packet_size);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_secondary_packet_size(&mut self, secondary_packet_size: Option<PacketSize>) {
|
||||
self.debug.traffic.secondary_packet_size = secondary_packet_size;
|
||||
}
|
||||
|
||||
pub fn set_custom_version(&mut self, version: &str) {
|
||||
@@ -243,7 +275,7 @@ pub struct Client {
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new_default<S: Into<String>>(id: S) -> Self {
|
||||
pub fn new_default<S: Into<String>>(id: S, version: S) -> Self {
|
||||
let network = NymNetworkDetails::new_mainnet();
|
||||
let nyxd_urls = network
|
||||
.endpoints
|
||||
@@ -257,7 +289,7 @@ impl Client {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Client {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
version: version.into(),
|
||||
id: id.into(),
|
||||
disabled_credentials_mode: true,
|
||||
nyxd_urls,
|
||||
@@ -282,7 +314,7 @@ impl Client {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct Traffic {
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// sent packet is going to be delayed at any given mix node.
|
||||
|
||||
@@ -724,7 +724,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn try_reconnect(&mut self) -> Result<(), GatewayClientError> {
|
||||
pub async fn try_reconnect(&mut self) -> Result<(), GatewayClientError> {
|
||||
if !self.connection.is_established() {
|
||||
self.establish_connection().await?;
|
||||
}
|
||||
@@ -738,6 +738,12 @@ impl<C, St> GatewayClient<C, St> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn disconnect(&mut self) -> Result<(), GatewayClientError> {
|
||||
self.recover_socket_connection().await?;
|
||||
self.connection = SocketState::NotConnected;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn authenticate_and_start(&mut self) -> Result<Arc<SharedKeys>, GatewayClientError>
|
||||
where
|
||||
C: DkgQueryClient + Send + Sync,
|
||||
|
||||
@@ -48,17 +48,21 @@ impl PacketRouter {
|
||||
// data he takes the SURB-ACK and first hop address.
|
||||
// currently SURB-ACKs are attached in EVERY packet, even cover, so this is always true
|
||||
let ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
|
||||
let outfox_ack_overhead =
|
||||
PacketSize::OutfoxAckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
|
||||
|
||||
for received_packet in unwrapped_packets {
|
||||
if received_packet.len() == PacketSize::AckPacket.plaintext_size()
|
||||
|| received_packet.len() == PacketSize::OutfoxAckPacket.plaintext_size()
|
||||
// we don't know the real size of the payload, it could be anything <= 48 bytes
|
||||
|| received_packet.len() <= PacketSize::OutfoxAckPacket.plaintext_size()
|
||||
{
|
||||
received_acks.push(received_packet);
|
||||
} else if received_packet.len()
|
||||
== PacketSize::RegularPacket.plaintext_size() - ack_overhead
|
||||
|| received_packet.len()
|
||||
== PacketSize::OutfoxRegularPacket.plaintext_size() - ack_overhead
|
||||
|| received_packet.len() == PacketSize::OutfoxRegularPacket.size() - 6
|
||||
== PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead
|
||||
|| received_packet.len()
|
||||
== PacketSize::OutfoxRegularPacket.size() - outfox_ack_overhead
|
||||
{
|
||||
trace!("routing regular packet");
|
||||
received_messages.push(received_packet);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
|
||||
@@ -29,6 +29,7 @@ log = { workspace = true }
|
||||
url = { version = "2.2", features = ["serde"] }
|
||||
tokio = { version = "1.24.1", features = ["sync", "time"] }
|
||||
futures = "0.3"
|
||||
openssl = { version = "0.10", features = ["vendored"], optional = true }
|
||||
|
||||
nym-coconut-interface = { path = "../../coconut-interface" }
|
||||
nym-network-defaults = { path = "../../network-defaults" }
|
||||
@@ -77,13 +78,14 @@ required-features = ["nyxd-client"]
|
||||
nyxd-client = [
|
||||
"async-trait",
|
||||
"cosmrs",
|
||||
"cosmwasm-std",
|
||||
"cw3",
|
||||
"cw4",
|
||||
"prost",
|
||||
"flate2",
|
||||
"sha2",
|
||||
"itertools",
|
||||
"cosmwasm-std",
|
||||
"openssl",
|
||||
"prost",
|
||||
"sha2",
|
||||
"signing"
|
||||
]
|
||||
signing = [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{nym_api, ValidatorClientError};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nym_api::error::NymAPIError;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// TODO: There's a significant argument to pull those out of the package and make a PR on https://github.com/cosmos/cosmos-rust/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// TODO: expose query-related capabilities to wasm client...
|
||||
|
||||
@@ -41,3 +41,6 @@ nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-co
|
||||
nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" }
|
||||
nym-name-service-common = { path = "../cosmwasm-smart-contracts/name-service" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
|
||||
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
|
||||
nym-types = { path = "../../common/types" }
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_types::helpers::ConsoleSigningOutput;
|
||||
use nym_validator_client::nyxd::error::NyxdError;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
pub struct MixnetOperatorsIdentityKey {
|
||||
#[clap(subcommand)]
|
||||
pub command: MixnetOperatorsIdentityKeyCommands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsIdentityKeyCommands {
|
||||
/// Register a name alias for a nym address
|
||||
Sign(SignArgs),
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct SignArgs {
|
||||
/// Path to private identity key (example: private_identity_key.pem)
|
||||
#[clap(long)]
|
||||
private_key: PathBuf,
|
||||
|
||||
/// Base58 encoded message to sign
|
||||
#[clap(long)]
|
||||
base58_msg: String,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
}
|
||||
|
||||
pub async fn sign(args: SignArgs) -> Result<(), NyxdError> {
|
||||
eprintln!(">>> loading: {}", args.private_key.display());
|
||||
let private_identity_key: identity::PrivateKey =
|
||||
nym_pemstore::load_key(args.private_key).expect("failed to load key");
|
||||
|
||||
print_signed_msg(&private_identity_key, &args.base58_msg, args.output);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_signed_msg(private_key: &identity::PrivateKey, raw_msg: &str, output: OutputFormat) {
|
||||
let trimmed = raw_msg.trim();
|
||||
eprintln!(">>> attempting to sign: {trimmed}");
|
||||
|
||||
let Ok(decoded) = bs58::decode(trimmed).into_vec() else {
|
||||
println!("failed to base58 decode the message, did you copy it correctly?");
|
||||
return;
|
||||
};
|
||||
|
||||
eprintln!(">>> decoding the message...");
|
||||
|
||||
// we don't really care about what particular information is embedded inside of it,
|
||||
// we just want to know if user correctly copied the string, i.e. whether it's a valid bs58 encoded json
|
||||
if serde_json::from_slice::<serde_json::Value>(&decoded).is_err() {
|
||||
println!("failed to parse the message after decoding, did you copy it correctly?");
|
||||
return;
|
||||
};
|
||||
|
||||
// if this is a valid json, it MUST be a valid string
|
||||
let decoded_string = String::from_utf8(decoded.clone()).unwrap();
|
||||
let signature = private_key.sign(&decoded).to_base58_string();
|
||||
|
||||
let sign_output = ConsoleSigningOutput::new(decoded_string, signature);
|
||||
println!("{}", output.format(&sign_output));
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod gateway;
|
||||
pub mod identity_key;
|
||||
pub mod mixnode;
|
||||
pub mod name;
|
||||
pub mod service;
|
||||
@@ -26,4 +27,6 @@ pub enum MixnetOperatorsCommands {
|
||||
ServiceProvider(service::MixnetOperatorsService),
|
||||
/// Manage your registered name
|
||||
Name(name::MixnetOperatorsName),
|
||||
/// Sign messages using your private identity key
|
||||
IdentityKey(identity_key::MixnetOperatorsIdentityKey),
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// due to code generated by JsonSchema
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// due to code generated by JsonSchema
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// due to code generated by JsonSchema
|
||||
@@ -88,7 +88,7 @@ impl MixNodeDetails {
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct MixNodeRewarding {
|
||||
/// Information provided by the operator that influence the cost function.
|
||||
/// Information provided by the operator that influence the cost function.
|
||||
pub cost_params: MixNodeCostParams,
|
||||
|
||||
/// Total pledge and compounded reward earned by the node operator.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::delegation::OwnerProxySubKey;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::MixnetContractError;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use nym_sphinx_acknowledgements::surb_ack::SurbAckRecoveryError;
|
||||
use nym_sphinx_addressing::nodes::NymNodeRoutingAddressError;
|
||||
use nym_sphinx_types::{NymPacketError, SphinxError};
|
||||
use nym_sphinx_types::{NymPacketError, OutfoxError, SphinxError};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -25,4 +25,7 @@ pub enum MixProcessingError {
|
||||
|
||||
#[error("the received packet was set to use the very old and very much deprecated 'VPN' mode")]
|
||||
ReceivedOldTypeVpnPacket,
|
||||
|
||||
#[error("failed to process received outfox packet: {0}")]
|
||||
OutfoxProcessingError(#[from] OutfoxError),
|
||||
}
|
||||
|
||||
@@ -20,12 +20,14 @@ use tracing::instrument;
|
||||
|
||||
type ForwardAck = MixPacket;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProcessedFinalHop {
|
||||
pub destination: DestinationAddressBytes,
|
||||
pub forward_ack: Option<ForwardAck>,
|
||||
pub message: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MixProcessingResult {
|
||||
/// Contains unwrapped data that should first get delayed before being sent to next hop.
|
||||
ForwardHop(MixPacket, Option<SphinxDelay>),
|
||||
@@ -141,7 +143,7 @@ impl SphinxPacketProcessor {
|
||||
match SurbAck::try_recover_first_hop_packet(&ack_data, packet_type) {
|
||||
Ok((first_hop, packet)) => (first_hop, packet),
|
||||
Err(err) => {
|
||||
debug!("Failed to recover first hop from ack data: {err}");
|
||||
info!("Failed to recover first hop from ack data: {err}");
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
@@ -205,7 +207,7 @@ impl SphinxPacketProcessor {
|
||||
if packet.is_final_hop() {
|
||||
self.process_final_hop(
|
||||
DestinationAddressBytes::from_bytes(next_address),
|
||||
packet.recover_plaintext().to_vec(),
|
||||
packet.recover_plaintext()?.to_vec(),
|
||||
packet_size,
|
||||
packet_type,
|
||||
)
|
||||
@@ -239,7 +241,14 @@ impl SphinxPacketProcessor {
|
||||
|
||||
// for forward packets, extract next hop and set delay (but do NOT delay here)
|
||||
// for final packets, extract SURBAck
|
||||
self.perform_final_processing(processed_packet, packet_size, packet_type)
|
||||
let final_processing_result =
|
||||
self.perform_final_processing(processed_packet, packet_size, packet_type);
|
||||
|
||||
if final_processing_result.is_err() {
|
||||
error!("{:?}", final_processing_result)
|
||||
}
|
||||
|
||||
final_processing_result
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,12 @@ thiserror = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
|
||||
nym-pemstore = { path = "../../pemstore" }
|
||||
nym-sphinx-addressing = { path = "../addressing" }
|
||||
nym-sphinx-params = { path = "../params" }
|
||||
nym-sphinx-routing = { path = "../routing" }
|
||||
nym-sphinx-types = { path = "../types" }
|
||||
nym-pemstore = { path = "../../pemstore" }
|
||||
nym-topology = { path = "../../topology" }
|
||||
|
||||
[features]
|
||||
serde = ["serde_crate", "generic-array"]
|
||||
serde = ["serde_crate", "generic-array"]
|
||||
|
||||
@@ -9,7 +9,7 @@ use nym_sphinx_addressing::nodes::{
|
||||
};
|
||||
use nym_sphinx_params::packet_sizes::PacketSize;
|
||||
use nym_sphinx_params::{PacketType, DEFAULT_NUM_MIX_HOPS};
|
||||
use nym_sphinx_types::delays::{self, Delay};
|
||||
use nym_sphinx_types::delays::Delay;
|
||||
use nym_sphinx_types::{NymPacket, NymPacketError, MIN_PACKET_SIZE};
|
||||
use nym_topology::{NymTopology, NymTopologyError};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
@@ -51,7 +51,7 @@ impl SurbAck {
|
||||
{
|
||||
let route =
|
||||
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?;
|
||||
let delays = delays::generate_from_average_duration(route.len(), average_delay);
|
||||
let delays = nym_sphinx_routing::generate_hop_delays(average_delay, route.len());
|
||||
let destination = recipient.as_sphinx_destination();
|
||||
|
||||
let surb_ack_payload = prepare_identifier(rng, ack_key, marshaled_fragment_id);
|
||||
|
||||
@@ -16,6 +16,7 @@ thiserror = "1"
|
||||
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
|
||||
nym-sphinx-addressing = { path = "../addressing" }
|
||||
nym-sphinx-params = { path = "../params" }
|
||||
nym-sphinx-routing = { path = "../routing" }
|
||||
nym-sphinx-types = { path = "../types" }
|
||||
nym-topology = { path = "../../topology" }
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::encryption_key::{SurbEncryptionKey, SurbEncryptionKeyError, SurbEncryptionKeySize};
|
||||
@@ -7,7 +7,7 @@ use nym_sphinx_addressing::clients::Recipient;
|
||||
use nym_sphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN};
|
||||
use nym_sphinx_params::packet_sizes::PacketSize;
|
||||
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
|
||||
use nym_sphinx_types::{delays, NymPacket, SURBMaterial, SphinxError, SURB};
|
||||
use nym_sphinx_types::{NymPacket, SURBMaterial, SphinxError, SURB};
|
||||
use nym_topology::{NymTopology, NymTopologyError};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use serde::de::{Error as SerdeError, Visitor};
|
||||
@@ -96,7 +96,7 @@ impl ReplySurb {
|
||||
{
|
||||
let route =
|
||||
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, recipient.gateway())?;
|
||||
let delays = delays::generate_from_average_duration(route.len(), average_delay);
|
||||
let delays = nym_sphinx_routing::generate_hop_delays(average_delay, route.len());
|
||||
let destination = recipient.as_sphinx_destination();
|
||||
|
||||
let surb_material = SURBMaterial::new(route, delays, destination);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::ChunkingError;
|
||||
|
||||
@@ -15,7 +15,8 @@ nym-crypto = { path = "../../crypto" }
|
||||
nym-sphinx-acknowledgements = { path = "../acknowledgements" }
|
||||
nym-sphinx-addressing = { path = "../addressing" }
|
||||
nym-sphinx-chunking = { path = "../chunking" }
|
||||
nym-sphinx-params = { path = "../params" }
|
||||
nym-sphinx-forwarding = { path = "../forwarding" }
|
||||
nym-sphinx-params = { path = "../params" }
|
||||
nym-sphinx-routing = { path = "../routing" }
|
||||
nym-sphinx-types = { path = "../types" }
|
||||
nym-topology = { path = "../../topology" }
|
||||
|
||||
@@ -13,7 +13,7 @@ use nym_sphinx_params::packet_sizes::PacketSize;
|
||||
use nym_sphinx_params::{
|
||||
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, PacketType, DEFAULT_NUM_MIX_HOPS,
|
||||
};
|
||||
use nym_sphinx_types::{delays, NymPacket};
|
||||
use nym_sphinx_types::NymPacket;
|
||||
use nym_topology::{NymTopology, NymTopologyError};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::convert::TryFrom;
|
||||
@@ -91,6 +91,7 @@ where
|
||||
>(rng, full_address.encryption_key());
|
||||
|
||||
let public_key_bytes = ephemeral_keypair.public_key().to_bytes();
|
||||
|
||||
let cover_size = packet_size.plaintext_size() - public_key_bytes.len() - ack_bytes.len();
|
||||
|
||||
let mut cover_content: Vec<_> = LOOP_COVER_MESSAGE_PAYLOAD
|
||||
@@ -119,22 +120,38 @@ where
|
||||
|
||||
let route =
|
||||
topology.random_route_to_gateway(rng, DEFAULT_NUM_MIX_HOPS, full_address.gateway())?;
|
||||
let delays = delays::generate_from_average_duration(route.len(), average_packet_delay);
|
||||
let delays = nym_sphinx_routing::generate_hop_delays(average_packet_delay, route.len());
|
||||
let destination = full_address.as_sphinx_destination();
|
||||
|
||||
// once merged, that's an easy rng injection point for sphinx packets : )
|
||||
let packet = NymPacket::sphinx_build(
|
||||
packet_size.payload_size(),
|
||||
packet_payload,
|
||||
&route,
|
||||
&destination,
|
||||
&delays,
|
||||
)?;
|
||||
|
||||
let first_hop_address =
|
||||
NymNodeRoutingAddress::try_from(route.first().unwrap().address).unwrap();
|
||||
|
||||
Ok(MixPacket::new(first_hop_address, packet, PacketType::Mix))
|
||||
// once merged, that's an easy rng injection point for sphinx packets : )
|
||||
let packet = match packet_type {
|
||||
PacketType::Mix => NymPacket::sphinx_build(
|
||||
packet_size.payload_size(),
|
||||
packet_payload,
|
||||
&route,
|
||||
&destination,
|
||||
&delays,
|
||||
)?,
|
||||
#[allow(deprecated)]
|
||||
PacketType::Vpn => NymPacket::sphinx_build(
|
||||
packet_size.payload_size(),
|
||||
packet_payload,
|
||||
&route,
|
||||
&destination,
|
||||
&delays,
|
||||
)?,
|
||||
PacketType::Outfox => NymPacket::outfox_build(
|
||||
packet_payload,
|
||||
&route,
|
||||
&destination,
|
||||
Some(packet_size.plaintext_size()),
|
||||
)?,
|
||||
};
|
||||
|
||||
Ok(MixPacket::new(first_hop_address, packet, packet_type))
|
||||
}
|
||||
|
||||
/// Helper function used to determine if given message represents a loop cover message.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::packet::{FramedNymPacket, Header};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use nym_sphinx_addressing::clients::Recipient;
|
||||
use nym_sphinx_types::Node;
|
||||
use nym_sphinx_types::{delays, Delay, Node};
|
||||
use thiserror::Error;
|
||||
|
||||
pub trait SphinxRouteMaker {
|
||||
@@ -41,3 +43,11 @@ impl SphinxRouteMaker for Vec<Node> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_hop_delays(average_packet_delay: Duration, num_hops: usize) -> Vec<Delay> {
|
||||
if average_packet_delay.is_zero() {
|
||||
vec![nym_sphinx_types::Delay::new_from_millis(0); num_hops]
|
||||
} else {
|
||||
delays::generate_from_average_duration(num_hops, average_packet_delay)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::message::{NymMessage, ACK_OVERHEAD, OUTFOX_ACK_OVERHEAD};
|
||||
@@ -14,7 +14,7 @@ use nym_sphinx_chunking::fragment::{Fragment, FragmentIdentifier};
|
||||
use nym_sphinx_forwarding::packet::MixPacket;
|
||||
use nym_sphinx_params::packet_sizes::PacketSize;
|
||||
use nym_sphinx_params::{PacketType, ReplySurbKeyDigestAlgorithm, DEFAULT_NUM_MIX_HOPS};
|
||||
use nym_sphinx_types::{delays, Delay, NymPacket};
|
||||
use nym_sphinx_types::{Delay, NymPacket};
|
||||
use nym_topology::{NymTopology, NymTopologyError};
|
||||
use rand::{CryptoRng, Rng};
|
||||
use std::convert::TryFrom;
|
||||
@@ -233,7 +233,7 @@ pub trait FragmentPreparer {
|
||||
|
||||
// including set of delays
|
||||
let delays =
|
||||
delays::generate_from_average_duration(route.len(), self.average_packet_delay());
|
||||
nym_sphinx_routing::generate_hop_delays(self.average_packet_delay(), route.len());
|
||||
|
||||
// create the actual sphinx packet here. With valid route and correct payload size,
|
||||
// there's absolutely no reason for this call to fail.
|
||||
|
||||
@@ -27,9 +27,9 @@ pub struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new<S: Into<String>>(id: S, provider_mix_address: S) -> Self {
|
||||
pub fn new<S: Into<String>>(id: S, version: S, provider_mix_address: S) -> Self {
|
||||
Config {
|
||||
base: BaseClientConfig::new(id),
|
||||
base: BaseClientConfig::new(id, version),
|
||||
socks5: Socks5::new(provider_mix_address),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use nym_client_core::client::base_client::{
|
||||
use nym_client_core::client::key_manager::persistence::KeyStore;
|
||||
use nym_client_core::client::replies::reply_storage::ReplyStorageBackend;
|
||||
use nym_client_core::config::DebugConfig;
|
||||
use nym_client_core::init::GatewaySetup;
|
||||
use nym_credential_storage::storage::Storage as CredentialStorage;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_sphinx::params::PacketType;
|
||||
@@ -53,6 +54,8 @@ pub struct NymClient<S> {
|
||||
config: Config,
|
||||
|
||||
storage: S,
|
||||
|
||||
setup_method: GatewaySetup,
|
||||
}
|
||||
|
||||
impl<S> NymClient<S>
|
||||
@@ -65,7 +68,16 @@ where
|
||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
|
||||
{
|
||||
pub fn new(config: Config, storage: S) -> Self {
|
||||
NymClient { config, storage }
|
||||
NymClient {
|
||||
config,
|
||||
storage,
|
||||
setup_method: GatewaySetup::MustLoad,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
|
||||
self.setup_method = setup;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -198,7 +210,8 @@ where
|
||||
};
|
||||
|
||||
let base_builder =
|
||||
BaseClientBuilder::new(&self.config.base, self.storage, dkg_query_client);
|
||||
BaseClientBuilder::new(&self.config.base, self.storage, dkg_query_client)
|
||||
.with_gateway_setup(self.setup_method);
|
||||
|
||||
let packet_type = self.config.base.debug.traffic.packet_type;
|
||||
let mut started_client = base_builder.start_base().await?;
|
||||
|
||||
@@ -428,18 +428,14 @@ impl SocksClient {
|
||||
Some(self.lane_queue_lengths.clone()),
|
||||
self.shutdown_listener.clone(),
|
||||
)
|
||||
.run(move |conn_id, read_data, socket_closed| {
|
||||
let provider_request = Socks5Request::new_send(
|
||||
request_version.provider_protocol,
|
||||
conn_id,
|
||||
read_data,
|
||||
socket_closed,
|
||||
);
|
||||
.run(move |socket_data| {
|
||||
let lane = TransmissionLane::ConnectionId(socket_data.header.connection_id);
|
||||
let provider_request =
|
||||
Socks5Request::new_send(request_version.provider_protocol, socket_data);
|
||||
let provider_message = Socks5ProviderRequest::new_provider_data(
|
||||
request_version.provider_interface,
|
||||
provider_request,
|
||||
);
|
||||
let lane = TransmissionLane::ConnectionId(conn_id);
|
||||
if anonymous {
|
||||
InputMessage::new_anonymous(
|
||||
recipient,
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Socks5ClientCoreError;
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
|
||||
use nym_client_core::client::received_buffer::ReconstructedMessagesReceiver;
|
||||
use nym_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_proxy_helpers::connection_controller::{ControllerCommand, ControllerSender};
|
||||
use nym_socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent};
|
||||
use nym_sphinx::receiver::ReconstructedMessage;
|
||||
use nym_task::TaskClient;
|
||||
|
||||
use crate::error::Socks5ClientCoreError;
|
||||
|
||||
pub(crate) struct MixnetResponseListener {
|
||||
buffer_requester: ReceivedBufferRequestSender,
|
||||
mix_response_receiver: ReconstructedMessagesReceiver,
|
||||
@@ -79,9 +80,9 @@ impl MixnetResponseListener {
|
||||
);
|
||||
Err(err_response.into())
|
||||
}
|
||||
Socks5ResponseContent::NetworkData(response) => {
|
||||
Socks5ResponseContent::NetworkData { content } => {
|
||||
self.controller_sender
|
||||
.unbounded_send(response.into())
|
||||
.unbounded_send(ControllerCommand::new_send(content))
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
use crate::message::OrderedMessage;
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use log::*;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
pub enum OrderedMessageError {
|
||||
#[error("received message with sequence number {received}, which is way higher than our current {current}")]
|
||||
MessageSequenceTooLarge { current: u64, received: u64 },
|
||||
|
||||
#[error("received message with sequence number {received}, while we're already at {current}!")]
|
||||
MessageAlreadyReconstructed { current: u64, received: u64 },
|
||||
|
||||
#[error("attempted to overwrite message at sequence {received}")]
|
||||
AttemptedToOverwriteSequence { received: u64 },
|
||||
}
|
||||
|
||||
/// Stores messages and emits them in order.
|
||||
///
|
||||
@@ -9,36 +24,58 @@ use std::collections::HashMap;
|
||||
/// to fill up with the full sequence.
|
||||
#[derive(Debug)]
|
||||
pub struct OrderedMessageBuffer {
|
||||
next_index: u64,
|
||||
messages: HashMap<u64, OrderedMessage>,
|
||||
next_sequence: u64,
|
||||
messages: BTreeMap<u64, Vec<u8>>,
|
||||
}
|
||||
|
||||
/// Data returned from `OrderedMessageBuffer` on a successful read of gapless ordered data.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ReadContiguousData {
|
||||
pub data: Vec<u8>,
|
||||
pub last_index: u64,
|
||||
pub last_sequence: u64,
|
||||
}
|
||||
|
||||
const MAX_REASONABLE_OFFSET: u64 = 1000;
|
||||
|
||||
impl OrderedMessageBuffer {
|
||||
pub fn new() -> OrderedMessageBuffer {
|
||||
OrderedMessageBuffer {
|
||||
next_index: 0,
|
||||
messages: HashMap::new(),
|
||||
next_sequence: 0,
|
||||
messages: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a message to the buffer. messages are sort on insertion, so
|
||||
/// that later on multiple reads for incomplete sequences don't result in
|
||||
/// useless sort work.
|
||||
pub fn write(&mut self, message: OrderedMessage) {
|
||||
pub fn write(&mut self, sequence: u64, data: Vec<u8>) -> Result<(), OrderedMessageError> {
|
||||
// reject messages that have clearly malformed sequence
|
||||
if sequence > self.next_sequence + MAX_REASONABLE_OFFSET {
|
||||
return Err(OrderedMessageError::MessageSequenceTooLarge {
|
||||
current: self.next_sequence,
|
||||
received: sequence,
|
||||
});
|
||||
}
|
||||
|
||||
if self.messages.contains_key(&sequence) {
|
||||
return Err(OrderedMessageError::AttemptedToOverwriteSequence { received: sequence });
|
||||
}
|
||||
|
||||
if sequence < self.next_sequence {
|
||||
return Err(OrderedMessageError::MessageAlreadyReconstructed {
|
||||
current: self.next_sequence,
|
||||
received: sequence,
|
||||
});
|
||||
}
|
||||
|
||||
trace!(
|
||||
"Writing message index: {} length {:?} to OrderedMessageBuffer.",
|
||||
message.index,
|
||||
message.data.len()
|
||||
"Writing message index: {} length {} to OrderedMessageBuffer.",
|
||||
sequence,
|
||||
data.len()
|
||||
);
|
||||
|
||||
self.messages.insert(message.index, message);
|
||||
self.messages.insert(sequence, data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `Option<Vec<u8>>` where it's `Some(bytes)` if there is gapless
|
||||
@@ -49,33 +86,31 @@ impl OrderedMessageBuffer {
|
||||
/// a read will return the bytes of messages 0, 1, 2. Subsequent reads will
|
||||
/// return `None` until message 3 comes in, at which point 3, 4, and any
|
||||
/// further contiguous messages which have arrived will be returned.
|
||||
#[must_use]
|
||||
pub fn read(&mut self) -> Option<ReadContiguousData> {
|
||||
if !self.messages.contains_key(&self.next_index) {
|
||||
if !self.messages.contains_key(&self.next_sequence) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut contiguous_messages = Vec::new();
|
||||
let mut index = self.next_index;
|
||||
let mut seq = self.next_sequence;
|
||||
|
||||
while let Some(ordered_message) = self.messages.remove(&index) {
|
||||
contiguous_messages.push(ordered_message);
|
||||
index += 1;
|
||||
while let Some(mut data) = self.messages.remove(&seq) {
|
||||
contiguous_messages.append(&mut data);
|
||||
seq += 1;
|
||||
}
|
||||
|
||||
let high_water = index;
|
||||
self.next_index = high_water;
|
||||
trace!("Next high water mark is: {}", high_water);
|
||||
let high_water = seq;
|
||||
self.next_sequence = high_water;
|
||||
trace!("Next high water mark is: {high_water}");
|
||||
|
||||
// dig out the bytes from inside the struct
|
||||
let data: Vec<u8> = contiguous_messages
|
||||
.into_iter()
|
||||
.flat_map(|message| message.data)
|
||||
.collect();
|
||||
|
||||
trace!("Returning {} bytes from ordered message buffer", data.len());
|
||||
trace!(
|
||||
"Returning {} bytes from ordered message buffer",
|
||||
contiguous_messages.len()
|
||||
);
|
||||
Some(ReadContiguousData {
|
||||
data,
|
||||
last_index: index,
|
||||
data: contiguous_messages,
|
||||
last_sequence: seq,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -90,6 +125,64 @@ impl Default for OrderedMessageBuffer {
|
||||
mod test_chunking_and_reassembling {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn trying_to_write_unreasonable_high_sequence() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
let first_message = vec![1, 2, 3, 4];
|
||||
let second_message = vec![5, 6, 7, 8];
|
||||
|
||||
buffer.write(0, first_message).unwrap();
|
||||
buffer.write(1, second_message).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Err(OrderedMessageError::MessageSequenceTooLarge {
|
||||
current: 0,
|
||||
received: 12345678
|
||||
}),
|
||||
buffer.write(12345678, b"foomp".to_vec())
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trying_to_overwrite_sequence() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
let message = vec![1, 2, 3, 4];
|
||||
|
||||
buffer.write(0, message.clone()).unwrap();
|
||||
buffer.write(1, message.clone()).unwrap();
|
||||
buffer.write(2, message.clone()).unwrap();
|
||||
buffer.write(3, message.clone()).unwrap();
|
||||
|
||||
for seq in 0..=3 {
|
||||
assert_eq!(
|
||||
Err(OrderedMessageError::AttemptedToOverwriteSequence { received: seq }),
|
||||
buffer.write(seq, message.clone())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn writing_past_data() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
let message = vec![1, 2, 3, 4];
|
||||
|
||||
buffer.write(0, message.clone()).unwrap();
|
||||
buffer.write(1, message.clone()).unwrap();
|
||||
buffer.write(2, message.clone()).unwrap();
|
||||
buffer.write(3, message.clone()).unwrap();
|
||||
let _ = buffer.read().unwrap();
|
||||
|
||||
for seq in 0..=3 {
|
||||
assert_eq!(
|
||||
Err(OrderedMessageError::MessageAlreadyReconstructed {
|
||||
current: 4,
|
||||
received: seq
|
||||
}),
|
||||
buffer.write(seq, message.clone())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod reading_from_and_writing_to_the_buffer {
|
||||
use super::*;
|
||||
@@ -102,20 +195,14 @@ mod test_chunking_and_reassembling {
|
||||
fn read_returns_ordered_bytes_and_resets_buffer() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
|
||||
let first_message = OrderedMessage {
|
||||
data: vec![1, 2, 3, 4],
|
||||
index: 0,
|
||||
};
|
||||
let second_message = OrderedMessage {
|
||||
data: vec![5, 6, 7, 8],
|
||||
index: 1,
|
||||
};
|
||||
let first_message = vec![1, 2, 3, 4];
|
||||
let second_message = vec![5, 6, 7, 8];
|
||||
|
||||
buffer.write(first_message);
|
||||
buffer.write(0, first_message).unwrap();
|
||||
let first_read = buffer.read().unwrap().data;
|
||||
assert_eq!(vec![1, 2, 3, 4], first_read);
|
||||
|
||||
buffer.write(second_message);
|
||||
buffer.write(1, second_message).unwrap();
|
||||
let second_read = buffer.read().unwrap().data;
|
||||
assert_eq!(vec![5, 6, 7, 8], second_read);
|
||||
|
||||
@@ -126,17 +213,11 @@ mod test_chunking_and_reassembling {
|
||||
fn test_multiple_adds_stacks_up_bytes_in_the_buffer() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
|
||||
let first_message = OrderedMessage {
|
||||
data: vec![1, 2, 3, 4],
|
||||
index: 0,
|
||||
};
|
||||
let second_message = OrderedMessage {
|
||||
data: vec![5, 6, 7, 8],
|
||||
index: 1,
|
||||
};
|
||||
let first_message = vec![1, 2, 3, 4];
|
||||
let second_message = vec![5, 6, 7, 8];
|
||||
|
||||
buffer.write(first_message);
|
||||
buffer.write(second_message);
|
||||
buffer.write(0, first_message).unwrap();
|
||||
buffer.write(1, second_message).unwrap();
|
||||
let second_read = buffer.read();
|
||||
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], second_read.unwrap().data);
|
||||
assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty
|
||||
@@ -146,17 +227,11 @@ mod test_chunking_and_reassembling {
|
||||
fn out_of_order_adds_results_in_ordered_byte_vector() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
|
||||
let first_message = OrderedMessage {
|
||||
data: vec![1, 2, 3, 4],
|
||||
index: 0,
|
||||
};
|
||||
let second_message = OrderedMessage {
|
||||
data: vec![5, 6, 7, 8],
|
||||
index: 1,
|
||||
};
|
||||
let first_message = vec![1, 2, 3, 4];
|
||||
let second_message = vec![5, 6, 7, 8];
|
||||
|
||||
buffer.write(second_message);
|
||||
buffer.write(first_message);
|
||||
buffer.write(1, second_message).unwrap();
|
||||
buffer.write(0, first_message).unwrap();
|
||||
let read = buffer.read().unwrap().data;
|
||||
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], read);
|
||||
assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty
|
||||
@@ -170,23 +245,13 @@ mod test_chunking_and_reassembling {
|
||||
fn setup() -> OrderedMessageBuffer {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
|
||||
let zero_message = OrderedMessage {
|
||||
data: vec![0, 0, 0, 0],
|
||||
index: 0,
|
||||
};
|
||||
let one_message = OrderedMessage {
|
||||
data: vec![1, 1, 1, 1],
|
||||
index: 1,
|
||||
};
|
||||
let zero_message = vec![0, 0, 0, 0];
|
||||
let one_message = vec![1, 1, 1, 1];
|
||||
let three_message = vec![3, 3, 3, 3];
|
||||
|
||||
let three_message = OrderedMessage {
|
||||
data: vec![3, 3, 3, 3],
|
||||
index: 3,
|
||||
};
|
||||
|
||||
buffer.write(zero_message);
|
||||
buffer.write(one_message);
|
||||
buffer.write(three_message);
|
||||
buffer.write(0, zero_message).unwrap();
|
||||
buffer.write(1, one_message).unwrap();
|
||||
buffer.write(3, three_message).unwrap();
|
||||
buffer
|
||||
}
|
||||
#[test]
|
||||
@@ -199,43 +264,31 @@ mod test_chunking_and_reassembling {
|
||||
assert_eq!(None, buffer.read());
|
||||
|
||||
// let's add another message, leaving a gap in place at index 2
|
||||
let five_message = OrderedMessage {
|
||||
data: vec![5, 5, 5, 5],
|
||||
index: 5,
|
||||
};
|
||||
buffer.write(five_message);
|
||||
let five_message = vec![5, 5, 5, 5];
|
||||
buffer.write(5, five_message).unwrap();
|
||||
assert_eq!(None, buffer.read());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filling_the_gap_allows_us_to_get_everything() {
|
||||
let mut buffer = setup();
|
||||
buffer.read(); // that burns the first two. We still have a gap before the 3s.
|
||||
let _ = buffer.read(); // that burns the first two. We still have a gap before the 3s.
|
||||
|
||||
let two_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 2,
|
||||
};
|
||||
buffer.write(two_message);
|
||||
let two_message = vec![2, 2, 2, 2];
|
||||
buffer.write(2, two_message).unwrap();
|
||||
|
||||
let more_ordered_bytes = buffer.read().unwrap().data;
|
||||
assert_eq!([2, 2, 2, 2, 3, 3, 3, 3].to_vec(), more_ordered_bytes);
|
||||
|
||||
// let's add another message
|
||||
let five_message = OrderedMessage {
|
||||
data: vec![5, 5, 5, 5],
|
||||
index: 5,
|
||||
};
|
||||
buffer.write(five_message);
|
||||
let five_message = vec![5, 5, 5, 5];
|
||||
buffer.write(5, five_message).unwrap();
|
||||
|
||||
assert_eq!(None, buffer.read());
|
||||
|
||||
// let's fill in the gap of 4s now and read again
|
||||
let four_message = OrderedMessage {
|
||||
data: vec![4, 4, 4, 4],
|
||||
index: 4,
|
||||
};
|
||||
buffer.write(four_message);
|
||||
let four_message = vec![4, 4, 4, 4];
|
||||
buffer.write(4, four_message).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
[4, 4, 4, 4, 5, 5, 5, 5].to_vec(),
|
||||
@@ -249,70 +302,47 @@ mod test_chunking_and_reassembling {
|
||||
#[test]
|
||||
fn filling_the_gap_allows_us_to_get_everything_when_last_element_is_empty() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
let zero_message = OrderedMessage {
|
||||
data: vec![0, 0, 0, 0],
|
||||
index: 0,
|
||||
};
|
||||
let one_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 1,
|
||||
};
|
||||
let two_message = OrderedMessage {
|
||||
data: vec![],
|
||||
index: 2,
|
||||
};
|
||||
let zero_message = vec![0, 0, 0, 0];
|
||||
let one_message = vec![2, 2, 2, 2];
|
||||
let two_message = vec![];
|
||||
|
||||
buffer.write(zero_message);
|
||||
buffer.write(0, zero_message).unwrap();
|
||||
assert!(buffer.read().is_some()); // burn the buffer
|
||||
|
||||
buffer.write(two_message);
|
||||
buffer.write(one_message);
|
||||
buffer.write(2, two_message).unwrap();
|
||||
buffer.write(1, one_message).unwrap();
|
||||
assert!(buffer.read().is_some());
|
||||
assert_eq!(buffer.next_index, 3);
|
||||
assert_eq!(buffer.next_sequence, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_with_gaps_bigger_than_one() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
let zero_message = OrderedMessage {
|
||||
data: vec![0, 0, 0, 0],
|
||||
index: 0,
|
||||
};
|
||||
let one_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 1,
|
||||
};
|
||||
let two_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 2,
|
||||
};
|
||||
let three_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 3,
|
||||
};
|
||||
let four_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 4,
|
||||
};
|
||||
buffer.write(zero_message);
|
||||
let zero_message = vec![0, 0, 0, 0];
|
||||
let one_message = vec![2, 2, 2, 2];
|
||||
let two_message = vec![2, 2, 2, 2];
|
||||
let three_message = vec![2, 2, 2, 2];
|
||||
let four_message = vec![2, 2, 2, 2];
|
||||
|
||||
buffer.write(0, zero_message).unwrap();
|
||||
assert!(buffer.read().is_some());
|
||||
assert_eq!(buffer.next_index, 1);
|
||||
assert_eq!(buffer.next_sequence, 1);
|
||||
|
||||
buffer.write(four_message);
|
||||
buffer.write(4, four_message).unwrap();
|
||||
assert!(buffer.read().is_none());
|
||||
assert_eq!(buffer.next_index, 1);
|
||||
assert_eq!(buffer.next_sequence, 1);
|
||||
|
||||
buffer.write(three_message);
|
||||
buffer.write(3, three_message).unwrap();
|
||||
assert!(buffer.read().is_none());
|
||||
assert_eq!(buffer.next_index, 1);
|
||||
assert_eq!(buffer.next_sequence, 1);
|
||||
|
||||
buffer.write(two_message);
|
||||
buffer.write(2, two_message).unwrap();
|
||||
assert!(buffer.read().is_none());
|
||||
assert_eq!(buffer.next_index, 1);
|
||||
assert_eq!(buffer.next_sequence, 1);
|
||||
|
||||
buffer.write(one_message);
|
||||
buffer.write(1, one_message).unwrap();
|
||||
assert!(buffer.read().is_some());
|
||||
assert_eq!(buffer.next_index, 5)
|
||||
assert_eq!(buffer.next_sequence, 5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
mod buffer;
|
||||
mod message;
|
||||
mod sender;
|
||||
|
||||
pub use buffer::{OrderedMessageBuffer, ReadContiguousData};
|
||||
pub use message::MessageError;
|
||||
pub use message::OrderedMessage;
|
||||
pub use sender::OrderedMessageSender;
|
||||
pub use buffer::{OrderedMessageBuffer, OrderedMessageError, ReadContiguousData};
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, PartialEq, Eq)]
|
||||
pub enum MessageError {
|
||||
#[error("the received message was empty")]
|
||||
NoData,
|
||||
|
||||
#[error("could not extract message index. Received {received} bytes, but expected {expected}")]
|
||||
IndexTooShort { received: usize, expected: usize },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct OrderedMessage {
|
||||
pub data: Vec<u8>,
|
||||
pub index: u64,
|
||||
}
|
||||
|
||||
impl OrderedMessage {
|
||||
/// Serializes an `OrderedMessage` into bytes.
|
||||
/// The output format is:
|
||||
/// | 8 bytes index | data... |
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.index
|
||||
.to_be_bytes()
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(self.data.into_iter())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Attempts to deserialize an `OrderedMessage` from bytes.
|
||||
pub fn try_from_bytes(data: Vec<u8>) -> Result<OrderedMessage, MessageError> {
|
||||
if data.is_empty() {
|
||||
return Err(MessageError::NoData);
|
||||
}
|
||||
|
||||
if data.len() < 8 {
|
||||
return Err(MessageError::IndexTooShort {
|
||||
received: data.len(),
|
||||
expected: 8,
|
||||
});
|
||||
}
|
||||
let index = u64::from_be_bytes([
|
||||
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
|
||||
]);
|
||||
Ok(OrderedMessage {
|
||||
data: data[8..].to_vec(),
|
||||
index,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Order messages by their index only, ignoring their data
|
||||
impl PartialOrd for OrderedMessage {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some((self.index).cmp(&(other.index)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod ordered_message_to_bytes {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn works() {
|
||||
let message = OrderedMessage {
|
||||
data: vec![123],
|
||||
index: 1,
|
||||
};
|
||||
let bytes = message.into_bytes();
|
||||
|
||||
let expected = vec![0, 0, 0, 0, 0, 0, 0, 1, 123];
|
||||
assert_eq!(expected, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod ordered_message_from_bytes {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fails_when_there_is_no_data() {
|
||||
let result = OrderedMessage::try_from_bytes(Vec::new());
|
||||
assert_eq!(Err(MessageError::NoData), result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fails_when_data_is_too_short() {
|
||||
let result = OrderedMessage::try_from_bytes(vec![1, 2, 3]);
|
||||
assert_eq!(
|
||||
Err(MessageError::IndexTooShort {
|
||||
received: 3,
|
||||
expected: 8
|
||||
}),
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_when_there_is_enough_to_make_a_sequence_number_but_no_message_data() {
|
||||
let expected = OrderedMessage {
|
||||
data: Vec::new(),
|
||||
index: 1,
|
||||
};
|
||||
let result = OrderedMessage::try_from_bytes(vec![0, 0, 0, 0, 0, 0, 0, 1]).unwrap();
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_when_there_is_seq_number_and_data() {
|
||||
let expected = OrderedMessage {
|
||||
data: vec![255, 255, 255],
|
||||
index: 1,
|
||||
};
|
||||
let result =
|
||||
OrderedMessage::try_from_bytes(vec![0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255]).unwrap();
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_message_does_not_affect_ordering() {
|
||||
let mut msg1 = OrderedMessage {
|
||||
data: vec![255, 255, 255],
|
||||
index: 1,
|
||||
};
|
||||
|
||||
let mut msg2 = OrderedMessage {
|
||||
data: vec![],
|
||||
index: 2,
|
||||
};
|
||||
|
||||
assert!(msg1 < msg2);
|
||||
|
||||
msg1.index = 2;
|
||||
msg2.index = 1;
|
||||
|
||||
assert!(msg1 > msg2);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
use crate::message::OrderedMessage;
|
||||
|
||||
/// Assigns sequence numbers to outbound byte vectors. These messages can then
|
||||
/// be reassembled into an ordered sequence by the `OrderedMessageSender`.
|
||||
#[derive(Debug)]
|
||||
pub struct OrderedMessageSender {
|
||||
next_index: u64,
|
||||
}
|
||||
|
||||
impl OrderedMessageSender {
|
||||
pub fn new() -> OrderedMessageSender {
|
||||
OrderedMessageSender { next_index: 0 }
|
||||
}
|
||||
|
||||
/// Turns raw bytes into an OrderedMessage containing the original bytes
|
||||
/// and a sequence number;
|
||||
pub fn wrap_message(&mut self, input: Vec<u8>) -> OrderedMessage {
|
||||
let message = OrderedMessage {
|
||||
data: input.to_vec(),
|
||||
index: self.next_index,
|
||||
};
|
||||
self.next_index += 1;
|
||||
message
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OrderedMessageSender {
|
||||
fn default() -> Self {
|
||||
OrderedMessageSender::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod ordered_message_sender {
|
||||
use super::*;
|
||||
|
||||
mod when_input_bytes_are_empty {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod sequence_index_numbers {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn increase_as_messages_are_sent() {
|
||||
let mut sender = OrderedMessageSender::new();
|
||||
let first_bytes = vec![1, 2, 3, 4];
|
||||
let second_bytes = vec![5, 6, 7, 8];
|
||||
|
||||
let first_message = sender.wrap_message(first_bytes);
|
||||
|
||||
assert_eq!(first_message.index, 0);
|
||||
|
||||
let second_message = sender.wrap_message(second_bytes);
|
||||
assert_eq!(second_message.index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use nym_ordered_buffer::{OrderedMessage, OrderedMessageBuffer, ReadContiguousData};
|
||||
use nym_socks5_requests::{ConnectionId, NetworkData, SendRequest};
|
||||
use nym_ordered_buffer::{OrderedMessageBuffer, ReadContiguousData};
|
||||
use nym_socks5_requests::{ConnectionId, SocketData};
|
||||
use nym_task::connections::{ConnectionCommand, ConnectionCommandSender};
|
||||
use nym_task::TaskClient;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@@ -40,29 +40,13 @@ pub enum ControllerCommand {
|
||||
connection_id: ConnectionId,
|
||||
},
|
||||
Send {
|
||||
connection_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
is_closed: bool,
|
||||
data: SocketData,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<NetworkData> for ControllerCommand {
|
||||
fn from(value: NetworkData) -> Self {
|
||||
ControllerCommand::Send {
|
||||
connection_id: value.connection_id,
|
||||
data: value.data,
|
||||
is_closed: value.is_closed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SendRequest> for ControllerCommand {
|
||||
fn from(value: SendRequest) -> Self {
|
||||
ControllerCommand::Send {
|
||||
connection_id: value.conn_id,
|
||||
data: value.data,
|
||||
is_closed: value.local_closed,
|
||||
}
|
||||
impl ControllerCommand {
|
||||
pub fn new_send(data: SocketData) -> Self {
|
||||
ControllerCommand::Send { data }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,18 +58,13 @@ struct ActiveConnection {
|
||||
}
|
||||
|
||||
impl ActiveConnection {
|
||||
fn write_to_buf(&mut self, payload: Vec<u8>, is_closed: bool) {
|
||||
let ordered_message = match OrderedMessage::try_from_bytes(payload) {
|
||||
Ok(msg) => msg,
|
||||
Err(err) => {
|
||||
error!("Malformed ordered message - {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
fn write_to_buf(&mut self, seq: u64, payload: Vec<u8>, is_closed: bool) {
|
||||
if is_closed {
|
||||
self.closed_at_index = Some(ordered_message.index);
|
||||
self.closed_at_index = Some(seq);
|
||||
}
|
||||
if let Err(err) = self.ordered_buffer.write(seq, payload) {
|
||||
error!("failed to write to the buffer: {err}")
|
||||
}
|
||||
self.ordered_buffer.write(ordered_message);
|
||||
}
|
||||
|
||||
fn read_from_buf(&mut self) -> Option<ReadContiguousData> {
|
||||
@@ -117,7 +96,7 @@ pub struct Controller {
|
||||
|
||||
// buffer for messages received before connection was established due to mixnet being able to
|
||||
// un-order messages. Note we don't ever expect to have more than 1-2 messages per connection here
|
||||
pending_messages: HashMap<ConnectionId, Vec<(Vec<u8>, bool)>>,
|
||||
pending_messages: HashMap<ConnectionId, Vec<SocketData>>,
|
||||
|
||||
shutdown: TaskClient,
|
||||
}
|
||||
@@ -154,8 +133,8 @@ impl Controller {
|
||||
// check if there were any pending messages
|
||||
if let Some(pending) = self.pending_messages.remove(&conn_id) {
|
||||
debug!("There were some pending messages for {}", conn_id);
|
||||
for (payload, is_closed) in pending {
|
||||
self.send_to_connection(conn_id, payload, is_closed)
|
||||
for data in pending {
|
||||
self.send_to_connection(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,20 +163,25 @@ impl Controller {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_to_connection(&mut self, conn_id: ConnectionId, payload: Vec<u8>, is_closed: bool) {
|
||||
if let Some(active_connection) = self.active_connections.get_mut(&conn_id) {
|
||||
if !payload.is_empty() {
|
||||
active_connection.write_to_buf(payload, is_closed);
|
||||
} else if !is_closed {
|
||||
error!("Tried to write an empty message to a not-closing connection. Please let us know if you see this message");
|
||||
}
|
||||
fn send_to_connection(&mut self, message: SocketData) {
|
||||
let hdr = message.header;
|
||||
if let Some(active_connection) = self.active_connections.get_mut(&hdr.connection_id) {
|
||||
// always write to the buffer even if payload is empty (because it could have been the keep-alive message)
|
||||
active_connection.write_to_buf(hdr.seq, message.data, hdr.local_socket_closed);
|
||||
|
||||
if let Some(payload) = active_connection.read_from_buf() {
|
||||
if let Some(closed_at_index) = active_connection.closed_at_index {
|
||||
if payload.last_index > closed_at_index {
|
||||
if payload.last_sequence > closed_at_index {
|
||||
active_connection.is_closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// however, don't send empty payload to the actual connection if it's not a close message
|
||||
// TODO: or should we?
|
||||
if payload.data.is_empty() && !active_connection.is_closed {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = active_connection
|
||||
.connection_sender
|
||||
.as_mut()
|
||||
@@ -207,34 +191,26 @@ impl Controller {
|
||||
socket_closed: active_connection.is_closed,
|
||||
})
|
||||
{
|
||||
error!("WTF IS THIS: {err}");
|
||||
error!("failed to send on the active connection channel: {err}");
|
||||
}
|
||||
|
||||
// TODO: ABOVE UNWRAP CAUSED A CRASH IN A NORMAL USE!!!!
|
||||
// TODO:
|
||||
// TODO: surprisingly it only happened on socks client, never on nSP
|
||||
// TODO:
|
||||
// TODO:
|
||||
// TODO:
|
||||
// TODO:
|
||||
}
|
||||
} else if !self.recently_closed.contains(&conn_id) {
|
||||
} else if !self.recently_closed.contains(&hdr.connection_id) {
|
||||
debug!("Received a 'Send' before 'Connect' - going to buffer the data");
|
||||
let pending = self
|
||||
.pending_messages
|
||||
.entry(conn_id)
|
||||
.entry(hdr.connection_id)
|
||||
.or_insert_with(Vec::new);
|
||||
pending.push((payload, is_closed));
|
||||
} else if !is_closed {
|
||||
pending.push(message);
|
||||
} else if !hdr.local_socket_closed {
|
||||
error!(
|
||||
"Tried to write to closed connection {} ({} bytes were 'lost)",
|
||||
conn_id,
|
||||
payload.len()
|
||||
hdr.connection_id,
|
||||
message.data.len()
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
"Tried to write to closed connection {}, but remote is already closed",
|
||||
conn_id
|
||||
hdr.connection_id
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -243,8 +219,8 @@ impl Controller {
|
||||
loop {
|
||||
tokio::select! {
|
||||
command = self.receiver.next() => match command {
|
||||
Some(ControllerCommand::Send{connection_id, data, is_closed}) => {
|
||||
self.send_to_connection(connection_id, data, is_closed)
|
||||
Some(ControllerCommand::Send{data}) => {
|
||||
self.send_to_connection(data)
|
||||
}
|
||||
Some(ControllerCommand::Insert{connection_id, connection_sender}) => {
|
||||
self.insert_connection(connection_id, connection_sender)
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
|
||||
pub mod available_reader;
|
||||
pub mod connection_controller;
|
||||
pub mod ordered_sender;
|
||||
pub mod proxy_runner;
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::proxy_runner::MixProxySender;
|
||||
use bytes::Bytes;
|
||||
use log::{debug, error};
|
||||
use nym_socks5_requests::{ConnectionId, SocketData};
|
||||
use std::io;
|
||||
|
||||
pub(crate) struct OrderedMessageSender<F, S> {
|
||||
connection_id: ConnectionId,
|
||||
// addresses are provided for better logging
|
||||
local_destination_address: String,
|
||||
remote_source_address: String,
|
||||
mixnet_sender: MixProxySender<S>,
|
||||
|
||||
next_message_seq: u64,
|
||||
mix_message_adapter: F,
|
||||
}
|
||||
|
||||
impl<F, S> OrderedMessageSender<F, S>
|
||||
where
|
||||
F: Fn(SocketData) -> S,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
local_destination_address: String,
|
||||
remote_source_address: String,
|
||||
connection_id: ConnectionId,
|
||||
mixnet_sender: MixProxySender<S>,
|
||||
mix_message_adapter: F,
|
||||
) -> Self {
|
||||
OrderedMessageSender {
|
||||
local_destination_address,
|
||||
remote_source_address,
|
||||
connection_id,
|
||||
mixnet_sender,
|
||||
next_message_seq: 0,
|
||||
mix_message_adapter,
|
||||
}
|
||||
}
|
||||
|
||||
fn sequence(&mut self) -> u64 {
|
||||
let next = self.next_message_seq;
|
||||
self.next_message_seq += 1;
|
||||
next
|
||||
}
|
||||
|
||||
fn construct_message(&mut self, data: Vec<u8>, local_socket_closed: bool) -> S {
|
||||
let data = SocketData::new(
|
||||
self.sequence(),
|
||||
self.connection_id,
|
||||
local_socket_closed,
|
||||
data,
|
||||
);
|
||||
(self.mix_message_adapter)(data)
|
||||
}
|
||||
|
||||
async fn send_message(&self, message: S) {
|
||||
if self.mixnet_sender.send(message).await.is_err() {
|
||||
panic!("BatchRealMessageReceiver has stopped receiving!")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn send_empty_close(&mut self) {
|
||||
let message = self.construct_message(Vec::new(), true);
|
||||
self.send_message(message).await
|
||||
}
|
||||
|
||||
pub(crate) async fn send_empty_keepalive(&mut self) {
|
||||
log::trace!("Sending keepalive for connection: {}", self.connection_id);
|
||||
let message = self.construct_message(Vec::new(), false);
|
||||
self.send_message(message).await
|
||||
}
|
||||
|
||||
pub(crate) fn process_data(&self, read_data: Option<io::Result<Bytes>>) -> ProcessedData {
|
||||
let (read_data, is_finished) = match read_data {
|
||||
Some(data) => match data {
|
||||
Ok(data) => (data, false),
|
||||
Err(err) => {
|
||||
error!(target: &*format!("({}) socks5 inbound", self.connection_id), "failed to read request from the socket - {err}");
|
||||
(Default::default(), true)
|
||||
}
|
||||
},
|
||||
None => (Default::default(), true),
|
||||
};
|
||||
|
||||
ProcessedData {
|
||||
data: read_data,
|
||||
is_done: is_finished,
|
||||
}
|
||||
}
|
||||
|
||||
fn log_sent_message(&self, data: &ProcessedData) {
|
||||
debug!(
|
||||
target: &*format!("({}) socks5 inbound", self.connection_id),
|
||||
"[{} bytes]\t{} → local → mixnet → remote → {}. Local closed: {}",
|
||||
data.data.len(),
|
||||
self.local_destination_address,
|
||||
self.remote_source_address,
|
||||
data.is_done
|
||||
);
|
||||
}
|
||||
|
||||
/// Send data read from local socket into the mixnet
|
||||
pub(crate) async fn send_data(&mut self, data: ProcessedData) {
|
||||
self.log_sent_message(&data);
|
||||
let message = self.construct_message(data.data.into(), data.is_done);
|
||||
self.send_message(message).await;
|
||||
}
|
||||
}
|
||||
|
||||
// helper wrapper to keep track of field meanings
|
||||
pub(crate) struct ProcessedData {
|
||||
data: Bytes,
|
||||
pub(crate) is_done: bool,
|
||||
}
|
||||
@@ -1,106 +1,22 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::MixProxySender;
|
||||
use super::SHUTDOWN_TIMEOUT;
|
||||
use crate::available_reader::AvailableReader;
|
||||
use crate::ordered_sender::OrderedMessageSender;
|
||||
use crate::proxy_runner::KEEPALIVE_INTERVAL;
|
||||
use bytes::Bytes;
|
||||
use futures::FutureExt;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use nym_ordered_buffer::OrderedMessageSender;
|
||||
use nym_socks5_requests::ConnectionId;
|
||||
use nym_socks5_requests::{ConnectionId, SocketData};
|
||||
use nym_task::connections::LaneQueueLengths;
|
||||
use nym_task::connections::TransmissionLane;
|
||||
use nym_task::TaskClient;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{io, sync::Arc};
|
||||
use tokio::select;
|
||||
use tokio::{net::tcp::OwnedReadHalf, sync::Notify, time::sleep};
|
||||
|
||||
async fn send_empty_close<F, S>(
|
||||
connection_id: ConnectionId,
|
||||
message_sender: &mut OrderedMessageSender,
|
||||
mix_sender: &MixProxySender<S>,
|
||||
adapter_fn: F,
|
||||
) where
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
|
||||
S: Debug,
|
||||
{
|
||||
let ordered_msg = message_sender.wrap_message(Vec::new()).into_bytes();
|
||||
mix_sender
|
||||
.send(adapter_fn(connection_id, ordered_msg, true))
|
||||
.await
|
||||
.expect("BatchRealMessageReceiver has stopped receiving!");
|
||||
}
|
||||
|
||||
async fn send_empty_keepalive<F, S>(
|
||||
connection_id: ConnectionId,
|
||||
message_sender: &mut OrderedMessageSender,
|
||||
mix_sender: &MixProxySender<S>,
|
||||
adapter_fn: F,
|
||||
) where
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
|
||||
S: Debug,
|
||||
{
|
||||
log::trace!("Sending keepalive for connection: {connection_id}");
|
||||
let ordered_msg = message_sender.wrap_message(Vec::new()).into_bytes();
|
||||
mix_sender
|
||||
.send(adapter_fn(connection_id, ordered_msg, false))
|
||||
.await
|
||||
.expect("BatchRealMessageReceiver has stopped receiving!");
|
||||
}
|
||||
|
||||
async fn deal_with_data<F, S>(
|
||||
read_data: Option<io::Result<Bytes>>,
|
||||
local_destination_address: &str,
|
||||
remote_source_address: &str,
|
||||
connection_id: ConnectionId,
|
||||
message_sender: &mut OrderedMessageSender,
|
||||
mix_sender: &MixProxySender<S>,
|
||||
adapter_fn: F,
|
||||
) -> bool
|
||||
where
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
|
||||
S: Debug,
|
||||
{
|
||||
let (read_data, is_finished) = match read_data {
|
||||
Some(data) => match data {
|
||||
Ok(data) => (data, false),
|
||||
Err(err) => {
|
||||
error!(target: &*format!("({connection_id}) socks5 inbound"), "failed to read request from the socket - {err}");
|
||||
(Default::default(), true)
|
||||
}
|
||||
},
|
||||
None => (Default::default(), true),
|
||||
};
|
||||
|
||||
debug!(
|
||||
target: &*format!("({connection_id}) socks5 inbound"),
|
||||
"[{} bytes]\t{} → local → mixnet → remote → {}. Local closed: {}",
|
||||
read_data.len(),
|
||||
local_destination_address,
|
||||
remote_source_address,
|
||||
is_finished
|
||||
);
|
||||
|
||||
// if we're sending through the mixnet increase the sequence number...
|
||||
let ordered_msg = message_sender.wrap_message(read_data.to_vec()).into_bytes();
|
||||
log::trace!(
|
||||
"pushing data down the input sender: size: {}",
|
||||
ordered_msg.len()
|
||||
);
|
||||
|
||||
mix_sender
|
||||
.send(adapter_fn(connection_id, ordered_msg, is_finished))
|
||||
.await
|
||||
.expect("InputMessageReceiver has stopped receiving!");
|
||||
|
||||
is_finished
|
||||
}
|
||||
|
||||
async fn wait_until_lane_empty(lane_queue_lengths: &Option<LaneQueueLengths>, connection_id: u64) {
|
||||
if let Some(lane_queue_lengths) = lane_queue_lengths {
|
||||
if tokio::time::timeout(
|
||||
@@ -158,27 +74,21 @@ async fn wait_for_lane(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) async fn run_inbound<F, S>(
|
||||
mut reader: OwnedReadHalf,
|
||||
local_destination_address: String, // addresses are provided for better logging
|
||||
remote_source_address: String,
|
||||
mut message_sender: OrderedMessageSender<F, S>,
|
||||
connection_id: ConnectionId,
|
||||
mix_sender: MixProxySender<S>,
|
||||
available_plaintext_per_mix_packet: usize,
|
||||
adapter_fn: F,
|
||||
shutdown_notify: Arc<Notify>,
|
||||
lane_queue_lengths: Option<LaneQueueLengths>,
|
||||
mut shutdown_listener: TaskClient,
|
||||
) -> OwnedReadHalf
|
||||
where
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S + Send + 'static,
|
||||
S: Debug,
|
||||
F: Fn(SocketData) -> S + Send + 'static,
|
||||
{
|
||||
// TODO: this multiplication by 4 is completely arbitrary here
|
||||
let mut available_reader =
|
||||
AvailableReader::new(&mut reader, Some(available_plaintext_per_mix_packet * 4));
|
||||
let mut message_sender = OrderedMessageSender::new();
|
||||
|
||||
// Shutdown if outbound signled to shutdown
|
||||
let shutdown_future = shutdown_notify.notified().then(|_| sleep(SHUTDOWN_TIMEOUT));
|
||||
@@ -217,7 +127,7 @@ where
|
||||
);
|
||||
// inform remote just in case it was closed because of lack of heartbeat.
|
||||
// worst case the remote will just have couple of false negatives
|
||||
send_empty_close(connection_id, &mut message_sender, &mix_sender, &adapter_fn).await;
|
||||
message_sender.send_empty_close().await;
|
||||
break;
|
||||
}
|
||||
_ = shutdown_listener.recv() => {
|
||||
@@ -233,7 +143,7 @@ where
|
||||
break;
|
||||
}
|
||||
_ = keepalive_timer.tick() => {
|
||||
send_empty_keepalive(connection_id, &mut message_sender, &mix_sender, &adapter_fn).await;
|
||||
message_sender.send_empty_keepalive().await;
|
||||
}
|
||||
// Read the next data when there is space in the lane.
|
||||
// The purpose of chaining the wait here is that it makes sure we can cancel the
|
||||
@@ -241,15 +151,12 @@ where
|
||||
read_data = wait_until_lane_almost_empty(&lane_queue_lengths, connection_id)
|
||||
.then(|_| available_reader.next()), if !we_are_closed =>
|
||||
{
|
||||
if deal_with_data(
|
||||
read_data,
|
||||
&local_destination_address,
|
||||
&remote_source_address,
|
||||
connection_id,
|
||||
&mut message_sender,
|
||||
&mix_sender,
|
||||
&adapter_fn,
|
||||
).await {
|
||||
let processed = message_sender.process_data(read_data);
|
||||
let is_done = processed.is_done;
|
||||
|
||||
message_sender.send_data(processed).await;
|
||||
|
||||
if is_done {
|
||||
// After reading the last data, notify the closing_future to wait until the
|
||||
// lane is clear before exiting.
|
||||
// We don't wait here since we want to be able to cancel the wait on close or
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::connection_controller::ConnectionReceiver;
|
||||
use nym_socks5_requests::ConnectionId;
|
||||
use crate::ordered_sender::OrderedMessageSender;
|
||||
use nym_socks5_requests::{ConnectionId, SocketData};
|
||||
use nym_task::connections::LaneQueueLengths;
|
||||
use nym_task::TaskClient;
|
||||
use std::fmt::Debug;
|
||||
@@ -92,20 +93,24 @@ where
|
||||
// request/response as required by entity running particular side of the proxy.
|
||||
pub async fn run<F>(mut self, adapter_fn: F) -> Self
|
||||
where
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S + Send + Sync + 'static,
|
||||
F: Fn(SocketData) -> S + Send + Sync + 'static,
|
||||
{
|
||||
let (read_half, write_half) = self.socket.take().unwrap().into_split();
|
||||
let shutdown_notify = Arc::new(Notify::new());
|
||||
|
||||
// should run until either inbound closes or is notified from outbound
|
||||
let inbound_future = inbound::run_inbound(
|
||||
read_half,
|
||||
let ordered_sender = OrderedMessageSender::new(
|
||||
self.local_destination_address.clone(),
|
||||
self.remote_source_address.clone(),
|
||||
self.connection_id,
|
||||
self.mix_sender.clone(),
|
||||
self.available_plaintext_per_mix_packet,
|
||||
adapter_fn,
|
||||
);
|
||||
let inbound_future = inbound::run_inbound(
|
||||
read_half,
|
||||
ordered_sender,
|
||||
self.connection_id,
|
||||
self.available_plaintext_per_mix_packet,
|
||||
Arc::clone(&shutdown_notify),
|
||||
self.lane_queue_lengths.clone(),
|
||||
self.shutdown_listener.clone(),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use nym_service_providers_common::interface;
|
||||
use nym_service_providers_common::interface::ServiceProviderMessagingError;
|
||||
use std::mem;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use request::*;
|
||||
@@ -16,6 +17,185 @@ pub mod version;
|
||||
pub type Socks5ProviderRequest = interface::Request<Socks5Request>;
|
||||
pub type Socks5ProviderResponse = interface::Response<Socks5Request>;
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
#[error(
|
||||
"didn't receive enough data to recover socket data. got {received}, but expected at least {expected}"
|
||||
)]
|
||||
pub struct InsufficientSocketDataError {
|
||||
received: usize,
|
||||
expected: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct SocketDataHeader {
|
||||
pub seq: u64,
|
||||
pub connection_id: ConnectionId,
|
||||
pub local_socket_closed: bool,
|
||||
}
|
||||
|
||||
impl SocketDataHeader {
|
||||
const SERIALIZED_LEN: usize = mem::size_of::<ConnectionId>() + 1 + mem::size_of::<u64>();
|
||||
|
||||
// we need to have two serialization methods for backwards compatibility,
|
||||
// since we serialized those fields differently depending on whether it was ingress vs egress...
|
||||
|
||||
pub fn try_from_request_bytes(
|
||||
b: &[u8],
|
||||
) -> Result<SocketDataHeader, InsufficientSocketDataError> {
|
||||
if b.len() != Self::SERIALIZED_LEN {
|
||||
return Err(InsufficientSocketDataError {
|
||||
received: b.len(),
|
||||
expected: Self::SERIALIZED_LEN,
|
||||
});
|
||||
}
|
||||
|
||||
// the unwraps here are fine as we just ensured we have the exact amount of bytes we need
|
||||
let connection_id = ConnectionId::from_be_bytes(b[0..8].try_into().unwrap());
|
||||
let local_socket_closed = b[8] != 0;
|
||||
let seq = u64::from_be_bytes(b[9..].try_into().unwrap());
|
||||
|
||||
Ok(SocketDataHeader {
|
||||
seq,
|
||||
connection_id,
|
||||
local_socket_closed,
|
||||
})
|
||||
}
|
||||
|
||||
// the serialization of the header looks as follows:
|
||||
// (it's vital it's not modified as we need this exact structure for backwards compatibility)
|
||||
// CONNECTION_ID (8B) || SOCKET_CLOSED (1B) || SEQUENCE (8B)
|
||||
pub fn into_request_bytes(self) -> Vec<u8> {
|
||||
self.into_request_bytes_iter().collect()
|
||||
}
|
||||
|
||||
pub fn into_request_bytes_iter(self) -> impl Iterator<Item = u8> {
|
||||
self.connection_id
|
||||
.to_be_bytes()
|
||||
.into_iter()
|
||||
.chain(std::iter::once(self.local_socket_closed as u8))
|
||||
.chain(self.seq.to_be_bytes().into_iter())
|
||||
}
|
||||
|
||||
pub fn try_from_response_bytes(
|
||||
b: &[u8],
|
||||
) -> Result<SocketDataHeader, InsufficientSocketDataError> {
|
||||
if b.len() != Self::SERIALIZED_LEN {
|
||||
return Err(InsufficientSocketDataError {
|
||||
received: b.len(),
|
||||
expected: Self::SERIALIZED_LEN,
|
||||
});
|
||||
}
|
||||
|
||||
// the unwraps here are fine as we just ensured we have the exact amount of bytes we need
|
||||
let local_socket_closed = b[0] != 0;
|
||||
let connection_id = ConnectionId::from_be_bytes(b[1..9].try_into().unwrap());
|
||||
let seq = u64::from_be_bytes(b[9..].try_into().unwrap());
|
||||
|
||||
Ok(SocketDataHeader {
|
||||
seq,
|
||||
connection_id,
|
||||
local_socket_closed,
|
||||
})
|
||||
}
|
||||
|
||||
// the serialization of the header looks as follows:
|
||||
// (it's vital it's not modified as we need this exact structure for backwards compatibility)
|
||||
// SOCKET_CLOSED (1B) || CONNECTION_ID (8B) || SEQUENCE (8B)
|
||||
pub fn into_response_bytes(self) -> Vec<u8> {
|
||||
self.into_response_bytes_iter().collect()
|
||||
}
|
||||
|
||||
pub fn into_response_bytes_iter(self) -> impl Iterator<Item = u8> {
|
||||
std::iter::once(self.local_socket_closed as u8)
|
||||
.chain(self.connection_id.to_be_bytes().into_iter())
|
||||
.chain(self.seq.to_be_bytes().into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SocketData {
|
||||
pub header: SocketDataHeader,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl SocketData {
|
||||
pub fn new(
|
||||
seq: u64,
|
||||
connection_id: ConnectionId,
|
||||
local_socket_closed: bool,
|
||||
data: Vec<u8>,
|
||||
) -> Self {
|
||||
SocketData {
|
||||
header: SocketDataHeader {
|
||||
seq,
|
||||
connection_id,
|
||||
local_socket_closed,
|
||||
},
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_deserialization_len(b: &[u8]) -> Result<(), InsufficientSocketDataError> {
|
||||
if b.is_empty() {
|
||||
return Err(InsufficientSocketDataError {
|
||||
received: 0,
|
||||
expected: SocketDataHeader::SERIALIZED_LEN,
|
||||
});
|
||||
}
|
||||
|
||||
if b.len() < SocketDataHeader::SERIALIZED_LEN {
|
||||
return Err(InsufficientSocketDataError {
|
||||
received: b.len(),
|
||||
expected: SocketDataHeader::SERIALIZED_LEN,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// we need to have two serialization methods for backwards compatibility,
|
||||
// since we serialized those fields differently depending on whether it was ingress vs egress...
|
||||
pub fn try_from_request_bytes(b: &[u8]) -> Result<SocketData, InsufficientSocketDataError> {
|
||||
Self::verify_deserialization_len(b)?;
|
||||
let header =
|
||||
SocketDataHeader::try_from_request_bytes(&b[..SocketDataHeader::SERIALIZED_LEN])?;
|
||||
let data = b[SocketDataHeader::SERIALIZED_LEN..].to_vec();
|
||||
|
||||
Ok(SocketData { header, data })
|
||||
}
|
||||
|
||||
// the serialization of the socket data looks as follows:
|
||||
// HEADER || DATA
|
||||
pub fn into_request_bytes(self) -> Vec<u8> {
|
||||
self.into_request_bytes_iter().collect()
|
||||
}
|
||||
|
||||
pub fn into_request_bytes_iter(self) -> impl Iterator<Item = u8> {
|
||||
self.header
|
||||
.into_request_bytes_iter()
|
||||
.chain(self.data.into_iter())
|
||||
}
|
||||
|
||||
pub fn try_from_response_bytes(b: &[u8]) -> Result<SocketData, InsufficientSocketDataError> {
|
||||
Self::verify_deserialization_len(b)?;
|
||||
|
||||
let header =
|
||||
SocketDataHeader::try_from_response_bytes(&b[..SocketDataHeader::SERIALIZED_LEN])?;
|
||||
let data = b[SocketDataHeader::SERIALIZED_LEN..].to_vec();
|
||||
|
||||
Ok(SocketData { header, data })
|
||||
}
|
||||
|
||||
pub fn into_response_bytes(self) -> Vec<u8> {
|
||||
self.into_response_bytes_iter().collect()
|
||||
}
|
||||
|
||||
pub fn into_response_bytes_iter(self) -> impl Iterator<Item = u8> {
|
||||
self.header
|
||||
.into_response_bytes_iter()
|
||||
.chain(self.data.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Socks5RequestError {
|
||||
#[error("failed to deserialize received request: {source}")]
|
||||
@@ -51,6 +231,103 @@ mod tests {
|
||||
use super::*;
|
||||
use nym_service_providers_common::interface::RequestContent;
|
||||
|
||||
#[cfg(test)]
|
||||
mod socket_data_serialization {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn for_requests() {
|
||||
assert_eq!(
|
||||
InsufficientSocketDataError {
|
||||
received: 0,
|
||||
expected: SocketDataHeader::SERIALIZED_LEN
|
||||
},
|
||||
SocketData::try_from_request_bytes(&[]).unwrap_err()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
InsufficientSocketDataError {
|
||||
received: 10,
|
||||
expected: SocketDataHeader::SERIALIZED_LEN
|
||||
},
|
||||
SocketData::try_from_request_bytes(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).unwrap_err()
|
||||
);
|
||||
|
||||
let good_data = SocketData::new(42, 12345, false, vec![2, 3]);
|
||||
let serialized = good_data.clone().into_request_bytes();
|
||||
|
||||
assert_eq!(
|
||||
good_data,
|
||||
SocketData::try_from_request_bytes(&serialized).unwrap()
|
||||
);
|
||||
assert_ne!(
|
||||
good_data,
|
||||
SocketData::try_from_response_bytes(&serialized).unwrap()
|
||||
);
|
||||
|
||||
let raw_bytes = [
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 1, 2, 3, 4, 5, 6, 7, 255, 255, 255,
|
||||
];
|
||||
assert_eq!(
|
||||
SocketData {
|
||||
header: SocketDataHeader {
|
||||
seq: u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
|
||||
connection_id: ConnectionId::from_be_bytes([6, 6, 6, 6, 6, 6, 6, 6]),
|
||||
local_socket_closed: false,
|
||||
},
|
||||
data: vec![255, 255, 255],
|
||||
},
|
||||
SocketData::try_from_request_bytes(&raw_bytes).unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_responses() {
|
||||
assert_eq!(
|
||||
InsufficientSocketDataError {
|
||||
received: 0,
|
||||
expected: SocketDataHeader::SERIALIZED_LEN
|
||||
},
|
||||
SocketData::try_from_response_bytes(&[]).unwrap_err()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
InsufficientSocketDataError {
|
||||
received: 10,
|
||||
expected: SocketDataHeader::SERIALIZED_LEN
|
||||
},
|
||||
SocketData::try_from_response_bytes(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).unwrap_err()
|
||||
);
|
||||
|
||||
let good_data = SocketData::new(42, 12345, false, vec![2, 3]);
|
||||
let serialized = good_data.clone().into_response_bytes();
|
||||
|
||||
assert_eq!(
|
||||
good_data,
|
||||
SocketData::try_from_response_bytes(&serialized).unwrap()
|
||||
);
|
||||
assert_ne!(
|
||||
good_data,
|
||||
SocketData::try_from_request_bytes(&serialized).unwrap()
|
||||
);
|
||||
|
||||
let raw_bytes = [
|
||||
0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 1, 2, 3, 4, 5, 6, 7, 255, 255, 255,
|
||||
];
|
||||
assert_eq!(
|
||||
SocketData {
|
||||
header: SocketDataHeader {
|
||||
seq: u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
|
||||
connection_id: ConnectionId::from_be_bytes([6, 6, 6, 6, 6, 6, 6, 6]),
|
||||
local_socket_closed: false,
|
||||
},
|
||||
data: vec![255, 255, 255],
|
||||
},
|
||||
SocketData::try_from_response_bytes(&raw_bytes).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod interface_backwards_compatibility {
|
||||
use super::*;
|
||||
@@ -99,9 +376,10 @@ mod tests {
|
||||
match new_deserialized.content {
|
||||
RequestContent::ProviderData(req) => match req.content {
|
||||
Socks5RequestContent::Send(send_req) => {
|
||||
assert_eq!(send_req.conn_id, 7810961472501196273);
|
||||
assert_eq!(send_req.data.len(), 111);
|
||||
assert!(!send_req.local_closed);
|
||||
assert_eq!(send_req.data.header.connection_id, 7810961472501196273);
|
||||
assert_eq!(send_req.data.header.seq, 0);
|
||||
assert_eq!(send_req.data.data.len(), 103);
|
||||
assert!(!send_req.data.header.local_socket_closed);
|
||||
}
|
||||
_ => panic!("unexpected request"),
|
||||
},
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{make_bincode_serializer, Socks5ProtocolVersion, Socks5RequestError, Socks5Response};
|
||||
use crate::{
|
||||
make_bincode_serializer, InsufficientSocketDataError, SocketData, Socks5ProtocolVersion,
|
||||
Socks5RequestError, Socks5Response,
|
||||
};
|
||||
use nym_service_providers_common::interface::{Serializable, ServiceProviderRequest};
|
||||
use nym_sphinx_addressing::clients::{Recipient, RecipientFormattingError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -61,6 +64,9 @@ pub enum RequestDeserializationError {
|
||||
#[from]
|
||||
source: bincode::Error,
|
||||
},
|
||||
|
||||
#[error(transparent)]
|
||||
InvalidSocketData(#[from] InsufficientSocketDataError),
|
||||
}
|
||||
|
||||
impl RequestDeserializationError {
|
||||
@@ -79,13 +85,9 @@ pub struct ConnectRequest {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SendRequest {
|
||||
pub conn_id: ConnectionId,
|
||||
pub data: Vec<u8>,
|
||||
pub local_closed: bool,
|
||||
pub data: SocketData,
|
||||
}
|
||||
|
||||
pub type QueryRequestId = u64;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum QueryRequest {
|
||||
OpenProxy,
|
||||
@@ -180,26 +182,20 @@ impl Socks5Request {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_send(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
conn_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
local_closed: bool,
|
||||
) -> Socks5Request {
|
||||
pub fn new_send(protocol_version: Socks5ProtocolVersion, data: SocketData) -> Socks5Request {
|
||||
Socks5Request {
|
||||
protocol_version,
|
||||
content: Socks5RequestContent::new_send(conn_id, data, local_closed),
|
||||
content: Socks5RequestContent::new_send(data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_query(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
req_id: u64,
|
||||
query: QueryRequest,
|
||||
) -> Socks5Request {
|
||||
Socks5Request {
|
||||
protocol_version,
|
||||
content: Socks5RequestContent::Query((req_id, query)),
|
||||
content: Socks5RequestContent::Query(query),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,7 +212,7 @@ pub enum Socks5RequestContent {
|
||||
/// Re-use an existing TCP connection, sending more request data up it.
|
||||
Send(SendRequest),
|
||||
|
||||
Query((QueryRequestId, QueryRequest)),
|
||||
Query(QueryRequest),
|
||||
}
|
||||
|
||||
impl Socks5RequestContent {
|
||||
@@ -234,16 +230,8 @@ impl Socks5RequestContent {
|
||||
}
|
||||
|
||||
/// Construct a new Request::Send instance
|
||||
pub fn new_send(
|
||||
conn_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
local_closed: bool,
|
||||
) -> Socks5RequestContent {
|
||||
Socks5RequestContent::Send(SendRequest {
|
||||
conn_id,
|
||||
data,
|
||||
local_closed,
|
||||
})
|
||||
pub fn new_send(data: SocketData) -> Socks5RequestContent {
|
||||
Socks5RequestContent::Send(SendRequest { data })
|
||||
}
|
||||
|
||||
/// Deserialize the request type, connection id, destination address and port,
|
||||
@@ -260,6 +248,14 @@ impl Socks5RequestContent {
|
||||
/// The request_flag tells us whether this is a new connection request (`new_connect`),
|
||||
/// an already-established connection we should send up (`new_send`), or
|
||||
/// a request to close an established connection (`new_close`).
|
||||
|
||||
// connect:
|
||||
// RequestFlag::Connect || CONN_ID || ADDR_LEN || ADDR || <RETURN_ADDR>
|
||||
//
|
||||
// send:
|
||||
// RequestFlag::Send || CONN_ID || LOCAL_CLOSED || DATA
|
||||
// where DATA: SEQ || TRUE_DATA
|
||||
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<Socks5RequestContent, RequestDeserializationError> {
|
||||
// each request needs to at least contain flag and ConnectionId
|
||||
if b.is_empty() {
|
||||
@@ -317,21 +313,9 @@ impl Socks5RequestContent {
|
||||
return_address,
|
||||
))
|
||||
}
|
||||
RequestFlag::Send => {
|
||||
if b.len() < 9 {
|
||||
return Err(RequestDeserializationError::ConnectionIdTooShort);
|
||||
}
|
||||
let conn_id = u64::from_be_bytes([b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]]);
|
||||
|
||||
let local_closed = b[9] != 0;
|
||||
let data = b[10..].to_vec();
|
||||
|
||||
Ok(Socks5RequestContent::Send(SendRequest {
|
||||
conn_id,
|
||||
data,
|
||||
local_closed,
|
||||
}))
|
||||
}
|
||||
RequestFlag::Send => Ok(Socks5RequestContent::Send(SendRequest {
|
||||
data: SocketData::try_from_request_bytes(&b[1..])?,
|
||||
})),
|
||||
RequestFlag::Query => {
|
||||
use bincode::Options;
|
||||
let query = make_bincode_serializer().deserialize(&b[1..])?;
|
||||
@@ -362,9 +346,7 @@ impl Socks5RequestContent {
|
||||
}
|
||||
}
|
||||
Socks5RequestContent::Send(req) => std::iter::once(RequestFlag::Send as u8)
|
||||
.chain(req.conn_id.to_be_bytes().into_iter())
|
||||
.chain(std::iter::once(req.local_closed as u8))
|
||||
.chain(req.data.into_iter())
|
||||
.chain(req.data.into_request_bytes_iter())
|
||||
.collect(),
|
||||
|
||||
Socks5RequestContent::Query(query) => {
|
||||
@@ -621,26 +603,7 @@ mod request_deserialization_tests {
|
||||
|
||||
#[test]
|
||||
fn works_when_request_is_sized_properly_even_without_data() {
|
||||
// correct 8 bytes of connection_id, 1 byte of local_closed and 0 bytes request data
|
||||
let request_bytes = [RequestFlag::Send as u8, 1, 2, 3, 4, 5, 6, 7, 8, 0].to_vec();
|
||||
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
|
||||
match request {
|
||||
Socks5RequestContent::Send(SendRequest {
|
||||
conn_id,
|
||||
data,
|
||||
local_closed,
|
||||
}) => {
|
||||
assert_eq!(u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), conn_id);
|
||||
assert_eq!(Vec::<u8>::new(), data);
|
||||
assert!(!local_closed)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_when_request_is_sized_properly_and_has_data() {
|
||||
// correct 8 bytes of connection_id, 1 byte of local_closed and 3 bytes request data (all 255)
|
||||
// correct 8 bytes of connection_id, 1 byte of local_closed, 8 bytes of sequence and 0 bytes request data
|
||||
let request_bytes = [
|
||||
RequestFlag::Send as u8,
|
||||
1,
|
||||
@@ -652,6 +615,53 @@ mod request_deserialization_tests {
|
||||
7,
|
||||
8,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
]
|
||||
.to_vec();
|
||||
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
|
||||
match request {
|
||||
Socks5RequestContent::Send(SendRequest { data }) => {
|
||||
assert_eq!(
|
||||
u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
|
||||
data.header.connection_id
|
||||
);
|
||||
assert!(!data.header.local_socket_closed);
|
||||
assert_eq!(1, data.header.seq);
|
||||
assert!(data.data.is_empty());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_when_request_is_sized_properly_and_has_data() {
|
||||
// correct 8 bytes of connection_id, 1 byte of local_closed, 8 bytes of sequence and 3 bytes request data (all 255)
|
||||
let request_bytes = [
|
||||
RequestFlag::Send as u8,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
@@ -660,14 +670,14 @@ mod request_deserialization_tests {
|
||||
|
||||
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
|
||||
match request {
|
||||
Socks5RequestContent::Send(SendRequest {
|
||||
conn_id,
|
||||
data,
|
||||
local_closed,
|
||||
}) => {
|
||||
assert_eq!(u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), conn_id);
|
||||
assert_eq!(vec![255, 255, 255], data);
|
||||
assert!(!local_closed)
|
||||
Socks5RequestContent::Send(SendRequest { data }) => {
|
||||
assert_eq!(
|
||||
u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
|
||||
data.header.connection_id
|
||||
);
|
||||
assert!(data.header.local_socket_closed);
|
||||
assert_eq!(1, data.header.seq);
|
||||
assert_eq!(vec![255, 255, 255], data.data);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -680,13 +690,13 @@ mod request_deserialization_tests {
|
||||
|
||||
#[test]
|
||||
fn serialize_there_and_back() {
|
||||
let open_proxy = Socks5RequestContent::Query((42, QueryRequest::OpenProxy));
|
||||
let open_proxy = Socks5RequestContent::Query(QueryRequest::OpenProxy);
|
||||
let bytes_open_proxy = open_proxy.clone().into_bytes();
|
||||
assert_eq!(bytes_open_proxy, vec![42, 2, 0]);
|
||||
assert_eq!(bytes_open_proxy, vec![2, 0]);
|
||||
|
||||
let description = Socks5RequestContent::Query((8723647823, QueryRequest::Description));
|
||||
let description = Socks5RequestContent::Query(QueryRequest::Description);
|
||||
let bytes_description = description.clone().into_bytes();
|
||||
assert_eq!(bytes_description, vec![23, 2, 1]);
|
||||
assert_eq!(bytes_description, vec![2, 1]);
|
||||
|
||||
let open_proxy2 = Socks5RequestContent::try_from_bytes(&bytes_open_proxy).unwrap();
|
||||
let description2 = Socks5RequestContent::try_from_bytes(&bytes_description).unwrap();
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{make_bincode_serializer, ConnectionId, Socks5ProtocolVersion, Socks5RequestError, QueryRequestId};
|
||||
use crate::{
|
||||
make_bincode_serializer, ConnectionId, InsufficientSocketDataError, SocketData,
|
||||
Socks5ProtocolVersion, Socks5RequestError,
|
||||
};
|
||||
use nym_service_providers_common::interface::{Serializable, ServiceProviderResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tap::TapFallible;
|
||||
@@ -33,6 +36,12 @@ impl TryFrom<u8> for ResponseFlag {
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ResponseDeserializationError {
|
||||
#[error("the network data was malformed: {source}")]
|
||||
MalformedNetworkData {
|
||||
#[from]
|
||||
source: InsufficientSocketDataError,
|
||||
},
|
||||
|
||||
#[error("not enough bytes to recover the connection id")]
|
||||
ConnectionIdTooShort,
|
||||
|
||||
@@ -115,23 +124,14 @@ impl Socks5Response {
|
||||
|
||||
pub fn new_network_data(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
seq: u64,
|
||||
connection_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
is_closed: bool,
|
||||
) -> Socks5Response {
|
||||
Socks5Response {
|
||||
protocol_version,
|
||||
content: Socks5ResponseContent::new_network_data(connection_id, data, is_closed),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_closed_empty(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
connection_id: ConnectionId,
|
||||
) -> Socks5Response {
|
||||
Socks5Response {
|
||||
protocol_version,
|
||||
content: Socks5ResponseContent::new_closed_empty(connection_id),
|
||||
content: Socks5ResponseContent::new_network_data(seq, connection_id, data, is_closed),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,34 +148,32 @@ impl Socks5Response {
|
||||
|
||||
pub fn new_query(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
query_request_id: QueryRequestId,
|
||||
query_response: QueryResponse,
|
||||
) -> Socks5Response {
|
||||
Socks5Response {
|
||||
protocol_version,
|
||||
content: Socks5ResponseContent::Query((query_request_id, query_response)),
|
||||
content: Socks5ResponseContent::Query(query_response),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Socks5ResponseContent {
|
||||
NetworkData(NetworkData),
|
||||
NetworkData { content: SocketData },
|
||||
ConnectionError(ConnectionError),
|
||||
Query((QueryRequestId, QueryResponse)),
|
||||
Query(QueryResponse),
|
||||
}
|
||||
|
||||
impl Socks5ResponseContent {
|
||||
pub fn new_network_data(
|
||||
seq: u64,
|
||||
connection_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
is_closed: bool,
|
||||
) -> Socks5ResponseContent {
|
||||
Socks5ResponseContent::NetworkData(NetworkData::new(connection_id, data, is_closed))
|
||||
}
|
||||
|
||||
pub fn new_closed_empty(connection_id: ConnectionId) -> Socks5ResponseContent {
|
||||
Socks5ResponseContent::NetworkData(NetworkData::new_closed_empty(connection_id))
|
||||
Socks5ResponseContent::NetworkData {
|
||||
content: SocketData::new(seq, connection_id, is_closed, data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_connection_error(
|
||||
@@ -187,9 +185,9 @@ impl Socks5ResponseContent {
|
||||
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
match self {
|
||||
Socks5ResponseContent::NetworkData(res) => {
|
||||
Socks5ResponseContent::NetworkData { content } => {
|
||||
std::iter::once(ResponseFlag::NetworkData as u8)
|
||||
.chain(res.into_bytes().into_iter())
|
||||
.chain(content.into_response_bytes_iter())
|
||||
.collect()
|
||||
}
|
||||
Socks5ResponseContent::ConnectionError(res) => {
|
||||
@@ -221,9 +219,9 @@ impl Socks5ResponseContent {
|
||||
|
||||
let response_flag = ResponseFlag::try_from(b[0])?;
|
||||
match response_flag {
|
||||
ResponseFlag::NetworkData => Ok(Socks5ResponseContent::NetworkData(
|
||||
NetworkData::try_from_bytes(&b[1..])?,
|
||||
)),
|
||||
ResponseFlag::NetworkData => Ok(Socks5ResponseContent::NetworkData {
|
||||
content: SocketData::try_from_response_bytes(&b[1..])?,
|
||||
}),
|
||||
ResponseFlag::ConnectionError => Ok(Socks5ResponseContent::ConnectionError(
|
||||
ConnectionError::try_from_bytes(&b[1..])?,
|
||||
)),
|
||||
@@ -235,81 +233,14 @@ impl Socks5ResponseContent {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_query(&self) -> Option<(&QueryRequestId, &QueryResponse)> {
|
||||
pub fn as_query(&self) -> Option<&QueryResponse> {
|
||||
match self {
|
||||
Socks5ResponseContent::Query((query_id, query)) => Some((query_id, query)),
|
||||
Socks5ResponseContent::Query(query) => Some(query),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A remote network network data response retrieved by the Socks5 service provider. This
|
||||
/// can be serialized and sent back through the mixnet to the requesting
|
||||
/// application.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct NetworkData {
|
||||
pub data: Vec<u8>,
|
||||
pub connection_id: ConnectionId,
|
||||
pub is_closed: bool,
|
||||
}
|
||||
|
||||
impl NetworkData {
|
||||
/// Constructor for responses
|
||||
pub fn new(connection_id: ConnectionId, data: Vec<u8>, is_closed: bool) -> Self {
|
||||
NetworkData {
|
||||
data,
|
||||
connection_id,
|
||||
is_closed,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_closed_empty(connection_id: ConnectionId) -> Self {
|
||||
NetworkData {
|
||||
data: vec![],
|
||||
connection_id,
|
||||
is_closed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<NetworkData, ResponseDeserializationError> {
|
||||
if b.is_empty() {
|
||||
return Err(ResponseDeserializationError::NoData);
|
||||
}
|
||||
|
||||
let is_closed = b[0] != 0;
|
||||
|
||||
if b.len() < 9 {
|
||||
return Err(ResponseDeserializationError::ConnectionIdTooShort);
|
||||
}
|
||||
|
||||
let mut connection_id_bytes = b.to_vec();
|
||||
let data = connection_id_bytes.split_off(9);
|
||||
|
||||
let connection_id = u64::from_be_bytes([
|
||||
connection_id_bytes[1],
|
||||
connection_id_bytes[2],
|
||||
connection_id_bytes[3],
|
||||
connection_id_bytes[4],
|
||||
connection_id_bytes[5],
|
||||
connection_id_bytes[6],
|
||||
connection_id_bytes[7],
|
||||
connection_id_bytes[8],
|
||||
]);
|
||||
|
||||
let response = NetworkData::new(connection_id, data, is_closed);
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// Serializes the response into bytes so that it can be sent back through
|
||||
/// the mixnet to the requesting application.
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
std::iter::once(self.is_closed as u8)
|
||||
.chain(self.connection_id.to_be_bytes().iter().cloned())
|
||||
.chain(self.data.into_iter())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ConnectionError {
|
||||
pub connection_id: ConnectionId,
|
||||
@@ -374,57 +305,6 @@ pub enum QueryResponse {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod constructing_socks5_data_responses_from_bytes {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fails_when_zero_bytes_are_supplied() {
|
||||
let response_bytes = Vec::new();
|
||||
|
||||
let err = NetworkData::try_from_bytes(&response_bytes).unwrap_err();
|
||||
assert!(matches!(err, ResponseDeserializationError::NoData));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fails_when_connection_id_bytes_are_too_short() {
|
||||
let response_bytes = vec![0, 1, 2, 3, 4, 5, 6];
|
||||
let err = NetworkData::try_from_bytes(&response_bytes).unwrap_err();
|
||||
assert!(matches!(
|
||||
err,
|
||||
ResponseDeserializationError::ConnectionIdTooShort,
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_when_there_is_no_data() {
|
||||
let response_bytes = vec![0, 0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let expected = NetworkData::new(
|
||||
u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
|
||||
Vec::new(),
|
||||
false,
|
||||
);
|
||||
let actual = NetworkData::try_from_bytes(&response_bytes).unwrap();
|
||||
assert_eq!(expected.connection_id, actual.connection_id);
|
||||
assert_eq!(expected.data, actual.data);
|
||||
assert_eq!(expected.is_closed, actual.is_closed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_when_there_is_data() {
|
||||
let response_bytes = vec![0, 0, 1, 2, 3, 4, 5, 6, 7, 255, 255, 255];
|
||||
let expected = NetworkData::new(
|
||||
u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
|
||||
vec![255, 255, 255],
|
||||
false,
|
||||
);
|
||||
let actual = NetworkData::try_from_bytes(&response_bytes).unwrap();
|
||||
assert_eq!(expected.connection_id, actual.connection_id);
|
||||
assert_eq!(expected.data, actual.data);
|
||||
assert_eq!(expected.is_closed, actual.is_closed);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod connection_error_response_serde_tests {
|
||||
use super::*;
|
||||
|
||||
@@ -177,20 +177,29 @@ impl TaskManager {
|
||||
drop(notify_rx);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let interrupt_future = tokio::signal::ctrl_c();
|
||||
|
||||
// in wasm we'll never get our shutdown anyway...
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
futures::future::pending::<()>().await;
|
||||
let interrupt_future = futures::future::pending::<()>();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let wait_future = tokio::time::sleep(Duration::from_secs(self.shutdown_timer_secs));
|
||||
|
||||
// TODO: we should be using a `Delay` here for wasm
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let wait_future = futures::future::pending::<()>();
|
||||
|
||||
tokio::select! {
|
||||
_ = self.notify_tx.closed() => {
|
||||
log::info!("All registered tasks succesfully shutdown");
|
||||
},
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
_ = interrupt_future => {
|
||||
log::info!("Forcing shutdown");
|
||||
}
|
||||
_ = tokio::time::sleep(Duration::from_secs(self.shutdown_timer_secs)) => {
|
||||
log::info!("Timout reached, forcing shutdown");
|
||||
_ = wait_future => {
|
||||
log::info!("Timeout reached, forcing shutdown");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+7
-7
@@ -31,7 +31,7 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
"getrandom 0.2.10",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
@@ -936,9 +936,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -1148,9 +1148,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.142"
|
||||
version = "0.2.146"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
||||
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
@@ -1444,7 +1444,7 @@ dependencies = [
|
||||
"chacha20",
|
||||
"chacha20poly1305",
|
||||
"curve25519-dalek",
|
||||
"getrandom 0.2.8",
|
||||
"getrandom 0.2.10",
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"rayon",
|
||||
@@ -1750,7 +1750,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
"getrandom 0.2.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user