Compare commits

..

20 Commits

Author SHA1 Message Date
gala1234 e3c9b11258 clean status once modal closes 2022-04-11 14:11:52 +02:00
gala1234 e7179eaa46 adding seleting validator functionality 2022-04-11 13:43:19 +02:00
gala1234 84cf1c6a62 some ui 2022-04-11 12:40:28 +02:00
gala1234 f5e874f8d9 Merge branch 'develop' into feature/validator_selector 2022-04-11 12:04:45 +02:00
gala1234 183f2779f0 story wip 2022-04-05 16:30:53 +02:00
gala1234 92e56c0121 wip story 2022-04-05 14:43:50 +02:00
gala1234 550e0ce856 wip 2022-04-05 14:27:48 +02:00
gala1234 3b0215ccee Merge branch 'develop' into feature/validator_selector 2022-04-05 13:39:59 +02:00
gala1234 38a8621032 starting storybook implementation 2022-04-05 13:38:10 +02:00
gala1234 caa0bc4e1e some ui changes on modal 2022-04-05 10:10:36 +02:00
gala1234 abcb0cbf5e wip display validators settings on a modal 2022-04-04 12:30:11 +02:00
gala1234 cdbcfbe3bf wip creating settings modal 2022-04-01 17:44:17 +02:00
gala1234 b8ee730561 adding dummy urls on mainnet and some dropdown improvements 2022-03-31 10:01:46 +02:00
gala1234 26a067c14d playing with mui 2022-03-30 17:41:04 +02:00
gala1234 69fe8e5cce some indent fix 2022-03-30 17:13:10 +02:00
gala1234 4fbf1cd876 connexion to fetch validator urls 2022-03-30 17:03:00 +02:00
gala1234 c693412258 Merge branch 'feature/wallet-expose-validator-urls' into feature/validator_selector 2022-03-30 13:43:19 +02:00
gala1234 72825a2ad3 start validators fetching 2022-03-30 13:40:02 +02:00
gala1234 89a19815fc first validators dropdown implementation 2022-03-30 13:33:52 +02:00
Jon Häggblad 394f0d30bf wallet: expose validator urls to the frontent 2022-03-30 11:51:41 +02:00
1260 changed files with 33933 additions and 86087 deletions
+4 -4
View File
@@ -19,10 +19,10 @@
Cargo.* @durch @futurechimp @jstuczyn @neacsu @octol Cargo.* @durch @futurechimp @jstuczyn @neacsu @octol
# JS rules: # JS rules:
*.js @mmsinclair @fmtabbara *.js @mmsinclair @fmtabbara @Aid19801
*.ts @mmsinclair @fmtabbara *.ts @mmsinclair @fmtabbara @Aid19801
*.tsx @mmsinclair @fmtabbara *.tsx @mmsinclair @fmtabbara @Aid19801
*.jsx @mmsinclair @fmtabbara *.jsx @mmsinclair @fmtabbara @Aid19801
# Something looking like possible documentation rules: # Something looking like possible documentation rules:
*.md @mfahampshire *.md @mfahampshire
-9
View File
@@ -1,9 +0,0 @@
# Description
Closes: #XXXX
<!-- If appropriate, insert relevant description here -->
# Checklist:
- [ ] added a changelog entry to `CHANGELOG.md`
-36
View File
@@ -1,36 +0,0 @@
name: Daily security audit
on: workflow_dispatch
jobs:
security_audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
notification:
if: ${{ failure() }}
needs: security_audit
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: Keybase - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Keybase - Send Notification
env:
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "Nym daily audit"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBTECH_TEAM }}"
KEYBASE_NYM_CHANNEL: "test"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
+2 -2
View File
@@ -13,9 +13,9 @@ jobs:
- name: Install rsync - name: Install rsync
run: sudo apt-get install rsync run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v2
with: with:
node-version: 16 node-version: '16'
- name: Setup yarn - name: Setup yarn
run: npm install -g yarn run: npm install -g yarn
- name: Build - name: Build
+2 -9
View File
@@ -10,7 +10,7 @@ on:
jobs: jobs:
build: build:
runs-on: [ self-hosted, custom-linux ] runs-on: [ self-hosted, custom-linux-exoscale ]
# Enable sccache via environment variable # Enable sccache via environment variable
env: env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
@@ -41,13 +41,6 @@ jobs:
command: test command: test
args: --workspace --all-features args: --workspace --all-features
- name: Run expensive tests
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --all-features -- --ignored
- name: Check formatting - name: Check formatting
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
@@ -58,7 +51,7 @@ jobs:
name: Clippy checks name: Clippy checks
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace --all-features args: --all-features
- name: Run clippy - name: Run clippy
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
-72
View File
@@ -1,72 +0,0 @@
name: Continuous integration on dispatch
on: workflow_dispatch
jobs:
build:
runs-on: [ self-hosted, custom-linux ]
# Enable sccache via environment variable
env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
- name: Check out repository code
uses: actions/checkout@v2
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --all-features
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- uses: actions-rs/clippy-check@v1
name: Clippy checks
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features
- name: Run clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --workspace -- -D warnings
- name: Build all binaries with coconut enabled
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --features=coconut
- name: Run all tests with coconut enabled
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --features=coconut
- name: Run clippy with coconut enabled
uses: actions-rs/cargo@v1
with:
command: clippy
args: --features=coconut -- -D warnings
-56
View File
@@ -1,56 +0,0 @@
name: Nym Connect (rust)
on:
push:
paths-ignore:
- 'explorer/**'
jobs:
build:
runs-on: [ self-hosted, custom-linux ]
env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
- name: Check out repository code
uses: actions/checkout@v2
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path nym-connect/Cargo.toml --workspace
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path nym-connect/Cargo.toml --workspace
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path nym-connect/Cargo.toml --all -- --check
- uses: actions-rs/clippy-check@v1
name: Clippy checks
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --manifest-path nym-connect/Cargo.toml --workspace --all-features
- name: Run clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --manifest-path nym-connect/Cargo.toml --workspace --all-features -- -D warnings
-38
View File
@@ -1,38 +0,0 @@
name: Build release of Nym smart contracts
on:
workflow_dispatch:
defaults:
run:
working-directory: contracts
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: wasm32-unknown-unknown
override: true
components: rustfmt, clippy
- name: Build release contracts
run: RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown
- name: Upload Mixnet Contract Artifact
uses: actions/upload-artifact@v3
with:
name: mixnet_contract.wasm
path: contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm
retention-days: 5
- name: Upload Vesting Contract Artifact
uses: actions/upload-artifact@v3
with:
name: vesting_contract.wasm
path: contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
retention-days: 5
+2 -2
View File
@@ -14,9 +14,9 @@ jobs:
runs-on: custom-runner-linux runs-on: custom-runner-linux
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-node@v3 - uses: actions/setup-node@v2
with: with:
node-version: 16 node-version: '16'
- name: Setup yarn - name: Setup yarn
run: npm install -g yarn run: npm install -g yarn
- name: Run ESLint - name: Run ESLint
+2 -28
View File
@@ -1,7 +1,6 @@
name: CI for Network Explorer name: CI for Network Explorer
on: on:
workflow_dispatch:
push: push:
paths: paths:
- 'explorer/**' - 'explorer/**'
@@ -18,9 +17,9 @@ jobs:
- name: Install rsync - name: Install rsync
run: sudo apt-get install rsync run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v2
with: with:
node-version: 16 node-version: '16'
- name: Setup yarn - name: Setup yarn
run: npm install -g yarn run: npm install -g yarn
continue-on-error: true continue-on-error: true
@@ -32,8 +31,6 @@ jobs:
# continue-on-error: true # continue-on-error: true
- run: yarn && yarn build - run: yarn && yarn build
continue-on-error: true continue-on-error: true
- run: yarn storybook:build
name: Build storybook
- name: Deploy branch to CI www - name: Deploy branch to CI www
continue-on-error: true continue-on-error: true
uses: easingthemes/ssh-deploy@main uses: easingthemes/ssh-deploy@main
@@ -45,17 +42,6 @@ jobs:
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }} TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/" EXCLUDE: "/dist/, /node_modules/"
- name: Deploy storybook to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ne-sb-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Keybase - Node Install - name: Keybase - Node Install
run: npm install run: npm install
working-directory: .github/workflows/support-files working-directory: .github/workflows/support-files
@@ -65,7 +51,6 @@ jobs:
NYM_PROJECT_NAME: "Network Explorer" NYM_PROJECT_NAME: "Network Explorer"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}" NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}" NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
NYM_CI_WWW_LOCATION_STORYBOOK: "ne-sb-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}" GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}" GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}" KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
@@ -76,14 +61,3 @@ jobs:
uses: docker://keybaseio/client:stable-node uses: docker://keybaseio/client:stable-node
with: with:
args: .github/workflows/support-files/notifications/entry_point.sh args: .github/workflows/support-files/notifications/entry_point.sh
- name: Deploy
if: github.event_name == 'workflow_dispatch'
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CD_PROD_NE_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "explorer/dist/"
REMOTE_HOST: ${{ secrets.CD_PROD_NE_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CD_PROD_NE_REMOTE_USER }}
TARGET: ${{ secrets.CD_PROD_NE_REMOTE_TARGET }}
EXCLUDE: "/dist/, /node_modules/"
+3 -61
View File
@@ -2,7 +2,7 @@ name: Nightly builds
on: on:
schedule: schedule:
- cron: '14 1 * * *' - cron: '14 4 * * *'
jobs: jobs:
matrix_prep: matrix_prep:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -44,31 +44,12 @@ jobs:
command: build command: build
args: --workspace args: --workspace
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run all tests - name: Run all tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --workspace args: --workspace
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run expensive tests
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --all-features -- --ignored
- name: Check formatting - name: Check formatting
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
@@ -94,9 +75,9 @@ jobs:
command: clippy command: clippy
args: --workspace --all-targets -- -D warnings args: --workspace --all-targets -- -D warnings
- name: Reclaim some disk space - name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-latest' }} if: ${{ matrix.os == 'windows-latest' }}
with: with:
command: clean command: clean
@@ -107,57 +88,18 @@ jobs:
command: build command: build
args: --workspace --features=coconut args: --workspace --features=coconut
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run all tests with coconut enabled - name: Run all tests with coconut enabled
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --workspace --features=coconut args: --workspace --features=coconut
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run clippy with coconut enabled - name: Run clippy with coconut enabled
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }} if: ${{ matrix.rust != 'nightly' }}
with: with:
command: clippy command: clippy
args: --workspace --all-targets --features=coconut -- -D warnings args: --workspace --all-targets --features=coconut -- -D warnings
# nym-wallet (the rust part)
- name: Build nym-wallet rust code
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Run nym-wallet tests
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Check nym-wallet formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
- name: Run clippy for nym-wallet
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
notification: notification:
needs: build needs: build
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -1,50 +0,0 @@
[
{
"os":"ubuntu-latest",
"rust":"stable",
"runOnEvent":"workflow_dispatch"
},
{
"os":"windows-latest",
"rust":"stable",
"runOnEvent":"workflow_dispatch"
},
{
"os":"macos-latest",
"rust":"stable",
"runOnEvent":"workflow_dispatch"
},
{
"os":"ubuntu-latest",
"rust":"beta",
"runOnEvent":"workflow_dispatch"
},
{
"os":"windows-latest",
"rust":"beta",
"runOnEvent":"workflow_dispatch"
},
{
"os":"macos-latest",
"rust":"beta",
"runOnEvent":"workflow_dispatch"
},
{
"os":"ubuntu-latest",
"rust":"nightly",
"runOnEvent":"workflow_dispatch"
},
{
"os":"windows-latest",
"rust":"nightly",
"runOnEvent":"workflow_dispatch"
},
{
"os":"macos-latest",
"rust":"nightly",
"runOnEvent":"workflow_dispatch"
}
]
@@ -1,174 +0,0 @@
name: Nightly builds on dispatch
on: workflow_dispatch
jobs:
matrix_prep:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from nightly_build_matrix_includes.json
- uses: actions/checkout@v2
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/nightly_build_matrix_on_dispatch.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
build:
needs: matrix_prep
strategy:
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
if: matrix.os == 'ubuntu-latest'
- name: Check out repository code
uses: actions/checkout@v2
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
components: rustfmt, clippy
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run expensive tests
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --all-features -- --ignored
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- uses: actions-rs/clippy-check@v1
name: Clippy checks
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features
- name: Run clippy
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --workspace --all-targets -- -D warnings
- name: Reclaim some disk space
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-latest' }}
with:
command: clean
# COCONUT stuff
- name: Build all binaries with coconut enabled
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --features=coconut
- name: Run all tests with coconut enabled
uses: actions-rs/cargo@v1
with:
command: test
args: --workspace --features=coconut
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- name: Run clippy with coconut enabled
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --workspace --all-targets --features=coconut -- -D warnings
# nym-wallet (the rust part)
- name: Build nym-wallet rust code
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Run nym-wallet tests
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Check nym-wallet formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
- name: Run clippy for nym-wallet
uses: actions-rs/cargo@v1
if: ${{ matrix.rust != 'nightly' }}
with:
command: clippy
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
notification:
needs: build
runs-on: ubuntu-latest
steps:
- name: Collect jobs status
uses: technote-space/workflow-conclusion-action@v2
- name: Check out repository code
uses: actions/checkout@v2
- name: Keybase - Node Install
if: env.WORKFLOW_CONCLUSION == 'failure'
run: npm install
working-directory: .github/workflows/support-files
- name: Keybase - Send Notification
if: env.WORKFLOW_CONCLUSION == 'failure'
env:
NYM_NOTIFICATION_KIND: nightly
NYM_PROJECT_NAME: "Nym nightly build"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMTECH_TEAM }}"
KEYBASE_NYM_CHANNEL: "${{ secrets.KEYBASE_CHANNEL_DEV_CORE_ID }}"
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
-50
View File
@@ -1,50 +0,0 @@
name: Publish Nym CLI binaries
on:
workflow_dispatch:
release:
types: [created]
env:
NETWORK: mainnet
jobs:
publish-nym-cli:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: Check the release tag starts with `nym-cli-`
if: startsWith(github.ref, 'refs/tags/nym-cli-') == false && github.event_name != 'workflow_dispatch'
uses: actions/github-script@v3
with:
script: |
core.setFailed('Release tag did not start with nym-cli-...')
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build binary
run: make build-nym-cli
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: nym-cli-${{ matrix.platform }}
path: |
target/release/nym-cli*
retention-days: 30
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
target/release/nym-cli
@@ -1,96 +0,0 @@
name: Publish Nym Connect (MacOS)
on:
workflow_dispatch:
release:
types: [created]
defaults:
run:
working-directory: nym-connect
jobs:
publish-tauri:
strategy:
fail-fast: false
matrix:
platform: [macos-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- name: Check the release tag starts with `nym-connect-`
if: startsWith(github.ref, 'refs/tags/nym-connect-') == false && github.event_name != 'workflow_dispatch'
uses: actions/github-script@v3
with:
script: |
core.setFailed('Release tag did not start with nym-connect-...')
- name: Node v16
uses: actions/setup-node@v3
with:
node-version: 16
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install the Apple developer certificate for code signing
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
# import certificate and provisioning profile from secrets
echo -n "$APPLE_CERTIFICATE" | base64 --decode --output $CERTIFICATE_PATH
# create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# import certificate to keychain
security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
- name: Create env file
uses: timheuer/base64-to-file@v1.1
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install app dependencies and build it
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
run: yarn && yarn build
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: nym-connect_1.0.0_x64.dmg
path: nym-connect/target/release/bundle/dmg/nym-connect_1.0.0_x64.dmg
retention-days: 30
- name: Clean up keychain
if: ${{ always() }}
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
nym-connect/target/release/bundle/dmg/*.dmg
nym-connect/target/release/bundle/macos/*.app.tar.gz*
@@ -1,68 +0,0 @@
name: Publish Nym Connect (Ubuntu)
on:
workflow_dispatch:
release:
types: [created]
defaults:
run:
working-directory: nym-connect
jobs:
publish-tauri:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- name: Tauri dependencies
run: >
sudo apt-get update &&
sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev
- name: Check the release tag starts with `nym-connect-`
if: startsWith(github.ref, 'refs/tags/nym-connect-') == false && github.event_name != 'workflow_dispatch'
uses: actions/github-script@v3
with:
script: |
core.setFailed('Release tag did not start with nym-connect-...')
- name: Node v16
uses: actions/setup-node@v3
with:
node-version: 16
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install app dependencies
run: yarn
- name: Create env file
uses: timheuer/base64-to-file@v1.1
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Build app
run: yarn build
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-connect.AppImage.tar.gz
path: nym-connect/target/release/bundle/appimage/nym-connect_1.0.0_amd64.AppImage
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-connect/target/release/bundle/appimage/*.AppImage
nym-connect/target/release/bundle/appimage/*.AppImage.tar.gz*
@@ -1,90 +0,0 @@
name: Publish Nym Connect (Windows 10)
on:
workflow_dispatch:
release:
types: [created]
defaults:
run:
working-directory: nym-connect
jobs:
publish-tauri:
strategy:
fail-fast: false
matrix:
platform: [windows10]
runs-on: ${{ matrix.platform }}
steps:
- name: Clean up first
continue-on-error: true
working-directory: .
run: |
cd ..
del /s /q /A:H nym
rmdir /s /q nym
- uses: actions/checkout@v3
- name: Check the release tag starts with `nym-connect-`
if: startsWith(github.ref, 'refs/tags/nym-connect-') == false && github.event_name != 'workflow_dispatch'
uses: actions/github-script@v3
with:
script: |
core.setFailed('Release tag did not start with nym-connect-...')
- name: Import signing certificate
env:
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
run: |
New-Item -ItemType directory -Path certificate
Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE
certutil -decode certificate/tempCert.txt certificate/certificate.pfx
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
uses: actions/setup-node@v3
with:
node-version: 16
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Create env file
uses: timheuer/base64-to-file@v1.1
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install app dependencies
run: yarn
- name: Build and sign it
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
run: yarn build
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: nym-connect_1.0.0_x64_en-US.msi
path: nym-connect/target/release/bundle/msi/nym-connect_1.0.0_x64_en-US.msi
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-connect/target/release/bundle/msi/*.msi
nym-connect/target/release/bundle/msi/*.msi.zip*
-59
View File
@@ -1,59 +0,0 @@
name: CI for nym-connect
on:
push:
paths:
- 'nym-connect/**'
defaults:
run:
working-directory: nym-connect
jobs:
build:
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Install Yarn
run: npm install -g yarn
- run: yarn
continue-on-error: true
- name: Set environment from the example
run: cp .env.sample .env
- run: yarn storybook:build
- name: Deploy branch to CI www
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "nym-connect/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/nym-connect-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Keybase - Node Install
run: npm install
working-directory: .github/workflows/support-files
# - name: Keybase - Send Notification
# env:
# NYM_NOTIFICATION_KIND: nym-connect
# NYM_PROJECT_NAME: "nym-connect"
# NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
# NYM_CI_WWW_LOCATION: "nym-connect-${{ env.GITHUB_REF_SLUG }}"
# GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
# GIT_BRANCH: "${GITHUB_REF##*/}"
# KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
# KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
# KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
# KEYBASE_NYM_CHANNEL: "ci-nym-connect"
# IS_SUCCESS: "${{ job.status == 'success' }}"
# uses: docker://keybaseio/client:stable-node
# with:
# args: .github/workflows/support-files/notifications/entry_point.sh
+1 -25
View File
@@ -1,12 +1,7 @@
name: Publish Nym binaries name: Publish Nym binaries
on: on:
workflow_dispatch:
release: release:
types: [created] types: [created]
env:
NETWORK: mainnet
jobs: jobs:
publish-nym: publish-nym:
@@ -20,7 +15,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Check the release tag starts with `nym-binaries-` - name: Check the release tag starts with `nym-binaries-`
if: startsWith(github.ref, 'refs/tags/nym-binaries-') == false && github.event_name != 'workflow_dispatch' if: startsWith(github.ref, 'refs/tags/nym-binaries-') == false
uses: actions/github-script@v3 uses: actions/github-script@v3
with: with:
script: | script: |
@@ -37,24 +32,8 @@ jobs:
command: build command: build
args: --workspace --release args: --workspace --release
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: my-artifact
path: |
target/release/nym-client
target/release/nym-gateway
target/release/nym-mixnode
target/release/nym-socks5-client
target/release/nym-validator-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
retention-days: 30
- name: Upload to release based on tag name - name: Upload to release based on tag name
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with: with:
files: | files: |
target/release/nym-client target/release/nym-client
@@ -62,6 +41,3 @@ jobs:
target/release/nym-mixnode target/release/nym-mixnode
target/release/nym-socks5-client target/release/nym-socks5-client
target/release/nym-validator-api target/release/nym-validator-api
target/release/nym-network-requester
target/release/nym-network-statistics
target/release/nym-cli
+8 -23
View File
@@ -1,6 +1,5 @@
name: Publish Nym Wallet (MacOS) name: Publish Nym Wallet (MacOS)
on: on:
workflow_dispatch:
release: release:
types: [created] types: [created]
@@ -20,16 +19,16 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Check the release tag starts with `nym-wallet-` - name: Check the release tag starts with `nym-wallet-`
if: startsWith(github.ref, 'refs/tags/nym-wallet-') == false && github.event_name != 'workflow_dispatch' if: startsWith(github.ref, 'refs/tags/nym-wallet-') == false
uses: actions/github-script@v3 uses: actions/github-script@v3
with: with:
script: | script: |
core.setFailed('Release tag did not start with nym-wallet-...') core.setFailed('Release tag did not start with nym-wallet-...')
- name: Node v16 - name: Node v16
uses: actions/setup-node@v3 uses: actions/setup-node@v1
with: with:
node-version: 16 node-version: 16.x
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
@@ -56,12 +55,6 @@ jobs:
security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH
- name: Create env file
uses: timheuer/base64-to-file@v1.1
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install app dependencies and build it - name: Install app dependencies and build it
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -75,22 +68,14 @@ jobs:
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
run: yarn && yarn build run: yarn && yarn build
- name: Upload Artifact - name: Upload to release based on tag name
uses: actions/upload-artifact@v3 uses: softprops/action-gh-release@v1
with: with:
name: nym-wallet.app.tar.gz files: |
path: nym-wallet/target/release/bundle/macos/nym-wallet.app.tar.gz nym-wallet/target/release/bundle/dmg/*.dmg
retention-days: 5 nym-wallet/target/release/bundle/macos/*.app.tar.gz*
- name: Clean up keychain - name: Clean up keychain
if: ${{ always() }} if: ${{ always() }}
run: | run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
if: github.event_name == 'release'
with:
files: |
nym-wallet/target/release/bundle/dmg/*.dmg
nym-wallet/target/release/bundle/macos/*.app.tar.gz*
@@ -30,21 +30,15 @@ jobs:
core.setFailed('Release tag did not start with nym-wallet-...') core.setFailed('Release tag did not start with nym-wallet-...')
- name: Node v16 - name: Node v16
uses: actions/setup-node@v3 uses: actions/setup-node@v1
with: with:
node-version: 16 node-version: 16.x
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
- name: Install app dependencies - name: Install app dependencies
run: yarn run: yarn
- name: Create env file
uses: timheuer/base64-to-file@v1.1
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Build app - name: Build app
run: yarn build run: yarn build
env: env:
@@ -45,21 +45,15 @@ jobs:
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText) 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 v16
uses: actions/setup-node@v3 uses: actions/setup-node@v1
with: with:
node-version: 16 node-version: 16.x
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
- name: Create env file
uses: timheuer/base64-to-file@v1.1
with:
fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install app dependencies - name: Install app dependencies
run: yarn run: yarn
-32
View File
@@ -1,32 +0,0 @@
name: Release Nym Wallet
on:
workflow_dispatch:
inputs:
nym_wallet_version:
description: 'The version of the Nym Wallet to release'
default: '1.0.x'
required: true
type: string
jobs:
create-release:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- name: Create release
uses: softprops/action-gh-release@v1
with:
body: >-
This is a pre-release
Download the wallet for your platform:
- [Linux](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_amd64_ubuntu20.04.AppImage)
- [MacOS](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_x64_macos_11.dmg)
- [Windows](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_x64_windows.msi)
prerelease: true
name: Nym Wallet v${{ inputs.nym_wallet_version}}
tag_name: nym-wallet-v${{ inputs.nym_wallet_version}}
+2 -2
View File
@@ -13,9 +13,9 @@ jobs:
- name: Install rsync - name: Install rsync
run: sudo apt-get install rsync run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x - uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v3 - uses: actions/setup-node@v2
with: with:
node-version: 16 node-version: '16'
- name: Setup yarn - name: Setup yarn
run: npm install -g yarn run: npm install -g yarn
- name: Build dependencies - name: Build dependencies
+2 -2
View File
@@ -34,9 +34,9 @@ jobs:
toolchain: stable toolchain: stable
- name: Node v16 - name: Node v16
uses: actions/setup-node@v3 uses: actions/setup-node@v1
with: with:
node-version: 16 node-version: 16.x
- name: Install yarn for building application - name: Install yarn for building application
run: yarn install run: yarn install
@@ -1,6 +1,5 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/ > :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION_STORYBOOK }}.{{ env.NYM_CI_WWW_BASE }}
> ✅ **SUCCESS** > ✅ **SUCCESS**
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }} > `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }} > `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
@@ -3,7 +3,7 @@ require('dotenv').config();
const Bot = require('keybase-bot'); const Bot = require('keybase-bot');
let context = { let context = {
kinds: ['nym-wallet', 'ts-packages', 'network-explorer', 'nightly', 'nym-connect'], kinds: ['nym-wallet', 'ts-packages', 'network-explorer', 'nightly'],
}; };
/** /**
@@ -89,7 +89,7 @@ async function sendKeybaseMessage(messageBody) {
}); });
const channel = { const channel = {
name: context.env.KEYBASE_NYMBOT_TEAM || 'nymtech_bot', name: 'nymtech_bot',
membersType: 'team', membersType: 'team',
topicName: context.keybase.channel, topicName: context.keybase.channel,
topic_type: 'CHAT', topic_type: 'CHAT',
@@ -1,29 +0,0 @@
const Handlebars = require('handlebars');
const fs = require('fs');
const path = require('path');
async function addToContextAndValidate(context) {
if (!context.env.NYM_CI_WWW_LOCATION) {
throw new Error('Please ensure the env var NYM_CI_WWW_LOCATION is set');
}
if (!context.env.NYM_CI_WWW_BASE) {
throw new Error('Please ensure the env var NYM_CI_WWW_BASE is set');
}
}
async function getMessageBody(context) {
const source = fs
.readFileSync(
context.env.IS_SUCCESS === 'true'
? path.resolve(__dirname, 'templates', 'success')
: path.resolve(__dirname, 'templates', 'failure'),
)
.toString();
const template = Handlebars.compile(source);
return template(context);
}
module.exports = {
addToContextAndValidate,
getMessageBody,
};
@@ -1,11 +0,0 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }}
> 🔴 **FAILURE** :cry:
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
Commit message:
```
{{ env.GIT_COMMIT_MESSAGE }}
```
@@ -1,11 +0,0 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View storybook:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
> ✅ **SUCCESS**
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
```
{{ env.GIT_COMMIT_MESSAGE }}
```
-59
View File
@@ -1,59 +0,0 @@
name: Nym Wallet (rust)
on:
push:
paths-ignore:
- 'explorer/**'
pull_request:
paths-ignore:
- 'explorer/**'
jobs:
build:
runs-on: [ self-hosted, custom-linux ]
env:
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
steps:
- name: Install Dependencies (Linux)
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
- name: Check out repository code
uses: actions/checkout@v2
- name: Install rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Run all tests
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path nym-wallet/Cargo.toml --workspace
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
- uses: actions-rs/clippy-check@v1
name: Clippy checks
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-features
- name: Run clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-features -- -D warnings
-251
View File
@@ -1,255 +1,5 @@
# Changelog # Changelog
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- nym-cli: added CLI tool for interacting with the Nyx blockchain and Nym mixnet smart contracts ([#1577])
- validator-client: added `query_contract_smart` and `query_contract_raw` on `NymdClient` ([#1558])
- network-requester: added additional Blockstream Green wallet endpoint to `example.allowed.list` ([#1611](https://github.com/nymtech/nym/pull/1611))
- common/ledger: new library for communicating with a Ledger device ([#1640])
- native-client/socks5-client: `disable_loop_cover_traffic_stream` Debug config option to disable the separate loop cover traffic stream ([#1666])
- native-client/socks5-client: `disable_main_poisson_packet_distribution` Debug config option to make the client ignore poisson distribution in the main packet stream and ONLY send real message (and as fast as they come) ([#1664])
### Fixed
- validator-api, mixnode, gateway should now prefer values in config.toml over mainnet defaults ([#1645])
### Changed
- validator-client: made `fee` argument optional for `execute` and `execute_multiple` ([#1541])
- socks5 client: graceful shutdown should fix error on disconnect in nym-connect ([#1591])
- wasm-client: fixed build errors on MacOS and changed example JS code to use mainnet ([#1585])
- gateway-client: will attempt to read now as many as 8 websocket messages at once, assuming they're already available on the socket ([#1669])
[#1541]: https://github.com/nymtech/nym/pull/1541
[#1558]: https://github.com/nymtech/nym/pull/1558
[#1577]: https://github.com/nymtech/nym/pull/1577
[#1585]: https://github.com/nymtech/nym/pull/1585
[#1591]: https://github.com/nymtech/nym/pull/1591
[#1640]: https://github.com/nymtech/nym/pull/1640
[#1645]: https://github.com/nymtech/nym/pull/1645
[#1664]: https://github.com/nymtech/nym/pull/1664
[#1666]: https://github.com/nymtech/nym/pull/1645
[#1669]: https://github.com/nymtech/nym/pull/1669
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
### Added
- socks5 client/websocket client: add `--force-register-gateway` flag, useful when rerunning init ([#1353])
- all: added network compilation target to `--help` (or `--version`) commands ([#1256]).
- explorer-api: learned how to sum the delegations by owner in a new endpoint.
- explorer-api: add apy values to `mix_nodes` endpoint
- gateway: Added gateway coconut verifications and validator-api communication for double spending protection ([#1261])
- network-explorer-ui: Upgrade to React Router 6
- rewarding: replace circulating supply with staking supply in reward calculations ([#1324])
- validator-api: add `estimated_node_profit` and `estimated_operator_cost` to `reward-estimate` endpoint ([#1284])
- validator-api: add detailed mixnode bond endpoints, and explorer-api makes use of that data to append stake saturation
- validator-api: add Swagger to document the REST API ([#1249]).
- validator-api: Added new endpoints for coconut spending flow and communications with coconut & multisig contracts ([#1261])
- validator-api: add `uptime`, `estimated_operator_apy`, `estimated_delegators_apy` to `/mixnodes/detailed` endpoint ([#1393])
- validator-api: add node info cache storing simulated active set inclusion probabilities
- network-statistics: a new mixnet service that aggregates and exposes anonymized data about mixnet services ([#1328])
- mixnode: Added basic mixnode hardware reporting to the HTTP API ([#1308]).
- validator-api: endpoint, in coconut mode, for returning the validator-api cosmos address ([#1404]).
- validator-client: add `denom` argument and add simple test for querying an account balance
- gateway, validator-api: Checks for coconut credential double spending attempts, taking the coconut bandwidth contract as source of truth ([#1457])
- coconut-bandwidth-contract: Record the state of a coconut credential; create specific proposal for releasing funds ([#1457])
- inclusion-probability: add simulator for active set inclusion probability
### Fixed
- mixnode, gateway: attempting to determine reconnection backoff to persistently failing mixnode could result in a crash ([#1260])
- mixnode: the mixnode learned how to shutdown gracefully
- mixnode: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service.
- native & socks5 clients: fail early when clients try to re-init with a different gateway, which is not supported yet ([#1322])
- native & socks5 clients: rerun init will now reuse previous gateway configuration instead of failing ([#1353])
- native & socks5 clients: deduplicate big chunks of init logic
- validator: fixed local docker-compose setup to work on Apple M1 ([#1329])
- explorer-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service ([#1482]).
- network-requester: fix filter for suffix-only domains ([#1487])
- validator-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service; cleaner shutdown, without panics ([#1496], [#1573]).
### Changed
- validator-client: created internal `Coin` type that replaces coins from `cosmrs` and `cosmwasm` for API entrypoints [[#1295]]
- all: updated all `cosmwasm`-related dependencies to `1.0.0` and `cw-storage-plus` to `0.13.4` [[#1318]]
- all: updated `rocket` to `0.5.0-rc.2`.
- network-requester: allow to voluntarily store and send statistical data about the number of bytes the proxied server serves ([#1328])
- gateway: allow to voluntarily send statistical data about the number of active inboxes served by a gateway ([#1376])
- gateway & mixnode: move detailed build info back to `--version` from `--help`.
- socks5 client/websocket client: upgrade to latest clap and switched to declarative commandline parsing.
- validator-api: fee payment for multisig operations comes from the gateway account instead of the validator APIs' accounts ([#1419])
- multisig-contract: Limit the proposal creating functionality to one address (coconut-bandwidth-contract address) ([#1457])
- All binaries and cosmwasm blobs are configured at runtime now; binaries are configured using environment variables or .env files and contracts keep the configuration parameters in storage ([#1463])
- gateway, network-statistics: include gateway id in the sent statistical data ([#1478])
- network explorer: tweak how active set probability is shown ([#1503])
- validator-api: rewarder set update fails without panicking on possible nymd queries ([#1520])
- network-requester, socks5 client (nym-connect): send and receive respectively a message error to be displayed about filter check failure ([#1576])
[#1249]: https://github.com/nymtech/nym/pull/1249
[#1256]: https://github.com/nymtech/nym/pull/1256
[#1260]: https://github.com/nymtech/nym/pull/1260
[#1261]: https://github.com/nymtech/nym/pull/1261
[#1267]: https://github.com/nymtech/nym/pull/1267
[#1278]: https://github.com/nymtech/nym/pull/1278
[#1295]: https://github.com/nymtech/nym/pull/1295
[#1302]: https://github.com/nymtech/nym/pull/1302
[#1308]: https://github.com/nymtech/nym/pull/1308
[#1318]: https://github.com/nymtech/nym/pull/1318
[#1322]: https://github.com/nymtech/nym/pull/1322
[#1324]: https://github.com/nymtech/nym/pull/1324
[#1328]: https://github.com/nymtech/nym/pull/1328
[#1329]: https://github.com/nymtech/nym/pull/1329
[#1353]: https://github.com/nymtech/nym/pull/1353
[#1376]: https://github.com/nymtech/nym/pull/1376
[#1393]: https://github.com/nymtech/nym/pull/1393
[#1404]: https://github.com/nymtech/nym/pull/1404
[#1419]: https://github.com/nymtech/nym/pull/1419
[#1457]: https://github.com/nymtech/nym/pull/1457
[#1463]: https://github.com/nymtech/nym/pull/1463
[#1478]: https://github.com/nymtech/nym/pull/1478
[#1482]: https://github.com/nymtech/nym/pull/1482
[#1487]: https://github.com/nymtech/nym/pull/1487
[#1496]: https://github.com/nymtech/nym/pull/1496
[#1503]: https://github.com/nymtech/nym/pull/1503
[#1520]: https://github.com/nymtech/nym/pull/1520
[#1573]: https://github.com/nymtech/nym/pull/1573
[#1576]: https://github.com/nymtech/nym/pull/1576
## [v1.0.1](https://github.com/nymtech/nym/tree/v1.0.1) (2022-05-04)
### Added
- validator-api: introduced endpoint for getting average mixnode uptime ([#1238])
### Changed
- all: the default behaviour of validator client is changed to use `broadcast_sync` and poll for transaction inclusion instead of using `broadcast_commit` to deal with timeouts ([#1246])
### Fixed
- nym-network-requester: is included in the Github Actions for building release binaries
[#1238]: https://github.com/nymtech/nym/pull/1238
[#1246]: https://github.com/nymtech/nym/pull/1246
## [v1.0.0](https://github.com/nymtech/nym/tree/v1.0.0) (2022-05-03)
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.1...v1.0.0)
**Merged pull requests:**
- Feature/show pending delegations [\#1229](https://github.com/nymtech/nym/pull/1229) ([fmtabbara](https://github.com/fmtabbara))
- Bucket inclusion probabilities [\#1224](https://github.com/nymtech/nym/pull/1224) ([durch](https://github.com/durch))
- Create a new bundled delegation when compounding rewards [\#1221](https://github.com/nymtech/nym/pull/1221) ([durch](https://github.com/durch))
## [nym-binaries-1.0.0](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0) (2022-04-27)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.3...nym-binaries-1.0.0)
## [nym-binaries-1.0.0-rc.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.2) (2022-04-15)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.2...nym-binaries-1.0.0-rc.2)
## [nym-binaries-1.0.0-rc.1](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.1) (2022-03-28)
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.0...nym-binaries-1.0.0-rc.1)
**Fixed bugs:**
- \[Issue\]cargo build --release issue [\#1101](https://github.com/nymtech/nym/issues/1101)
- appimage fail to load in Fedora [\#1098](https://github.com/nymtech/nym/issues/1098)
- \[Issue\] React Example project does not compile when using @nymproject/nym-client-wasm v0.9.0-1 [\#878](https://github.com/nymtech/nym/issues/878)
**Closed issues:**
- Make mainnet coin transfers work [\#1096](https://github.com/nymtech/nym/issues/1096)
- Make Nym wallet validators configurable at runtime [\#1026](https://github.com/nymtech/nym/issues/1026)
- Project Platypus e2e / integration testing [\#942](https://github.com/nymtech/nym/issues/942)
- \[Coconut\]: Replace ElGamal with Pedersen commitments [\#901](https://github.com/nymtech/nym/issues/901)
**Merged pull requests:**
- Different values for mixes and gateways [\#1169](https://github.com/nymtech/nym/pull/1169) ([durch](https://github.com/durch))
- Add global blacklist to validator-cache [\#1168](https://github.com/nymtech/nym/pull/1168) ([durch](https://github.com/durch))
- Feature/upgrade rewarding sandbox [\#1167](https://github.com/nymtech/nym/pull/1167) ([durch](https://github.com/durch))
- Bump node-forge from 1.2.1 to 1.3.0 [\#1165](https://github.com/nymtech/nym/pull/1165) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump minimist from 1.2.5 to 1.2.6 in /nym-wallet/webdriver [\#1164](https://github.com/nymtech/nym/pull/1164) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump minimist from 1.2.5 to 1.2.6 in /clients/tauri-client [\#1163](https://github.com/nymtech/nym/pull/1163) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump minimist from 1.2.5 to 1.2.6 in /clients/webassembly/js-example [\#1162](https://github.com/nymtech/nym/pull/1162) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump minimist from 1.2.5 to 1.2.6 in /clients/native/examples/js-examples/websocket [\#1160](https://github.com/nymtech/nym/pull/1160) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump minimist from 1.2.5 to 1.2.6 in /docker/typescript\_client/upload\_contract [\#1159](https://github.com/nymtech/nym/pull/1159) ([dependabot[bot]](https://github.com/apps/dependabot))
- Feature/vesting full [\#1158](https://github.com/nymtech/nym/pull/1158) ([fmtabbara](https://github.com/fmtabbara))
- get\_current\_epoch tauri [\#1156](https://github.com/nymtech/nym/pull/1156) ([durch](https://github.com/durch))
- Cleanup [\#1155](https://github.com/nymtech/nym/pull/1155) ([durch](https://github.com/durch))
- Feature flag reward payments [\#1154](https://github.com/nymtech/nym/pull/1154) ([durch](https://github.com/durch))
- Add Query endpoints for calculating rewards [\#1152](https://github.com/nymtech/nym/pull/1152) ([durch](https://github.com/durch))
- Pending endpoints [\#1150](https://github.com/nymtech/nym/pull/1150) ([durch](https://github.com/durch))
- wallet: add logging [\#1149](https://github.com/nymtech/nym/pull/1149) ([octol](https://github.com/octol))
- wallet: use Urls rather than Strings for validator urls [\#1148](https://github.com/nymtech/nym/pull/1148) ([octol](https://github.com/octol))
- Change accumulated reward to Option, migrate delegations [\#1147](https://github.com/nymtech/nym/pull/1147) ([durch](https://github.com/durch))
- wallet: fetch validators url remotely if available [\#1146](https://github.com/nymtech/nym/pull/1146) ([octol](https://github.com/octol))
- Fix delegated\_free calculation [\#1145](https://github.com/nymtech/nym/pull/1145) ([durch](https://github.com/durch))
- Update Nym wallet dependencies to use `ts-packages` [\#1144](https://github.com/nymtech/nym/pull/1144) ([mmsinclair](https://github.com/mmsinclair))
- wallet: try validators one by one if available [\#1143](https://github.com/nymtech/nym/pull/1143) ([octol](https://github.com/octol))
- Update Network Explorer Packages and add mix node identity key copy [\#1142](https://github.com/nymtech/nym/pull/1142) ([mmsinclair](https://github.com/mmsinclair))
- Feature/vesting token pool selector [\#1140](https://github.com/nymtech/nym/pull/1140) ([fmtabbara](https://github.com/fmtabbara))
- Add `ts-packages` for shared Typescript packages [\#1139](https://github.com/nymtech/nym/pull/1139) ([mmsinclair](https://github.com/mmsinclair))
- allow main-net prefix and denom to work [\#1137](https://github.com/nymtech/nym/pull/1137) ([tommyv1987](https://github.com/tommyv1987))
- Upgrade blake3 to v1.3.1 and tauri to 1.0.0-rc.3 [\#1136](https://github.com/nymtech/nym/pull/1136) ([mmsinclair](https://github.com/mmsinclair))
- Bump url-parse from 1.5.7 to 1.5.10 in /clients/native/examples/js-examples/websocket [\#1134](https://github.com/nymtech/nym/pull/1134) ([dependabot[bot]](https://github.com/apps/dependabot))
- Use network explorer map data with disputed areas [\#1133](https://github.com/nymtech/nym/pull/1133) ([Baro1905](https://github.com/Baro1905))
- Feature/vesting UI [\#1132](https://github.com/nymtech/nym/pull/1132) ([fmtabbara](https://github.com/fmtabbara))
- Refactor to a lazy rewarding system [\#1127](https://github.com/nymtech/nym/pull/1127) ([durch](https://github.com/durch))
- Bump ws from 6.2.1 to 6.2.2 in /clients/webassembly/js-example [\#1126](https://github.com/nymtech/nym/pull/1126) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump url-parse from 1.4.7 to 1.5.7 in /clients/webassembly/react-example [\#1125](https://github.com/nymtech/nym/pull/1125) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump url-parse from 1.5.4 to 1.5.7 in /clients/native/examples/js-examples/websocket [\#1124](https://github.com/nymtech/nym/pull/1124) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump url-parse from 1.5.1 to 1.5.7 in /clients/webassembly/js-example [\#1122](https://github.com/nymtech/nym/pull/1122) ([dependabot[bot]](https://github.com/apps/dependabot))
- update contract address [\#1121](https://github.com/nymtech/nym/pull/1121) ([tommyv1987](https://github.com/tommyv1987))
- Refactor GitHub Actions notifications [\#1119](https://github.com/nymtech/nym/pull/1119) ([mmsinclair](https://github.com/mmsinclair))
- Change `pledge` to `bond` in gateway list [\#1118](https://github.com/nymtech/nym/pull/1118) ([mmsinclair](https://github.com/mmsinclair))
- Bump follow-redirects from 1.14.7 to 1.14.8 in /contracts/basic-bandwidth-generation [\#1117](https://github.com/nymtech/nym/pull/1117) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.3 to 1.14.8 in /explorer [\#1116](https://github.com/nymtech/nym/pull/1116) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.5 to 1.14.8 in /nym-wallet [\#1115](https://github.com/nymtech/nym/pull/1115) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.7 to 1.14.8 in /clients/native/examples/js-examples/websocket [\#1114](https://github.com/nymtech/nym/pull/1114) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.7 to 1.14.8 in /testnet-faucet [\#1113](https://github.com/nymtech/nym/pull/1113) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump follow-redirects from 1.14.1 to 1.14.8 in /clients/webassembly/js-example [\#1112](https://github.com/nymtech/nym/pull/1112) ([dependabot[bot]](https://github.com/apps/dependabot))
- Feature/vesting get current period [\#1111](https://github.com/nymtech/nym/pull/1111) ([durch](https://github.com/durch))
- Bump simple-get from 2.8.1 to 2.8.2 in /contracts/basic-bandwidth-generation [\#1110](https://github.com/nymtech/nym/pull/1110) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump simple-get from 3.1.0 to 3.1.1 in /explorer [\#1109](https://github.com/nymtech/nym/pull/1109) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump simple-get from 3.1.0 to 3.1.1 in /clients/tauri-client [\#1108](https://github.com/nymtech/nym/pull/1108) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump simple-get from 3.1.0 to 3.1.1 in /nym-wallet [\#1107](https://github.com/nymtech/nym/pull/1107) ([dependabot[bot]](https://github.com/apps/dependabot))
- Bump node-sass from 4.14.1 to 7.0.0 in /clients/webassembly/react-example [\#1105](https://github.com/nymtech/nym/pull/1105) ([dependabot[bot]](https://github.com/apps/dependabot))
- Fix hardcoded period logic [\#1104](https://github.com/nymtech/nym/pull/1104) ([durch](https://github.com/durch))
- Fixed underflow in rewarding all delegators [\#1099](https://github.com/nymtech/nym/pull/1099) ([jstuczyn](https://github.com/jstuczyn))
- Emit original bond as part of rewarding event [\#1094](https://github.com/nymtech/nym/pull/1094) ([jstuczyn](https://github.com/jstuczyn))
- Add UpdateMixnodeConfigOnBehalf to vestng contract [\#1091](https://github.com/nymtech/nym/pull/1091) ([durch](https://github.com/durch))
- Fixes infinite loops in requests involving pagination [\#1085](https://github.com/nymtech/nym/pull/1085) ([jstuczyn](https://github.com/jstuczyn))
- Removes migration code [\#1071](https://github.com/nymtech/nym/pull/1071) ([jstuczyn](https://github.com/jstuczyn))
- feature/pedersen-commitments [\#1048](https://github.com/nymtech/nym/pull/1048) ([danielementary](https://github.com/danielementary))
- Feature/reuse init owner [\#970](https://github.com/nymtech/nym/pull/970) ([neacsu](https://github.com/neacsu))
## [v0.12.1](https://github.com/nymtech/nym/tree/v0.12.1) (2021-12-23)
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.0...v0.12.1)
**Implemented enhancements:**
- Add version check to binaries [\#967](https://github.com/nymtech/nym/issues/967)
**Fixed bugs:**
- \[Issue\] NYM wallet doesn't work after login [\#995](https://github.com/nymtech/nym/issues/995)
- \[Issue\] [\#993](https://github.com/nymtech/nym/issues/993)
- NYM wallet setup trouble\[Issue\] [\#958](https://github.com/nymtech/nym/issues/958)
## [v0.12.0](https://github.com/nymtech/nym/tree/v0.12.0) (2021-12-21) ## [v0.12.0](https://github.com/nymtech/nym/tree/v0.12.0) (2021-12-21)
[Full Changelog](https://github.com/nymtech/nym/compare/v0.11.0...v0.12.0) [Full Changelog](https://github.com/nymtech/nym/compare/v0.11.0...v0.12.0)
@@ -308,7 +58,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
**Merged pull requests:** **Merged pull requests:**
- Update wallet to align with versioning on nodes and gateways [\#991](https://github.com/nymtech/nym/pull/991) ([tommyv1987](https://github.com/tommyv1987))
- Fix success view messages. [\#990](https://github.com/nymtech/nym/pull/990) ([tommyv1987](https://github.com/tommyv1987)) - Fix success view messages. [\#990](https://github.com/nymtech/nym/pull/990) ([tommyv1987](https://github.com/tommyv1987))
- Feature/enable signature check [\#989](https://github.com/nymtech/nym/pull/989) ([neacsu](https://github.com/neacsu)) - Feature/enable signature check [\#989](https://github.com/nymtech/nym/pull/989) ([neacsu](https://github.com/neacsu))
- Update mixnet contract address [\#988](https://github.com/nymtech/nym/pull/988) ([neacsu](https://github.com/neacsu)) - Update mixnet contract address [\#988](https://github.com/nymtech/nym/pull/988) ([neacsu](https://github.com/neacsu))
Generated
+1093 -1328
View File
File diff suppressed because it is too large Load Diff
+5 -21
View File
@@ -9,10 +9,6 @@ overflow-checks = true
[profile.dev] [profile.dev]
panic = "abort" panic = "abort"
[profile.test]
# equivalent of running in `--release` (but since we're in test profile we're keeping overflow checks and all of those by default)
opt-level = 3
[workspace] [workspace]
resolver = "2" resolver = "2"
@@ -22,24 +18,19 @@ members = [
"clients/native", "clients/native",
"clients/native/websocket-requests", "clients/native/websocket-requests",
"clients/socks5", "clients/socks5",
"common/bandwidth-claim-contract", # "clients/tauri-client/src-tauri",
"common/client-libs/gateway-client", "common/client-libs/gateway-client",
"common/client-libs/mixnet-client", "common/client-libs/mixnet-client",
"common/client-libs/validator-client", "common/client-libs/validator-client",
"common/coconut-interface", "common/coconut-interface",
"common/commands",
"common/config", "common/config",
"common/credentials",
"common/crypto",
"common/bandwidth-claim-contract",
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract", "common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/contracts-common", "common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/mixnet-contract", "common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/vesting-contract", "common/cosmwasm-smart-contracts/vesting-contract",
"common/credential-storage",
"common/credentials",
"common/crypto",
"common/crypto/dkg",
"common/execute",
"common/inclusion-probability",
"common/mixnode-common", "common/mixnode-common",
"common/network-defaults", "common/network-defaults",
"common/nonexhaustive-delayqueue", "common/nonexhaustive-delayqueue",
@@ -57,21 +48,15 @@ members = [
"common/pemstore", "common/pemstore",
"common/socks5/proxy-helpers", "common/socks5/proxy-helpers",
"common/socks5/requests", "common/socks5/requests",
"common/statistics",
"common/task",
"common/topology", "common/topology",
"common/types",
"common/wasm-utils", "common/wasm-utils",
"explorer-api", "explorer-api",
"gateway", "gateway",
"gateway/gateway-requests", "gateway/gateway-requests",
"mixnode", "mixnode",
"service-providers/network-requester", "service-providers/network-requester",
"service-providers/network-statistics",
"validator-api", "validator-api",
"validator-api/validator-api-requests", "validator-api/validator-api-requests",
"tools/nym-cli",
"tools/ts-rs-cli"
] ]
default-members = [ default-members = [
@@ -79,10 +64,9 @@ default-members = [
"clients/socks5", "clients/socks5",
"gateway", "gateway",
"service-providers/network-requester", "service-providers/network-requester",
"service-providers/network-statistics",
"mixnode", "mixnode",
"validator-api", "validator-api",
"explorer-api", "explorer-api",
] ]
exclude = ["explorer", "contracts", "clients/webassembly", "nym-wallet", "nym-connect"] exclude = ["explorer", "contracts", "tokenomics-py", "clients/webassembly"]
+9 -62
View File
@@ -1,13 +1,11 @@
test: clippy-all cargo-test wasm fmt test: build clippy-all cargo-test wasm fmt
test-all: test cargo-test-expensive
no-clippy: build cargo-test wasm fmt no-clippy: build cargo-test wasm fmt
happy: fmt clippy-happy test happy: fmt clippy-happy test
clippy-all: clippy-main clippy-coconut clippy-all-contracts clippy-all-wallet clippy-all-connect clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet
cargo-test: test-main test-contracts test-wallet test-connect test-coconut cargo-test: test-main test-contracts test-wallet
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive test-coconut-expensive build: build-contracts build-wallet build-main
build: build-contracts build-wallet build-main build-connect fmt: fmt-main fmt-contracts fmt-wallet
fmt: fmt-main fmt-contracts fmt-wallet fmt-connect
clippy-happy-main: clippy-happy-main:
cargo clippy cargo clippy
@@ -18,18 +16,8 @@ clippy-happy-contracts:
clippy-happy-wallet: clippy-happy-wallet:
cargo clippy --manifest-path nym-wallet/Cargo.toml cargo clippy --manifest-path nym-wallet/Cargo.toml
clippy-happy-connect: clippy-all-main:
cargo clippy --manifest-path nym-connect/Cargo.toml cargo clippy --workspace --all-features -- -D warnings
clippy-main:
cargo clippy --workspace -- -D warnings
clippy-coconut:
cargo clippy --workspace --features coconut -- -D warnings
clippy-wasm:
cargo clippy --workspace --features wasm -- -D warnings
clippy-all-contracts: clippy-all-contracts:
cargo clippy --workspace --manifest-path contracts/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings cargo clippy --workspace --manifest-path contracts/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings
@@ -37,43 +25,15 @@ clippy-all-contracts:
clippy-all-wallet: clippy-all-wallet:
cargo clippy --workspace --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings cargo clippy --workspace --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings
clippy-all-connect:
cargo clippy --workspace --manifest-path nym-connect/Cargo.toml --all-features -- -D warnings
test-main: test-main:
cargo test --workspace cargo test --all-features --workspace
test-coconut:
cargo test --workspace --features coconut
test-wasm:
cargo test --workspace --features wasm
test-main-expensive:
cargo test --workspace -- --ignored
test-coconut-expensive:
cargo test --workspace --features coconut -- --ignored
test-contracts: test-contracts:
cargo test --manifest-path contracts/Cargo.toml --all-features cargo test --manifest-path contracts/Cargo.toml --all-features
test-contracts-expensive:
cargo test --manifest-path contracts/Cargo.toml --all-features -- --ignored
test-wallet: test-wallet:
cargo test --manifest-path nym-wallet/Cargo.toml --all-features cargo test --manifest-path nym-wallet/Cargo.toml --all-features
test-wallet-expensive:
cargo test --manifest-path nym-wallet/Cargo.toml --all-features -- --ignored
test-connect:
cargo test --manifest-path nym-connect/Cargo.toml --all-features
test-connect-expensive:
cargo test --manifest-path nym-connect/Cargo.toml --all-features -- --ignored
build-main: build-main:
cargo build --workspace cargo build --workspace
@@ -83,12 +43,6 @@ build-contracts:
build-wallet: build-wallet:
cargo build --manifest-path nym-wallet/Cargo.toml --workspace cargo build --manifest-path nym-wallet/Cargo.toml --workspace
build-connect:
cargo build --manifest-path nym-connect/Cargo.toml --workspace
build-nym-cli:
cargo build --release --manifest-path tools/nym-cli/Cargo.toml
fmt-main: fmt-main:
cargo fmt --all cargo fmt --all
@@ -98,12 +52,5 @@ fmt-contracts:
fmt-wallet: fmt-wallet:
cargo fmt --manifest-path nym-wallet/Cargo.toml --all cargo fmt --manifest-path nym-wallet/Cargo.toml --all
fmt-connect:
cargo fmt --manifest-path nym-connect/Cargo.toml --all
wasm: wasm:
RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
generate-typescript:
cd tools/ts-rs-cli && cargo run && cd ../..
yarn types:lint:fix
+18 -22
View File
@@ -9,8 +9,8 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
* nym-mixnode - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers. * nym-mixnode - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers.
* nym-client - an executable which you can build into your own applications. Use it for interacting with Nym nodes. * nym-client - an executable which you can build into your own applications. Use it for interacting with Nym nodes.
* nym-socks5-client - a Socks5 proxy you can run on your machine and use with existing applications. * nym-socks5-client - a Socks5 proxy you can run on your machine, and use with existing applications
* 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-gateway - acts sort of like a mailbox for mixnet messages, removing the need for directly 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-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-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.
@@ -40,40 +40,36 @@ Node, node operator and delegator rewards are determined according to the princi
|Symbol|Definition| |Symbol|Definition|
|---|---| |---|---|
|<img src="https://render.githubusercontent.com/render/math?math=R#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}R#gh-dark-mode-only">|global share of rewards available, starts at 2% of the reward pool. |<img src="https://render.githubusercontent.com/render/math?math=R">|global share of rewards available, starts at 2% of the reward pool.
|<img src="https://render.githubusercontent.com/render/math?math=R_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}R_{i}#gh-dark-mode-only">|node reward for mixnode `i`. |<img src="https://render.githubusercontent.com/render/math?math=R_{i}">|node reward for mixnode `i`.
|<img src="https://render.githubusercontent.com/render/math?math=\sigma_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\sigma_{i}#gh-dark-mode-only">|ratio of total node stake (node bond + all delegations) to the token circulating supply. |<img src="https://render.githubusercontent.com/render/math?math=\sigma_{i}">|ratio of total node stake (node bond + all delegations) to the token circulating supply.
|<img src="https://render.githubusercontent.com/render/math?math=\lambda_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\lambda_{i}#gh-dark-mode-only">|ratio of stake operator has pledged to their node to the token circulating supply. |<img src="https://render.githubusercontent.com/render/math?math=\lambda_{i}">|ratio of stake operator has pledged to their node to the token circulating supply.
|<img src="https://render.githubusercontent.com/render/math?math=\omega_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\omega_{i}#gh-dark-mode-only">|fraction of total effort undertaken by node `i`, set to `1/k`. |<img src="https://render.githubusercontent.com/render/math?math=\omega_{i}">|fraction of total effort undertaken by node `i`, set to `1/k`.
|<img src="https://render.githubusercontent.com/render/math?math=k#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}k#gh-dark-mode-only">|number of nodes stakeholders are incentivised to create, set by the validators, a matter of governance. Currently determined by the `reward set` size, and set to 720 in testnet Sandbox. |<img src="https://render.githubusercontent.com/render/math?math=k">|number of nodes stakeholders are incentivised to create, set by the validators, a matter of governance. Currently determined by the `reward set` size, and set to 720 in testnet Sandbox.
|<img src="https://render.githubusercontent.com/render/math?math=\alpha#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\alpha#gh-dark-mode-only">|Sybil attack resistance parameter - the higher this parameter is set the stronger the reduction in competitivness gets for a Sybil attacker. |<img src="https://render.githubusercontent.com/render/math?math=\alpha">|Sybil attack resistance parameter - the higher this parameter is set the stronger the reduction in competitivness gets for a Sybil attacker.
|<img src="https://render.githubusercontent.com/render/math?math=PM_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PM_{i}#gh-dark-mode-only">|declared profit margin of operator `i`, defaults to 10% in. |<img src="https://render.githubusercontent.com/render/math?math=PM_{i}">|declared profit margin of operator `i`, defaults to 10% in.
|<img src="https://render.githubusercontent.com/render/math?math=PF_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PF_{i}#gh-dark-mode-only">|uptime of node `i`, scaled to 0 - 1, for the rewarding epoch |<img src="https://render.githubusercontent.com/render/math?math=PF_{i}">|uptime of node `i`, scaled to 0 - 1, for the rewarding epoch
|<img src="https://render.githubusercontent.com/render/math?math=PP_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PP_{i}#gh-dark-mode-only">|cost of operating node `i` for the duration of the rewarding epoch, set to 40 NYMT. |<img src="https://render.githubusercontent.com/render/math?math=PP_{i}">|cost of operating node `i` for the duration of the rewarding eopoch, set to 40 NYMT.
Node reward for node `i` is determined as: Node reward for node `i` is determined as:
<img src="https://render.githubusercontent.com/render/math?math=R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)#gh-light-mode-only"> <img src="https://render.githubusercontent.com/render/math?math=R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)">
<img src="https://render.githubusercontent.com/render/math?math=\color{white}R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)#gh-dark-mode-only">
where: where:
<img src="https://render.githubusercontent.com/render/math?math=\sigma^'_{i} = min\{\sigma_{i}, 1/k\}#gh-light-mode-only"> <img src="https://render.githubusercontent.com/render/math?math=\sigma^'_{i} = min\{\sigma_{i}, 1/k\}">
<img src="https://render.githubusercontent.com/render/math?math=\color{white}\sigma^'_{i} = min\{\sigma_{i}, 1/k\}#gh-dark-mode-only">
and and
<img src="https://render.githubusercontent.com/render/math?math=\lambda^'_{i} = min\{\lambda_{i}, 1/k\}#gh-light-mode-only"> <img src="https://render.githubusercontent.com/render/math?math=\lambda^'_{i} = min\{\lambda_{i}, 1/k\}">
<img src="https://render.githubusercontent.com/render/math?math=\color{white}\lambda^'_{i} = min\{\lambda_{i}, 1/k\}#gh-dark-mode-only">
Operator of node `i` is credited with the following amount: Operator of node `i` is credited with the following amount:
<img src="https://render.githubusercontent.com/render/math?math=min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}#gh-light-mode-only"> <img src="https://render.githubusercontent.com/render/math?math=min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}">
<img src="https://render.githubusercontent.com/render/math?math=\color{white}min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}#gh-dark-mode-only">
Delegate with stake `s` recieves: Delegate with stake `s` recieves:
<img src="https://render.githubusercontent.com/render/math?math=max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}#gh-light-mode-only"> <img src="https://render.githubusercontent.com/render/math?math=max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}">
<img src="https://render.githubusercontent.com/render/math?math=\color{white}max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}#gh-dark-mode-only">
where `s'` is stake `s` scaled over total token circulating supply. where `s'` is stake `s` scaled over total token circulating supply.
-10
View File
@@ -1,10 +0,0 @@
Critical bug or security issue 💥
If you're here because you're trying to figure out how to notify us of a security issue, go to Discord, and alert the core engineers:
Dave Hrycyszyn futurechimp#5430
Drazen Urch drazen#4873
Jedrzej Stuczynski "Jedrzej | Nym#5666"
Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues.
-8
View File
@@ -1,8 +0,0 @@
Update fonts by doing the following:
1. Go to https://fonts.google.com/specimen/Open+Sans
2. Add all the styles you want and select `@import`
3. Copy the url (e.g. curl https://fonts.googleapis.com/css2\?family\=Open+Sans:ital,wght@0,300\;0,400\;0,500\;0,600\;0,700\;0,800\;1,300\;1,400\;1,500\;1,600\;1,700\;1,800\&display\=swap)
4. Run `curl curl https://fonts.googleapis.com/css2\?family\=Open+Sans:ital,wght@0,300\;0,400\;0,500\;0,600\;0,700\;0,800\;1,300\;1,400\;1,500\;1,600\;1,700\;1,800\&display\=swap`
5. Use the response as the CSS import directives and download the font files for each font weight
6. Remember to delete any old font files
-96
View File
@@ -1,96 +0,0 @@
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-stretch: normal;
font-display: swap;
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk5hkaVc.ttf) format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-stretch: normal;
font-display: swap;
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkaVc.ttf) format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 500;
font-stretch: normal;
font-display: swap;
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk_RkaVc.ttf) format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-stretch: normal;
font-display: swap;
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkxhjaVc.ttf) format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 700;
font-stretch: normal;
font-display: swap;
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkyFjaVc.ttf) format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 800;
font-stretch: normal;
font-display: swap;
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk0ZjaVc.ttf) format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: normal;
font-display: swap;
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0C4n.ttf) format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: normal;
font-display: swap;
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0C4n.ttf) format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 500;
font-stretch: normal;
font-display: swap;
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjr0C4n.ttf) format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: normal;
font-display: swap;
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsgH1y4n.ttf) format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: normal;
font-display: swap;
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsg-1y4n.ttf) format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 800;
font-stretch: normal;
font-display: swap;
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgshZ1y4n.ttf) format('truetype');
}
-7
View File
@@ -1,7 +0,0 @@
<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M171.7,30.3001 C132.7,-8.7999 69.3001,-8.7999 30.3001,30.3001 C-8.7999,69.4001 -8.7999,132.7 30.3001,171.7 C69.4001,210.8 132.7,210.8 171.7,171.7 C210.8,132.7 210.8,69.3001 171.7,30.3001 Z M163.1,163.1 C128.8,197.4 73.1001,197.4 38.8001,163.1 C4.5001,128.8 4.5001,73.1001 38.8001,38.8001 C73.1001,4.5001 128.8,4.5001 163.1,38.8001 C197.5,73.2001 197.5,128.8 163.1,163.1 Z" id="Shape" fill="#fff"></path>
<path d="M163.1,38.9 C128.8,4.60005 73.1002,4.60005 38.8002,38.9 C4.50019,73.2 4.50019,128.9 38.8002,163.2 C73.1002,197.5 128.8,197.5 163.1,163.2 C197.5,128.8 197.5,73.2 163.1,38.9 Z" id="Shape" fill="#000"></path>
<g id="T" transform="translate(25, 25) scale(5,5)">
<path d="M18.4804688,24 C19.203125,24 19.7182617,23.8608398 20.0258789,23.5825195 C20.3334961,23.3041992 20.4873047,22.9453125 20.4873047,22.5058594 C20.4873047,22.0566406 20.3334961,21.6928711 20.0258789,21.4145508 C19.7182617,21.1362305 19.203125,20.9970703 18.4804688,20.9970703 L18.4804688,20.9970703 L16.4589844,20.9970703 L16.4589844,9.24902344 L19.7548828,9.24902344 L19.7548828,12.0908203 C19.7548828,12.8134766 19.894043,13.3286133 20.1723633,13.6362305 C20.4506836,13.9438477 20.8095703,14.0976562 21.2490234,14.0976562 C21.6982422,14.0976562 22.0620117,13.9438477 22.340332,13.6362305 C22.6186523,13.3286133 22.7578125,12.8134766 22.7578125,12.0908203 L22.7578125,12.0908203 L22.7578125,6.24609375 L7.20117188,6.23144531 L7.20117188,12.0908203 C7.20117188,12.8134766 7.34033203,13.3286133 7.61865234,13.6362305 C7.89697266,13.9438477 8.25585938,14.0976562 8.6953125,14.0976562 C9.14453125,14.0976562 9.50830078,13.9438477 9.78662109,13.6362305 C10.0649414,13.3286133 10.2041016,12.8134766 10.2041016,12.0908203 L10.2041016,12.0908203 L10.2041016,9.24902344 L13.4560547,9.24902344 L13.4560547,20.9970703 L11.4492188,20.9970703 C10.7265625,20.9970703 10.2114258,21.1362305 9.90380859,21.4145508 C9.59619141,21.6928711 9.44238281,22.0517578 9.44238281,22.4912109 C9.44238281,22.9404297 9.59619141,23.3041992 9.90380859,23.5825195 C10.2114258,23.8608398 10.7265625,24 11.4492188,24 L11.4492188,24 L18.4804688,24 Z" id="T" fill="#fff"></path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

+1 -1
View File
@@ -1,4 +1,4 @@
<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M170.7 29.3001C131.7 -9.7999 68.3001 -9.7999 29.3001 29.3001C-9.7999 68.4001 -9.7999 131.7 29.3001 170.7C68.4001 209.8 131.7 209.8 170.7 170.7C209.8 131.7 209.8 68.3001 170.7 29.3001ZM162.1 162.1C127.8 196.4 72.1001 196.4 37.8001 162.1C3.5001 127.8 3.5001 72.1001 37.8001 37.8001C72.1001 3.5001 127.8 3.5001 162.1 37.8001C196.5 72.2001 196.5 127.8 162.1 162.1Z" fill="white"/> <path d="M170.7 29.3001C131.7 -9.7999 68.3001 -9.7999 29.3001 29.3001C-9.7999 68.4001 -9.7999 131.7 29.3001 170.7C68.4001 209.8 131.7 209.8 170.7 170.7C209.8 131.7 209.8 68.3001 170.7 29.3001ZM162.1 162.1C127.8 196.4 72.1001 196.4 37.8001 162.1C3.5001 127.8 3.5001 72.1001 37.8001 37.8001C72.1001 3.5001 127.8 3.5001 162.1 37.8001C196.5 72.2001 196.5 127.8 162.1 162.1Z" fill="white"/>
<path d="M162.1 37.9C127.8 3.60005 72.1002 3.60005 37.8002 37.9C3.50019 72.2 3.50019 127.9 37.8002 162.2C72.1002 196.5 127.8 196.5 162.1 162.2C196.5 127.8 196.5 72.2 162.1 37.9ZM63.0002 170.7C56.8002 167.4 51.1002 163.2 46.1002 158.4V41.7C51.3002 36.7 57.2002 32.5 63.6002 29.1L137 140.9V29.3C143.2 32.6 148.9 36.8 153.9 41.6V158.3C148.7 163.3 142.8 167.5 136.4 170.9L63.0002 59.1V170.7Z" fill="#070B15"/> <path d="M162.1 37.9C127.8 3.60005 72.1002 3.60005 37.8002 37.9C3.50019 72.2 3.50019 127.9 37.8002 162.2C72.1002 196.5 127.8 196.5 162.1 162.2C196.5 127.8 196.5 72.2 162.1 37.9ZM63.0002 170.7C56.8002 167.4 51.1002 163.2 46.1002 158.4V41.7C51.3002 36.7 57.2002 32.5 63.6002 29.1L137 140.9V29.3C143.2 32.6 148.9 36.8 153.9 41.6V158.3C148.7 163.3 142.8 167.5 136.4 170.9L63.0002 59.1V170.7Z" fill="#070B15"/>
<path d="M154 158.3V41.7C148.9 36.9 143.2 32.7 137.1 29.4V140.9L63.5 29C57.1 32.4 51.2 36.6 46 41.6V158.3C51.1 163.1 56.8 167.3 62.9 170.6V59.1L136.5 171C142.9 167.6 148.8 163.3 154 158.3Z" fill="white"/> <path d="M154 158.3V41.7C148.9 36.9 143.2 32.7 137.1 29.4V140.9L63.5 29C57.1 32.4 51.2 36.6 46 41.6V158.3C51.1 163.1 56.8 167.3 62.9 170.6V59.1L136.5 171C142.9 167.6 148.8 163.3 154 158.3Z" fill="white"/>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

-7
View File
@@ -1,7 +0,0 @@
<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M171.7,30.3001 C132.7,-8.7999 69.3001,-8.7999 30.3001,30.3001 C-8.7999,69.4001 -8.7999,132.7 30.3001,171.7 C69.4001,210.8 132.7,210.8 171.7,171.7 C210.8,132.7 210.8,69.3001 171.7,30.3001 Z M163.1,163.1 C128.8,197.4 73.1001,197.4 38.8001,163.1 C4.5001,128.8 4.5001,73.1001 38.8001,38.8001 C73.1001,4.5001 128.8,4.5001 163.1,38.8001 C197.5,73.2001 197.5,128.8 163.1,163.1 Z" id="Shape" fill="#141521"></path>
<path d="M163.1,38.9 C128.8,4.60005 73.1002,4.60005 38.8002,38.9 C4.50019,73.2 4.50019,128.9 38.8002,163.2 C73.1002,197.5 128.8,197.5 163.1,163.2 C197.5,128.8 197.5,73.2 163.1,38.9 Z" id="Shape" fill="#FFFFFF"></path>
<g id="T" transform="translate(25, 25) scale(5,5)">
<path d="M18.4804688,24 C19.203125,24 19.7182617,23.8608398 20.0258789,23.5825195 C20.3334961,23.3041992 20.4873047,22.9453125 20.4873047,22.5058594 C20.4873047,22.0566406 20.3334961,21.6928711 20.0258789,21.4145508 C19.7182617,21.1362305 19.203125,20.9970703 18.4804688,20.9970703 L18.4804688,20.9970703 L16.4589844,20.9970703 L16.4589844,9.24902344 L19.7548828,9.24902344 L19.7548828,12.0908203 C19.7548828,12.8134766 19.894043,13.3286133 20.1723633,13.6362305 C20.4506836,13.9438477 20.8095703,14.0976562 21.2490234,14.0976562 C21.6982422,14.0976562 22.0620117,13.9438477 22.340332,13.6362305 C22.6186523,13.3286133 22.7578125,12.8134766 22.7578125,12.0908203 L22.7578125,12.0908203 L22.7578125,6.24609375 L7.20117188,6.23144531 L7.20117188,12.0908203 C7.20117188,12.8134766 7.34033203,13.3286133 7.61865234,13.6362305 C7.89697266,13.9438477 8.25585938,14.0976562 8.6953125,14.0976562 C9.14453125,14.0976562 9.50830078,13.9438477 9.78662109,13.6362305 C10.0649414,13.3286133 10.2041016,12.8134766 10.2041016,12.0908203 L10.2041016,12.0908203 L10.2041016,9.24902344 L13.4560547,9.24902344 L13.4560547,20.9970703 L11.4492188,20.9970703 C10.7265625,20.9970703 10.2114258,21.1362305 9.90380859,21.4145508 C9.59619141,21.6928711 9.44238281,22.0517578 9.44238281,22.4912109 C9.44238281,22.9404297 9.59619141,23.3041992 9.90380859,23.5825195 C10.2114258,23.8608398 10.7265625,24 11.4492188,24 L11.4492188,24 L18.4804688,24 Z" id="T" fill="#000" fill-rule="nonzero"></path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

+1 -1
View File
@@ -1,4 +1,4 @@
<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M170.7 29.3001C131.7 -9.7999 68.3001 -9.7999 29.3001 29.3001C-9.7999 68.4001 -9.7999 131.7 29.3001 170.7C68.4001 209.8 131.7 209.8 170.7 170.7C209.8 131.7 209.8 68.3001 170.7 29.3001ZM162.1 162.1C127.8 196.4 72.1001 196.4 37.8001 162.1C3.5001 127.8 3.5001 72.1001 37.8001 37.8001C72.1001 3.5001 127.8 3.5001 162.1 37.8001C196.5 72.2001 196.5 127.8 162.1 162.1Z" fill="#141521"/> <path d="M170.7 29.3001C131.7 -9.7999 68.3001 -9.7999 29.3001 29.3001C-9.7999 68.4001 -9.7999 131.7 29.3001 170.7C68.4001 209.8 131.7 209.8 170.7 170.7C209.8 131.7 209.8 68.3001 170.7 29.3001ZM162.1 162.1C127.8 196.4 72.1001 196.4 37.8001 162.1C3.5001 127.8 3.5001 72.1001 37.8001 37.8001C72.1001 3.5001 127.8 3.5001 162.1 37.8001C196.5 72.2001 196.5 127.8 162.1 162.1Z" fill="#141521"/>
<path d="M162.1 37.9C127.8 3.60005 72.1002 3.60005 37.8002 37.9C3.50019 72.2 3.50019 127.9 37.8002 162.2C72.1002 196.5 127.8 196.5 162.1 162.2C196.5 127.8 196.5 72.2 162.1 37.9ZM63.0002 170.7C56.8002 167.4 51.1002 163.2 46.1002 158.4V41.7C51.3002 36.7 57.2002 32.5 63.6002 29.1L137 140.9V29.3C143.2 32.6 148.9 36.8 153.9 41.6V158.3C148.7 163.3 142.8 167.5 136.4 170.9L63.0002 59.1V170.7Z" fill="white"/> <path d="M162.1 37.9C127.8 3.60005 72.1002 3.60005 37.8002 37.9C3.50019 72.2 3.50019 127.9 37.8002 162.2C72.1002 196.5 127.8 196.5 162.1 162.2C196.5 127.8 196.5 72.2 162.1 37.9ZM63.0002 170.7C56.8002 167.4 51.1002 163.2 46.1002 158.4V41.7C51.3002 36.7 57.2002 32.5 63.6002 29.1L137 140.9V29.3C143.2 32.6 148.9 36.8 153.9 41.6V158.3C148.7 163.3 142.8 167.5 136.4 170.9L63.0002 59.1V170.7Z" fill="white"/>
<path d="M154 158.3V41.7C148.9 36.9 143.2 32.7 137.1 29.4V140.9L63.5 29C57.1 32.4 51.2 36.6 46 41.6V158.3C51.1 163.1 56.8 167.3 62.9 170.6V59.1L136.5 171C142.9 167.6 148.8 163.3 154 158.3Z" fill="#141521"/> <path d="M154 158.3V41.7C148.9 36.9 143.2 32.7 137.1 29.4V140.9L63.5 29C57.1 32.4 51.2 36.6 46 41.6V158.3C51.1 163.1 56.8 167.3 62.9 170.6V59.1L136.5 171C142.9 167.6 148.8 163.3 154 158.3Z" fill="#141521"/>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

+6 -30
View File
@@ -1,59 +1,35 @@
[package] [package]
name = "client-core" name = "client-core"
version = "1.0.1" version = "1.0.0-rc.1"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
dirs = "4.0" dirs = "3.0"
futures = "0.3" futures = "0.3"
humantime-serde = "1.0" humantime-serde = "1.0"
log = "0.4" log = "0.4"
rand = { version = "0.7.3", features = ["wasm-bindgen"] } rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
sled = { version = "0.34", optional = true } sled = "0.34"
thiserror = "1.0.34" tokio = { version = "1.4", features = ["macros"] }
url = { version ="2.2", features = ["serde"] } url = { version ="2.2", features = ["serde"] }
# internal # internal
config = { path = "../../common/config" } config = { path = "../../common/config" }
crypto = { path = "../../common/crypto" } crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" } gateway-client = { path = "../../common/client-libs/gateway-client" }
#gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm", "coconut"] }
gateway-requests = { path = "../../gateway/gateway-requests" } gateway-requests = { path = "../../gateway/gateway-requests" }
nonexhaustive-delayqueue = { path = "../../common/nonexhaustive-delayqueue" } nonexhaustive-delayqueue = { path = "../../common/nonexhaustive-delayqueue" }
nymsphinx = { path = "../../common/nymsphinx" } nymsphinx = { path = "../../common/nymsphinx" }
pemstore = { path = "../../common/pemstore" } pemstore = { path = "../../common/pemstore" }
topology = { path = "../../common/topology" } topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", default-features = false } validator-client = { path = "../../common/client-libs/validator-client" }
tap = "1.0.1"
tokio = { version = "1.21.2", features = ["time", "macros"]}
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen-futures]
version = "0.4"
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
version = "0.2.83"
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-timer]
git = "https://github.com/mmsinclair/wasm-timer"
rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6"
[target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers]
version = "0.2.4"
features = ["futures"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.task]
path = "../../common/task"
[dev-dependencies] [dev-dependencies]
tempfile = "3.1.0" tempfile = "3.1.0"
[features] [features]
default = ["reply-surb"] coconut = ["gateway-client/coconut", "gateway-requests/coconut"]
wasm = ["gateway-client/wasm"]
coconut = ["gateway-client/coconut", "gateway-requests/coconut"]
reply-surb = ["sled"]
@@ -3,26 +3,19 @@
use crate::client::mix_traffic::BatchMixMessageSender; use crate::client::mix_traffic::BatchMixMessageSender;
use crate::client::topology_control::TopologyAccessor; use crate::client::topology_control::TopologyAccessor;
use crate::spawn_future;
use futures::task::{Context, Poll}; use futures::task::{Context, Poll};
use futures::{Future, Stream, StreamExt}; use futures::{Future, Stream, StreamExt};
use log::*; use log::*;
use nymsphinx::acknowledgements::AckKey; use nymsphinx::acknowledgements::AckKey;
use nymsphinx::addressing::clients::Recipient; use nymsphinx::addressing::clients::Recipient;
use nymsphinx::cover::generate_loop_cover_packet; use nymsphinx::cover::generate_loop_cover_packet;
use nymsphinx::params::PacketSize;
use nymsphinx::utils::sample_poisson_duration; use nymsphinx::utils::sample_poisson_duration;
use rand::{rngs::OsRng, CryptoRng, Rng}; use rand::{rngs::OsRng, CryptoRng, Rng};
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use tokio::task::JoinHandle;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time; use tokio::time;
#[cfg(target_arch = "wasm32")]
use wasm_timer;
pub struct LoopCoverTrafficStream<R> pub struct LoopCoverTrafficStream<R>
where where
R: CryptoRng + Rng, R: CryptoRng + Rng,
@@ -31,22 +24,18 @@ where
ack_key: Arc<AckKey>, ack_key: Arc<AckKey>,
/// Average delay an acknowledgement packet is going to get delay at a single mixnode. /// Average delay an acknowledgement packet is going to get delay at a single mixnode.
average_ack_delay: Duration, average_ack_delay: time::Duration,
/// Average delay a data packet is going to get delay at a single mixnode. /// Average delay a data packet is going to get delay at a single mixnode.
average_packet_delay: Duration, average_packet_delay: time::Duration,
/// Average delay between sending subsequent cover packets. /// Average delay between sending subsequent cover packets.
average_cover_message_sending_delay: Duration, average_cover_message_sending_delay: time::Duration,
/// Internal state, determined by `average_message_sending_delay`, /// Internal state, determined by `average_message_sending_delay`,
/// used to keep track of when a next packet should be sent out. /// used to keep track of when a next packet should be sent out.
#[cfg(not(target_arch = "wasm32"))]
next_delay: Pin<Box<time::Sleep>>, next_delay: Pin<Box<time::Sleep>>,
#[cfg(target_arch = "wasm32")]
next_delay: Pin<Box<wasm_timer::Delay>>,
/// Channel used for sending prepared sphinx packets to `MixTrafficController` that sends them /// Channel used for sending prepared sphinx packets to `MixTrafficController` that sends them
/// out to the network without any further delays. /// out to the network without any further delays.
mix_tx: BatchMixMessageSender, mix_tx: BatchMixMessageSender,
@@ -59,9 +48,6 @@ where
/// Accessor to the common instance of network topology. /// Accessor to the common instance of network topology.
topology_access: TopologyAccessor, topology_access: TopologyAccessor,
/// Predefined packet size used for the loop cover messages.
packet_size: PacketSize,
} }
impl<R> Stream for LoopCoverTrafficStream<R> impl<R> Stream for LoopCoverTrafficStream<R>
@@ -83,21 +69,13 @@ where
// we know it's time to send a message, so let's prepare delay for the next one // we know it's time to send a message, so let's prepare delay for the next one
// Get the `now` by looking at the current `delay` deadline // Get the `now` by looking at the current `delay` deadline
let avg_delay = self.average_cover_message_sending_delay; let avg_delay = self.average_cover_message_sending_delay;
let now = self.next_delay.deadline();
let next_poisson_delay = sample_poisson_duration(&mut self.rng, avg_delay); let next_poisson_delay = sample_poisson_duration(&mut self.rng, avg_delay);
// The next interval value is `next_poisson_delay` after the one that just // The next interval value is `next_poisson_delay` after the one that just
// yielded. // yielded.
#[cfg(not(target_arch = "wasm32"))] let next = now + next_poisson_delay;
{ self.next_delay.as_mut().reset(next);
let now = self.next_delay.deadline();
let next = now + next_poisson_delay;
self.next_delay.as_mut().reset(next);
}
#[cfg(target_arch = "wasm32")]
{
self.next_delay.as_mut().reset(next_poisson_delay);
}
Poll::Ready(Some(())) Poll::Ready(Some(()))
} }
@@ -106,42 +84,30 @@ where
// obviously when we finally make shared rng that is on 'higher' level, this should become // obviously when we finally make shared rng that is on 'higher' level, this should become
// generic `R` // generic `R`
impl LoopCoverTrafficStream<OsRng> { impl LoopCoverTrafficStream<OsRng> {
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
ack_key: Arc<AckKey>, ack_key: Arc<AckKey>,
average_ack_delay: Duration, average_ack_delay: time::Duration,
average_packet_delay: Duration, average_packet_delay: time::Duration,
average_cover_message_sending_delay: Duration, average_cover_message_sending_delay: time::Duration,
mix_tx: BatchMixMessageSender, mix_tx: BatchMixMessageSender,
our_full_destination: Recipient, our_full_destination: Recipient,
topology_access: TopologyAccessor, topology_access: TopologyAccessor,
) -> Self { ) -> Self {
let rng = OsRng; let rng = OsRng;
#[cfg(not(target_arch = "wasm32"))]
let next_delay = Box::pin(time::sleep(Default::default()));
#[cfg(target_arch = "wasm32")]
let next_delay = Box::pin(wasm_timer::Delay::new(Default::default()));
LoopCoverTrafficStream { LoopCoverTrafficStream {
ack_key, ack_key,
average_ack_delay, average_ack_delay,
average_packet_delay, average_packet_delay,
average_cover_message_sending_delay, average_cover_message_sending_delay,
next_delay, next_delay: Box::pin(time::sleep(Default::default())),
mix_tx, mix_tx,
our_full_destination, our_full_destination,
rng, rng,
topology_access, topology_access,
packet_size: Default::default(),
} }
} }
pub fn set_custom_packet_size(&mut self, packet_size: PacketSize) {
self.packet_size = packet_size;
}
async fn on_new_message(&mut self) { async fn on_new_message(&mut self) {
trace!("next cover message!"); trace!("next cover message!");
@@ -167,7 +133,6 @@ impl LoopCoverTrafficStream<OsRng> {
&self.our_full_destination, &self.our_full_destination,
self.average_ack_delay, self.average_ack_delay,
self.average_packet_delay, self.average_packet_delay,
self.packet_size,
) )
.expect("Somehow failed to generate a loop cover message with a valid topology"); .expect("Somehow failed to generate a loop cover message with a valid topology");
@@ -184,56 +149,24 @@ impl LoopCoverTrafficStream<OsRng> {
// JS: due to identical logical structure to OutQueueControl::on_message(), this is also // JS: due to identical logical structure to OutQueueControl::on_message(), this is also
// presumably required to prevent bugs in the future. Exact reason is still unknown to me. // presumably required to prevent bugs in the future. Exact reason is still unknown to me.
// TODO: temporary and BAD workaround for wasm (we should find a way to yield here in wasm)
#[cfg(not(target_arch = "wasm32"))]
tokio::task::yield_now().await; tokio::task::yield_now().await;
} }
#[cfg(not(target_arch = "wasm32"))] async fn run(&mut self) {
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) {
// we should set initial delay only when we actually start the stream // we should set initial delay only when we actually start the stream
let sampled = self.next_delay = Box::pin(time::sleep(sample_poisson_duration(
sample_poisson_duration(&mut self.rng, self.average_cover_message_sending_delay); &mut self.rng,
self.next_delay = Box::pin(time::sleep(sampled)); self.average_cover_message_sending_delay,
)));
spawn_future(async move { while self.next().await.is_some() {
debug!("Started LoopCoverTrafficStream with graceful shutdown support"); self.on_new_message().await;
}
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
log::trace!("LoopCoverTrafficStream: Received shutdown");
}
next = self.next() => {
if next.is_some() {
self.on_new_message().await;
} else {
log::trace!("LoopCoverTrafficStream: Stopping since channel closed");
break;
}
}
}
}
assert!(shutdown.is_shutdown_poll());
log::debug!("LoopCoverTrafficStream: Exiting");
})
} }
#[cfg(target_arch = "wasm32")] pub fn start(mut self) -> JoinHandle<()> {
pub fn start(mut self) { tokio::spawn(async move {
// we should set initial delay only when we actually start the stream self.run().await;
let sampled =
sample_poisson_duration(&mut self.rng, self.average_cover_message_sending_delay);
self.next_delay = Box::pin(wasm_timer::Delay::new(sampled));
spawn_future(async move {
debug!("Started LoopCoverTrafficStream without graceful shutdown support");
while self.next().await.is_some() {
self.on_new_message().await;
}
}) })
} }
} }
+8 -33
View File
@@ -1,12 +1,12 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::spawn_future;
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::StreamExt; use futures::StreamExt;
use gateway_client::GatewayClient; use gateway_client::GatewayClient;
use log::*; use log::*;
use nymsphinx::forwarding::packet::MixPacket; use nymsphinx::forwarding::packet::MixPacket;
use tokio::task::JoinHandle;
pub type BatchMixMessageSender = mpsc::UnboundedSender<Vec<MixPacket>>; pub type BatchMixMessageSender = mpsc::UnboundedSender<Vec<MixPacket>>;
pub type BatchMixMessageReceiver = mpsc::UnboundedReceiver<Vec<MixPacket>>; pub type BatchMixMessageReceiver = mpsc::UnboundedReceiver<Vec<MixPacket>>;
@@ -65,40 +65,15 @@ impl MixTrafficController {
} }
} }
#[cfg(not(target_arch = "wasm32"))] pub async fn run(&mut self) {
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) { while let Some(mix_packets) = self.mix_rx.next().await {
spawn_future(async move { self.on_messages(mix_packets).await;
debug!("Started MixTrafficController with graceful shutdown support"); }
while !shutdown.is_shutdown() {
tokio::select! {
mix_packets = self.mix_rx.next() => match mix_packets {
Some(mix_packets) => {
self.on_messages(mix_packets).await;
},
None => {
log::trace!("MixTrafficController: Stopping since channel closed");
break;
}
},
_ = shutdown.recv() => {
log::trace!("MixTrafficController: Received shutdown");
}
}
}
assert!(shutdown.is_shutdown_poll());
log::debug!("MixTrafficController: Exiting");
})
} }
#[cfg(target_arch = "wasm32")] pub fn start(mut self) -> JoinHandle<()> {
pub fn start(mut self) { tokio::spawn(async move {
spawn_future(async move { self.run().await;
debug!("Started MixTrafficController without graceful shutdown support");
while let Some(mix_packets) = self.mix_rx.next().await {
self.on_messages(mix_packets).await;
}
}) })
} }
} }
-10
View File
@@ -1,18 +1,8 @@
use std::sync::atomic::AtomicBool;
pub mod cover_traffic_stream; pub mod cover_traffic_stream;
pub mod inbound_messages; pub mod inbound_messages;
pub mod key_manager; pub mod key_manager;
pub mod mix_traffic; pub mod mix_traffic;
pub mod real_messages_control; pub mod real_messages_control;
pub mod received_buffer; pub mod received_buffer;
#[cfg(feature = "reply-surb")]
pub mod reply_key_storage; pub mod reply_key_storage;
pub mod topology_control; pub mod topology_control;
// This is *NOT* used to signal shutdown.
// It's critical that we don't have any tasks finishing early, this is an additional safety check
// that tasks exiting are doing so because shutdown has been signalled, and no other reason.
// In particular for tasks that rely on their associated channel being closed to signal shutdown,
// and don't have access to a shutdown listener channel.
pub static SHUTDOWN_HAS_BEEN_SIGNALLED: AtomicBool = AtomicBool::new(false);
@@ -63,41 +63,14 @@ impl AcknowledgementListener {
.unwrap(); .unwrap();
} }
async fn handle_ack_receiver_item(&mut self, item: Vec<Vec<u8>>) { pub(super) async fn run(&mut self) {
// realistically we would only be getting one ack at the time debug!("Started AcknowledgementListener");
for ack in item { while let Some(acks) = self.ack_receiver.next().await {
self.on_ack(ack).await; // realistically we would only be getting one ack at the time
} for ack in acks {
} self.on_ack(ack).await;
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started AcknowledgementListener with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
acks = self.ack_receiver.next() => match acks {
Some(acks) => self.handle_ack_receiver_item(acks).await,
None => {
log::trace!("AcknowledgementListener: Stopping since channel closed");
break;
}
},
_ = shutdown.recv() => {
log::trace!("AcknowledgementListener: Received shutdown");
}
} }
} }
assert!(shutdown.is_shutdown_poll()); error!("TODO: error msg. Or maybe panic?")
log::debug!("AcknowledgementListener: Exiting");
}
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) {
debug!("Started AcknowledgementListener without graceful shutdown support");
while let Some(acks) = self.ack_receiver.next().await {
self.handle_ack_receiver_item(acks).await
}
} }
} }
@@ -6,7 +6,7 @@ use crate::client::real_messages_control::acknowledgement_control::Retransmissio
use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
use futures::StreamExt; use futures::StreamExt;
use log::*; use log::*;
use nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue, QueueKey}; use nonexhaustive_delayqueue::{Expired, NonExhaustiveDelayQueue, QueueKey, TimerError};
use nymsphinx::chunking::fragment::FragmentIdentifier; use nymsphinx::chunking::fragment::FragmentIdentifier;
use nymsphinx::Delay as SphinxDelay; use nymsphinx::Delay as SphinxDelay;
use std::collections::HashMap; use std::collections::HashMap;
@@ -209,11 +209,16 @@ impl ActionController {
} }
// note: when the entry expires it's automatically removed from pending_acks_timers // note: when the entry expires it's automatically removed from pending_acks_timers
fn handle_expired_ack_timer(&mut self, expired_ack: Expired<FragmentIdentifier>) { fn handle_expired_ack_timer(
&mut self,
expired_ack: Result<Expired<FragmentIdentifier>, TimerError>,
) {
// I'm honestly not sure how to handle it, because getting it means other things in our // I'm honestly not sure how to handle it, because getting it means other things in our
// system are already misbehaving. If we ever see this panic, then I guess we should worry // system are already misbehaving. If we ever see this panic, then I guess we should worry
// about it. Perhaps just reschedule it at later point? // about it. Perhaps just reschedule it at later point?
let frag_id = expired_ack.into_inner(); let frag_id = expired_ack
.expect("Tokio timer returned an error!")
.into_inner();
trace!("{} has expired", frag_id); trace!("{} has expired", frag_id);
@@ -245,44 +250,13 @@ impl ActionController {
} }
} }
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started ActionController with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
action = self.incoming_actions.next() => match action {
Some(action) => self.process_action(action),
None => {
log::trace!(
"ActionController: Stopping since incoming actions channel closed"
);
break;
}
},
expired_ack = self.pending_acks_timers.next() => match expired_ack {
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack),
None => {
log::trace!("ActionController: Stopping since ack channel closed");
break;
}
},
_ = shutdown.recv() => {
log::trace!("ActionController: Received shutdown");
}
}
}
assert!(shutdown.is_shutdown_poll());
log::debug!("ActionController: Exiting");
}
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) { pub(super) async fn run(&mut self) {
debug!("Started ActionController without graceful shutdown support");
loop { loop {
// at some point there will be a global shutdown signal here as the third option
tokio::select! { tokio::select! {
// we NEVER expect for ANY sender to get dropped so unwrap here is fine
action = self.incoming_actions.next() => self.process_action(action.unwrap()), action = self.incoming_actions.next() => self.process_action(action.unwrap()),
// pending ack queue Stream CANNOT return a `None` so unwrap here is fine
expired_ack = self.pending_acks_timers.next() => self.handle_expired_ack_timer(expired_ack.unwrap()) expired_ack = self.pending_acks_timers.next() => self.handle_expired_ack_timer(expired_ack.unwrap())
} }
} }
@@ -3,6 +3,7 @@
use super::action_controller::{Action, ActionSender}; use super::action_controller::{Action, ActionSender};
use super::PendingAcknowledgement; use super::PendingAcknowledgement;
use crate::client::reply_key_storage::ReplyKeyStorage;
use crate::client::{ use crate::client::{
inbound_messages::{InputMessage, InputMessageReceiver}, inbound_messages::{InputMessage, InputMessageReceiver},
real_messages_control::real_traffic_stream::{BatchRealMessageSender, RealMessage}, real_messages_control::real_traffic_stream::{BatchRealMessageSender, RealMessage},
@@ -16,9 +17,6 @@ use nymsphinx::{acknowledgements::AckKey, addressing::clients::Recipient};
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use std::sync::Arc; use std::sync::Arc;
#[cfg(feature = "reply-surb")]
use crate::client::reply_key_storage::ReplyKeyStorage;
/// Module responsible for dealing with the received messages: splitting them, creating acknowledgements, /// Module responsible for dealing with the received messages: splitting them, creating acknowledgements,
/// putting everything into sphinx packets, etc. /// putting everything into sphinx packets, etc.
/// It also makes an initial sending attempt for said messages. /// It also makes an initial sending attempt for said messages.
@@ -33,7 +31,6 @@ where
action_sender: ActionSender, action_sender: ActionSender,
real_message_sender: BatchRealMessageSender, real_message_sender: BatchRealMessageSender,
topology_access: TopologyAccessor, topology_access: TopologyAccessor,
#[cfg(feature = "reply-surb")]
reply_key_storage: ReplyKeyStorage, reply_key_storage: ReplyKeyStorage,
} }
@@ -52,7 +49,7 @@ where
action_sender: ActionSender, action_sender: ActionSender,
real_message_sender: BatchRealMessageSender, real_message_sender: BatchRealMessageSender,
topology_access: TopologyAccessor, topology_access: TopologyAccessor,
#[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage, reply_key_storage: ReplyKeyStorage,
) -> Self { ) -> Self {
InputMessageListener { InputMessageListener {
ack_key, ack_key,
@@ -62,7 +59,6 @@ where
action_sender, action_sender,
real_message_sender, real_message_sender,
topology_access, topology_access,
#[cfg(feature = "reply-surb")]
reply_key_storage, reply_key_storage,
} }
} }
@@ -121,16 +117,12 @@ where
.prepare_and_split_message(content, with_reply_surb, topology) .prepare_and_split_message(content, with_reply_surb, topology)
.expect("somehow the topology was invalid after all!"); .expect("somehow the topology was invalid after all!");
#[cfg(feature = "reply-surb")]
if let Some(reply_key) = reply_key { if let Some(reply_key) = reply_key {
self.reply_key_storage self.reply_key_storage
.insert_encryption_key(reply_key) .insert_encryption_key(reply_key)
.expect("Failed to insert surb reply key to the store!") .expect("Failed to insert surb reply key to the store!")
} }
#[cfg(not(feature = "reply-surb"))]
let _reply_key = reply_key;
// encrypt chunks, put them inside sphinx packets and generate acks // encrypt chunks, put them inside sphinx packets and generate acks
let mut pending_acks = Vec::with_capacity(split_message.len()); let mut pending_acks = Vec::with_capacity(split_message.len());
let mut real_messages = Vec::with_capacity(split_message.len()); let mut real_messages = Vec::with_capacity(split_message.len());
@@ -141,6 +133,7 @@ where
let prepared_fragment = self let prepared_fragment = self
.message_preparer .message_preparer
.prepare_chunk_for_sending(chunk_clone, topology, &self.ack_key, &recipient) .prepare_chunk_for_sending(chunk_clone, topology, &self.ack_key, &recipient)
.await
.unwrap(); .unwrap();
real_messages.push(RealMessage::new( real_messages.push(RealMessage::new(
@@ -188,35 +181,11 @@ where
} }
} }
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started InputMessageListener with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
input_msg = self.input_receiver.next() => match input_msg {
Some(input_msg) => {
self.on_input_message(input_msg).await;
},
None => {
log::trace!("InputMessageListener: Stopping since channel closed");
break;
}
},
_ = shutdown.recv() => {
log::trace!("InputMessageListener: Received shutdown");
}
}
}
assert!(shutdown.is_shutdown_poll());
log::debug!("InputMessageListener: Exiting");
}
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) { pub(super) async fn run(&mut self) {
debug!("Started InputMessageListener without graceful shutdown support"); debug!("Started InputMessageListener");
while let Some(input_msg) = self.input_receiver.next().await { while let Some(input_msg) = self.input_receiver.next().await {
self.on_input_message(input_msg).await; self.on_input_message(input_msg).await;
} }
error!("TODO: error msg. Or maybe panic?")
} }
} }
@@ -8,12 +8,11 @@ use self::{
sent_notification_listener::SentNotificationListener, sent_notification_listener::SentNotificationListener,
}; };
use super::real_traffic_stream::BatchRealMessageSender; use super::real_traffic_stream::BatchRealMessageSender;
use crate::client::reply_key_storage::ReplyKeyStorage;
use crate::client::{inbound_messages::InputMessageReceiver, topology_control::TopologyAccessor}; use crate::client::{inbound_messages::InputMessageReceiver, topology_control::TopologyAccessor};
use crate::spawn_future;
use futures::channel::mpsc; use futures::channel::mpsc;
use gateway_client::AcknowledgementReceiver; use gateway_client::AcknowledgementReceiver;
use log::*; use log::*;
use nymsphinx::params::PacketSize;
use nymsphinx::{ use nymsphinx::{
acknowledgements::AckKey, acknowledgements::AckKey,
addressing::clients::Recipient, addressing::clients::Recipient,
@@ -26,9 +25,7 @@ use std::{
sync::{Arc, Weak}, sync::{Arc, Weak},
time::Duration, time::Duration,
}; };
use tokio::task::JoinHandle;
#[cfg(feature = "reply-surb")]
use crate::client::reply_key_storage::ReplyKeyStorage;
mod acknowledgement_listener; mod acknowledgement_listener;
mod action_controller; mod action_controller;
@@ -122,9 +119,6 @@ pub(super) struct Config {
/// Average delay a data packet is going to get delayed at a single mixnode. /// Average delay a data packet is going to get delayed at a single mixnode.
average_packet_delay: Duration, average_packet_delay: Duration,
/// Predefined packet size used for the encapsulated messages.
packet_size: PacketSize,
} }
impl Config { impl Config {
@@ -139,40 +133,33 @@ impl Config {
ack_wait_multiplier, ack_wait_multiplier,
average_ack_delay, average_ack_delay,
average_packet_delay, average_packet_delay,
packet_size: Default::default(),
} }
} }
pub fn with_custom_packet_size(mut self, packet_size: PacketSize) -> Self {
self.packet_size = packet_size;
self
}
} }
pub(super) struct AcknowledgementController<R> pub(super) struct AcknowledgementController<R>
where where
R: CryptoRng + Rng, R: CryptoRng + Rng,
{ {
acknowledgement_listener: AcknowledgementListener, acknowledgement_listener: Option<AcknowledgementListener>,
input_message_listener: InputMessageListener<R>, input_message_listener: Option<InputMessageListener<R>>,
retransmission_request_listener: RetransmissionRequestListener<R>, retransmission_request_listener: Option<RetransmissionRequestListener<R>>,
sent_notification_listener: SentNotificationListener, sent_notification_listener: Option<SentNotificationListener>,
action_controller: ActionController, action_controller: Option<ActionController>,
} }
impl<R> AcknowledgementController<R> impl<R> AcknowledgementController<R>
where where
R: 'static + CryptoRng + Rng + Clone + Send, R: 'static + CryptoRng + Rng + Clone + Send,
{ {
#[allow(clippy::too_many_arguments)]
pub(super) fn new( pub(super) fn new(
config: Config, config: Config,
rng: R, rng: R,
topology_access: TopologyAccessor, topology_access: TopologyAccessor,
ack_key: Arc<AckKey>, ack_key: Arc<AckKey>,
ack_recipient: Recipient, ack_recipient: Recipient,
reply_key_storage: ReplyKeyStorage,
connectors: AcknowledgementControllerConnectors, connectors: AcknowledgementControllerConnectors,
#[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage,
) -> Self { ) -> Self {
let (retransmission_tx, retransmission_rx) = mpsc::unbounded(); let (retransmission_tx, retransmission_rx) = mpsc::unbounded();
@@ -186,8 +173,7 @@ where
ack_recipient, ack_recipient,
config.average_packet_delay, config.average_packet_delay,
config.average_ack_delay, config.average_ack_delay,
) );
.with_custom_real_message_packet_size(config.packet_size);
// will listen for any acks coming from the network // will listen for any acks coming from the network
let acknowledgement_listener = AcknowledgementListener::new( let acknowledgement_listener = AcknowledgementListener::new(
@@ -205,7 +191,6 @@ where
action_sender.clone(), action_sender.clone(),
connectors.real_message_sender.clone(), connectors.real_message_sender.clone(),
topology_access.clone(), topology_access.clone(),
#[cfg(feature = "reply-surb")]
reply_key_storage, reply_key_storage,
); );
@@ -226,87 +211,66 @@ where
SentNotificationListener::new(connectors.sent_notifier, action_sender); SentNotificationListener::new(connectors.sent_notifier, action_sender);
AcknowledgementController { AcknowledgementController {
acknowledgement_listener, acknowledgement_listener: Some(acknowledgement_listener),
input_message_listener, input_message_listener: Some(input_message_listener),
retransmission_request_listener, retransmission_request_listener: Some(retransmission_request_listener),
sent_notification_listener, sent_notification_listener: Some(sent_notification_listener),
action_controller, action_controller: Some(action_controller),
} }
} }
#[cfg(not(target_arch = "wasm32"))] pub(super) async fn run(&mut self) {
pub(super) fn start_with_shutdown(self, shutdown: task::ShutdownListener) { let mut acknowledgement_listener = self.acknowledgement_listener.take().unwrap();
let mut acknowledgement_listener = self.acknowledgement_listener; let mut input_message_listener = self.input_message_listener.take().unwrap();
let mut input_message_listener = self.input_message_listener; let mut retransmission_request_listener =
let mut retransmission_request_listener = self.retransmission_request_listener; self.retransmission_request_listener.take().unwrap();
let mut sent_notification_listener = self.sent_notification_listener; let mut sent_notification_listener = self.sent_notification_listener.take().unwrap();
let mut action_controller = self.action_controller; let mut action_controller = self.action_controller.take().unwrap();
let shutdown_handle = shutdown.clone(); // the below are log messages are errors as at the current stage we do not expect any of
spawn_future(async move { // the task to ever finish. This will of course change once we introduce
acknowledgement_listener // graceful shutdowns.
.run_with_shutdown(shutdown_handle) let ack_listener_fut = tokio::spawn(async move {
.await;
debug!("The acknowledgement listener has finished execution!");
});
let shutdown_handle = shutdown.clone();
spawn_future(async move {
input_message_listener
.run_with_shutdown(shutdown_handle)
.await;
debug!("The input listener has finished execution!");
});
let shutdown_handle = shutdown.clone();
spawn_future(async move {
retransmission_request_listener
.run_with_shutdown(shutdown_handle)
.await;
debug!("The retransmission request listener has finished execution!");
});
let shutdown_handle = shutdown.clone();
spawn_future(async move {
sent_notification_listener
.run_with_shutdown(shutdown_handle)
.await;
debug!("The sent notification listener has finished execution!");
});
spawn_future(async move {
action_controller.run_with_shutdown(shutdown).await;
debug!("The controller has finished execution!");
});
}
#[cfg(target_arch = "wasm32")]
pub(super) fn start(self) {
let mut acknowledgement_listener = self.acknowledgement_listener;
let mut input_message_listener = self.input_message_listener;
let mut retransmission_request_listener = self.retransmission_request_listener;
let mut sent_notification_listener = self.sent_notification_listener;
let mut action_controller = self.action_controller;
spawn_future(async move {
acknowledgement_listener.run().await; acknowledgement_listener.run().await;
error!("The acknowledgement listener has finished execution!"); error!("The acknowledgement listener has finished execution!");
acknowledgement_listener
}); });
spawn_future(async move { let input_listener_fut = tokio::spawn(async move {
input_message_listener.run().await; input_message_listener.run().await;
error!("The input listener has finished execution!"); error!("The input listener has finished execution!");
input_message_listener
}); });
spawn_future(async move { let retransmission_req_fut = tokio::spawn(async move {
retransmission_request_listener.run().await; retransmission_request_listener.run().await;
error!("The retransmission request listener has finished execution!"); error!("The retransmission request listener has finished execution!");
retransmission_request_listener
}); });
spawn_future(async move { let sent_notification_fut = tokio::spawn(async move {
sent_notification_listener.run().await; sent_notification_listener.run().await;
error!("The sent notification listener has finished execution!"); error!("The sent notification listener has finished execution!");
sent_notification_listener
}); });
spawn_future(async move { let action_controller_fut = tokio::spawn(async move {
action_controller.run().await; action_controller.run().await;
error!("The controller has finished execution!"); error!("The controller has finished execution!");
action_controller
}); });
// technically we don't have to bring `AcknowledgementController` back to a valid state
// but we can do it, so why not? Perhaps it might be useful if we wanted to allow
// for restarts of certain modules without killing the entire process.
self.acknowledgement_listener = Some(ack_listener_fut.await.unwrap());
self.input_message_listener = Some(input_listener_fut.await.unwrap());
self.retransmission_request_listener = Some(retransmission_req_fut.await.unwrap());
self.sent_notification_listener = Some(sent_notification_fut.await.unwrap());
self.action_controller = Some(action_controller_fut.await.unwrap());
}
#[allow(dead_code)]
pub(super) fn start(mut self) -> JoinHandle<Self> {
tokio::spawn(async move {
self.run().await;
self
})
} }
} }
@@ -33,7 +33,6 @@ impl<R> RetransmissionRequestListener<R>
where where
R: CryptoRng + Rng, R: CryptoRng + Rng,
{ {
#[allow(clippy::too_many_arguments)]
pub(super) fn new( pub(super) fn new(
ack_key: Arc<AckKey>, ack_key: Arc<AckKey>,
ack_recipient: Recipient, ack_recipient: Recipient,
@@ -84,6 +83,7 @@ where
let prepared_fragment = self let prepared_fragment = self
.message_preparer .message_preparer
.prepare_chunk_for_sending(chunk_clone, topology_ref, &self.ack_key, packet_recipient) .prepare_chunk_for_sending(chunk_clone, topology_ref, &self.ack_key, packet_recipient)
.await
.unwrap(); .unwrap();
// if we have the ONLY strong reference to the ack data, it means it was removed from the // if we have the ONLY strong reference to the ack data, it means it was removed from the
@@ -120,34 +120,11 @@ where
.unwrap(); .unwrap();
} }
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started RetransmissionRequestListener with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
timed_out_ack = self.request_receiver.next() => match timed_out_ack {
Some(timed_out_ack) => self.on_retransmission_request(timed_out_ack).await,
None => {
log::trace!("RetransmissionRequestListener: Stopping since channel closed");
break;
}
},
_ = shutdown.recv() => {
log::trace!("RetransmissionRequestListener: Received shutdown");
}
}
}
assert!(shutdown.is_shutdown_poll());
log::debug!("RetransmissionRequestListener: Exiting");
}
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) { pub(super) async fn run(&mut self) {
debug!("Started RetransmissionRequestListener without graceful shutdown support"); debug!("Started RetransmissionRequestListener");
while let Some(timed_out_ack) = self.request_receiver.next().await { while let Some(timed_out_ack) = self.request_receiver.next().await {
self.on_retransmission_request(timed_out_ack).await; self.on_retransmission_request(timed_out_ack).await;
} }
error!("TODO: error msg. Or maybe panic?")
} }
} }
@@ -42,36 +42,11 @@ impl SentNotificationListener {
.unwrap(); .unwrap();
} }
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started SentNotificationListener with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
frag_id = self.sent_notifier.next() => match frag_id {
Some(frag_id) => {
self.on_sent_message(frag_id).await;
}
None => {
log::trace!("SentNotificationListener: Stopping since channel closed");
break;
}
},
_ = shutdown.recv() => {
log::trace!("SentNotificationListener: Received shutdown");
}
}
}
assert!(shutdown.is_shutdown_poll());
log::debug!("SentNotificationListener: Exiting");
}
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) { pub(super) async fn run(&mut self) {
debug!("Started SentNotificationListener without graceful shutdown support"); debug!("Started SentNotificationListener");
while let Some(frag_id) = self.sent_notifier.next().await { while let Some(frag_id) = self.sent_notifier.next().await {
self.on_sent_message(frag_id).await; self.on_sent_message(frag_id).await;
} }
error!("TODO: error msg. Or maybe panic?")
} }
} }
@@ -9,23 +9,20 @@ use self::{
acknowledgement_control::AcknowledgementController, real_traffic_stream::OutQueueControl, acknowledgement_control::AcknowledgementController, real_traffic_stream::OutQueueControl,
}; };
use crate::client::real_messages_control::acknowledgement_control::AcknowledgementControllerConnectors; use crate::client::real_messages_control::acknowledgement_control::AcknowledgementControllerConnectors;
use crate::client::reply_key_storage::ReplyKeyStorage;
use crate::client::{ use crate::client::{
inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender, inbound_messages::InputMessageReceiver, mix_traffic::BatchMixMessageSender,
topology_control::TopologyAccessor, topology_control::TopologyAccessor,
}; };
use crate::spawn_future;
use futures::channel::mpsc; use futures::channel::mpsc;
use gateway_client::AcknowledgementReceiver; use gateway_client::AcknowledgementReceiver;
use log::*; use log::*;
use nymsphinx::acknowledgements::AckKey; use nymsphinx::acknowledgements::AckKey;
use nymsphinx::addressing::clients::Recipient; use nymsphinx::addressing::clients::Recipient;
use nymsphinx::params::PacketSize;
use rand::{rngs::OsRng, CryptoRng, Rng}; use rand::{rngs::OsRng, CryptoRng, Rng};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::task::JoinHandle;
#[cfg(feature = "reply-surb")]
use crate::client::reply_key_storage::ReplyKeyStorage;
mod acknowledgement_control; mod acknowledgement_control;
mod real_traffic_stream; mod real_traffic_stream;
@@ -52,18 +49,9 @@ pub struct Config {
/// Average delay an acknowledgement packet is going to get delayed at a single mixnode. /// Average delay an acknowledgement packet is going to get delayed at a single mixnode.
average_ack_delay_duration: Duration, average_ack_delay_duration: Duration,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
disable_main_poisson_packet_distribution: bool,
/// Predefined packet size used for the encapsulated messages.
packet_size: PacketSize,
} }
impl Config { impl Config {
// TODO: change the config into a builder
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
ack_key: Arc<AckKey>, ack_key: Arc<AckKey>,
ack_wait_multiplier: f64, ack_wait_multiplier: f64,
@@ -71,7 +59,6 @@ impl Config {
average_ack_delay_duration: Duration, average_ack_delay_duration: Duration,
average_message_sending_delay: Duration, average_message_sending_delay: Duration,
average_packet_delay_duration: Duration, average_packet_delay_duration: Duration,
disable_main_poisson_packet_distribution: bool,
self_recipient: Recipient, self_recipient: Recipient,
) -> Self { ) -> Self {
Config { Config {
@@ -82,22 +69,16 @@ impl Config {
average_message_sending_delay, average_message_sending_delay,
average_packet_delay_duration, average_packet_delay_duration,
average_ack_delay_duration, average_ack_delay_duration,
disable_main_poisson_packet_distribution,
packet_size: Default::default(),
} }
} }
pub fn set_custom_packet_size(&mut self, packet_size: PacketSize) {
self.packet_size = packet_size;
}
} }
pub struct RealMessagesController<R> pub struct RealMessagesController<R>
where where
R: CryptoRng + Rng, R: CryptoRng + Rng,
{ {
out_queue_control: OutQueueControl<R>, out_queue_control: Option<OutQueueControl<R>>,
ack_control: AcknowledgementController<R>, ack_control: Option<AcknowledgementController<R>>,
} }
// obviously when we finally make shared rng that is on 'higher' level, this should become // obviously when we finally make shared rng that is on 'higher' level, this should become
@@ -109,7 +90,7 @@ impl RealMessagesController<OsRng> {
input_receiver: InputMessageReceiver, input_receiver: InputMessageReceiver,
mix_sender: BatchMixMessageSender, mix_sender: BatchMixMessageSender,
topology_access: TopologyAccessor, topology_access: TopologyAccessor,
#[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage, reply_key_storage: ReplyKeyStorage,
) -> Self { ) -> Self {
let rng = OsRng; let rng = OsRng;
@@ -128,8 +109,7 @@ impl RealMessagesController<OsRng> {
config.ack_wait_multiplier, config.ack_wait_multiplier,
config.average_ack_delay_duration, config.average_ack_delay_duration,
config.average_packet_delay_duration, config.average_packet_delay_duration,
) );
.with_custom_packet_size(config.packet_size);
let ack_control = AcknowledgementController::new( let ack_control = AcknowledgementController::new(
ack_control_config, ack_control_config,
@@ -137,18 +117,15 @@ impl RealMessagesController<OsRng> {
topology_access.clone(), topology_access.clone(),
Arc::clone(&config.ack_key), Arc::clone(&config.ack_key),
config.self_recipient, config.self_recipient,
ack_controller_connectors,
#[cfg(feature = "reply-surb")]
reply_key_storage, reply_key_storage,
ack_controller_connectors,
); );
let out_queue_config = real_traffic_stream::Config::new( let out_queue_config = real_traffic_stream::Config::new(
config.average_ack_delay_duration, config.average_ack_delay_duration,
config.average_packet_delay_duration, config.average_packet_delay_duration,
config.average_message_sending_delay, config.average_message_sending_delay,
config.disable_main_poisson_packet_distribution, );
)
.with_custom_cover_packet_size(config.packet_size);
let out_queue_control = OutQueueControl::new( let out_queue_control = OutQueueControl::new(
out_queue_config, out_queue_config,
@@ -162,33 +139,40 @@ impl RealMessagesController<OsRng> {
); );
RealMessagesController { RealMessagesController {
out_queue_control, out_queue_control: Some(out_queue_control),
ack_control, ack_control: Some(ack_control),
} }
} }
#[cfg(not(target_arch = "wasm32"))] pub(super) async fn run(&mut self) {
pub fn start_with_shutdown(self, shutdown: task::ShutdownListener) { let mut out_queue_control = self.out_queue_control.take().unwrap();
let mut out_queue_control = self.out_queue_control; let mut ack_control = self.ack_control.take().unwrap();
let ack_control = self.ack_control;
let shutdown_handle = shutdown.clone(); // the below are log messages are errors as at the current stage we do not expect any of
spawn_future(async move { // the task to ever finish. This will of course change once we introduce
out_queue_control.run_with_shutdown(shutdown_handle).await; // graceful shutdowns.
debug!("The out queue controller has finished execution!"); let out_queue_control_fut = tokio::spawn(async move {
out_queue_control.run_out_queue_control().await;
error!("The out queue controller has finished execution!");
out_queue_control
}); });
ack_control.start_with_shutdown(shutdown); let ack_control_fut = tokio::spawn(async move {
ack_control.run().await;
error!("The acknowledgement controller has finished execution!");
ack_control
});
// technically we don't have to bring `RealMessagesController` back to a valid state
// but we can do it, so why not? Perhaps it might be useful if we wanted to allow
// for restarts of certain modules without killing the entire process.
self.out_queue_control = Some(out_queue_control_fut.await.unwrap());
self.ack_control = Some(ack_control_fut.await.unwrap());
} }
#[cfg(target_arch = "wasm32")] pub fn start(mut self) -> JoinHandle<Self> {
pub fn start(self) { tokio::spawn(async move {
let mut out_queue_control = self.out_queue_control; self.run().await;
let ack_control = self.ack_control; self
})
spawn_future(async move {
out_queue_control.run().await;
debug!("The out queue controller has finished execution!");
});
ack_control.start();
} }
} }
@@ -19,14 +19,8 @@ use std::collections::VecDeque;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use nymsphinx::params::PacketSize;
#[cfg(not(target_arch = "wasm32"))]
use tokio::time; use tokio::time;
#[cfg(target_arch = "wasm32")]
use wasm_timer;
/// Configurable parameters of the `OutQueueControl` /// Configurable parameters of the `OutQueueControl`
pub(crate) struct Config { pub(crate) struct Config {
/// Average delay an acknowledgement packet is going to get delay at a single mixnode. /// Average delay an acknowledgement packet is going to get delay at a single mixnode.
@@ -37,13 +31,6 @@ pub(crate) struct Config {
/// Average delay between sending subsequent packets. /// Average delay between sending subsequent packets.
average_message_sending_delay: Duration, average_message_sending_delay: Duration,
/// Controls whether the stream constantly produces packets according to the predefined
/// poisson distribution.
disable_poisson_packet_distribution: bool,
/// Predefined packet size used for the loop cover messages.
cover_packet_size: PacketSize,
} }
impl Config { impl Config {
@@ -51,21 +38,13 @@ impl Config {
average_ack_delay: Duration, average_ack_delay: Duration,
average_packet_delay: Duration, average_packet_delay: Duration,
average_message_sending_delay: Duration, average_message_sending_delay: Duration,
disable_poisson_packet_distribution: bool,
) -> Self { ) -> Self {
Config { Config {
average_ack_delay, average_ack_delay,
average_packet_delay, average_packet_delay,
average_message_sending_delay, average_message_sending_delay,
disable_poisson_packet_distribution,
cover_packet_size: Default::default(),
} }
} }
pub fn with_custom_cover_packet_size(mut self, packet_size: PacketSize) -> Self {
self.cover_packet_size = packet_size;
self
}
} }
pub(crate) struct OutQueueControl<R> pub(crate) struct OutQueueControl<R>
@@ -83,11 +62,7 @@ where
/// Internal state, determined by `average_message_sending_delay`, /// Internal state, determined by `average_message_sending_delay`,
/// used to keep track of when a next packet should be sent out. /// used to keep track of when a next packet should be sent out.
#[cfg(not(target_arch = "wasm32"))] next_delay: Pin<Box<time::Sleep>>,
next_delay: Option<Pin<Box<time::Sleep>>>,
#[cfg(target_arch = "wasm32")]
next_delay: Option<Pin<Box<wasm_timer::Delay>>>,
/// Channel used for sending prepared sphinx packets to `MixTrafficController` that sends them /// Channel used for sending prepared sphinx packets to `MixTrafficController` that sends them
/// out to the network without any further delays. /// out to the network without any further delays.
@@ -134,6 +109,55 @@ pub(crate) enum StreamMessage {
Real(Box<RealMessage>), Real(Box<RealMessage>),
} }
impl<R> Stream for OutQueueControl<R>
where
R: CryptoRng + Rng + Unpin,
{
type Item = StreamMessage;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
// it is not yet time to return a message
if self.next_delay.as_mut().poll(cx).is_pending() {
return Poll::Pending;
};
// we know it's time to send a message, so let's prepare delay for the next one
// Get the `now` by looking at the current `delay` deadline
let avg_delay = self.config.average_message_sending_delay;
let now = self.next_delay.deadline();
let next_poisson_delay = sample_poisson_duration(&mut self.rng, avg_delay);
// The next interval value is `next_poisson_delay` after the one that just
// yielded.
let next = now + next_poisson_delay;
self.next_delay.as_mut().reset(next);
// check if we have anything immediately available
if let Some(real_available) = self.received_buffer.pop_front() {
return Poll::Ready(Some(StreamMessage::Real(Box::new(real_available))));
}
// decide what kind of message to send
match Pin::new(&mut self.real_receiver).poll_next(cx) {
// in the case our real message channel stream was closed, we should also indicate we are closed
// (and whoever is using the stream should panic)
Poll::Ready(None) => Poll::Ready(None),
// if there are more messages available, return first one and store the rest
Poll::Ready(Some(real_messages)) => {
self.received_buffer = real_messages.into();
// we MUST HAVE received at least ONE message
Poll::Ready(Some(StreamMessage::Real(Box::new(
self.received_buffer.pop_front().unwrap(),
))))
}
// otherwise construct a dummy one
Poll::Pending => Poll::Ready(Some(StreamMessage::Cover)),
}
}
}
impl<R> OutQueueControl<R> impl<R> OutQueueControl<R>
where where
R: CryptoRng + Rng + Unpin, R: CryptoRng + Rng + Unpin,
@@ -155,7 +179,7 @@ where
config, config,
ack_key, ack_key,
sent_notifier, sent_notifier,
next_delay: None, next_delay: Box::pin(time::sleep(Default::default())),
mix_tx, mix_tx,
real_receiver, real_receiver,
our_full_destination, our_full_destination,
@@ -202,7 +226,6 @@ where
&self.our_full_destination, &self.our_full_destination,
self.config.average_ack_delay, self.config.average_ack_delay,
self.config.average_packet_delay, self.config.average_packet_delay,
self.config.cover_packet_size,
) )
.expect("Somehow failed to generate a loop cover message with a valid topology") .expect("Somehow failed to generate a loop cover message with a valid topology")
} }
@@ -216,175 +239,31 @@ where
// - we run out of memory // - we run out of memory
// - the receiver channel is closed // - the receiver channel is closed
// in either case there's no recovery and we can only panic // in either case there's no recovery and we can only panic
if let Err(err) = self.mix_tx.unbounded_send(vec![next_message]) { self.mix_tx.unbounded_send(vec![next_message]).unwrap();
log::warn!(
"Failed to send {} packets (possible process shutdown?)",
err.into_inner().len()
);
}
// JS: Not entirely sure why or how it fixes stuff, but without the yield call, // JS: Not entirely sure why or how it fixes stuff, but without the yield call,
// the UnboundedReceiver [of mix_rx] will not get a chance to read anything // the UnboundedReceiver [of mix_rx] will not get a chance to read anything
// JS2: Basically it was the case that with high enough rate, the stream had already a next value // JS2: Basically it was the case that with high enough rate, the stream had already a next value
// ready and hence was immediately re-scheduled causing other tasks to be starved; // ready and hence was immediately re-scheduled causing other tasks to be starved;
// yield makes it go back the scheduling queue regardless of its value availability // yield makes it go back the scheduling queue regardless of its value availability
// TODO: temporary and BAD workaround for wasm (we should find a way to yield here in wasm)
#[cfg(not(target_arch = "wasm32"))]
tokio::task::yield_now().await; tokio::task::yield_now().await;
} }
fn poll_poisson(&mut self, cx: &mut Context<'_>) -> Poll<Option<StreamMessage>> { // Send messages at certain rate and if no real traffic is available, send cover message.
if let Some(ref mut next_delay) = &mut self.next_delay { async fn run_normal_out_queue(&mut self) {
// it is not yet time to return a message // we should set initial delay only when we actually start the stream
if next_delay.as_mut().poll(cx).is_pending() { self.next_delay = Box::pin(time::sleep(sample_poisson_duration(
return Poll::Pending; &mut self.rng,
}; self.config.average_message_sending_delay,
)));
// we know it's time to send a message, so let's prepare delay for the next one
// Get the `now` by looking at the current `delay` deadline
let avg_delay = self.config.average_message_sending_delay;
let next_poisson_delay = sample_poisson_duration(&mut self.rng, avg_delay);
// The next interval value is `next_poisson_delay` after the one that just
// yielded.
#[cfg(not(target_arch = "wasm32"))]
{
let now = next_delay.deadline();
let next = now + next_poisson_delay;
next_delay.as_mut().reset(next);
}
#[cfg(target_arch = "wasm32")]
{
next_delay.as_mut().reset(next_poisson_delay);
}
// check if we have anything immediately available
if let Some(real_available) = self.received_buffer.pop_front() {
return Poll::Ready(Some(StreamMessage::Real(Box::new(real_available))));
}
// decide what kind of message to send
match Pin::new(&mut self.real_receiver).poll_next(cx) {
// in the case our real message channel stream was closed, we should also indicate we are closed
// (and whoever is using the stream should panic)
Poll::Ready(None) => Poll::Ready(None),
// if there are more messages available, return first one and store the rest
Poll::Ready(Some(real_messages)) => {
self.received_buffer = real_messages.into();
// we MUST HAVE received at least ONE message
Poll::Ready(Some(StreamMessage::Real(Box::new(
self.received_buffer.pop_front().unwrap(),
))))
}
// otherwise construct a dummy one
Poll::Pending => Poll::Ready(Some(StreamMessage::Cover)),
}
} else {
// we never set an initial delay - let's do it now
cx.waker().wake_by_ref();
let sampled =
sample_poisson_duration(&mut self.rng, self.config.average_message_sending_delay);
#[cfg(not(target_arch = "wasm32"))]
let next_delay = Box::pin(time::sleep(sampled));
#[cfg(target_arch = "wasm32")]
let next_delay = Box::pin(wasm_timer::Delay::new(sampled));
self.next_delay = Some(next_delay);
Poll::Pending
}
}
fn poll_immediate(&mut self, cx: &mut Context<'_>) -> Poll<Option<StreamMessage>> {
// check if we have anything immediately available
if let Some(real_available) = self.received_buffer.pop_front() {
// if there are more messages immediately available, notify the runtime
// because we should be polled again
if !self.received_buffer.is_empty() {
cx.waker().wake_by_ref()
}
return Poll::Ready(Some(StreamMessage::Real(Box::new(real_available))));
}
match Pin::new(&mut self.real_receiver).poll_next(cx) {
// in the case our real message channel stream was closed, we should also indicate we are closed
// (and whoever is using the stream should panic)
Poll::Ready(None) => Poll::Ready(None),
// if there are more messages available, return first one and store the rest
Poll::Ready(Some(real_messages)) => {
self.received_buffer = real_messages.into();
// we MUST HAVE received at least ONE message
Poll::Ready(Some(StreamMessage::Real(Box::new(
self.received_buffer.pop_front().unwrap(),
))))
}
// if there's nothing, then there's nothing
Poll::Pending => Poll::Pending,
}
}
fn poll_next_message(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<StreamMessage>> {
if self.config.disable_poisson_packet_distribution {
self.poll_immediate(cx)
} else {
self.poll_poisson(cx)
}
}
#[cfg(not(target_arch = "wasm32"))]
pub(super) async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started OutQueueControl with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
log::trace!("OutQueueControl: Received shutdown");
}
next_message = self.next() => match next_message {
Some(next_message) => {
self.on_message(next_message).await;
},
None => {
log::trace!("OutQueueControl: Stopping since channel closed");
break;
}
}
}
}
assert!(shutdown.is_shutdown_poll());
log::debug!("OutQueueControl: Exiting");
}
#[cfg(target_arch = "wasm32")]
pub(super) async fn run(&mut self) {
debug!("Started OutQueueControl without graceful shutdown support");
while let Some(next_message) = self.next().await { while let Some(next_message) = self.next().await {
self.on_message(next_message).await; self.on_message(next_message).await;
} }
} }
}
impl<R> Stream for OutQueueControl<R> pub(crate) async fn run_out_queue_control(&mut self) {
where debug!("Starting out queue controller...");
R: CryptoRng + Rng + Unpin, self.run_normal_out_queue().await
{
type Item = StreamMessage;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.poll_next_message(cx)
} }
} }
+52 -150
View File
@@ -1,25 +1,21 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::spawn_future; use crate::client::reply_key_storage::ReplyKeyStorage;
use crypto::asymmetric::encryption; use crypto::asymmetric::encryption;
use crypto::symmetric::stream_cipher;
use crypto::Digest;
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::lock::Mutex; use futures::lock::Mutex;
use futures::StreamExt; use futures::StreamExt;
use gateway_client::MixnetMessageReceiver; use gateway_client::MixnetMessageReceiver;
use log::*; use log::*;
use nymsphinx::anonymous_replies::{encryption_key::EncryptionKeyDigest, SurbEncryptionKey};
use nymsphinx::params::{ReplySurbEncryptionAlgorithm, ReplySurbKeyDigestAlgorithm};
use nymsphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage}; use nymsphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage};
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::Arc; use std::sync::Arc;
use tokio::task::JoinHandle;
#[cfg(feature = "reply-surb")]
use crate::client::reply_key_storage::ReplyKeyStorage;
#[cfg(feature = "reply-surb")]
use crypto::{symmetric::stream_cipher, Digest};
#[cfg(feature = "reply-surb")]
use nymsphinx::anonymous_replies::{encryption_key::EncryptionKeyDigest, SurbEncryptionKey};
#[cfg(feature = "reply-surb")]
use nymsphinx::params::{ReplySurbEncryptionAlgorithm, ReplySurbKeyDigestAlgorithm};
// Buffer Requests to say "hey, send any reconstructed messages to this channel" // Buffer Requests to say "hey, send any reconstructed messages to this channel"
// or to say "hey, I'm going offline, don't send anything more to me. Just buffer them instead" // or to say "hey, I'm going offline, don't send anything more to me. Just buffer them instead"
@@ -117,14 +113,13 @@ struct ReceivedMessagesBuffer {
/// Storage containing keys to all [`ReplySURB`]s ever sent out that we did not receive back. /// Storage containing keys to all [`ReplySURB`]s ever sent out that we did not receive back.
// There's no need to put it behind a Mutex since it's already properly concurrent // There's no need to put it behind a Mutex since it's already properly concurrent
#[cfg(feature = "reply-surb")]
reply_key_storage: ReplyKeyStorage, reply_key_storage: ReplyKeyStorage,
} }
impl ReceivedMessagesBuffer { impl ReceivedMessagesBuffer {
fn new( fn new(
local_encryption_keypair: Arc<encryption::KeyPair>, local_encryption_keypair: Arc<encryption::KeyPair>,
#[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage, reply_key_storage: ReplyKeyStorage,
) -> Self { ) -> Self {
ReceivedMessagesBuffer { ReceivedMessagesBuffer {
inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner { inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner {
@@ -134,7 +129,6 @@ impl ReceivedMessagesBuffer {
message_sender: None, message_sender: None,
recently_reconstructed: HashSet::new(), recently_reconstructed: HashSet::new(),
})), })),
#[cfg(feature = "reply-surb")]
reply_key_storage, reply_key_storage,
} }
} }
@@ -183,7 +177,6 @@ impl ReceivedMessagesBuffer {
self.inner.lock().await.messages.extend(msgs) self.inner.lock().await.messages.extend(msgs)
} }
#[cfg(feature = "reply-surb")]
fn process_received_reply( fn process_received_reply(
reply_ciphertext: &[u8], reply_ciphertext: &[u8],
reply_key: SurbEncryptionKey, reply_key: SurbEncryptionKey,
@@ -216,49 +209,37 @@ impl ReceivedMessagesBuffer {
let mut completed_messages = Vec::new(); let mut completed_messages = Vec::new();
let mut inner_guard = self.inner.lock().await; let mut inner_guard = self.inner.lock().await;
let reply_surb_digest_size = ReplySurbKeyDigestAlgorithm::output_size();
// first check if this is a reply or a chunked message // first check if this is a reply or a chunked message
// TODO: verify with @AP if this way of doing it is safe or whether it could // TODO: verify with @AP if this way of doing it is safe or whether it could
// cause some attacks due to, I don't know, stupid edge case collisions? // cause some attacks due to, I don't know, stupid edge case collisions?
// Update: this DOES introduce a possible leakage: https://github.com/nymtech/nym/issues/296 // Update: this DOES introduce a possible leakage: https://github.com/nymtech/nym/issues/296
for msg in msgs { for msg in msgs {
// TODO: let possible_key_digest =
// 1. make it nicer EncryptionKeyDigest::clone_from_slice(&msg[..reply_surb_digest_size]);
// 2. make it not feature-locked
#[cfg(feature = "reply-surb")] // check first `HasherOutputSize` bytes if they correspond to known encryption key
// if yes - this is a reply message
// TODO: this might be a bottleneck - since the keys are stored on disk we, presumably,
// are doing a disk operation every single received fragment
if let Some(reply_encryption_key) = self
.reply_key_storage
.get_and_remove_encryption_key(possible_key_digest)
.expect("storage operation failed!")
{ {
let reply_surb_digest_size = ReplySurbKeyDigestAlgorithm::output_size(); if let Some(completed_message) = Self::process_received_reply(
&msg[reply_surb_digest_size..],
let possible_key_digest = reply_encryption_key,
EncryptionKeyDigest::clone_from_slice(&msg[..reply_surb_digest_size]); ) {
completed_messages.push(completed_message)
// check first `HasherOutputSize` bytes if they correspond to known encryption key }
// if yes - this is a reply message } else {
// otherwise - it's a 'normal' message
// TODO: this might be a bottleneck - since the keys are stored on disk we, presumably, if let Some(completed_message) = inner_guard.process_received_fragment(msg) {
// are doing a disk operation every single received fragment completed_messages.push(completed_message)
if let Some(reply_encryption_key) = self
.reply_key_storage
.get_and_remove_encryption_key(possible_key_digest)
.expect("storage operation failed!")
{
if let Some(completed_message) = Self::process_received_reply(
&msg[reply_surb_digest_size..],
reply_encryption_key,
) {
completed_messages.push(completed_message)
}
} else {
// otherwise - it's a 'normal' message
if let Some(completed_message) = inner_guard.process_received_fragment(msg) {
completed_messages.push(completed_message)
}
} }
}
#[cfg(not(feature = "reply-surb"))]
if let Some(completed_message) = inner_guard.process_received_fragment(msg) {
completed_messages.push(completed_message)
} }
} }
@@ -309,48 +290,19 @@ impl RequestReceiver {
} }
} }
async fn handle_message(&mut self, message: ReceivedBufferMessage) { fn start(mut self) -> JoinHandle<()> {
match message { tokio::spawn(async move {
ReceivedBufferMessage::ReceiverAnnounce(sender) => { while let Some(request) = self.query_receiver.next().await {
self.received_buffer.connect_sender(sender).await; match request {
} ReceivedBufferMessage::ReceiverAnnounce(sender) => {
ReceivedBufferMessage::ReceiverDisconnect => { self.received_buffer.connect_sender(sender).await;
self.received_buffer.disconnect_sender().await
}
}
}
#[cfg(not(target_arch = "wasm32"))]
async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) {
debug!("Started RequestReceiver with graceful shutdown support");
while !shutdown.is_shutdown() {
tokio::select! {
biased;
_ = shutdown.recv() => {
log::trace!("RequestReceiver: Received shutdown");
}
request = self.query_receiver.next() => {
match request {
Some(message) => self.handle_message(message).await,
None => {
log::trace!("RequestReceiver: Stopping since channel closed");
break;
},
} }
}, ReceivedBufferMessage::ReceiverDisconnect => {
self.received_buffer.disconnect_sender().await
}
}
} }
} })
assert!(shutdown.is_shutdown_poll());
log::debug!("RequestReceiver: Exiting");
}
#[cfg(target_arch = "wasm32")]
async fn run(&mut self) {
debug!("Started RequestReceiver without graceful shutdown support");
while let Some(message) = self.query_receiver.next().await {
self.handle_message(message).await
}
} }
} }
@@ -369,37 +321,12 @@ impl FragmentedMessageReceiver {
mixnet_packet_receiver, mixnet_packet_receiver,
} }
} }
fn start(mut self) -> JoinHandle<()> {
#[cfg(not(target_arch = "wasm32"))] tokio::spawn(async move {
async fn run_with_shutdown(&mut self, mut shutdown: task::ShutdownListener) { while let Some(new_messages) = self.mixnet_packet_receiver.next().await {
debug!("Started FragmentedMessageReceiver with graceful shutdown support"); self.received_buffer.handle_new_received(new_messages).await;
while !shutdown.is_shutdown() {
tokio::select! {
new_messages = self.mixnet_packet_receiver.next() => match new_messages {
Some(new_messages) => {
self.received_buffer.handle_new_received(new_messages).await;
}
None => {
log::trace!("FragmentedMessageReceiver: Stopping since channel closed");
break;
}
},
_ = shutdown.recv() => {
log::trace!("FragmentedMessageReceiver: Received shutdown");
}
} }
} })
assert!(shutdown.is_shutdown_poll());
log::debug!("FragmentedMessageReceiver: Exiting");
}
#[cfg(target_arch = "wasm32")]
async fn run(&mut self) {
debug!("Started FragmentedMessageReceiver without graceful shutdown support");
while let Some(new_messages) = self.mixnet_packet_receiver.next().await {
self.received_buffer.handle_new_received(new_messages).await;
}
} }
} }
@@ -413,13 +340,10 @@ impl ReceivedMessagesBufferController {
local_encryption_keypair: Arc<encryption::KeyPair>, local_encryption_keypair: Arc<encryption::KeyPair>,
query_receiver: ReceivedBufferRequestReceiver, query_receiver: ReceivedBufferRequestReceiver,
mixnet_packet_receiver: MixnetMessageReceiver, mixnet_packet_receiver: MixnetMessageReceiver,
#[cfg(feature = "reply-surb")] reply_key_storage: ReplyKeyStorage, reply_key_storage: ReplyKeyStorage,
) -> Self { ) -> Self {
let received_buffer = ReceivedMessagesBuffer::new( let received_buffer =
local_encryption_keypair, ReceivedMessagesBuffer::new(local_encryption_keypair, reply_key_storage);
#[cfg(feature = "reply-surb")]
reply_key_storage,
);
ReceivedMessagesBufferController { ReceivedMessagesBufferController {
fragmented_message_receiver: FragmentedMessageReceiver::new( fragmented_message_receiver: FragmentedMessageReceiver::new(
@@ -430,31 +354,9 @@ impl ReceivedMessagesBufferController {
} }
} }
#[cfg(not(target_arch = "wasm32"))]
pub fn start_with_shutdown(self, shutdown: task::ShutdownListener) {
let mut fragmented_message_receiver = self.fragmented_message_receiver;
let mut request_receiver = self.request_receiver;
let shutdown_handle = shutdown.clone();
spawn_future(async move {
fragmented_message_receiver
.run_with_shutdown(shutdown_handle)
.await;
});
spawn_future(async move {
request_receiver.run_with_shutdown(shutdown).await;
});
}
#[cfg(target_arch = "wasm32")]
pub fn start(self) { pub fn start(self) {
let mut fragmented_message_receiver = self.fragmented_message_receiver; // TODO: should we do anything with JoinHandle(s) returned by start methods?
let mut request_receiver = self.request_receiver; self.fragmented_message_receiver.start();
spawn_future(async move { self.request_receiver.start();
fragmented_message_receiver.run().await;
});
spawn_future(async move {
request_receiver.run().await;
});
} }
} }
@@ -1,7 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::spawn_future;
use log::*; use log::*;
use nymsphinx::addressing::clients::Recipient; use nymsphinx::addressing::clients::Recipient;
use nymsphinx::params::DEFAULT_NUM_MIX_HOPS; use nymsphinx::params::DEFAULT_NUM_MIX_HOPS;
@@ -12,6 +11,7 @@ use std::sync::Arc;
use std::time; use std::time;
use std::time::Duration; use std::time::Duration;
use tokio::sync::{RwLock, RwLockReadGuard}; use tokio::sync::{RwLock, RwLockReadGuard};
use tokio::task::JoinHandle;
use topology::{nym_topology_from_bonds, NymTopology}; use topology::{nym_topology_from_bonds, NymTopology};
use url::Url; use url::Url;
@@ -303,34 +303,10 @@ impl TopologyRefresher {
self.topology_accessor.is_routable().await self.topology_accessor.is_routable().await
} }
#[cfg(not(target_arch = "wasm32"))] pub fn start(mut self) -> JoinHandle<()> {
pub fn start_with_shutdown(mut self, mut shutdown: task::ShutdownListener) { tokio::spawn(async move {
spawn_future(async move { loop {
debug!("Started TopologyRefresher with graceful shutdown support"); tokio::time::sleep(self.refresh_rate).await;
while !shutdown.is_shutdown() {
tokio::select! {
_ = tokio::time::sleep(self.refresh_rate) => {
self.refresh().await;
},
_ = shutdown.recv() => {
log::trace!("TopologyRefresher: Received shutdown");
},
}
}
assert!(shutdown.is_shutdown_poll());
log::debug!("TopologyRefresher: Exiting");
})
}
#[cfg(target_arch = "wasm32")]
pub fn start(mut self) {
use futures::StreamExt;
spawn_future(async move {
let mut interval =
gloo_timers::future::IntervalStream::new(self.refresh_rate.as_millis() as u32);
while let Some(_) = interval.next().await {
self.refresh().await; self.refresh().await;
} }
}) })
+86 -80
View File
@@ -1,6 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use config::defaults::*;
use config::NymConfig; use config::NymConfig;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::marker::PhantomData; use std::marker::PhantomData;
@@ -8,9 +9,6 @@ use std::path::PathBuf;
use std::time::Duration; use std::time::Duration;
use url::Url; use url::Url;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
pub mod persistence; pub mod persistence;
pub const MISSING_VALUE: &str = "MISSING VALUE"; pub const MISSING_VALUE: &str = "MISSING VALUE";
@@ -105,32 +103,53 @@ impl<T: NymConfig> Config<T> {
self::Client::<T>::default_reply_encryption_key_store_path(&id); self::Client::<T>::default_reply_encryption_key_store_path(&id);
} }
if self.client.database_path.as_os_str().is_empty() { #[cfg(not(feature = "coconut"))]
self.client.database_path = self::Client::<T>::default_database_path(&id); if self
.client
.backup_bandwidth_token_keys_dir
.as_os_str()
.is_empty()
{
self.client.backup_bandwidth_token_keys_dir =
self::Client::<T>::default_backup_bandwidth_token_keys_dir(&id);
} }
self.client.id = id; self.client.id = id;
} }
pub fn with_disabled_credentials(&mut self, disabled_credentials_mode: bool) { pub fn with_testnet_mode(&mut self, testnet_mode: bool) {
self.client.disabled_credentials_mode = disabled_credentials_mode; self.client.testnet_mode = testnet_mode;
} }
pub fn with_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpoint) { pub fn with_gateway_endpoint<S: Into<String>>(&mut self, id: S, owner: S, listener: S) {
self.client.gateway_endpoint = gateway_endpoint; self.client.gateway_endpoint = GatewayEndpoint {
gateway_id: id.into(),
gateway_owner: owner.into(),
gateway_listener: listener.into(),
};
} }
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) { pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
self.client.gateway_endpoint.gateway_id = id.into(); self.client.gateway_endpoint.gateway_id = id.into();
} }
#[cfg(not(feature = "coconut"))]
pub fn with_eth_private_key<S: Into<String>>(&mut self, eth_private_key: S) {
self.client.eth_private_key = eth_private_key.into();
}
#[cfg(not(feature = "coconut"))]
pub fn with_eth_endpoint<S: Into<String>>(&mut self, eth_endpoint: S) {
self.client.eth_endpoint = eth_endpoint.into();
}
pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) { pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
self.client.validator_api_urls = validator_api_urls; self.client.validator_api_urls = validator_api_urls;
} }
pub fn set_high_default_traffic_volume(&mut self) { pub fn set_high_default_traffic_volume(&mut self) {
self.debug.average_packet_delay = Duration::from_millis(10); self.debug.average_packet_delay = Duration::from_millis(10);
self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2_000_000); // basically don't really send cover messages self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2000000); // basically don't really send cover messages
self.debug.message_sending_average_delay = Duration::from_millis(4); // 250 "real" messages / s self.debug.message_sending_average_delay = Duration::from_millis(4); // 250 "real" messages / s
} }
@@ -142,8 +161,8 @@ impl<T: NymConfig> Config<T> {
self.client.id.clone() self.client.id.clone()
} }
pub fn get_disabled_credentials_mode(&self) -> bool { pub fn get_testnet_mode(&self) -> bool {
self.client.disabled_credentials_mode self.client.testnet_mode
} }
pub fn get_nym_root_directory(&self) -> PathBuf { pub fn get_nym_root_directory(&self) -> PathBuf {
@@ -194,12 +213,19 @@ impl<T: NymConfig> Config<T> {
self.client.gateway_endpoint.gateway_listener.clone() self.client.gateway_endpoint.gateway_listener.clone()
} }
pub fn get_gateway_endpoint(&self) -> &GatewayEndpoint { #[cfg(not(feature = "coconut"))]
&self.client.gateway_endpoint pub fn get_backup_bandwidth_token_keys_dir(&self) -> PathBuf {
self.client.backup_bandwidth_token_keys_dir.clone()
} }
pub fn get_database_path(&self) -> PathBuf { #[cfg(not(feature = "coconut"))]
self.client.database_path.clone() pub fn get_eth_endpoint(&self) -> String {
self.client.eth_endpoint.clone()
}
#[cfg(not(feature = "coconut"))]
pub fn get_eth_private_key(&self) -> String {
self.client.eth_private_key.clone()
} }
// Debug getters // Debug getters
@@ -239,18 +265,6 @@ impl<T: NymConfig> Config<T> {
self.debug.topology_resolution_timeout self.debug.topology_resolution_timeout
} }
pub fn get_disabled_loop_cover_traffic_stream(&self) -> bool {
self.debug.disable_loop_cover_traffic_stream
}
pub fn get_disabled_main_poisson_packet_distribution(&self) -> bool {
self.debug.disable_main_poisson_packet_distribution
}
pub fn get_use_extended_packet_size(&self) -> bool {
self.debug.use_extended_packet_size
}
pub fn get_version(&self) -> &str { pub fn get_version(&self) -> &str {
&self.client.version &self.client.version
} }
@@ -266,32 +280,20 @@ impl<T: NymConfig> Default for Config<T> {
} }
} }
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(getter_with_clone))] struct GatewayEndpoint {
pub struct GatewayEndpoint {
/// gateway_id specifies ID of the gateway to which the client should send messages. /// gateway_id specifies ID of the gateway to which the client should send messages.
/// If initially omitted, a random gateway will be chosen from the available topology. /// If initially omitted, a random gateway will be chosen from the available topology.
pub gateway_id: String, gateway_id: String,
/// Address of the gateway owner to which the client should send messages. /// Address of the gateway owner to which the client should send messages.
pub gateway_owner: String, gateway_owner: String,
/// Address of the gateway listener to which all client requests should be sent. /// Address of the gateway listener to which all client requests should be sent.
pub gateway_listener: String, gateway_listener: String,
} }
impl From<topology::gateway::Node> for GatewayEndpoint { #[derive(Debug, Deserialize, PartialEq, Serialize)]
fn from(node: topology::gateway::Node) -> GatewayEndpoint {
let gateway_listener = node.clients_address();
GatewayEndpoint {
gateway_id: node.identity_key.to_base58_string(),
gateway_owner: node.owner,
gateway_listener,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct Client<T> { pub struct Client<T> {
/// Version of the client for which this configuration was created. /// Version of the client for which this configuration was created.
#[serde(default = "missing_string_value")] #[serde(default = "missing_string_value")]
@@ -300,10 +302,10 @@ pub struct Client<T> {
/// ID specifies the human readable ID of this particular client. /// ID specifies the human readable ID of this particular client.
id: String, id: String,
/// Indicates whether this client is running in a disabled credentials mode, thus attempting /// Indicates whether this client is running in a testnet mode, thus attempting
/// to claim bandwidth without presenting bandwidth credentials. /// to claim bandwidth without presenting bandwidth credentials.
#[serde(default)] #[serde(default)]
disabled_credentials_mode: bool, testnet_mode: bool,
/// Addresses to APIs running on validator from which the client gets the view of the network. /// Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls: Vec<Url>, validator_api_urls: Vec<Url>,
@@ -335,15 +337,26 @@ pub struct Client<T> {
/// Information regarding how the client should send data to gateway. /// Information regarding how the client should send data to gateway.
gateway_endpoint: GatewayEndpoint, gateway_endpoint: GatewayEndpoint,
/// Path to the database containing bandwidth credentials of this client. /// Path to directory containing public/private keys used for bandwidth token purchase.
database_path: PathBuf, /// Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
/// The public key is the name of the file, while the private key is the content.
#[cfg(not(feature = "coconut"))]
backup_bandwidth_token_keys_dir: PathBuf,
/// Ethereum private key.
#[cfg(not(feature = "coconut"))]
eth_private_key: String,
/// Address to an Ethereum full node.
#[cfg(not(feature = "coconut"))]
eth_endpoint: String,
/// nym_home_directory specifies absolute path to the home nym Clients directory. /// nym_home_directory specifies absolute path to the home nym Clients directory.
/// It is expected to use default value and hence .toml file should not redefine this field. /// It is expected to use default value and hence .toml file should not redefine this field.
nym_root_directory: PathBuf, nym_root_directory: PathBuf,
#[serde(skip)] #[serde(skip)]
super_struct: PhantomData<T>, super_struct: PhantomData<*const T>,
} }
impl<T: NymConfig> Default for Client<T> { impl<T: NymConfig> Default for Client<T> {
@@ -352,8 +365,8 @@ impl<T: NymConfig> Default for Client<T> {
Client { Client {
version: env!("CARGO_PKG_VERSION").to_string(), version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(), id: "".to_string(),
disabled_credentials_mode: true, testnet_mode: false,
validator_api_urls: vec![], validator_api_urls: default_api_endpoints(),
private_identity_key_file: Default::default(), private_identity_key_file: Default::default(),
public_identity_key_file: Default::default(), public_identity_key_file: Default::default(),
private_encryption_key_file: Default::default(), private_encryption_key_file: Default::default(),
@@ -362,7 +375,12 @@ impl<T: NymConfig> Default for Client<T> {
ack_key_file: Default::default(), ack_key_file: Default::default(),
reply_encryption_key_store_path: Default::default(), reply_encryption_key_store_path: Default::default(),
gateway_endpoint: Default::default(), gateway_endpoint: Default::default(),
database_path: Default::default(), #[cfg(not(feature = "coconut"))]
backup_bandwidth_token_keys_dir: Default::default(),
#[cfg(not(feature = "coconut"))]
eth_private_key: "".to_string(),
#[cfg(not(feature = "coconut"))]
eth_endpoint: "".to_string(),
nym_root_directory: T::default_root_directory(), nym_root_directory: T::default_root_directory(),
super_struct: Default::default(), super_struct: Default::default(),
} }
@@ -397,12 +415,14 @@ impl<T: NymConfig> Client<T> {
fn default_reply_encryption_key_store_path(id: &str) -> PathBuf { fn default_reply_encryption_key_store_path(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("reply_key_store") T::default_data_directory(Some(id)).join("reply_key_store")
} }
fn default_database_path(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("db.sqlite") #[cfg(not(feature = "coconut"))]
fn default_backup_bandwidth_token_keys_dir(id: &str) -> PathBuf {
T::default_data_directory(Some(id)).join("backup_bandwidth_token_keys")
} }
} }
#[derive(Debug, Default, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Logging {} pub struct Logging {}
@@ -414,64 +434,53 @@ pub struct Debug {
/// So for a packet going through three mix nodes, on average, it will take three times this value /// So for a packet going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination. /// until the packet reaches its destination.
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub average_packet_delay: Duration, average_packet_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average, /// The parameter of Poisson distribution determining how long, on average,
/// sent acknowledgement is going to be delayed at any given mix node. /// sent acknowledgement is going to be delayed at any given mix node.
/// So for an ack going through three mix nodes, on average, it will take three times this value /// So for an ack going through three mix nodes, on average, it will take three times this value
/// until the packet reaches its destination. /// until the packet reaches its destination.
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub average_ack_delay: Duration, average_ack_delay: Duration,
/// Value multiplied with the expected round trip time of an acknowledgement packet before /// Value multiplied with the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens. /// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 1. /// In an ideal network with 0 latency, this value would have been 1.
pub ack_wait_multiplier: f64, ack_wait_multiplier: f64,
/// Value added to the expected round trip time of an acknowledgement packet before /// Value added to the expected round trip time of an acknowledgement packet before
/// it is assumed it was lost and retransmission of the data packet happens. /// it is assumed it was lost and retransmission of the data packet happens.
/// In an ideal network with 0 latency, this value would have been 0. /// In an ideal network with 0 latency, this value would have been 0.
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub ack_wait_addition: Duration, ack_wait_addition: Duration,
/// The parameter of Poisson distribution determining how long, on average, /// The parameter of Poisson distribution determining how long, on average,
/// it is going to take for another loop cover traffic message to be sent. /// it is going to take for another loop cover traffic message to be sent.
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub loop_cover_traffic_average_delay: Duration, loop_cover_traffic_average_delay: Duration,
/// The parameter of Poisson distribution determining how long, on average, /// The parameter of Poisson distribution determining how long, on average,
/// it is going to take another 'real traffic stream' message to be sent. /// it is going to take another 'real traffic stream' message to be sent.
/// If no real packets are available and cover traffic is enabled, /// If no real packets are available and cover traffic is enabled,
/// a loop cover message is sent instead in order to preserve the rate. /// a loop cover message is sent instead in order to preserve the rate.
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub message_sending_average_delay: Duration, message_sending_average_delay: Duration,
/// How long we're willing to wait for a response to a message sent to the gateway, /// How long we're willing to wait for a response to a message sent to the gateway,
/// before giving up on it. /// before giving up on it.
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub gateway_response_timeout: Duration, gateway_response_timeout: Duration,
/// The uniform delay every which clients are querying the directory server /// The uniform delay every which clients are querying the directory server
/// to try to obtain a compatible network topology to send sphinx packets through. /// to try to obtain a compatible network topology to send sphinx packets through.
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub topology_refresh_rate: Duration, topology_refresh_rate: Duration,
/// During topology refresh, test packets are sent through every single possible network /// During topology refresh, test packets are sent through every single possible network
/// path. This timeout determines waiting period until it is decided that the packet /// path. This timeout determines waiting period until it is decided that the packet
/// did not reach its destination. /// did not reach its destination.
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
pub topology_resolution_timeout: Duration, topology_resolution_timeout: Duration,
/// Controls whether the dedicated loop cover traffic stream should be enabled.
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
pub disable_loop_cover_traffic_stream: bool,
/// Controls whether the main packet stream constantly produces packets according to the predefined
/// poisson distribution.
pub disable_main_poisson_packet_distribution: bool,
/// Controls whether the sent sphinx packet use the NON-DEFAULT bigger size.
pub use_extended_packet_size: bool,
} }
impl Default for Debug { impl Default for Debug {
@@ -486,9 +495,6 @@ impl Default for Debug {
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT, gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE, topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT, topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_loop_cover_traffic_stream: false,
disable_main_poisson_packet_distribution: false,
use_extended_packet_size: false,
} }
} }
} }
-27
View File
@@ -1,27 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crypto::asymmetric::identity::Ed25519RecoveryError;
use gateway_client::error::GatewayClientError;
use validator_client::ValidatorClientError;
#[derive(thiserror::Error, Debug)]
pub enum ClientCoreError {
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("Gateway client error: {0}")]
GatewayClientError(#[from] GatewayClientError),
#[error("Ed25519 error: {0}")]
Ed25519RecoveryError(#[from] Ed25519RecoveryError),
#[error("Validator client error: {0}")]
ValidatorClientError(#[from] ValidatorClientError),
#[error("No gateway with id: {0}")]
NoGatewayWithId(String),
#[error("No gateways on network")]
NoGatewaysOnNetwork,
#[error("List of validator apis is empty")]
ListOfValidatorApisIsEmpty,
#[error("Could not load existing gateway configuration: {0}")]
CouldNotLoadExistingGatewayConfiguration(std::io::Error),
}
-149
View File
@@ -1,149 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
//! Collection of initialization steps used by client implementations
use std::{sync::Arc, time::Duration};
use config::NymConfig;
use crypto::asymmetric::{encryption, identity};
use gateway_client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
use rand::rngs::OsRng;
use rand::seq::SliceRandom;
use rand::thread_rng;
use tap::TapFallible;
use topology::{filter::VersionFilterable, gateway};
use url::Url;
use crate::{
client::key_manager::KeyManager,
config::{persistence::key_pathfinder::ClientKeyPathfinder, Config},
error::ClientCoreError,
};
pub async fn query_gateway_details(
validator_servers: Vec<Url>,
chosen_gateway_id: Option<&str>,
) -> Result<gateway::Node, ClientCoreError> {
let validator_api = validator_servers
.choose(&mut thread_rng())
.ok_or(ClientCoreError::ListOfValidatorApisIsEmpty)?;
let validator_client = validator_client::ApiClient::new(validator_api.clone());
log::trace!("Fetching list of gateways from: {}", validator_api);
let gateways = validator_client.get_cached_gateways().await?;
let valid_gateways = gateways
.into_iter()
.filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<gateway::Node>>();
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
// if we have chosen particular gateway - use it, otherwise choose a random one.
// (remember that in active topology all gateways have at least 100 reputation so should
// be working correctly)
if let Some(gateway_id) = chosen_gateway_id {
filtered_gateways
.iter()
.find(|gateway| gateway.identity_key.to_base58_string() == gateway_id)
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_id.to_string()))
.cloned()
} else {
filtered_gateways
.choose(&mut rand::thread_rng())
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
.cloned()
}
}
pub async fn register_with_gateway_and_store_keys<T>(
gateway_details: gateway::Node,
config: &Config<T>,
) -> Result<(), ClientCoreError>
where
T: NymConfig,
{
let mut rng = OsRng;
let mut key_manager = KeyManager::new(&mut rng);
let shared_keys =
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await?;
key_manager.insert_gateway_shared_key(shared_keys);
let pathfinder = ClientKeyPathfinder::new_from_config(config);
Ok(key_manager
.store_keys(&pathfinder)
.tap_err(|err| log::error!("Failed to generate keys: {err}"))?)
}
async fn register_with_gateway(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
) -> Result<Arc<SharedKeys>, ClientCoreError> {
let timeout = Duration::from_millis(1500);
let mut gateway_client = GatewayClient::new_init(
gateway.clients_address(),
gateway.identity_key,
gateway.owner.clone(),
our_identity.clone(),
timeout,
#[cfg(not(target_arch = "wasm32"))]
None,
);
gateway_client
.establish_connection()
.await
.tap_err(|_| log::warn!("Failed to establish connection with gateway!"))?;
let shared_keys = gateway_client
.perform_initial_authentication()
.await
.tap_err(|_| log::warn!("Failed to register with the gateway!"))?;
Ok(shared_keys)
}
pub fn show_address<T>(config: &Config<T>) -> Result<(), ClientCoreError>
where
T: config::NymConfig,
{
fn load_identity_keys(
pathfinder: &ClientKeyPathfinder,
) -> Result<identity::KeyPair, ClientCoreError> {
let identity_keypair: identity::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
pathfinder.private_identity_key().to_owned(),
pathfinder.public_identity_key().to_owned(),
))
.tap_err(|_| log::error!("Failed to read stored identity key files"))?;
Ok(identity_keypair)
}
fn load_sphinx_keys(
pathfinder: &ClientKeyPathfinder,
) -> Result<encryption::KeyPair, ClientCoreError> {
let sphinx_keypair: encryption::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
pathfinder.private_encryption_key().to_owned(),
pathfinder.public_encryption_key().to_owned(),
))
.tap_err(|_| log::error!("Failed to read stored sphinx key files"))?;
Ok(sphinx_keypair)
}
let pathfinder = ClientKeyPathfinder::new_from_config(config);
let identity_keypair = load_identity_keys(&pathfinder)?;
let sphinx_keypair = load_sphinx_keys(&pathfinder)?;
let client_recipient = Recipient::new(
*identity_keypair.public_key(),
*sphinx_keypair.public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(config.get_gateway_id())?,
);
println!("\nThe address of this client is: {}", client_recipient);
Ok(())
}
-21
View File
@@ -1,23 +1,2 @@
use std::future::Future;
pub mod client; pub mod client;
pub mod config; pub mod config;
pub mod error;
pub mod init;
#[cfg(target_arch = "wasm32")]
pub(crate) fn spawn_future<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
wasm_bindgen_futures::spawn_local(future);
}
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn spawn_future<F>(future: F)
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
tokio::spawn(future);
}
File diff suppressed because it is too large Load Diff
+3 -4
View File
@@ -15,16 +15,15 @@ rand = "0.7.3"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0" thiserror = "1.0"
url = "2.2" url = "2.2"
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
coconut-interface = { path = "../../common/coconut-interface" } coconut-interface = { path = "../../common/coconut-interface" }
config = { path = "../../common/config" }
credentials = { path = "../../common/credentials" } credentials = { path = "../../common/credentials" }
credential-storage = { path = "../../common/credential-storage" }
crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] } crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
network-defaults = { path = "../../common/network-defaults" } network-defaults = { path = "../../common/network-defaults" }
pemstore = { path = "../../common/pemstore" } pemstore = { path = "../../common/pemstore" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] } validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
[features] [features]
coconut = ["credentials/coconut"] coconut = ["credentials/coconut"]
+38 -24
View File
@@ -1,53 +1,67 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net> // Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::Result;
use bip39::Mnemonic; use bip39::Mnemonic;
use network_defaults::{NymNetworkDetails, VOUCHER_INFO}; use coconut_bandwidth_contract_common::deposit::DepositData;
use std::str::FromStr; use std::str::FromStr;
use url::Url; use url::Url;
use validator_client::nymd;
use validator_client::nymd::traits::CoconutBandwidthSigningClient; use crate::error::Result;
use validator_client::nymd::{Coin, Fee, NymdClient, SigningNymdClient}; use crate::{CONTRACT_ADDRESS, MNEMONIC, NYMD_URL};
use coconut_bandwidth_contract_common::msg::ExecuteMsg;
use network_defaults::DEFAULT_NETWORK;
use validator_client::nymd::{
AccountId, CosmosCoin, Decimal, Denom, NymdClient, SigningNymdClient,
};
pub(crate) struct Client { pub(crate) struct Client {
nymd_client: NymdClient<SigningNymdClient>, nymd_client: NymdClient<SigningNymdClient>,
mix_denom_base: String, denom: Denom,
contract_address: AccountId,
} }
impl Client { impl Client {
pub fn new(nymd_url: &str, mnemonic: &str) -> Self { pub fn new() -> Self {
let nymd_url = Url::from_str(nymd_url).unwrap(); let nymd_url = Url::from_str(NYMD_URL).unwrap();
let mnemonic = Mnemonic::from_str(mnemonic).unwrap(); let mnemonic = Mnemonic::from_str(MNEMONIC).unwrap();
let network_details = NymNetworkDetails::new_from_env(); let nymd_client = NymdClient::connect_with_mnemonic(
let config = nymd::Config::try_from_nym_network_details(&network_details) DEFAULT_NETWORK,
.expect("failed to construct valid validator client config with the provided network"); nymd_url.as_ref(),
let nymd_client = None,
NymdClient::connect_with_mnemonic(config, nymd_url.as_ref(), mnemonic, None).unwrap(); None,
None,
mnemonic,
None,
)
.unwrap();
let denom = Denom::from_str(network_defaults::DENOM).unwrap();
let contract_address = AccountId::from_str(CONTRACT_ADDRESS).unwrap();
Client { Client {
nymd_client, nymd_client,
mix_denom_base: network_details.chain_details.mix_denom.base, denom,
contract_address,
} }
} }
pub async fn deposit( pub async fn deposit(
&self, &self,
amount: u64, amount: u64,
info: &str,
verification_key: String, verification_key: String,
encryption_key: String, encryption_key: String,
fee: Option<Fee>,
) -> Result<String> { ) -> Result<String> {
let amount = Coin::new(amount as u128, self.mix_denom_base.clone()); let req = ExecuteMsg::DepositFunds {
data: DepositData::new(info.to_string(), verification_key, encryption_key),
};
let funds = vec![CosmosCoin {
denom: self.denom.clone(),
amount: Decimal::from(amount),
}];
Ok(self Ok(self
.nymd_client .nymd_client
.deposit( .execute(&self.contract_address, &req, Default::default(), "", funds)
amount,
String::from(VOUCHER_INFO),
verification_key,
encryption_key,
fee,
)
.await? .await?
.transaction_hash .transaction_hash
.to_string()) .to_string())
+23 -29
View File
@@ -6,10 +6,9 @@ use clap::{Args, Subcommand};
use pickledb::PickleDb; use pickledb::PickleDb;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use std::str::FromStr; use std::str::FromStr;
use url::Url;
use coconut_interface::{Attribute, Base58, BlindSignRequest, Bytable, Parameters}; use coconut_interface::{Attribute, Base58, BlindSignRequest, Bytable, Parameters};
use credential_storage::storage::Storage;
use credential_storage::PersistentStorage;
use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES}; use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use credentials::coconut::utils::obtain_aggregate_signature; use credentials::coconut::utils::obtain_aggregate_signature;
use crypto::asymmetric::{encryption, identity}; use crypto::asymmetric::{encryption, identity};
@@ -19,6 +18,7 @@ use validator_client::nymd::tx::Hash;
use crate::client::Client; use crate::client::Client;
use crate::error::{CredentialClientError, Result}; use crate::error::{CredentialClientError, Result};
use crate::state::{KeyPair, RequestData, State}; use crate::state::{KeyPair, RequestData, State};
use crate::SIGNER_AUTHORITIES;
#[derive(Subcommand)] #[derive(Subcommand)]
pub(crate) enum Commands { pub(crate) enum Commands {
@@ -32,17 +32,11 @@ pub(crate) enum Commands {
#[async_trait] #[async_trait]
pub(crate) trait Execute { pub(crate) trait Execute {
async fn execute(&self, db: &mut PickleDb, shared_storage: PersistentStorage) -> Result<()>; async fn execute(&self, db: &mut PickleDb) -> Result<()>;
} }
#[derive(Args, Clone)] #[derive(Args, Clone)]
pub(crate) struct Deposit { pub(crate) struct Deposit {
/// The nymd URL that should be used
#[clap(long)]
nymd_url: String,
/// A mnemonic for the account that does the deposit
#[clap(long)]
mnemonic: String,
/// The amount that needs to be deposited /// The amount that needs to be deposited
#[clap(long)] #[clap(long)]
amount: u64, amount: u64,
@@ -50,18 +44,18 @@ pub(crate) struct Deposit {
#[async_trait] #[async_trait]
impl Execute for Deposit { impl Execute for Deposit {
async fn execute(&self, db: &mut PickleDb, _shared_storage: PersistentStorage) -> Result<()> { async fn execute(&self, db: &mut PickleDb) -> Result<()> {
let mut rng = OsRng; let mut rng = OsRng;
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng)); let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng)); let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
let client = Client::new(&self.nymd_url, &self.mnemonic); let client = Client::new();
let tx_hash = client let tx_hash = client
.deposit( .deposit(
self.amount, self.amount,
VOUCHER_INFO,
signing_keypair.public_key.clone(), signing_keypair.public_key.clone(),
encryption_keypair.public_key.clone(), encryption_keypair.public_key.clone(),
None,
) )
.await?; .await?;
@@ -86,7 +80,7 @@ pub(crate) struct ListDeposits {}
#[async_trait] #[async_trait]
impl Execute for ListDeposits { impl Execute for ListDeposits {
async fn execute(&self, db: &mut PickleDb, _shared_storage: PersistentStorage) -> Result<()> { async fn execute(&self, db: &mut PickleDb) -> Result<()> {
for kv in db.iter() { for kv in db.iter() {
println!("{:?}", kv.get_value::<State>()); println!("{:?}", kv.get_value::<State>());
} }
@@ -100,10 +94,6 @@ pub(crate) struct GetCredential {
/// The hash of a successful deposit transaction /// The hash of a successful deposit transaction
#[clap(long)] #[clap(long)]
tx_hash: String, tx_hash: String,
/// The URLs to the validator-api endpoints the are run as coconut signer authorities, separated
/// by comma (,)
#[clap(long)]
signer_authorities: String,
/// If we want to get the signature without attaching a blind sign request; it is expected that /// If we want to get the signature without attaching a blind sign request; it is expected that
/// there is already a signature stored on the signer /// there is already a signature stored on the signer
#[clap(long, parse(from_flag))] #[clap(long, parse(from_flag))]
@@ -112,12 +102,11 @@ pub(crate) struct GetCredential {
#[async_trait] #[async_trait]
impl Execute for GetCredential { impl Execute for GetCredential {
async fn execute(&self, db: &mut PickleDb, shared_storage: PersistentStorage) -> Result<()> { async fn execute(&self, db: &mut PickleDb) -> Result<()> {
let mut state = db let mut state = db
.get::<State>(&self.tx_hash) .get::<State>(&self.tx_hash)
.ok_or(CredentialClientError::NoDeposit)?; .ok_or(CredentialClientError::NoDeposit)?;
let urls = SIGNER_AUTHORITIES.map(|addr| Url::from_str(addr).unwrap());
let urls = config::parse_validators(&self.signer_authorities);
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap(); let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let bandwidth_credential_attributes = if self.__no_request { let bandwidth_credential_attributes = if self.__no_request {
@@ -173,15 +162,6 @@ impl Execute for GetCredential {
let signature = let signature =
obtain_aggregate_signature(&params, &bandwidth_credential_attributes, &urls).await?; obtain_aggregate_signature(&params, &bandwidth_credential_attributes, &urls).await?;
shared_storage
.insert_coconut_credential(
state.amount.to_string(),
VOUCHER_INFO.to_string(),
bandwidth_credential_attributes.get_private_attributes()[0].to_bs58(),
bandwidth_credential_attributes.get_private_attributes()[1].to_bs58(),
signature.to_bs58(),
)
.await?;
state.signature = Some(signature.to_bs58()); state.signature = Some(signature.to_bs58());
db.set(&self.tx_hash, &state).unwrap(); db.set(&self.tx_hash, &state).unwrap();
@@ -190,3 +170,17 @@ impl Execute for GetCredential {
Ok(()) Ok(())
} }
} }
#[derive(Args, Clone)]
pub(crate) struct SpendCredential {
/// Spend one of the acquired credentials
#[clap(long)]
id: usize,
}
#[async_trait]
impl Execute for SpendCredential {
async fn execute(&self, _db: &mut PickleDb) -> Result<()> {
Ok(())
}
}
-4
View File
@@ -3,7 +3,6 @@
use thiserror::Error; use thiserror::Error;
use credential_storage::error::StorageError;
use credentials::error::Error as CredentialError; use credentials::error::Error as CredentialError;
use crypto::asymmetric::encryption::KeyRecoveryError; use crypto::asymmetric::encryption::KeyRecoveryError;
use crypto::asymmetric::identity::Ed25519RecoveryError; use crypto::asymmetric::identity::Ed25519RecoveryError;
@@ -39,7 +38,4 @@ pub enum CredentialClientError {
#[error("Could not parse X25519 data")] #[error("Could not parse X25519 data")]
X25519ParseError(#[from] KeyRecoveryError), X25519ParseError(#[from] KeyRecoveryError),
#[error("Could not use shared storage")]
SharedStorageError(#[from] StorageError),
} }
+10 -17
View File
@@ -11,24 +11,20 @@ cfg_if::cfg_if! {
use commands::{Commands, Execute}; use commands::{Commands, Execute};
use error::Result; use error::Result;
use network_defaults::setup_env;
use clap::Parser; use clap::Parser;
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod}; use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
pub const MNEMONIC: &str = "sun surge soon stomach flavor country gorilla dress oblige stamp attract hip soldier agree steel prize nuclear know enjoy arm bargain always theme matter";
pub const NYMD_URL: &str = "http://127.0.0.1:26657";
pub const CONTRACT_ADDRESS: &str = "nymt1vhjnzk9ly03dugffvzfcwgry4dgc8x0sscmfl2";
pub const SIGNER_AUTHORITIES: [&str; 1] = [
"http://127.0.0.1:8080",
];
#[derive(Parser)] #[derive(Parser)]
#[clap(author = "Nymtech", version, about)] #[clap(author = "Nymtech", version, about)]
struct Cli { struct Cli {
/// Path pointing to an env file that configures the client.
#[clap(long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
/// Path where the sqlite credental database will be located.
/// It should point to a $HOME/$CLIENT_ID/data/db.sqlite file of
/// the client that is supposed to use the credential.
#[clap(long)]
pub(crate) credential_db_path: std::path::PathBuf,
#[clap(subcommand)] #[clap(subcommand)]
command: Commands, command: Commands,
} }
@@ -36,9 +32,6 @@ cfg_if::cfg_if! {
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
let args = Cli::parse(); let args = Cli::parse();
setup_env(args.config_env_file.clone());
let shared_storage = credential_storage::initialise_storage(args.credential_db_path.clone()).await;
let mut db = match PickleDb::load( let mut db = match PickleDb::load(
"credential.db", "credential.db",
PickleDbDumpPolicy::AutoDump, PickleDbDumpPolicy::AutoDump,
@@ -53,9 +46,9 @@ cfg_if::cfg_if! {
}; };
match &args.command { match &args.command {
Commands::Deposit(m) => m.execute(&mut db, shared_storage).await?, Commands::Deposit(m) => m.execute(&mut db).await?,
Commands::ListDeposits(m) => m.execute(&mut db, shared_storage).await?, Commands::ListDeposits(m) => m.execute(&mut db).await?,
Commands::GetCredential(m) => m.execute(&mut db, shared_storage).await?, Commands::GetCredential(m) => m.execute(&mut db).await?,
} }
Ok(()) Ok(())
+2402
View File
File diff suppressed because it is too large Load Diff
+12 -13
View File
@@ -1,8 +1,7 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.0.2" version = "1.0.0-rc.1"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.56"
@@ -20,39 +19,39 @@ futures = "0.3" # bunch of futures stuff, however, now that I think about it, it
# and the single instance of abortable we have should really be refactored anyway # and the single instance of abortable we have should really be refactored anyway
url = "2.2" url = "2.2"
clap = { version = "3.2.8", features = ["cargo", "derive"] } clap = "2.33.0" # for the command line arguments
dirs = "4.0" dirs = "3.0" # for determining default store directories in config
dotenv = "0.15.0" # for obtaining environmental variables (only used for RUST_LOG for time being)
log = "0.4" # self explanatory log = "0.4" # self explanatory
pretty_env_logger = "0.4" # for formatting log messages pretty_env_logger = "0.4" # for formatting log messages
rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
sled = "0.34" # for storage of replySURB decryption keys sled = "0.34" # for storage of replySURB decryption keys
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] } # async runtime tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal"] } # async runtime
tokio-tungstenite = "0.14" # websocket tokio-tungstenite = "0.14" # websocket
## internal ## internal
client-core = { path = "../client-core" } client-core = { path = "../client-core" }
coconut-interface = { path = "../../common/coconut-interface", optional = true } coconut-interface = { path = "../../common/coconut-interface", optional = true }
config = { path = "../../common/config" }
credential-storage = { path = "../../common/credential-storage" }
credentials = { path = "../../common/credentials", optional = true } credentials = { path = "../../common/credentials", optional = true }
config = { path = "../../common/config" }
crypto = { path = "../../common/crypto" } crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" } gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" } gateway-requests = { path = "../../gateway/gateway-requests" }
network-defaults = { path = "../../common/network-defaults" }
nymsphinx = { path = "../../common/nymsphinx" } nymsphinx = { path = "../../common/nymsphinx" }
pemstore = { path = "../../common/pemstore" } pemstore = { path = "../../common/pemstore" }
task = { path = "../../common/task" }
topology = { path = "../../common/topology" } topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
version-checker = { path = "../../common/version-checker" }
websocket-requests = { path = "websocket-requests" } websocket-requests = { path = "websocket-requests" }
validator-client = { path = "../../common/client-libs/validator-client" }
version-checker = { path = "../../common/version-checker" }
network-defaults = { path = "../../common/network-defaults" }
[features] [features]
coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-requests/coconut", "gateway-client/coconut", "client-core/coconut"] coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-requests/coconut", "gateway-client/coconut"]
eth = []
[dev-dependencies] [dev-dependencies]
serde_json = "1.0" # for the "textsend" example serde_json = "1.0" # for the "textsend" example
[build-dependencies] [build-dependencies]
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] } vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
+27 -109
View File
@@ -27,58 +27,6 @@
"node": ">=10.0.0" "node": ">=10.0.0"
} }
}, },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.15",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -644,9 +592,9 @@
} }
}, },
"node_modules/async": { "node_modules/async": {
"version": "2.6.4", "version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"lodash": "^4.17.14" "lodash": "^4.17.14"
@@ -3581,13 +3529,13 @@
} }
}, },
"node_modules/terser": { "node_modules/terser": {
"version": "5.15.0", "version": "5.12.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0", "acorn": "^8.5.0",
"commander": "^2.20.0", "commander": "^2.20.0",
"source-map": "~0.7.2",
"source-map-support": "~0.5.20" "source-map-support": "~0.5.20"
}, },
"bin": { "bin": {
@@ -3635,6 +3583,14 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
}, },
"node_modules/terser/node_modules/source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"engines": {
"node": ">= 8"
}
},
"node_modules/thunky": { "node_modules/thunky": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
@@ -4374,49 +4330,6 @@
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
"dev": true "dev": true
}, },
"@jridgewell/gen-mapping": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
"integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
"requires": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9"
}
},
"@jridgewell/resolve-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
},
"@jridgewell/set-array": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
},
"@jridgewell/source-map": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
"integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
"requires": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
}
},
"@jridgewell/sourcemap-codec": {
"version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
},
"@jridgewell/trace-mapping": {
"version": "0.3.15",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
"requires": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"@nodelib/fs.scandir": { "@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -4912,9 +4825,9 @@
"dev": true "dev": true
}, },
"async": { "async": {
"version": "2.6.4", "version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
"dev": true, "dev": true,
"requires": { "requires": {
"lodash": "^4.17.14" "lodash": "^4.17.14"
@@ -7145,13 +7058,13 @@
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
}, },
"terser": { "terser": {
"version": "5.15.0", "version": "5.12.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==", "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
"requires": { "requires": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0", "acorn": "^8.5.0",
"commander": "^2.20.0", "commander": "^2.20.0",
"source-map": "~0.7.2",
"source-map-support": "~0.5.20" "source-map-support": "~0.5.20"
}, },
"dependencies": { "dependencies": {
@@ -7159,6 +7072,11 @@
"version": "2.20.3", "version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
} }
} }
}, },
+2 -6
View File
@@ -11,7 +11,7 @@ use std::path::PathBuf;
mod template; mod template;
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)] #[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub enum SocketType { pub enum SocketType {
WebSocket, WebSocket,
@@ -50,10 +50,6 @@ impl NymConfig for Config {
.join("clients") .join("clients")
} }
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("clients"))
}
fn root_directory(&self) -> PathBuf { fn root_directory(&self) -> PathBuf {
self.base.get_nym_root_directory() self.base.get_nym_root_directory()
} }
@@ -109,7 +105,7 @@ impl Config {
} }
} }
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Socket { pub struct Socket {
socket_type: SocketType, socket_type: SocketType,
+12 -4
View File
@@ -19,9 +19,9 @@ version = '{{ client.version }}'
# Human readable ID of this particular client. # Human readable ID of this particular client.
id = '{{ client.id }}' id = '{{ client.id }}'
# Indicates whether this client is running in a disabled credentials mode, thus attempting # Indicates whether this client is running in a testnet mode, thus attempting
# to claim bandwidth without presenting bandwidth credentials. # to claim bandwidth without presenting bandwidth credentials.
disabled_credentials_mode = {{ client.disabled_credentials_mode }} testnet_mode = {{ client.testnet_mode }}
# Addresses to APIs running on validator from which the client gets the view of the network. # Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls = [ validator_api_urls = [
@@ -46,8 +46,16 @@ public_encryption_key_file = '{{ client.public_encryption_key_file }}'
# sent but not received back. # sent but not received back.
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}' reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
# Path to the database containing bandwidth credentials # Path to directory containing public/private keys used for bandwidth token purchase.
database_path = '{{ client.database_path }}' # Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
# The public key is the name of the file, while the private key is the content.
backup_bandwidth_token_keys_dir = '{{ client.backup_bandwidth_token_keys_dir }}'
# Ethereum private key.
eth_private_key = '{{ client.eth_private_key }}'
# Addess to an Ethereum full node.
eth_endpoint = '{{ client.eth_endpoint }}'
##### additional client config options ##### ##### additional client config options #####
+29 -80
View File
@@ -31,9 +31,7 @@ use log::*;
use nymsphinx::addressing::clients::Recipient; use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity; use nymsphinx::addressing::nodes::NodeIdentity;
use nymsphinx::anonymous_replies::ReplySurb; use nymsphinx::anonymous_replies::ReplySurb;
use nymsphinx::params::PacketSize;
use nymsphinx::receiver::ReconstructedMessage; use nymsphinx::receiver::ReconstructedMessage;
use task::{wait_for_signal, ShutdownListener, ShutdownNotifier};
use crate::client::config::{Config, SocketType}; use crate::client::config::{Config, SocketType};
use crate::websocket; use crate::websocket;
@@ -87,11 +85,10 @@ impl NymClient {
&self, &self,
topology_accessor: TopologyAccessor, topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender, mix_tx: BatchMixMessageSender,
shutdown: ShutdownListener,
) { ) {
info!("Starting loop cover traffic stream..."); info!("Starting loop cover traffic stream...");
let mut stream = LoopCoverTrafficStream::new( LoopCoverTrafficStream::new(
self.key_manager.ack_key(), self.key_manager.ack_key(),
self.config.get_base().get_average_ack_delay(), self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_average_packet_delay(), self.config.get_base().get_average_packet_delay(),
@@ -101,13 +98,8 @@ impl NymClient {
mix_tx, mix_tx,
self.as_mix_recipient(), self.as_mix_recipient(),
topology_accessor, topology_accessor,
); )
.start();
if self.config.get_base().get_use_extended_packet_size() {
stream.set_custom_packet_size(PacketSize::ExtendedPacket)
}
stream.start_with_shutdown(shutdown);
} }
fn start_real_traffic_controller( fn start_real_traffic_controller(
@@ -117,25 +109,17 @@ impl NymClient {
ack_receiver: AcknowledgementReceiver, ack_receiver: AcknowledgementReceiver,
input_receiver: InputMessageReceiver, input_receiver: InputMessageReceiver,
mix_sender: BatchMixMessageSender, mix_sender: BatchMixMessageSender,
shutdown: ShutdownListener,
) { ) {
let mut controller_config = real_messages_control::Config::new( let controller_config = real_messages_control::Config::new(
self.key_manager.ack_key(), self.key_manager.ack_key(),
self.config.get_base().get_ack_wait_multiplier(), self.config.get_base().get_ack_wait_multiplier(),
self.config.get_base().get_ack_wait_addition(), self.config.get_base().get_ack_wait_addition(),
self.config.get_base().get_average_ack_delay(), self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_message_sending_average_delay(), self.config.get_base().get_message_sending_average_delay(),
self.config.get_base().get_average_packet_delay(), self.config.get_base().get_average_packet_delay(),
self.config
.get_base()
.get_disabled_main_poisson_packet_distribution(),
self.as_mix_recipient(), self.as_mix_recipient(),
); );
if self.config.get_base().get_use_extended_packet_size() {
controller_config.set_custom_packet_size(PacketSize::ExtendedPacket)
}
info!("Starting real traffic stream..."); info!("Starting real traffic stream...");
RealMessagesController::new( RealMessagesController::new(
@@ -146,7 +130,7 @@ impl NymClient {
topology_accessor, topology_accessor,
reply_key_storage, reply_key_storage,
) )
.start_with_shutdown(shutdown); .start();
} }
// buffer controlling all messages fetched from provider // buffer controlling all messages fetched from provider
@@ -156,7 +140,6 @@ impl NymClient {
query_receiver: ReceivedBufferRequestReceiver, query_receiver: ReceivedBufferRequestReceiver,
mixnet_receiver: MixnetMessageReceiver, mixnet_receiver: MixnetMessageReceiver,
reply_key_storage: ReplyKeyStorage, reply_key_storage: ReplyKeyStorage,
shutdown: ShutdownListener,
) { ) {
info!("Starting received messages buffer controller..."); info!("Starting received messages buffer controller...");
ReceivedMessagesBufferController::new( ReceivedMessagesBufferController::new(
@@ -165,14 +148,13 @@ impl NymClient {
mixnet_receiver, mixnet_receiver,
reply_key_storage, reply_key_storage,
) )
.start_with_shutdown(shutdown) .start()
} }
async fn start_gateway_client( async fn start_gateway_client(
&mut self, &mut self,
mixnet_message_sender: MixnetMessageSender, mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender, ack_sender: AcknowledgementSender,
shutdown: ShutdownListener,
) -> GatewayClient { ) -> GatewayClient {
let gateway_id = self.config.get_base().get_gateway_id(); let gateway_id = self.config.get_base().get_gateway_id();
if gateway_id.is_empty() { if gateway_id.is_empty() {
@@ -192,14 +174,14 @@ impl NymClient {
#[cfg(feature = "coconut")] #[cfg(feature = "coconut")]
let bandwidth_controller = BandwidthController::new( let bandwidth_controller = BandwidthController::new(
credential_storage::initialise_storage(self.config.get_base().get_database_path())
.await,
self.config.get_base().get_validator_api_endpoints(), self.config.get_base().get_validator_api_endpoints(),
*self.key_manager.identity_keypair().public_key(),
); );
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
let bandwidth_controller = BandwidthController::new( let bandwidth_controller = BandwidthController::new(
credential_storage::initialise_storage(self.config.get_base().get_database_path()) self.config.get_base().get_eth_endpoint(),
.await, self.config.get_base().get_eth_private_key(),
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
) )
.expect("Could not create bandwidth controller"); .expect("Could not create bandwidth controller");
@@ -213,12 +195,11 @@ impl NymClient {
ack_sender, ack_sender,
self.config.get_base().get_gateway_response_timeout(), self.config.get_base().get_gateway_response_timeout(),
Some(bandwidth_controller), Some(bandwidth_controller),
Some(shutdown),
); );
gateway_client if self.config.get_base().get_testnet_mode() {
.set_disabled_credentials_mode(self.config.get_base().get_disabled_credentials_mode()); gateway_client.set_testnet_mode(true)
}
gateway_client gateway_client
.authenticate_and_start() .authenticate_and_start()
.await .await
@@ -229,11 +210,7 @@ impl NymClient {
// future responsible for periodically polling directory server and updating // future responsible for periodically polling directory server and updating
// the current global view of topology // the current global view of topology
async fn start_topology_refresher( async fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
&mut self,
topology_accessor: TopologyAccessor,
shutdown: ShutdownListener,
) {
let topology_refresher_config = TopologyRefresherConfig::new( let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_base().get_validator_api_endpoints(), self.config.get_base().get_validator_api_endpoints(),
self.config.get_base().get_topology_refresh_rate(), self.config.get_base().get_topology_refresh_rate(),
@@ -255,7 +232,7 @@ impl NymClient {
} }
info!("Starting topology refresher..."); info!("Starting topology refresher...");
topology_refresher.start_with_shutdown(shutdown); topology_refresher.start();
} }
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic) // controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
@@ -266,10 +243,9 @@ impl NymClient {
&mut self, &mut self,
mix_rx: BatchMixMessageReceiver, mix_rx: BatchMixMessageReceiver,
gateway_client: GatewayClient, gateway_client: GatewayClient,
shutdown: ShutdownListener,
) { ) {
info!("Starting mix traffic controller..."); info!("Starting mix traffic controller...");
MixTrafficController::new(mix_rx, gateway_client).start_with_shutdown(shutdown); MixTrafficController::new(mix_rx, gateway_client).start();
} }
fn start_websocket_listener( fn start_websocket_listener(
@@ -330,26 +306,20 @@ impl NymClient {
/// blocking version of `start` method. Will run forever (or until SIGINT is sent) /// blocking version of `start` method. Will run forever (or until SIGINT is sent)
pub async fn run_forever(&mut self) { pub async fn run_forever(&mut self) {
let shutdown = self.start().await; self.start().await;
wait_for_signal().await; if let Err(e) = tokio::signal::ctrl_c().await {
error!(
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
e
);
}
println!( println!(
"Received signal - the client will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)." "Received SIGINT - the client will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)."
); );
log::info!("Sending shutdown");
shutdown.signal_shutdown().ok();
// Some of these components have shutdown signalling implemented as part of socks5 work,
// but since it's not fully implemented (yet) for all the components of the native client,
// we don't try to wait and instead just stop immediately.
//log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
//shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-client");
} }
pub async fn start(&mut self) -> ShutdownNotifier { pub async fn start(&mut self) {
info!("Starting nym client"); info!("Starting nym client");
// channels for inter-component communication // channels for inter-component communication
// TODO: make the channels be internally created by the relevant components // TODO: make the channels be internally created by the relevant components
@@ -379,49 +349,30 @@ impl NymClient {
ReplyKeyStorage::load(self.config.get_base().get_reply_encryption_key_store_path()) ReplyKeyStorage::load(self.config.get_base().get_reply_encryption_key_store_path())
.expect("Failed to load reply key storage!"); .expect("Failed to load reply key storage!");
// Shutdown notifier for signalling tasks to stop
let shutdown = ShutdownNotifier::default();
// the components are started in very specific order. Unless you know what you are doing, // the components are started in very specific order. Unless you know what you are doing,
// do not change that. // do not change that.
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe()) self.start_topology_refresher(shared_topology_accessor.clone())
.await; .await;
self.start_received_messages_buffer_controller( self.start_received_messages_buffer_controller(
received_buffer_request_receiver, received_buffer_request_receiver,
mixnet_messages_receiver, mixnet_messages_receiver,
reply_key_storage.clone(), reply_key_storage.clone(),
shutdown.subscribe(),
); );
let gateway_client = self let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe()) .start_gateway_client(mixnet_messages_sender, ack_sender)
.await; .await;
self.start_mix_traffic_controller( self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
sphinx_message_receiver,
gateway_client,
shutdown.subscribe(),
);
self.start_real_traffic_controller( self.start_real_traffic_controller(
shared_topology_accessor.clone(), shared_topology_accessor.clone(),
reply_key_storage, reply_key_storage,
ack_receiver, ack_receiver,
input_receiver, input_receiver,
sphinx_message_sender.clone(), sphinx_message_sender.clone(),
shutdown.subscribe(),
); );
if !self self.start_cover_traffic_stream(shared_topology_accessor, sphinx_message_sender);
.config
.get_base()
.get_disabled_loop_cover_traffic_stream()
{
self.start_cover_traffic_stream(
shared_topology_accessor,
sphinx_message_sender,
shutdown.subscribe(),
);
}
match self.config.get_socket_type() { match self.config.get_socket_type() {
SocketType::WebSocket => { SocketType::WebSocket => {
@@ -446,7 +397,5 @@ impl NymClient {
info!("Client startup finished!"); info!("Client startup finished!");
info!("The address of this client is: {}", self.as_mix_recipient()); info!("The address of this client is: {}", self.as_mix_recipient());
shutdown
} }
} }
+270 -148
View File
@@ -1,176 +1,298 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use clap::Args; use clap::{App, Arg, ArgMatches};
use client_core::{config::GatewayEndpoint, error::ClientCoreError}; use client_core::client::key_manager::KeyManager;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
#[cfg(feature = "coconut")]
use coconut_interface::{Credential, Parameters};
use config::NymConfig; use config::NymConfig;
#[cfg(feature = "coconut")]
use credentials::coconut::{
bandwidth::prepare_for_spending, bandwidth::BandwidthVoucher, bandwidth::TOTAL_ATTRIBUTES,
utils::obtain_aggregate_signature,
};
#[cfg(feature = "coconut")]
use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::{encryption, identity};
use gateway_client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys;
#[cfg(feature = "coconut")]
use network_defaults::{BANDWIDTH_VALUE, VOUCHER_INFO};
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
use rand::rngs::OsRng;
use rand::seq::SliceRandom;
use rand::thread_rng;
use std::convert::TryInto;
use std::sync::Arc;
use std::time::Duration;
use topology::{filter::VersionFilterable, gateway};
use url::Url;
#[cfg(feature = "coconut")]
use validator_client::nymd::tx::Hash;
use crate::{ use crate::client::config::Config;
client::config::Config, use crate::commands::override_config;
commands::{override_config, OverrideConfig}, #[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))]
use crate::commands::{
DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
TESTNET_MODE_ARG_NAME,
}; };
#[derive(Args, Clone)] pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
pub(crate) struct Init { let app = App::new("init")
/// Id of the nym-mixnet-client we want to create config for. .about("Initialise a Nym client. Do this first!")
#[clap(long)] .arg(Arg::with_name("id")
id: String, .long("id")
.help("Id of the nym-mixnet-client we want to create config for.")
.takes_value(true)
.required(true)
)
.arg(Arg::with_name("gateway")
.long("gateway")
.help("Id of the gateway we are going to connect to.")
.takes_value(true)
)
.arg(Arg::with_name("validators")
.long("validators")
.help("Comma separated list of rest endpoints of the validators")
.takes_value(true),
)
.arg(Arg::with_name("disable-socket")
.long("disable-socket")
.help("Whether to not start the websocket")
)
.arg(Arg::with_name("port")
.short("p")
.long("port")
.help("Port for the socket (if applicable) to listen on in all subsequent runs")
.takes_value(true)
)
.arg(Arg::with_name("fastmode")
.long("fastmode")
.hidden(true) // this will prevent this flag from being displayed in `--help`
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
);
#[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))]
let app = app
.arg(
Arg::with_name(TESTNET_MODE_ARG_NAME)
.long(TESTNET_MODE_ARG_NAME)
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
)
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
.long(ETH_ENDPOINT_ARG_NAME)
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true)
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT)
.required(true))
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
.long(ETH_PRIVATE_KEY_ARG_NAME)
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true)
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
.required(true)
);
/// Id of the gateway we are going to connect to. app
#[clap(long)]
gateway: Option<String>,
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
/// potentially causing loss of access.
#[clap(long)]
force_register_gateway: bool,
/// Comma separated list of rest endpoints of the validators
#[clap(long)]
validators: Option<String>,
/// Whether to not start the websocket
#[clap(long)]
disable_socket: bool,
/// Port for the socket (if applicable) to listen on in all subsequent runs
#[clap(short, long)]
port: Option<u16>,
/// Mostly debug-related option to increase default traffic rate so that you would not need to
/// modify config post init
#[clap(long, hidden = true)]
fastmode: bool,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[cfg(feature = "coconut")]
#[clap(long)]
enabled_credentials_mode: bool,
} }
impl From<Init> for OverrideConfig { // this behaviour should definitely be changed, we shouldn't
fn from(init_config: Init) -> Self { // need to get bandwidth credential for registration
OverrideConfig { #[cfg(feature = "coconut")]
validators: init_config.validators, async fn _prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential {
disable_socket: init_config.disable_socket, let verification_key = obtain_aggregate_verification_key(validators)
port: init_config.port, .await
fastmode: init_config.fastmode, .expect("could not obtain aggregate verification key of validators");
#[cfg(feature = "coconut")] let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
enabled_credentials_mode: init_config.enabled_credentials_mode, let mut rng = OsRng;
} let bandwidth_credential_attributes = BandwidthVoucher::new(
&params,
BANDWIDTH_VALUE.to_string(),
VOUCHER_INFO.to_string(),
Hash::new([0; 32]),
// workaround for putting a valid value here, without deriving clone for the private
// key, until we have actual useful values
identity::PrivateKey::from_base58_string(
identity::KeyPair::new(&mut rng)
.private_key()
.to_base58_string(),
)
.unwrap(),
encryption::KeyPair::new(&mut rng).private_key().clone(),
);
let bandwidth_credential =
obtain_aggregate_signature(&params, &bandwidth_credential_attributes, validators)
.await
.expect("could not obtain bandwidth credential");
prepare_for_spending(
raw_identity,
&bandwidth_credential,
&bandwidth_credential_attributes,
&verification_key,
)
.expect("could not prepare out bandwidth credential for spending")
}
async fn register_with_gateway(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
) -> Arc<SharedKeys> {
let timeout = Duration::from_millis(1500);
let mut gateway_client = GatewayClient::new_init(
gateway.clients_address(),
gateway.identity_key,
gateway.owner.clone(),
our_identity.clone(),
timeout,
);
gateway_client
.establish_connection()
.await
.expect("failed to establish connection with the gateway!");
gateway_client
.perform_initial_authentication()
.await
.expect("failed to register with the gateway!")
}
async fn gateway_details(
validator_servers: Vec<Url>,
chosen_gateway_id: Option<&str>,
) -> gateway::Node {
let validator_api = validator_servers
.choose(&mut thread_rng())
.expect("The list of validator apis is empty");
let validator_client = validator_client::ApiClient::new(validator_api.clone());
let gateways = validator_client.get_cached_gateways().await.unwrap();
let valid_gateways = gateways
.into_iter()
.filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<gateway::Node>>();
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
// if we have chosen particular gateway - use it, otherwise choose a random one.
// (remember that in active topology all gateways have at least 100 reputation so should
// be working correctly)
if let Some(gateway_id) = chosen_gateway_id {
filtered_gateways
.iter()
.find(|gateway| gateway.identity_key.to_base58_string() == gateway_id)
.expect(&*format!("no gateway with id {} exists!", gateway_id))
.clone()
} else {
filtered_gateways
.choose(&mut rand::thread_rng())
.expect("there are no gateways on the network!")
.clone()
} }
} }
pub(crate) async fn execute(args: &Init) { fn show_address(config: &Config) {
fn load_identity_keys(pathfinder: &ClientKeyPathfinder) -> identity::KeyPair {
let identity_keypair: identity::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
pathfinder.private_identity_key().to_owned(),
pathfinder.public_identity_key().to_owned(),
))
.expect("Failed to read stored identity key files");
identity_keypair
}
fn load_sphinx_keys(pathfinder: &ClientKeyPathfinder) -> encryption::KeyPair {
let sphinx_keypair: encryption::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
pathfinder.private_encryption_key().to_owned(),
pathfinder.public_encryption_key().to_owned(),
))
.expect("Failed to read stored sphinx key files");
sphinx_keypair
}
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
let identity_keypair = load_identity_keys(&pathfinder);
let sphinx_keypair = load_sphinx_keys(&pathfinder);
let client_recipient = Recipient::new(
*identity_keypair.public_key(),
*sphinx_keypair.public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(config.get_base().get_gateway_id()).unwrap(),
);
println!("\nThe address of this client is: {}", client_recipient);
}
pub async fn execute(matches: ArgMatches<'static>) {
println!("Initialising client..."); println!("Initialising client...");
let id = &args.id; let id = matches.value_of("id").unwrap(); // required for now
let already_init = Config::default_config_file_path(Some(id)).exists(); let already_init = if Config::default_config_file_path(Some(id)).exists() {
if already_init { println!("Client \"{}\" was already initialised before! Config information will be overwritten (but keys will be kept)!", id);
println!( true
"Client \"{}\" was already initialised before! \ } else {
Config information will be overwritten (but keys will be kept)!", false
id };
);
}
// Usually you only register with the gateway on the first init, however you can force
// re-registering if wanted.
let user_wants_force_register = args.force_register_gateway;
// If the client was already initialized, don't generate new keys and don't re-register with
// the gateway (because this would create a new shared key).
// Unless the user really wants to.
let register_gateway = !already_init || user_wants_force_register;
// Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway.as_deref();
let mut config = Config::new(id); let mut config = Config::new(id);
let override_config_fields = OverrideConfig::from(args.clone());
config = override_config(config, override_config_fields);
let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config) let mut rng = OsRng;
.await
.unwrap_or_else(|err| { // TODO: ideally that should be the last thing that's being done to config.
eprintln!("Failed to setup gateway\nError: {err}"); // However, we are later further overriding it with gateway id
std::process::exit(1) config = override_config(config, &matches);
}); if matches.is_present("fastmode") {
config.get_base_mut().with_gateway_endpoint(gateway); config.get_base_mut().set_high_default_traffic_volume();
}
// if client was already initialised, don't generate new keys, not re-register with gateway
// (because this would create new shared key)
if !already_init {
// create identity, encryption and ack keys.
let mut key_manager = KeyManager::new(&mut rng);
let chosen_gateway_id = matches.value_of("gateway");
let gateway_details = gateway_details(
config.get_base().get_validator_api_endpoints(),
chosen_gateway_id,
)
.await;
let shared_keys =
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
config.get_base_mut().with_gateway_endpoint(
gateway_details.identity_key.to_base58_string(),
gateway_details.owner.clone(),
gateway_details.clients_address(),
);
key_manager.insert_gateway_shared_key(shared_keys);
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
key_manager
.store_keys(&pathfinder)
.expect("Failed to generated keys");
println!("Saved all generated keys");
}
let config_save_location = config.get_config_file_save_location(); let config_save_location = config.get_config_file_save_location();
config config
.save_to_file(None) .save_to_file(None)
.expect("Failed to save the config file"); .expect("Failed to save the config file");
println!("Saved configuration file to {:?}", config_save_location); println!("Saved configuration file to {:?}", config_save_location);
println!("Using gateway: {}", config.get_base().get_gateway_id()); println!("Using gateway: {}", config.get_base().get_gateway_id(),);
log::debug!("Gateway id: {}", config.get_base().get_gateway_id()); println!("Client configuration completed.\n\n\n");
log::debug!("Gateway owner: {}", config.get_base().get_gateway_owner());
log::debug!(
"Gateway listener: {}",
config.get_base().get_gateway_listener()
);
println!("Client configuration completed.");
client_core::init::show_address(config.get_base()).unwrap_or_else(|err| { show_address(&config);
eprintln!("Failed to show address\nError: {err}");
std::process::exit(1)
});
}
async fn setup_gateway(
id: &str,
register: bool,
user_chosen_gateway_id: Option<&str>,
config: &Config,
) -> Result<GatewayEndpoint, ClientCoreError> {
if register {
// Get the gateway details by querying the validator-api. Either pick one at random or use
// the chosen one if it's among the available ones.
println!("Configuring gateway");
let gateway = client_core::init::query_gateway_details(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
log::debug!("Querying gateway gives: {}", gateway);
// Registering with gateway by setting up and writing shared keys to disk
log::trace!("Registering gateway");
client_core::init::register_with_gateway_and_store_keys(gateway.clone(), config.get_base())
.await?;
println!("Saved all generated keys");
Ok(gateway.into())
} else if user_chosen_gateway_id.is_some() {
// Just set the config, don't register or create any keys
// This assumes that the user knows what they are doing, and that the existing keys are
// valid for the gateway being used
println!("Using gateway provided by user, keeping existing keys");
let gateway = client_core::init::query_gateway_details(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
log::debug!("Querying gateway gives: {}", gateway);
Ok(gateway.into())
} else {
println!("Not registering gateway, will reuse existing config and keys");
let existing_config = Config::load_from_file(Some(id)).map_err(|err| {
log::error!(
"Unable to configure gateway: {err}. \n
Seems like the client was already initialized but it was not possible to read \
the existing configuration file. \n
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
removing the existing configuration and starting over."
);
ClientCoreError::CouldNotLoadExistingGatewayConfiguration(err)
})?;
Ok(existing_config.get_base().get_gateway_endpoint().clone())
}
} }
+55 -105
View File
@@ -2,129 +2,79 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::{Config, SocketType}; use crate::client::config::{Config, SocketType};
use clap::{Parser, Subcommand}; use clap::ArgMatches;
use url::Url;
pub(crate) const TESTNET_MODE_ARG_NAME: &str = "testnet-mode";
#[cfg(not(feature = "coconut"))]
pub(crate) const ETH_ENDPOINT_ARG_NAME: &str = "eth_endpoint";
#[cfg(not(feature = "coconut"))]
pub(crate) const ETH_PRIVATE_KEY_ARG_NAME: &str = "eth_private_key";
#[cfg(not(feature = "coconut"))]
pub(crate) const DEFAULT_ETH_ENDPOINT: &str =
"https://rinkeby.infura.io/v3/00000000000000000000000000000000";
#[cfg(not(feature = "coconut"))]
pub(crate) const DEFAULT_ETH_PRIVATE_KEY: &str =
"0000000000000000000000000000000000000000000000000000000000000001";
pub(crate) mod init; pub(crate) mod init;
pub(crate) mod run; pub(crate) mod run;
pub(crate) mod upgrade; pub(crate) mod upgrade;
fn long_version() -> String { fn parse_validators(raw: &str) -> Vec<Url> {
format!( raw.split(',')
r#" .map(|raw_validator| {
{:<20}{} raw_validator
{:<20}{} .trim()
{:<20}{} .parse()
{:<20}{} .expect("one of the provided validator api urls is invalid")
{:<20}{} })
{:<20}{} .collect()
{:<20}{}
{:<20}{}
"#,
"Build Timestamp:",
env!("VERGEN_BUILD_TIMESTAMP"),
"Build Version:",
env!("VERGEN_BUILD_SEMVER"),
"Commit SHA:",
env!("VERGEN_GIT_SHA"),
"Commit Date:",
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
"Commit Branch:",
env!("VERGEN_GIT_BRANCH"),
"rustc Version:",
env!("VERGEN_RUSTC_SEMVER"),
"rustc Channel:",
env!("VERGEN_RUSTC_CHANNEL"),
"cargo Profile:",
env!("VERGEN_CARGO_PROFILE"),
)
} }
fn long_version_static() -> &'static str { pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config {
Box::leak(long_version().into_boxed_str()) if let Some(raw_validators) = matches.value_of("validators") {
}
#[derive(Parser)]
#[clap(author = "Nymtech", version, long_version = long_version_static(), about)]
pub(crate) struct Cli {
/// Path pointing to an env file that configures the client.
#[clap(long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
pub(crate) enum Commands {
/// Initialise a Nym client. Do this first!
Init(init::Init),
/// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run),
/// Try to upgrade the client
Upgrade(upgrade::Upgrade),
}
// Configuration that can be overridden.
pub(crate) struct OverrideConfig {
validators: Option<String>,
disable_socket: bool,
port: Option<u16>,
fastmode: bool,
#[cfg(feature = "coconut")]
enabled_credentials_mode: bool,
}
pub(crate) async fn execute(args: &Cli) {
match &args.command {
Commands::Init(m) => init::execute(m).await,
Commands::Run(m) => run::execute(m).await,
Commands::Upgrade(m) => upgrade::execute(m),
}
}
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
if let Some(raw_validators) = args.validators {
config config
.get_base_mut() .get_base_mut()
.set_custom_validator_apis(config::parse_validators(&raw_validators)); .set_custom_validator_apis(parse_validators(raw_validators));
} else if std::env::var(network_defaults::var_names::CONFIGURED).is_ok() {
let raw_validators = std::env::var(network_defaults::var_names::API_VALIDATOR)
.expect("api validator not set");
config
.get_base_mut()
.set_custom_validator_apis(config::parse_validators(&raw_validators));
} }
if args.disable_socket { if let Some(gateway_id) = matches.value_of("gateway") {
config.get_base_mut().with_gateway_id(gateway_id);
}
if matches.is_present("disable-socket") {
config = config.with_socket(SocketType::None); config = config.with_socket(SocketType::None);
} }
if let Some(port) = args.port { if let Some(port) = matches.value_of("port").map(|port| port.parse::<u16>()) {
config = config.with_port(port); if let Err(err) = port {
} // if port was overridden, it must be parsable
panic!("Invalid port value provided - {:?}", err);
#[cfg(feature = "coconut")]
{
if args.enabled_credentials_mode {
config.get_base_mut().with_disabled_credentials(false)
} }
config = config.with_port(port.unwrap());
} }
if args.fastmode { #[cfg(not(feature = "coconut"))]
config.get_base_mut().set_high_default_traffic_volume(); if let Some(eth_endpoint) = matches.value_of(ETH_ENDPOINT_ARG_NAME) {
config.get_base_mut().with_eth_endpoint(eth_endpoint);
} else if !cfg!(feature = "eth") {
config
.get_base_mut()
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT);
}
#[cfg(not(feature = "coconut"))]
if let Some(eth_private_key) = matches.value_of(ETH_PRIVATE_KEY_ARG_NAME) {
config.get_base_mut().with_eth_private_key(eth_private_key);
} else if !cfg!(feature = "eth") {
config
.get_base_mut()
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
}
if !cfg!(feature = "eth") || matches.is_present(TESTNET_MODE_ARG_NAME) {
config.get_base_mut().with_testnet_mode(true)
} }
config config
} }
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn verify_cli() {
Cli::command().debug_assert();
}
}
+62 -54
View File
@@ -1,57 +1,66 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::{ use crate::client::config::Config;
client::{config::Config, NymClient}, use crate::client::NymClient;
commands::{override_config, OverrideConfig}, use crate::commands::override_config;
}; #[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))]
use clap::Args; use crate::commands::{ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME, TESTNET_MODE_ARG_NAME};
use clap::{App, Arg, ArgMatches};
use config::NymConfig; use config::NymConfig;
use log::*; use log::*;
use version_checker::is_minor_version_compatible; use version_checker::is_minor_version_compatible;
#[derive(Args, Clone)] pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
pub(crate) struct Run { let app = App::new("run")
/// Id of the nym-mixnet-client we want to run. .about("Run the Nym client with provided configuration client optionally overriding set parameters")
#[clap(long)] .arg(Arg::with_name("id")
id: String, .long("id")
.help("Id of the nym-mixnet-client we want to run.")
.takes_value(true)
.required(true)
)
// the rest of arguments are optional, they are used to override settings in config file
.arg(Arg::with_name("validators")
.long("validators")
.help("Comma separated list rest rest endpoints of the validators")
.takes_value(true),
)
.arg(Arg::with_name("gateway")
.long("gateway")
.help("Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened")
.takes_value(true)
)
.arg(Arg::with_name("disable-socket")
.long("disable-socket")
.help("Whether to not start the websocket")
)
.arg(Arg::with_name("port")
.short("p")
.long("port")
.help("Port for the socket (if applicable) to listen on")
.takes_value(true)
);
#[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))]
let app = app
.arg(
Arg::with_name(TESTNET_MODE_ARG_NAME)
.long(TESTNET_MODE_ARG_NAME)
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
)
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
.long(ETH_ENDPOINT_ARG_NAME)
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true))
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
.long(ETH_PRIVATE_KEY_ARG_NAME)
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true));
/// Comma separated list of rest endpoints of the validators app
#[clap(long)]
validators: Option<String>,
/// Id of the gateway we want to connect to. If overridden, it is user's responsibility to
/// ensure prior registration happened
#[clap(long)]
gateway: Option<String>,
/// Whether to not start the websocket
#[clap(long)]
disable_socket: bool,
/// Port for the socket to listen on
#[clap(short, long)]
port: Option<u16>,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[cfg(feature = "coconut")]
#[clap(long)]
enabled_credentials_mode: bool,
}
impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self {
OverrideConfig {
validators: run_config.validators,
disable_socket: run_config.disable_socket,
port: run_config.port,
fastmode: false,
#[cfg(feature = "coconut")]
enabled_credentials_mode: run_config.enabled_credentials_mode,
}
}
} }
// this only checks compatibility between config the binary. It does not take into consideration // this only checks compatibility between config the binary. It does not take into consideration
@@ -59,10 +68,8 @@ impl From<Run> for OverrideConfig {
fn version_check(cfg: &Config) -> bool { fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION"); let binary_version = env!("CARGO_PKG_VERSION");
let config_version = cfg.get_base().get_version(); let config_version = cfg.get_base().get_version();
if binary_version == config_version { if binary_version != config_version {
true warn!("The mixnode binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
} else {
warn!("The native-client binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
if is_minor_version_compatible(binary_version, config_version) { if is_minor_version_compatible(binary_version, config_version) {
info!("but they are still semver compatible. However, consider running the `upgrade` command"); info!("but they are still semver compatible. However, consider running the `upgrade` command");
true true
@@ -70,11 +77,13 @@ fn version_check(cfg: &Config) -> bool {
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again"); error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
false false
} }
} else {
true
} }
} }
pub(crate) async fn execute(args: &Run) { pub async fn execute(matches: ArgMatches<'static>) {
let id = &args.id; let id = matches.value_of("id").unwrap();
let mut config = match Config::load_from_file(Some(id)) { let mut config = match Config::load_from_file(Some(id)) {
Ok(cfg) => cfg, Ok(cfg) => cfg,
@@ -84,8 +93,7 @@ pub(crate) async fn execute(args: &Run) {
} }
}; };
let override_config_fields = OverrideConfig::from(args.clone()); config = override_config(config, &matches);
config = override_config(config, override_config_fields);
if !version_check(&config) { if !version_check(&config) {
error!("failed the local version check"); error!("failed the local version check");
+30 -19
View File
@@ -2,13 +2,12 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::{Config, MISSING_VALUE}; use crate::client::config::{Config, MISSING_VALUE};
use clap::{App, Arg, ArgMatches};
use config::defaults::default_api_endpoints;
use config::NymConfig; use config::NymConfig;
use version_checker::Version;
use clap::Args;
use std::fmt::Display; use std::fmt::Display;
use std::process; use std::process;
use version_checker::Version;
#[allow(dead_code)] #[allow(dead_code)]
fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! { fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
@@ -50,11 +49,14 @@ fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> !
process::exit(1) process::exit(1)
} }
#[derive(Args, Clone)] pub fn command_args<'a, 'b>() -> App<'a, 'b> {
pub(crate) struct Upgrade { App::new("upgrade").about("Try to upgrade the client").arg(
/// Id of the nym-client we want to upgrade Arg::with_name("id")
#[clap(long)] .long("id")
id: String, .help("Id of the nym-client we want to upgrade")
.takes_value(true)
.required(true),
)
} }
fn parse_config_version(config: &Config) -> Version { fn parse_config_version(config: &Config) -> Version {
@@ -93,7 +95,7 @@ fn parse_package_version() -> Version {
fn minor_0_12_upgrade( fn minor_0_12_upgrade(
mut config: Config, mut config: Config,
_matches: &Upgrade, _matches: &ArgMatches<'_>,
config_version: &Version, config_version: &Version,
package_version: &Version, package_version: &Version,
) -> Config { ) -> Config {
@@ -105,6 +107,15 @@ fn minor_0_12_upgrade(
print_start_upgrade(&config_version, &to_version); print_start_upgrade(&config_version, &to_version);
println!(
"Setting validator API endpoints to {:?}",
default_api_endpoints()
);
config
.get_base_mut()
.set_custom_validator_apis(default_api_endpoints());
config config
.get_base_mut() .get_base_mut()
.set_custom_version(to_version.to_string().as_ref()); .set_custom_version(to_version.to_string().as_ref());
@@ -120,30 +131,30 @@ fn minor_0_12_upgrade(
config config
} }
fn do_upgrade(mut config: Config, args: &Upgrade, package_version: &Version) { fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Version) {
loop { loop {
let config_version = parse_config_version(&config); let config_version = parse_config_version(&config);
if &config_version == package_version { if config_version == package_version {
println!("You're using the most recent version!"); println!("You're using the most recent version!");
return; return;
} }
config = match config_version.major { config = match config_version.major {
0 => match config_version.minor { 0 => match config_version.minor {
9 | 10 => outdated_upgrade(&config_version, package_version), 9 | 10 => outdated_upgrade(&config_version, &package_version),
11 => minor_0_12_upgrade(config, args, &config_version, package_version), 11 => minor_0_12_upgrade(config, matches, &config_version, &package_version),
_ => unsupported_upgrade(&config_version, package_version), _ => unsupported_upgrade(&config_version, &package_version),
}, },
_ => unsupported_upgrade(&config_version, package_version), _ => unsupported_upgrade(&config_version, &package_version),
} }
} }
} }
pub(crate) fn execute(args: &Upgrade) { pub fn execute(matches: &ArgMatches<'_>) {
let package_version = parse_package_version(); let package_version = parse_package_version();
let id = &args.id; let id = matches.value_of("id").unwrap();
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| { let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {:?}", err); eprintln!("failed to load existing config file! - {:?}", err);
@@ -156,5 +167,5 @@ pub(crate) fn execute(args: &Upgrade) {
} }
// here be upgrade path to 0.9.X and beyond based on version number from config // here be upgrade path to 0.9.X and beyond based on version number from config
do_upgrade(existing_config, args, &package_version) do_upgrade(existing_config, matches, package_version)
} }
+57 -7
View File
@@ -1,8 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use clap::{crate_version, Parser}; use clap::{crate_version, App, ArgMatches};
use network_defaults::setup_env;
pub mod client; pub mod client;
pub mod commands; pub mod commands;
@@ -10,12 +9,34 @@ pub mod websocket;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
dotenv::dotenv().ok();
setup_logging(); setup_logging();
println!("{}", banner()); println!("{}", banner());
let args = commands::Cli::parse(); let arg_matches = App::new("Nym Client")
setup_env(args.config_env_file.clone()); .version(crate_version!())
commands::execute(&args).await; .long_version(&*long_version())
.author("Nymtech")
.about("Implementation of the Nym Client")
.subcommand(commands::init::command_args())
.subcommand(commands::run::command_args())
.subcommand(commands::upgrade::command_args())
.get_matches();
execute(arg_matches).await;
}
async fn execute(matches: ArgMatches<'static>) {
match matches.subcommand() {
("init", Some(m)) => commands::init::execute(m.clone()).await,
("run", Some(m)) => commands::run::execute(m.clone()).await,
("upgrade", Some(m)) => commands::upgrade::execute(m),
_ => println!("{}", usage()),
}
}
fn usage() -> &'static str {
"usage: --help to see available options.\n\n"
} }
fn banner() -> String { fn banner() -> String {
@@ -35,6 +56,37 @@ fn banner() -> String {
) )
} }
fn long_version() -> String {
format!(
r#"
{:<20}{}
{:<20}{}
{:<20}{}
{:<20}{}
{:<20}{}
{:<20}{}
{:<20}{}
{:<20}{}
"#,
"Build Timestamp:",
env!("VERGEN_BUILD_TIMESTAMP"),
"Build Version:",
env!("VERGEN_BUILD_SEMVER"),
"Commit SHA:",
env!("VERGEN_GIT_SHA"),
"Commit Date:",
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
"Commit Branch:",
env!("VERGEN_GIT_BRANCH"),
"rustc Version:",
env!("VERGEN_RUSTC_SEMVER"),
"rustc Channel:",
env!("VERGEN_RUSTC_CHANNEL"),
"cargo Profile:",
env!("VERGEN_CARGO_PROFILE"),
)
}
fn setup_logging() { fn setup_logging() {
let mut log_builder = pretty_env_logger::formatted_timed_builder(); let mut log_builder = pretty_env_logger::formatted_timed_builder();
if let Ok(s) = ::std::env::var("RUST_LOG") { if let Ok(s) = ::std::env::var("RUST_LOG") {
@@ -52,7 +104,5 @@ fn setup_logging() {
.filter_module("want", log::LevelFilter::Warn) .filter_module("want", log::LevelFilter::Warn)
.filter_module("tungstenite", log::LevelFilter::Warn) .filter_module("tungstenite", log::LevelFilter::Warn)
.filter_module("tokio_tungstenite", log::LevelFilter::Warn) .filter_module("tokio_tungstenite", log::LevelFilter::Warn)
.filter_module("handlebars", log::LevelFilter::Warn)
.filter_module("sled", log::LevelFilter::Warn)
.init(); .init();
} }
+1 -1
View File
@@ -10,4 +10,4 @@ edition = "2021"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
nymsphinx = { path = "../../../common/nymsphinx" } nymsphinx = { path = "../../../common/nymsphinx" }
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
// no need to go fancy here like we've done in other places. // no need to go fancy here like we've done in other places.
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] #[derive(PartialEq, Clone, Serialize, Deserialize)]
pub struct Error { pub struct Error {
pub kind: ErrorKind, pub kind: ErrorKind,
pub message: String, pub message: String,
@@ -30,7 +30,7 @@ impl Error {
} }
#[repr(u8)] #[repr(u8)]
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)] #[derive(PartialEq, Clone, Serialize, Deserialize)]
pub enum ErrorKind { pub enum ErrorKind {
/// The received request contained no data. /// The received request contained no data.
EmptyRequest = 0x01, EmptyRequest = 0x01,
+12 -14
View File
@@ -1,8 +1,7 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.0.2" version = "1.0.0-rc.1"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021" edition = "2021"
rust-version = "1.56" rust-version = "1.56"
@@ -11,8 +10,9 @@ name = "nym_socks5"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
clap = { version = "3.2.8", features = ["cargo", "derive"] } clap = "2.33.0"
dirs = "4.0" dirs = "3.0" # for determining default store directories in config
dotenv = "0.15.0"
futures = "0.3" futures = "0.3"
log = "0.4" log = "0.4"
pin-project = "1.0" pin-project = "1.0"
@@ -20,32 +20,30 @@ pretty_env_logger = "0.4"
rand = { version = "0.7.3", features = ["wasm-bindgen"] } rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization
snafu = "0.6" snafu = "0.6"
tokio = { version = "1.21.2", features = ["rt-multi-thread", "net", "signal"] } tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal"] }
url = "2.2" url = "2.2"
# internal # internal
client-core = { path = "../client-core" } client-core = { path = "../client-core" }
coconut-interface = { path = "../../common/coconut-interface", optional = true } coconut-interface = { path = "../../common/coconut-interface", optional = true }
config = { path = "../../common/config" }
credential-storage = { path = "../../common/credential-storage" }
credentials = { path = "../../common/credentials", optional = true } credentials = { path = "../../common/credentials", optional = true }
config = { path = "../../common/config" }
crypto = { path = "../../common/crypto" } crypto = { path = "../../common/crypto" }
gateway-client = { path = "../../common/client-libs/gateway-client" } gateway-client = { path = "../../common/client-libs/gateway-client" }
gateway-requests = { path = "../../gateway/gateway-requests" } gateway-requests = { path = "../../gateway/gateway-requests" }
network-defaults = { path = "../../common/network-defaults" }
nymsphinx = { path = "../../common/nymsphinx" } nymsphinx = { path = "../../common/nymsphinx" }
ordered-buffer = { path = "../../common/socks5/ordered-buffer" } ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
socks5-requests = { path = "../../common/socks5/requests" }
topology = { path = "../../common/topology" }
pemstore = { path = "../../common/pemstore" } pemstore = { path = "../../common/pemstore" }
proxy-helpers = { path = "../../common/socks5/proxy-helpers" } proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
socks5-requests = { path = "../../common/socks5/requests" } validator-client = { path = "../../common/client-libs/validator-client" }
task = { path = "../../common/task" }
topology = { path = "../../common/topology" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
version-checker = { path = "../../common/version-checker" } version-checker = { path = "../../common/version-checker" }
network-defaults = { path = "../../common/network-defaults" }
[features] [features]
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut", "credentials/coconut", "client-core/coconut"] coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut", "credentials/coconut"]
eth = [] eth = []
[build-dependencies] [build-dependencies]
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] } vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
+1 -5
View File
@@ -33,10 +33,6 @@ impl NymConfig for Config {
.join("socks5-clients") .join("socks5-clients")
} }
fn try_default_root_directory() -> Option<PathBuf> {
dirs::home_dir().map(|path| path.join(".nym").join("socks5-clients"))
}
fn root_directory(&self) -> PathBuf { fn root_directory(&self) -> PathBuf {
self.base.get_nym_root_directory() self.base.get_nym_root_directory()
} }
@@ -93,7 +89,7 @@ impl Config {
} }
} }
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] #[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Socks5 { pub struct Socks5 {
/// The port on which the client will be listening for incoming requests /// The port on which the client will be listening for incoming requests
+12 -4
View File
@@ -19,9 +19,9 @@ version = '{{ client.version }}'
# Human readable ID of this particular client. # Human readable ID of this particular client.
id = '{{ client.id }}' id = '{{ client.id }}'
# Indicates whether this client is running in a disabled credentials mode, thus attempting # Indicates whether this client is running in a testnet mode, thus attempting
# to claim bandwidth without presenting bandwidth credentials. # to claim bandwidth without presenting bandwidth credentials.
disabled_credentials_mode = {{ client.disabled_credentials_mode }} testnet_mode = {{ client.testnet_mode }}
# Addresses to APIs running on validator from which the client gets the view of the network. # Addresses to APIs running on validator from which the client gets the view of the network.
validator_api_urls = [ validator_api_urls = [
@@ -46,8 +46,16 @@ public_encryption_key_file = '{{ client.public_encryption_key_file }}'
# sent but not received back. # sent but not received back.
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}' reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
# Path to the database containing bandwidth credentials # Path to directory containing public/private keys used for bandwidth token purchase.
database_path = '{{ client.database_path }}' # Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
# The public key is the name of the file, while the private key is the content.
backup_bandwidth_token_keys_dir = '{{ client.backup_bandwidth_token_keys_dir }}'
# Ethereum private key.
eth_private_key = '{{ client.eth_private_key }}'
# Addess to an Ethereum full node.
eth_endpoint = '{{ client.eth_endpoint }}'
##### additional client config options ##### ##### additional client config options #####
+32 -127
View File
@@ -1,8 +1,6 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use std::sync::atomic::Ordering;
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream; use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
use client_core::client::inbound_messages::{ use client_core::client::inbound_messages::{
InputMessage, InputMessageReceiver, InputMessageSender, InputMessage, InputMessageReceiver, InputMessageSender,
@@ -22,7 +20,6 @@ use client_core::client::topology_control::{
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder; use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use crypto::asymmetric::identity; use crypto::asymmetric::identity;
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::StreamExt;
use gateway_client::bandwidth::BandwidthController; use gateway_client::bandwidth::BandwidthController;
use gateway_client::{ use gateway_client::{
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver, AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
@@ -31,8 +28,6 @@ use gateway_client::{
use log::*; use log::*;
use nymsphinx::addressing::clients::Recipient; use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity; use nymsphinx::addressing::nodes::NodeIdentity;
use nymsphinx::params::PacketSize;
use task::{wait_for_signal, ShutdownListener, ShutdownNotifier};
use crate::client::config::Config; use crate::client::config::Config;
use crate::socks::{ use crate::socks::{
@@ -40,17 +35,7 @@ use crate::socks::{
server::SphinxSocksServer, server::SphinxSocksServer,
}; };
pub mod config; pub(crate) mod config;
// Channels used to control the main task from outside
pub type Socks5ControlMessageSender = mpsc::UnboundedSender<Socks5ControlMessage>;
pub type Socks5ControlMessageReceiver = mpsc::UnboundedReceiver<Socks5ControlMessage>;
#[derive(Debug)]
pub enum Socks5ControlMessage {
/// Tell the main task to stop
Stop,
}
pub struct NymClient { pub struct NymClient {
/// Client configuration options, including, among other things, packet sending rates, /// Client configuration options, including, among other things, packet sending rates,
@@ -88,11 +73,10 @@ impl NymClient {
&self, &self,
topology_accessor: TopologyAccessor, topology_accessor: TopologyAccessor,
mix_tx: BatchMixMessageSender, mix_tx: BatchMixMessageSender,
shutdown: ShutdownListener,
) { ) {
info!("Starting loop cover traffic stream..."); info!("Starting loop cover traffic stream...");
let mut stream = LoopCoverTrafficStream::new( LoopCoverTrafficStream::new(
self.key_manager.ack_key(), self.key_manager.ack_key(),
self.config.get_base().get_average_ack_delay(), self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_average_packet_delay(), self.config.get_base().get_average_packet_delay(),
@@ -102,13 +86,8 @@ impl NymClient {
mix_tx, mix_tx,
self.as_mix_recipient(), self.as_mix_recipient(),
topology_accessor, topology_accessor,
); )
.start();
if self.config.get_base().get_use_extended_packet_size() {
stream.set_custom_packet_size(PacketSize::ExtendedPacket)
}
stream.start_with_shutdown(shutdown);
} }
fn start_real_traffic_controller( fn start_real_traffic_controller(
@@ -118,25 +97,17 @@ impl NymClient {
ack_receiver: AcknowledgementReceiver, ack_receiver: AcknowledgementReceiver,
input_receiver: InputMessageReceiver, input_receiver: InputMessageReceiver,
mix_sender: BatchMixMessageSender, mix_sender: BatchMixMessageSender,
shutdown: ShutdownListener,
) { ) {
let mut controller_config = client_core::client::real_messages_control::Config::new( let controller_config = client_core::client::real_messages_control::Config::new(
self.key_manager.ack_key(), self.key_manager.ack_key(),
self.config.get_base().get_ack_wait_multiplier(), self.config.get_base().get_ack_wait_multiplier(),
self.config.get_base().get_ack_wait_addition(), self.config.get_base().get_ack_wait_addition(),
self.config.get_base().get_average_ack_delay(), self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_message_sending_average_delay(), self.config.get_base().get_message_sending_average_delay(),
self.config.get_base().get_average_packet_delay(), self.config.get_base().get_average_packet_delay(),
self.config
.get_base()
.get_disabled_main_poisson_packet_distribution(),
self.as_mix_recipient(), self.as_mix_recipient(),
); );
if self.config.get_base().get_use_extended_packet_size() {
controller_config.set_custom_packet_size(PacketSize::ExtendedPacket)
}
info!("Starting real traffic stream..."); info!("Starting real traffic stream...");
RealMessagesController::new( RealMessagesController::new(
@@ -147,7 +118,7 @@ impl NymClient {
topology_accessor, topology_accessor,
reply_key_storage, reply_key_storage,
) )
.start_with_shutdown(shutdown); .start();
} }
// buffer controlling all messages fetched from provider // buffer controlling all messages fetched from provider
@@ -157,7 +128,6 @@ impl NymClient {
query_receiver: ReceivedBufferRequestReceiver, query_receiver: ReceivedBufferRequestReceiver,
mixnet_receiver: MixnetMessageReceiver, mixnet_receiver: MixnetMessageReceiver,
reply_key_storage: ReplyKeyStorage, reply_key_storage: ReplyKeyStorage,
shutdown: ShutdownListener,
) { ) {
info!("Starting received messages buffer controller..."); info!("Starting received messages buffer controller...");
ReceivedMessagesBufferController::new( ReceivedMessagesBufferController::new(
@@ -166,14 +136,13 @@ impl NymClient {
mixnet_receiver, mixnet_receiver,
reply_key_storage, reply_key_storage,
) )
.start_with_shutdown(shutdown); .start()
} }
async fn start_gateway_client( async fn start_gateway_client(
&mut self, &mut self,
mixnet_message_sender: MixnetMessageSender, mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender, ack_sender: AcknowledgementSender,
shutdown: ShutdownListener,
) -> GatewayClient { ) -> GatewayClient {
let gateway_id = self.config.get_base().get_gateway_id(); let gateway_id = self.config.get_base().get_gateway_id();
if gateway_id.is_empty() { if gateway_id.is_empty() {
@@ -193,14 +162,14 @@ impl NymClient {
#[cfg(feature = "coconut")] #[cfg(feature = "coconut")]
let bandwidth_controller = BandwidthController::new( let bandwidth_controller = BandwidthController::new(
credential_storage::initialise_storage(self.config.get_base().get_database_path())
.await,
self.config.get_base().get_validator_api_endpoints(), self.config.get_base().get_validator_api_endpoints(),
*self.key_manager.identity_keypair().public_key(),
); );
#[cfg(not(feature = "coconut"))] #[cfg(not(feature = "coconut"))]
let bandwidth_controller = BandwidthController::new( let bandwidth_controller = BandwidthController::new(
credential_storage::initialise_storage(self.config.get_base().get_database_path()) self.config.get_base().get_eth_endpoint(),
.await, self.config.get_base().get_eth_private_key(),
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
) )
.expect("Could not create bandwidth controller"); .expect("Could not create bandwidth controller");
@@ -214,12 +183,11 @@ impl NymClient {
ack_sender, ack_sender,
self.config.get_base().get_gateway_response_timeout(), self.config.get_base().get_gateway_response_timeout(),
Some(bandwidth_controller), Some(bandwidth_controller),
Some(shutdown),
); );
gateway_client if self.config.get_base().get_testnet_mode() {
.set_disabled_credentials_mode(self.config.get_base().get_disabled_credentials_mode()); gateway_client.set_testnet_mode(true)
}
gateway_client gateway_client
.authenticate_and_start() .authenticate_and_start()
.await .await
@@ -230,11 +198,7 @@ impl NymClient {
// future responsible for periodically polling directory server and updating // future responsible for periodically polling directory server and updating
// the current global view of topology // the current global view of topology
async fn start_topology_refresher( async fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
&mut self,
topology_accessor: TopologyAccessor,
shutdown: ShutdownListener,
) {
let topology_refresher_config = TopologyRefresherConfig::new( let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_base().get_validator_api_endpoints(), self.config.get_base().get_validator_api_endpoints(),
self.config.get_base().get_topology_refresh_rate(), self.config.get_base().get_topology_refresh_rate(),
@@ -256,7 +220,7 @@ impl NymClient {
} }
info!("Starting topology refresher..."); info!("Starting topology refresher...");
topology_refresher.start_with_shutdown(shutdown); topology_refresher.start();
} }
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic) // controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
@@ -267,17 +231,15 @@ impl NymClient {
&mut self, &mut self,
mix_rx: BatchMixMessageReceiver, mix_rx: BatchMixMessageReceiver,
gateway_client: GatewayClient, gateway_client: GatewayClient,
shutdown: ShutdownListener,
) { ) {
info!("Starting mix traffic controller..."); info!("Starting mix traffic controller...");
MixTrafficController::new(mix_rx, gateway_client).start_with_shutdown(shutdown); MixTrafficController::new(mix_rx, gateway_client).start();
} }
fn start_socks5_listener( fn start_socks5_listener(
&self, &self,
buffer_requester: ReceivedBufferRequestSender, buffer_requester: ReceivedBufferRequestSender,
msg_input: InputMessageSender, msg_input: InputMessageSender,
shutdown: ShutdownListener,
) { ) {
info!("Starting socks5 listener..."); info!("Starting socks5 listener...");
let auth_methods = vec![AuthenticationMethods::NoAuth as u8]; let auth_methods = vec![AuthenticationMethods::NoAuth as u8];
@@ -289,57 +251,26 @@ impl NymClient {
authenticator, authenticator,
self.config.get_provider_mix_address(), self.config.get_provider_mix_address(),
self.as_mix_recipient(), self.as_mix_recipient(),
shutdown,
); );
tokio::spawn(async move { sphinx_socks.serve(msg_input, buffer_requester).await }); tokio::spawn(async move { sphinx_socks.serve(msg_input, buffer_requester).await });
} }
/// blocking version of `start` method. Will run forever (or until SIGINT is sent) /// blocking version of `start` method. Will run forever (or until SIGINT is sent)
pub async fn run_forever(&mut self) { pub async fn run_forever(&mut self) {
let mut shutdown = self.start().await; self.start().await;
wait_for_signal().await; if let Err(e) = tokio::signal::ctrl_c().await {
error!(
log::info!("Sending shutdown"); "There was an error while capturing SIGINT - {:?}. We will terminate regardless",
client_core::client::SHUTDOWN_HAS_BEEN_SIGNALLED.store(true, Ordering::Relaxed); e
shutdown.signal_shutdown().ok(); );
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-socks5-client");
}
// Variant of `run_forever` that listends for remote control messages
pub async fn run_and_listen(&mut self, mut receiver: Socks5ControlMessageReceiver) {
let mut shutdown = self.start().await;
tokio::select! {
message = receiver.next() => {
log::debug!("Received message: {:?}", message);
match message {
Some(Socks5ControlMessage::Stop) => {
log::info!("Received stop message");
}
None => {
log::info!("Channel closed, stopping");
}
}
}
_ = tokio::signal::ctrl_c() => {
log::info!("Received SIGINT");
},
} }
log::info!("Sending shutdown"); println!(
client_core::client::SHUTDOWN_HAS_BEEN_SIGNALLED.store(true, Ordering::Relaxed); "Received SIGINT - the client will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)."
shutdown.signal_shutdown().ok(); );
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
shutdown.wait_for_shutdown().await;
log::info!("Stopping nym-socks5-client");
} }
pub async fn start(&mut self) -> ShutdownNotifier { pub async fn start(&mut self) {
info!("Starting nym client"); info!("Starting nym client");
// channels for inter-component communication // channels for inter-component communication
// TODO: make the channels be internally created by the relevant components // TODO: make the channels be internally created by the relevant components
@@ -369,59 +300,33 @@ impl NymClient {
ReplyKeyStorage::load(self.config.get_base().get_reply_encryption_key_store_path()) ReplyKeyStorage::load(self.config.get_base().get_reply_encryption_key_store_path())
.expect("Failed to load reply key storage!"); .expect("Failed to load reply key storage!");
// Shutdown notifier for signalling tasks to stop
let shutdown = ShutdownNotifier::default();
// the components are started in very specific order. Unless you know what you are doing, // the components are started in very specific order. Unless you know what you are doing,
// do not change that. // do not change that.
self.start_topology_refresher(shared_topology_accessor.clone(), shutdown.subscribe()) self.start_topology_refresher(shared_topology_accessor.clone())
.await; .await;
self.start_received_messages_buffer_controller( self.start_received_messages_buffer_controller(
received_buffer_request_receiver, received_buffer_request_receiver,
mixnet_messages_receiver, mixnet_messages_receiver,
reply_key_storage.clone(), reply_key_storage.clone(),
shutdown.subscribe(),
); );
let gateway_client = self let gateway_client = self
.start_gateway_client(mixnet_messages_sender, ack_sender, shutdown.subscribe()) .start_gateway_client(mixnet_messages_sender, ack_sender)
.await; .await;
self.start_mix_traffic_controller( self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
sphinx_message_receiver,
gateway_client,
shutdown.subscribe(),
);
self.start_real_traffic_controller( self.start_real_traffic_controller(
shared_topology_accessor.clone(), shared_topology_accessor.clone(),
reply_key_storage, reply_key_storage,
ack_receiver, ack_receiver,
input_receiver, input_receiver,
sphinx_message_sender.clone(), sphinx_message_sender.clone(),
shutdown.subscribe(),
); );
if !self self.start_cover_traffic_stream(shared_topology_accessor, sphinx_message_sender);
.config self.start_socks5_listener(received_buffer_request_sender, input_sender);
.get_base()
.get_disabled_loop_cover_traffic_stream()
{
self.start_cover_traffic_stream(
shared_topology_accessor,
sphinx_message_sender,
shutdown.subscribe(),
);
}
self.start_socks5_listener(
received_buffer_request_sender,
input_sender,
shutdown.subscribe(),
);
info!("Client startup finished!"); info!("Client startup finished!");
info!("The address of this client is: {}", self.as_mix_recipient()); info!("The address of this client is: {}", self.as_mix_recipient());
shutdown
} }
} }
+272 -147
View File
@@ -1,174 +1,299 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use clap::Args; use clap::{App, Arg, ArgMatches};
use client_core::{config::GatewayEndpoint, error::ClientCoreError}; use client_core::client::key_manager::KeyManager;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
#[cfg(feature = "coconut")]
use coconut_interface::{Credential, Parameters};
use config::NymConfig; use config::NymConfig;
#[cfg(feature = "coconut")]
use credentials::coconut::{
bandwidth::prepare_for_spending, bandwidth::BandwidthVoucher, bandwidth::TOTAL_ATTRIBUTES,
utils::obtain_aggregate_signature,
};
#[cfg(feature = "coconut")]
use credentials::obtain_aggregate_verification_key;
use crypto::asymmetric::{encryption, identity};
use gateway_client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys;
#[cfg(feature = "coconut")]
use network_defaults::BANDWIDTH_VALUE;
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
use rand::{prelude::SliceRandom, rngs::OsRng, thread_rng};
use std::convert::TryInto;
use std::sync::Arc;
use std::time::Duration;
use topology::{filter::VersionFilterable, gateway};
use url::Url;
#[cfg(feature = "coconut")]
use validator_client::nymd::tx::Hash;
use crate::{ use crate::client::config::Config;
client::config::Config, use crate::commands::override_config;
commands::{override_config, OverrideConfig}, #[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))]
use crate::commands::{
DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
TESTNET_MODE_ARG_NAME,
}; };
#[derive(Args, Clone)] pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
pub(crate) struct Init { let app = App::new("init")
/// Id of the nym-mixnet-client we want to create config for. .about("Initialise a Nym client. Do this first!")
#[clap(long)] .arg(Arg::with_name("id")
id: String, .long("id")
.help("Id of the nym-mixnet-client we want to create config for.")
.takes_value(true)
.required(true)
)
.arg(Arg::with_name("provider")
.long("provider")
.help("Address of the socks5 provider to send messages to.")
.takes_value(true)
.required(true)
)
.arg(Arg::with_name("gateway")
.long("gateway")
.help("Id of the gateway we are going to connect to.")
.takes_value(true)
)
.arg(Arg::with_name("validators")
.long("validators")
.help("Comma separated list of rest endpoints of the validators")
.takes_value(true),
)
.arg(Arg::with_name("port")
.short("p")
.long("port")
.help("Port for the socket to listen on in all subsequent runs")
.takes_value(true)
)
.arg(Arg::with_name("fastmode")
.long("fastmode")
.hidden(true) // this will prevent this flag from being displayed in `--help`
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
);
#[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))]
let app = app
.arg(
Arg::with_name(TESTNET_MODE_ARG_NAME)
.long(TESTNET_MODE_ARG_NAME)
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
)
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
.long(ETH_ENDPOINT_ARG_NAME)
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true)
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT)
.required(true))
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
.long(ETH_PRIVATE_KEY_ARG_NAME)
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true)
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
.required(true)
);
/// Address of the socks5 provider to send messages to. app
#[clap(long)]
provider: String,
/// Id of the gateway we are going to connect to.
#[clap(long)]
gateway: Option<String>,
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
/// potentially causing loss of access.
#[clap(long)]
force_register_gateway: bool,
/// Comma separated list of rest endpoints of the validators
#[clap(long)]
validators: Option<String>,
/// Port for the socket to listen on in all subsequent runs
#[clap(short, long)]
port: Option<u16>,
/// Mostly debug-related option to increase default traffic rate so that you would not need to
/// modify config post init
#[clap(long, hidden = true)]
fastmode: bool,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[cfg(feature = "coconut")]
#[clap(long)]
enabled_credentials_mode: bool,
} }
impl From<Init> for OverrideConfig { // this behaviour should definitely be changed, we shouldn't
fn from(init_config: Init) -> Self { // need to get bandwidth credential for registration
OverrideConfig { #[cfg(feature = "coconut")]
validators: init_config.validators, async fn _prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential {
port: init_config.port, let verification_key = obtain_aggregate_verification_key(validators)
fastmode: init_config.fastmode, .await
#[cfg(feature = "coconut")] .expect("could not obtain aggregate verification key of validators");
enabled_credentials_mode: init_config.enabled_credentials_mode,
} let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let mut rng = OsRng;
let bandwidth_credential_attributes = BandwidthVoucher::new(
&params,
BANDWIDTH_VALUE.to_string(),
network_defaults::VOUCHER_INFO.to_string(),
Hash::new([0; 32]),
// workaround for putting a valid value here, without deriving clone for the private
// key, until we have actual useful values
identity::PrivateKey::from_base58_string(
identity::KeyPair::new(&mut rng)
.private_key()
.to_base58_string(),
)
.unwrap(),
encryption::KeyPair::new(&mut rng).private_key().clone(),
);
let bandwidth_credential =
obtain_aggregate_signature(&params, &bandwidth_credential_attributes, validators)
.await
.expect("could not obtain bandwidth credential");
prepare_for_spending(
raw_identity,
&bandwidth_credential,
&bandwidth_credential_attributes,
&verification_key,
)
.expect("could not prepare out bandwidth credential for spending")
}
async fn register_with_gateway(
gateway: &gateway::Node,
our_identity: Arc<identity::KeyPair>,
) -> Arc<SharedKeys> {
let timeout = Duration::from_millis(1500);
let mut gateway_client = GatewayClient::new_init(
gateway.clients_address(),
gateway.identity_key,
gateway.owner.clone(),
our_identity.clone(),
timeout,
);
gateway_client
.establish_connection()
.await
.expect("failed to establish connection with the gateway!");
gateway_client
.perform_initial_authentication()
.await
.expect("failed to register with the gateway!")
}
async fn gateway_details(
validator_servers: Vec<Url>,
chosen_gateway_id: Option<&str>,
) -> gateway::Node {
let validator_api = validator_servers
.choose(&mut thread_rng())
.expect("The list of validator apis is empty");
let validator_client = validator_client::ApiClient::new(validator_api.clone());
let gateways = validator_client.get_cached_gateways().await.unwrap();
let valid_gateways = gateways
.into_iter()
.filter_map(|gateway| gateway.try_into().ok())
.collect::<Vec<gateway::Node>>();
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
// if we have chosen particular gateway - use it, otherwise choose a random one.
// (remember that in active topology all gateways have at least 100 reputation so should
// be working correctly)
if let Some(gateway_id) = chosen_gateway_id {
filtered_gateways
.iter()
.find(|gateway| gateway.identity_key.to_base58_string() == gateway_id)
.expect(&*format!("no gateway with id {} exists!", gateway_id))
.clone()
} else {
filtered_gateways
.choose(&mut rand::thread_rng())
.expect("there are no gateways on the network!")
.clone()
} }
} }
pub(crate) async fn execute(args: &Init) { fn show_address(config: &Config) {
fn load_identity_keys(pathfinder: &ClientKeyPathfinder) -> identity::KeyPair {
let identity_keypair: identity::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
pathfinder.private_identity_key().to_owned(),
pathfinder.public_identity_key().to_owned(),
))
.expect("Failed to read stored identity key files");
identity_keypair
}
fn load_sphinx_keys(pathfinder: &ClientKeyPathfinder) -> encryption::KeyPair {
let sphinx_keypair: encryption::KeyPair =
pemstore::load_keypair(&pemstore::KeyPairPath::new(
pathfinder.private_encryption_key().to_owned(),
pathfinder.public_encryption_key().to_owned(),
))
.expect("Failed to read stored sphinx key files");
sphinx_keypair
}
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
let identity_keypair = load_identity_keys(&pathfinder);
let sphinx_keypair = load_sphinx_keys(&pathfinder);
let client_recipient = Recipient::new(
*identity_keypair.public_key(),
*sphinx_keypair.public_key(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(config.get_base().get_gateway_id()).unwrap(),
);
println!("\nThe address of this client is: {}", client_recipient);
}
pub async fn execute(matches: ArgMatches<'static>) {
println!("Initialising client..."); println!("Initialising client...");
let id = &args.id; let id = matches.value_of("id").unwrap(); // required for now
let provider_address = &args.provider; let provider_address = matches.value_of("provider").unwrap();
let already_init = Config::default_config_file_path(Some(id)).exists(); let already_init = if Config::default_config_file_path(Some(id)).exists() {
if already_init { println!("Socks5 client \"{}\" was already initialised before! Config information will be overwritten (but keys will be kept)!", id);
println!( true
"SOCKS5 client \"{}\" was already initialised before! \ } else {
Config information will be overwritten (but keys will be kept)!", false
id };
);
}
// Usually you only register with the gateway on the first init, however you can force
// re-registering if wanted.
let user_wants_force_register = args.force_register_gateway;
// If the client was already initialized, don't generate new keys and don't re-register with
// the gateway (because this would create a new shared key).
// Unless the user really wants to.
let register_gateway = !already_init || user_wants_force_register;
// Attempt to use a user-provided gateway, if possible
let user_chosen_gateway_id = args.gateway.as_deref();
let mut config = Config::new(id, provider_address); let mut config = Config::new(id, provider_address);
let override_config_fields = OverrideConfig::from(args.clone());
config = override_config(config, override_config_fields);
let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config) let mut rng = OsRng;
.await
.unwrap_or_else(|err| { // TODO: ideally that should be the last thing that's being done to config.
eprintln!("Failed to setup gateway\nError: {err}"); // However, we are later further overriding it with gateway id
std::process::exit(1) config = override_config(config, &matches);
}); if matches.is_present("fastmode") {
config.get_base_mut().with_gateway_endpoint(gateway); config.get_base_mut().set_high_default_traffic_volume();
}
// if client was already initialised, don't generate new keys, not re-register with gateway
// (because this would create new shared key)
if !already_init {
// create identity, encryption and ack keys.
let mut key_manager = KeyManager::new(&mut rng);
let chosen_gateway_id = matches.value_of("gateway");
let gateway_details = gateway_details(
config.get_base().get_validator_api_endpoints(),
chosen_gateway_id,
)
.await;
let shared_keys =
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
config.get_base_mut().with_gateway_endpoint(
gateway_details.identity_key.to_base58_string(),
gateway_details.owner.clone(),
gateway_details.clients_address(),
);
key_manager.insert_gateway_shared_key(shared_keys);
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
key_manager
.store_keys(&pathfinder)
.expect("Failed to generated keys");
println!("Saved all generated keys");
}
let config_save_location = config.get_config_file_save_location(); let config_save_location = config.get_config_file_save_location();
config config
.save_to_file(None) .save_to_file(None)
.expect("Failed to save the config file"); .expect("Failed to save the config file");
println!("Saved configuration file to {:?}", config_save_location); println!("Saved configuration file to {:?}", config_save_location);
println!("Using gateway: {}", config.get_base().get_gateway_id()); println!("Using gateway: {}", config.get_base().get_gateway_id(),);
log::debug!("Gateway id: {}", config.get_base().get_gateway_id()); println!("Client configuration completed.\n\n\n");
log::debug!("Gateway owner: {}", config.get_base().get_gateway_owner());
log::debug!(
"Gateway listener: {}",
config.get_base().get_gateway_listener()
);
println!("Client configuration completed.");
client_core::init::show_address(config.get_base()).unwrap_or_else(|err| { show_address(&config);
eprintln!("Failed to show address\nError: {err}");
std::process::exit(1)
});
}
async fn setup_gateway(
id: &str,
register: bool,
user_chosen_gateway_id: Option<&str>,
config: &Config,
) -> Result<GatewayEndpoint, ClientCoreError> {
if register {
// Get the gateway details by querying the validator-api. Either pick one at random or use
// the chosen one if it's among the available ones.
println!("Configuring gateway");
let gateway = client_core::init::query_gateway_details(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
log::debug!("Querying gateway gives: {}", gateway);
// Registering with gateway by setting up and writing shared keys to disk
log::trace!("Registering gateway");
client_core::init::register_with_gateway_and_store_keys(gateway.clone(), config.get_base())
.await?;
println!("Saved all generated keys");
Ok(gateway.into())
} else if user_chosen_gateway_id.is_some() {
// Just set the config, don't register or create any keys
// This assumes that the user knows what they are doing, and that the existing keys are
// valid for the gateway being used
println!("Using gateway provided by user, keeping existing keys");
let gateway = client_core::init::query_gateway_details(
config.get_base().get_validator_api_endpoints(),
user_chosen_gateway_id,
)
.await?;
log::debug!("Querying gateway gives: {}", gateway);
Ok(gateway.into())
} else {
println!("Not registering gateway, will reuse existing config and keys");
let existing_config = Config::load_from_file(Some(id)).map_err(|err| {
log::error!(
"Unable to configure gateway: {err}. \n
Seems like the client was already initialized but it was not possible to read \
the existing configuration file. \n
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
removing the existing configuration and starting over."
);
ClientCoreError::CouldNotLoadExistingGatewayConfiguration(err)
})?;
Ok(existing_config.get_base().get_gateway_endpoint().clone())
}
} }
+53 -101
View File
@@ -2,123 +2,75 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::Config; use crate::client::config::Config;
use clap::{Parser, Subcommand}; use clap::ArgMatches;
use config::parse_validators; use url::Url;
pub mod init; pub(crate) mod init;
pub(crate) mod run; pub(crate) mod run;
pub(crate) mod upgrade; pub(crate) mod upgrade;
fn long_version() -> String { pub(crate) const TESTNET_MODE_ARG_NAME: &str = "testnet-mode";
format!( #[cfg(not(feature = "coconut"))]
r#" pub(crate) const ETH_ENDPOINT_ARG_NAME: &str = "eth_endpoint";
{:<20}{} #[cfg(not(feature = "coconut"))]
{:<20}{} pub(crate) const ETH_PRIVATE_KEY_ARG_NAME: &str = "eth_private_key";
{:<20}{} #[cfg(not(feature = "coconut"))]
{:<20}{} pub(crate) const DEFAULT_ETH_ENDPOINT: &str =
{:<20}{} "https://rinkeby.infura.io/v3/00000000000000000000000000000000";
{:<20}{} #[cfg(not(feature = "coconut"))]
{:<20}{} pub(crate) const DEFAULT_ETH_PRIVATE_KEY: &str =
{:<20}{} "0000000000000000000000000000000000000000000000000000000000000001";
"#,
"Build Timestamp:", fn parse_validators(raw: &str) -> Vec<Url> {
env!("VERGEN_BUILD_TIMESTAMP"), raw.split(',')
"Build Version:", .map(|raw_validator| {
env!("VERGEN_BUILD_SEMVER"), raw_validator
"Commit SHA:", .trim()
env!("VERGEN_GIT_SHA"), .parse()
"Commit Date:", .expect("one of the provided validator api urls is invalid")
env!("VERGEN_GIT_COMMIT_TIMESTAMP"), })
"Commit Branch:", .collect()
env!("VERGEN_GIT_BRANCH"),
"rustc Version:",
env!("VERGEN_RUSTC_SEMVER"),
"rustc Channel:",
env!("VERGEN_RUSTC_CHANNEL"),
"cargo Profile:",
env!("VERGEN_CARGO_PROFILE"),
)
} }
fn long_version_static() -> &'static str { pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config {
Box::leak(long_version().into_boxed_str()) if let Some(raw_validators) = matches.value_of("validators") {
}
#[derive(Parser)]
#[clap(author = "Nymtech", version, long_version = long_version_static(), about)]
pub(crate) struct Cli {
/// Path pointing to an env file that configures the client.
#[clap(long)]
pub(crate) config_env_file: Option<std::path::PathBuf>,
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
pub(crate) enum Commands {
/// Initialise a Nym client. Do this first!
Init(init::Init),
/// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run),
/// Try to upgrade the client
Upgrade(upgrade::Upgrade),
}
// Configuration that can be overridden.
pub(crate) struct OverrideConfig {
validators: Option<String>,
port: Option<u16>,
fastmode: bool,
#[cfg(feature = "coconut")]
enabled_credentials_mode: bool,
}
pub(crate) async fn execute(args: &Cli) {
match &args.command {
Commands::Init(m) => init::execute(m).await,
Commands::Run(m) => run::execute(m).await,
Commands::Upgrade(m) => upgrade::execute(m),
}
}
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
if let Some(raw_validators) = args.validators {
config config
.get_base_mut() .get_base_mut()
.set_custom_validator_apis(parse_validators(&raw_validators)); .set_custom_validator_apis(parse_validators(raw_validators));
} else if let Ok(raw_validators) = std::env::var(network_defaults::var_names::API_VALIDATOR) {
config
.get_base_mut()
.set_custom_validator_apis(parse_validators(&raw_validators));
} }
if let Some(port) = args.port { if let Some(gateway_id) = matches.value_of("gateway") {
config = config.with_port(port); config.get_base_mut().with_gateway_id(gateway_id);
} }
#[cfg(feature = "coconut")] if let Some(port) = matches.value_of("port").map(|port| port.parse::<u16>()) {
{ if let Err(err) = port {
if args.enabled_credentials_mode { // if port was overridden, it must be parsable
config.get_base_mut().with_disabled_credentials(false) panic!("Invalid port value provided - {:?}", err);
} }
config = config.with_port(port.unwrap());
} }
if args.fastmode { #[cfg(not(feature = "coconut"))]
config.get_base_mut().set_high_default_traffic_volume(); if let Some(eth_endpoint) = matches.value_of(ETH_ENDPOINT_ARG_NAME) {
config.get_base_mut().with_eth_endpoint(eth_endpoint);
} else if !cfg!(feature = "eth") {
config
.get_base_mut()
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT);
}
#[cfg(not(feature = "coconut"))]
if let Some(eth_private_key) = matches.value_of(ETH_PRIVATE_KEY_ARG_NAME) {
config.get_base_mut().with_eth_private_key(eth_private_key);
} else if !cfg!(feature = "eth") {
config
.get_base_mut()
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
}
if !cfg!(feature = "eth") || matches.is_present(TESTNET_MODE_ARG_NAME) {
config.get_base_mut().with_testnet_mode(true)
} }
config config
} }
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn verify_cli() {
Cli::command().debug_assert();
}
}
+68 -61
View File
@@ -1,61 +1,72 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::{ use crate::client::config::Config;
client::{config::Config, NymClient}, use crate::client::NymClient;
commands::{override_config, OverrideConfig}, use crate::commands::override_config;
}; #[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))]
use clap::Args; use crate::commands::{ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME, TESTNET_MODE_ARG_NAME};
use clap::{App, Arg, ArgMatches};
use config::NymConfig; use config::NymConfig;
use log::*; use log::*;
use version_checker::is_minor_version_compatible; use version_checker::is_minor_version_compatible;
#[derive(Args, Clone)] pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
pub(crate) struct Run { let app = App::new("run")
/// Id of the nym-mixnet-client we want to run. .about("Run the Nym client with provided configuration client optionally overriding set parameters")
#[clap(long)] .arg(Arg::with_name("id")
id: String, .long("id")
.help("Id of the nym-mixnet-client we want to run.")
.takes_value(true)
.required(true)
)
// the rest of arguments are optional, they are used to override settings in config file
.arg(Arg::with_name("config")
.long("config")
.help("Custom path to the nym-mixnet-client configuration file")
.takes_value(true)
)
.arg(Arg::with_name("provider")
.long("provider")
.help("Address of the socks5 provider to send messages to.")
.takes_value(true)
)
.arg(Arg::with_name("validators")
.long("validators")
.help("Comma separated list of rest endpoints of the validators")
.takes_value(true),
)
.arg(Arg::with_name("gateway")
.long("gateway")
.help("Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened")
.takes_value(true)
)
.arg(Arg::with_name("port")
.short("p")
.long("port")
.help("Port for the socket to listen on")
.takes_value(true)
);
#[cfg(feature = "eth")]
#[cfg(not(feature = "coconut"))]
let app = app
.arg(
Arg::with_name(TESTNET_MODE_ARG_NAME)
.long(TESTNET_MODE_ARG_NAME)
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
)
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
.long(ETH_ENDPOINT_ARG_NAME)
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true))
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
.long(ETH_PRIVATE_KEY_ARG_NAME)
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
.takes_value(true));
/// Custom path to the nym-mixnet-client configuration file app
#[clap(long)]
config: Option<String>,
/// Address of the socks5 provider to send messages to.
#[clap(long)]
provider: Option<String>,
/// Id of the gateway we want to connect to. If overridden, it is user's responsibility to
/// ensure prior registration happened
#[clap(long)]
gateway: Option<String>,
/// Comma separated list of rest endpoints of the validators
#[clap(long)]
validators: Option<String>,
/// Port for the socket to listen on
#[clap(short, long)]
port: Option<u16>,
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
/// with bandwidth credential requirement.
#[cfg(feature = "coconut")]
#[clap(long)]
enabled_credentials_mode: bool,
}
impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self {
OverrideConfig {
validators: run_config.validators,
port: run_config.port,
fastmode: false,
#[cfg(feature = "coconut")]
enabled_credentials_mode: run_config.enabled_credentials_mode,
}
}
} }
// this only checks compatibility between config the binary. It does not take into consideration // this only checks compatibility between config the binary. It does not take into consideration
@@ -63,13 +74,8 @@ impl From<Run> for OverrideConfig {
fn version_check(cfg: &Config) -> bool { fn version_check(cfg: &Config) -> bool {
let binary_version = env!("CARGO_PKG_VERSION"); let binary_version = env!("CARGO_PKG_VERSION");
let config_version = cfg.get_base().get_version(); let config_version = cfg.get_base().get_version();
if binary_version == config_version { if binary_version != config_version {
true warn!("The mixnode binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
} else {
warn!(
"The mixnode binary has different version than what is specified in config file! {} and {}",
binary_version, config_version
);
if is_minor_version_compatible(binary_version, config_version) { if is_minor_version_compatible(binary_version, config_version) {
info!("but they are still semver compatible. However, consider running the `upgrade` command"); info!("but they are still semver compatible. However, consider running the `upgrade` command");
true true
@@ -77,11 +83,13 @@ fn version_check(cfg: &Config) -> bool {
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again"); error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
false false
} }
} else {
true
} }
} }
pub(crate) async fn execute(args: &Run) { pub async fn execute(matches: ArgMatches<'static>) {
let id = &args.id; let id = matches.value_of("id").unwrap();
let mut config = match Config::load_from_file(Some(id)) { let mut config = match Config::load_from_file(Some(id)) {
Ok(cfg) => cfg, Ok(cfg) => cfg,
@@ -91,8 +99,7 @@ pub(crate) async fn execute(args: &Run) {
} }
}; };
let override_config_fields = OverrideConfig::from(args.clone()); config = override_config(config, &matches);
config = override_config(config, override_config_fields);
if !version_check(&config) { if !version_check(&config) {
error!("failed the local version check"); error!("failed the local version check");
+31 -19
View File
@@ -2,13 +2,13 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::client::config::{Config, MISSING_VALUE}; use crate::client::config::{Config, MISSING_VALUE};
use clap::{App, Arg, ArgMatches};
use config::defaults::default_api_endpoints;
use config::NymConfig; use config::NymConfig;
use std::fmt::Display;
use std::process;
use version_checker::Version; use version_checker::Version;
use clap::Args;
use std::{fmt::Display, process};
#[allow(dead_code)] #[allow(dead_code)]
fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! { fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
print_failed_upgrade(from_version, to_version); print_failed_upgrade(from_version, to_version);
@@ -49,11 +49,14 @@ fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> !
process::exit(1) process::exit(1)
} }
#[derive(Args, Clone)] pub fn command_args<'a, 'b>() -> App<'a, 'b> {
pub(crate) struct Upgrade { App::new("upgrade").about("Try to upgrade the client").arg(
/// Id of the nym-client we want to upgrade Arg::with_name("id")
#[clap(long)] .long("id")
id: String, .help("Id of the nym-client we want to upgrade")
.takes_value(true)
.required(true),
)
} }
fn parse_config_version(config: &Config) -> Version { fn parse_config_version(config: &Config) -> Version {
@@ -92,7 +95,7 @@ fn parse_package_version() -> Version {
fn minor_0_12_upgrade( fn minor_0_12_upgrade(
mut config: Config, mut config: Config,
_args: &Upgrade, _matches: &ArgMatches<'_>,
config_version: &Version, config_version: &Version,
package_version: &Version, package_version: &Version,
) -> Config { ) -> Config {
@@ -104,6 +107,15 @@ fn minor_0_12_upgrade(
print_start_upgrade(&config_version, &to_version); print_start_upgrade(&config_version, &to_version);
println!(
"Setting validator API endpoints to {:?}",
default_api_endpoints()
);
config
.get_base_mut()
.set_custom_validator_apis(default_api_endpoints());
config config
.get_base_mut() .get_base_mut()
.set_custom_version(to_version.to_string().as_ref()); .set_custom_version(to_version.to_string().as_ref());
@@ -119,30 +131,30 @@ fn minor_0_12_upgrade(
config config
} }
fn do_upgrade(mut config: Config, args: &Upgrade, package_version: &Version) { fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Version) {
loop { loop {
let config_version = parse_config_version(&config); let config_version = parse_config_version(&config);
if &config_version == package_version { if config_version == package_version {
println!("You're using the most recent version!"); println!("You're using the most recent version!");
return; return;
} }
config = match config_version.major { config = match config_version.major {
0 => match config_version.minor { 0 => match config_version.minor {
9 | 10 => outdated_upgrade(&config_version, package_version), 9 | 10 => outdated_upgrade(&config_version, &package_version),
11 => minor_0_12_upgrade(config, args, &config_version, package_version), 11 => minor_0_12_upgrade(config, matches, &config_version, &package_version),
_ => unsupported_upgrade(&config_version, package_version), _ => unsupported_upgrade(&config_version, &package_version),
}, },
_ => unsupported_upgrade(&config_version, package_version), _ => unsupported_upgrade(&config_version, &package_version),
} }
} }
} }
pub(crate) fn execute(args: &Upgrade) { pub fn execute(matches: &ArgMatches<'_>) {
let package_version = parse_package_version(); let package_version = parse_package_version();
let id = &args.id; let id = matches.value_of("id").unwrap();
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| { let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {:?}", err); eprintln!("failed to load existing config file! - {:?}", err);
@@ -155,5 +167,5 @@ pub(crate) fn execute(args: &Upgrade) {
} }
// here be upgrade path to 0.9.X and beyond based on version number from config // here be upgrade path to 0.9.X and beyond based on version number from config
do_upgrade(existing_config, args, &package_version) do_upgrade(existing_config, matches, package_version)
} }

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