Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ae0883241e | |||
| 11baa99c4b | |||
| 8bc23434ab | |||
| 8f6daf1e03 | |||
| 7f63377d22 | |||
| 46cb3eca38 | |||
| 7b22872c6b | |||
| 3342cb13c7 | |||
| 3cd5fc3b22 |
+2
-2
@@ -15,8 +15,8 @@
|
||||
* @futurechimp @mmsinclair
|
||||
|
||||
# Rust rules:
|
||||
*.rs @durch @futurechimp @jstuczyn @neacsu @octol
|
||||
Cargo.* @durch @futurechimp @jstuczyn @neacsu @octol
|
||||
*.rs @durch @futurechimp @jstuczyn @neacsu
|
||||
Cargo.* @durch @futurechimp @jstuczyn @neacsu
|
||||
|
||||
# JS rules:
|
||||
*.js @mmsinclair @fmtabbara @Aid19801
|
||||
|
||||
@@ -9,14 +9,28 @@ on:
|
||||
- 'explorer/**'
|
||||
|
||||
jobs:
|
||||
matrix_prep:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
# creates the matrix strategy from build_matrix_includes.json
|
||||
- uses: actions/checkout@v2
|
||||
- id: set-matrix
|
||||
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||
with:
|
||||
inputFile: '.github/workflows/build_matrix_includes.json'
|
||||
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux-exoscale ]
|
||||
# Enable sccache via environment variable
|
||||
env:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
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.os == 'windows-latest' }}
|
||||
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
|
||||
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
|
||||
@@ -25,7 +39,7 @@ jobs:
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
@@ -49,16 +63,25 @@ jobs:
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
# if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable'
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
|
||||
# COCONUT stuff
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Build all binaries with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -73,6 +96,7 @@ jobs:
|
||||
|
||||
- name: Run clippy with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --features=coconut -- -D warnings
|
||||
args: --features=coconut -- -D warnings
|
||||
@@ -10,10 +10,41 @@
|
||||
"rust":"stable",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"stable",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"pull_request"
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -41,10 +41,9 @@ jobs:
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
- name: Keybase - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
working-directory: .github/workflows/support-files/messages
|
||||
- name: Keybase - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: network-explorer
|
||||
NYM_PROJECT_NAME: "Network Explorer"
|
||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
||||
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
|
||||
@@ -57,4 +56,4 @@ jobs:
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
args: .github/workflows/support-files/messages/entry_point_notifications.sh
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
name: Nightly builds
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '14 4 * * *'
|
||||
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_includes.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: --all
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --all
|
||||
|
||||
- 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
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
|
||||
# COCONUT stuff
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Build all binaries with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --all --features=coconut
|
||||
|
||||
- name: Run all tests with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --all --features=coconut
|
||||
|
||||
- name: Run clippy with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --features=coconut -- -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_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
@@ -1,50 +0,0 @@
|
||||
[
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"stable",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"stable",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"stable",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"schedule"
|
||||
}
|
||||
]
|
||||
@@ -1,77 +0,0 @@
|
||||
name: Publish Nym Wallet (MacOS)
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nym-wallet
|
||||
|
||||
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-wallet-`
|
||||
if: startsWith(github.ref, 'refs/tags/nym-wallet-') == false
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Release tag did not start with nym-wallet-...')
|
||||
|
||||
- name: Node v16
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
- 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: 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 }}
|
||||
run: yarn && yarn build
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: nym-wallet/target/release/bundle/dmg/*.dmg
|
||||
|
||||
- name: Clean up keychain
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
|
||||
@@ -1,46 +0,0 @@
|
||||
name: Publish Nym Wallet (Ubuntu)
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nym-wallet
|
||||
|
||||
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
|
||||
- name: Check the release tag starts with `nym-wallet-`
|
||||
if: startsWith(github.ref, 'refs/tags/nym-wallet-') == false
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Release tag did not start with nym-wallet-...')
|
||||
|
||||
- name: Node v16
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install app dependencies and build it
|
||||
run: yarn && yarn build
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: nym-wallet/target/release/bundle/appimage/*.AppImage
|
||||
@@ -0,0 +1,12 @@
|
||||
name: Publish Nym Wallet
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- nym-wallet-*
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Run a one-line script
|
||||
run: echo Hello, world!
|
||||
@@ -10,10 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
nym-wallet-types:
|
||||
runs-on: [ self-hosted, custom-linux-exoscale ]
|
||||
# Enable sccache
|
||||
env:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
steps:
|
||||
- name: Prepare
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
KEYBASE_NYM_CHANNEL=
|
||||
KEYBASE_NYMBOT_USERNAME=
|
||||
KEYBASE_NYMBOT_PAPERKEY=
|
||||
|
||||
NYM_NOTIFICATION_KIND=nightly
|
||||
NYM_PROJECT_NAME=Nightly Build
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Custom GitHub Actions mock env vars
|
||||
IS_SUCCESS=true
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# GitHub Actions context mock env vars
|
||||
GITHUB_SHA=abcdef
|
||||
GITHUB_RUN_ID=123456
|
||||
GITHUB_REPOSITORY=nymtech/nym
|
||||
GITHUB_SERVER_URL=https://github.com
|
||||
GIT_BRANCH_NAME=feature/testing-support-files
|
||||
GIT_BRANCH=feature/testing-support-files
|
||||
GIT_COMMIT_MESSAGE=This is the commit message
|
||||
GITHUB_ACTOR=octocat
|
||||
|
||||
# add a Personal Access Token (PAT) generated from GitHub here for use in testing
|
||||
GITHUB_TOKEN=
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Network Explorer
|
||||
NYM_CI_WWW_LOCATION=some-branch
|
||||
NYM_CI_WWW_BASE=example.com
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Nightly builds
|
||||
WORKFLOW_CONCLUSION=success
|
||||
|
||||
SHOW_DEBUG=true
|
||||
@@ -1,5 +0,0 @@
|
||||
node_modules
|
||||
.idea
|
||||
|
||||
# don't commit the lock file to avoid cross-platform issues
|
||||
package-lock.json
|
||||
@@ -1 +0,0 @@
|
||||
16
|
||||
@@ -1,58 +0,0 @@
|
||||
# GitHub Actions Support Files
|
||||
|
||||
This is a collection of scripts and files to support GitHub Actions.
|
||||
|
||||
## Sending Notifications
|
||||
|
||||
These scripts send CI notifications to Keybase by creating messages from templates and env vars passed from GitHub Actions.
|
||||
|
||||
### Adding notifications to a GitHub Action
|
||||
|
||||
```
|
||||
jobs:
|
||||
build:
|
||||
...
|
||||
- name: Notifications - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files/notifications
|
||||
- name: Notifications - Send
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: "my-component"
|
||||
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-network-explorer"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
```
|
||||
|
||||
Notifications are run by adding the snippet above to a GitHub Action, and:
|
||||
|
||||
1. Installing node packages needed at run time
|
||||
2. Set the env vars as required:
|
||||
- `NYM_NOTIFICATION_KIND` matches the directory in `.github/workflows/support-files/${NYM_NOTIFICATION_KIND}` to provide the templates and extra scripting in `index.js`
|
||||
- Keybase credentials, channel and other env vars for the status of the build and repo
|
||||
3. Replacing the default entry point shell script on the `keybaseio/client:stable-node` docker image to run `.github/workflows/support-files/notifications/entry_point.sh`
|
||||
|
||||
### Running locally
|
||||
|
||||
You will need:
|
||||
- Node 16 LTS
|
||||
- npm
|
||||
|
||||
Copy `.github/workflows/support-files/.env.example` to `.github/workflows/support-files/.env` and valid Keybase credentials.
|
||||
|
||||
Then run `npm install` to get dependencies.
|
||||
|
||||
Start development mode for the notification type you want either by passing the value as an env var called `NYM_NOTIFICATION_KIND` or set the `.env` file values correctly.
|
||||
|
||||
```bash
|
||||
cd .github/workflows/support-files
|
||||
npm install
|
||||
cp .env.example .env
|
||||
vi .env
|
||||
npm run dev
|
||||
```
|
||||
@@ -1 +0,0 @@
|
||||
require('./notifications/send_message');
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
.idea
|
||||
+2
-5
@@ -4,14 +4,11 @@
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node dev.js",
|
||||
"format": "prettier --write **/*.js"
|
||||
"format": "prettier --write send_message.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^16.0.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"keybase-bot": "^3.6.1",
|
||||
"octokit": "^1.7.1"
|
||||
"keybase-bot": "^3.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "2.3.2"
|
||||
@@ -0,0 +1,69 @@
|
||||
const Bot = require('keybase-bot');
|
||||
const Handlebars = require('handlebars');
|
||||
const fs = require('fs');
|
||||
|
||||
async function main() {
|
||||
const data = { env: process.env };
|
||||
// const data = { ...PASTE TEST DATA HERE ... }; // -- DEV: uncomment to set test data
|
||||
|
||||
// validation of environment
|
||||
if(!(process.env.NYM_PROJECT_NAME || data.env.NYM_PROJECT_NAME)) {
|
||||
throw new Error('Please set env var NYM_PROJECT_NAME with the project name for displaying in notification messages');
|
||||
}
|
||||
const keybaseChannel = process.env.KEYBASE_NYM_CHANNEL || data.env.KEYBASE_NYM_CHANNEL;
|
||||
if(!keybaseChannel) {
|
||||
throw new Error('Please set env var KEYBASE_NYM_CHANNEL with the channel name for the notification message');
|
||||
}
|
||||
|
||||
// extract the git branch name
|
||||
const GIT_BRANCH_NAME = (process.env.GITHUB_REF || data.env.GITHUB_REF).split('/').slice(2).join('/');
|
||||
|
||||
data.env.GIT_BRANCH_NAME = GIT_BRANCH_NAME;
|
||||
const source = fs
|
||||
.readFileSync(process.env.IS_SUCCESS === 'true' ? 'success' : 'failure')
|
||||
.toString();
|
||||
const template = Handlebars.compile(source);
|
||||
const result = template(data);
|
||||
|
||||
// -- DEV: uncomment to show what is available in the handlebars template / show the result
|
||||
// console.dir({ data }, { depth: null });
|
||||
// console.log(result);
|
||||
|
||||
const bot = new Bot();
|
||||
try {
|
||||
const username = process.env.KEYBASE_NYMBOT_USERNAME;
|
||||
const paperkey = process.env.KEYBASE_NYMBOT_PAPERKEY;
|
||||
|
||||
if(!username) {
|
||||
throw new Error('Username is not defined. Please set env var KEYBASE_NYMBOT_USERNAME');
|
||||
}
|
||||
if(!paperkey) {
|
||||
throw new Error('Paperkey is not defined. Please set env var KEYBASE_NYMBOT_PAPERKEY');
|
||||
}
|
||||
|
||||
console.log(`Initialising keybase with user "${username}" and key: "${'*'.repeat(paperkey.length)}"...`);
|
||||
await bot.init(username, paperkey, { verbose: false });
|
||||
|
||||
const channel = {
|
||||
name: 'nymtech_bot',
|
||||
membersType: 'team',
|
||||
topicName: keybaseChannel,
|
||||
topic_type: 'CHAT',
|
||||
};
|
||||
const message = {
|
||||
body: result,
|
||||
};
|
||||
|
||||
console.log(`Sending to ${channel.name}#${channel.topicName}...`);
|
||||
await bot.chat.send(channel, message);
|
||||
|
||||
console.log('Message sent!');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exitCode = -1;
|
||||
} finally {
|
||||
await bot.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
> `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 }}:
|
||||
Commit message:
|
||||
```
|
||||
{{ env.GIT_COMMIT_MESSAGE }}
|
||||
```
|
||||
@@ -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,162 +0,0 @@
|
||||
const Handlebars = require('handlebars');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { Octokit, App } = require('octokit');
|
||||
|
||||
async function addToContextAndValidate(context) {
|
||||
if (!context.env.WORKFLOW_CONCLUSION) {
|
||||
throw new Error('Please ensure the env var WORKFLOW_CONCLUSION is set');
|
||||
}
|
||||
if (!context.env.GITHUB_TOKEN) {
|
||||
throw new Error('Please ensure the env var GITHUB_TOKEN is set');
|
||||
}
|
||||
if (!context.env.GITHUB_RUN_ID) {
|
||||
throw new Error('Please ensure the env var GITHUB_RUN_ID is set');
|
||||
}
|
||||
if (!context.env.GITHUB_REPOSITORY) {
|
||||
throw new Error('Please ensure the env var GITHUB_REPOSITORY is set');
|
||||
}
|
||||
}
|
||||
|
||||
async function getMessageBody(context) {
|
||||
const source = fs
|
||||
.readFileSync(
|
||||
context.env.WORKFLOW_CONCLUSION === 'success'
|
||||
? path.resolve(__dirname, 'templates', 'success')
|
||||
: path.resolve(__dirname, 'templates', 'failure'),
|
||||
)
|
||||
.toString();
|
||||
const template = Handlebars.compile(source);
|
||||
|
||||
// get job details from GitHub API
|
||||
const octokit = new Octokit({ auth: context.env.GITHUB_TOKEN });
|
||||
const [owner, repo] = context.env.GITHUB_REPOSITORY.split('/');
|
||||
const {
|
||||
data: { jobs },
|
||||
} = await octokit.rest.actions.listJobsForWorkflowRun({
|
||||
run_id: context.env.GITHUB_RUN_ID,
|
||||
owner,
|
||||
repo,
|
||||
});
|
||||
|
||||
// uncomment this to see what is available for each job
|
||||
if(process.env.SHOW_DEBUG) {
|
||||
console.dir(jobs, { depth: null });
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
a sample of the response is:
|
||||
|
||||
{
|
||||
total_count: 10,
|
||||
jobs: [
|
||||
{
|
||||
id: 5182940024,
|
||||
run_id: 1840752095,
|
||||
run_url: 'https://api.github.com/repos/nymtech/nym/actions/runs/1840752095',
|
||||
run_attempt: 1,
|
||||
node_id: 'CR_kwDODdjOis8AAAABNO1jeA',
|
||||
head_sha: 'aa00eb70d57751bfa556bd3602df87c7473367fc',
|
||||
url: 'https://api.github.com/repos/nymtech/nym/actions/jobs/5182940024',
|
||||
html_url: 'https://github.com/nymtech/nym/runs/5182940024?check_suite_focus=true',
|
||||
status: 'completed',
|
||||
conclusion: 'success',
|
||||
started_at: '2022-02-14T11:28:34Z',
|
||||
completed_at: '2022-02-14T11:28:38Z',
|
||||
name: 'matrix_prep',
|
||||
steps: [
|
||||
{
|
||||
name: 'Set up job',
|
||||
status: 'completed',
|
||||
conclusion: 'success',
|
||||
number: 1,
|
||||
started_at: '2022-02-14T13:28:34.000+02:00',
|
||||
completed_at: '2022-02-14T13:28:36.000+02:00'
|
||||
},
|
||||
{
|
||||
name: 'Run actions/checkout@v2',
|
||||
status: 'completed',
|
||||
conclusion: 'success',
|
||||
number: 2,
|
||||
started_at: '2022-02-14T13:28:36.000+02:00',
|
||||
completed_at: '2022-02-14T13:28:37.000+02:00'
|
||||
},
|
||||
...
|
||||
],
|
||||
check_run_url: 'https://api.github.com/repos/nymtech/nym/check-runs/5182940024',
|
||||
labels: [ 'ubuntu-latest' ],
|
||||
runner_id: 1,
|
||||
runner_name: 'Hosted Agent',
|
||||
runner_group_id: 2,
|
||||
runner_group_name: 'GitHub Actions'
|
||||
},
|
||||
{
|
||||
id: 5182943473,
|
||||
run_id: 1840752095,
|
||||
run_url: 'https://api.github.com/repos/nymtech/nym/actions/runs/1840752095',
|
||||
run_attempt: 1,
|
||||
node_id: 'CR_kwDODdjOis8AAAABNO1w8Q',
|
||||
head_sha: 'aa00eb70d57751bfa556bd3602df87c7473367fc',
|
||||
url: 'https://api.github.com/repos/nymtech/nym/actions/jobs/5182943473',
|
||||
html_url: 'https://github.com/nymtech/nym/runs/5182943473?check_suite_focus=true',
|
||||
status: 'completed',
|
||||
conclusion: 'failure',
|
||||
started_at: '2022-02-14T11:29:04Z',
|
||||
completed_at: '2022-02-14T11:55:45Z',
|
||||
name: 'build (macos-latest, stable, schedule)',
|
||||
steps: [
|
||||
{
|
||||
name: 'Set up job',
|
||||
status: 'completed',
|
||||
conclusion: 'success',
|
||||
number: 1,
|
||||
started_at: '2022-02-14T13:29:04.000+02:00',
|
||||
completed_at: '2022-02-14T13:29:26.000+02:00'
|
||||
},
|
||||
{
|
||||
name: 'Install Dependencies (Linux)',
|
||||
status: 'completed',
|
||||
conclusion: 'skipped',
|
||||
number: 2,
|
||||
started_at: '2022-02-14T13:29:26.000+02:00',
|
||||
completed_at: '2022-02-14T13:29:26.000+02:00'
|
||||
},
|
||||
{
|
||||
name: 'Keybase - Send Notification',
|
||||
status: 'completed',
|
||||
conclusion: 'failure',
|
||||
number: 15,
|
||||
started_at: '2022-02-14T13:55:44.000+02:00',
|
||||
completed_at: '2022-02-14T13:55:44.000+02:00'
|
||||
},
|
||||
],
|
||||
check_run_url: 'https://api.github.com/repos/nymtech/nym/check-runs/5182943473',
|
||||
labels: [ 'macos-latest' ],
|
||||
runner_id: 4,
|
||||
runner_name: 'GitHub Actions 4',
|
||||
runner_group_id: 2,
|
||||
runner_group_name: 'GitHub Actions'
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
const jobResults = jobs
|
||||
.map((job) => {
|
||||
const icon = job.conclusion === 'success' ? '🟩' : '🟥';
|
||||
|
||||
// each job is converted into formatted markdown text
|
||||
return `${icon} ${job.conclusion}: ${job.name} - ${job.html_url}`;
|
||||
})
|
||||
// and join with newlines for display in the template
|
||||
.join('\n');
|
||||
|
||||
return template({ ...context, jobResults });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addToContextAndValidate,
|
||||
getMessageBody,
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
|
||||
> :rocket: {{ env.NYM_PROJECT_NAME }}
|
||||
> 🔴 **FAILURE** :cry:
|
||||
> `when` {{ timestamp }}
|
||||
> `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 }}
|
||||
|
||||
{{ jobResults }}
|
||||
@@ -1,9 +0,0 @@
|
||||
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
|
||||
> :rocket: {{ env.NYM_PROJECT_NAME }}
|
||||
> ✅ **SUCCESS**
|
||||
> `when` {{ timestamp }}
|
||||
> `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 }}
|
||||
|
||||
{{ jobResults }}
|
||||
@@ -1,153 +0,0 @@
|
||||
require('dotenv').config();
|
||||
|
||||
const Bot = require('keybase-bot');
|
||||
|
||||
let context = {
|
||||
kinds: ['network-explorer', 'nightly'],
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate that all required env and context vars are available
|
||||
*/
|
||||
function validateContext() {
|
||||
if (!context.env.NYM_NOTIFICATION_KIND) {
|
||||
throw new Error(
|
||||
'Please set env var NYM_NOTIFICATION_KIND with the project kind that matches a directory in ".github/workflows/support-files"',
|
||||
);
|
||||
}
|
||||
if (!context.kinds.includes(context.env.NYM_NOTIFICATION_KIND)) {
|
||||
throw new Error(`Env var NYM_NOTIFICATION_KIND is not in ${context.kinds}`);
|
||||
}
|
||||
if (!context.env.NYM_PROJECT_NAME) {
|
||||
throw new Error(
|
||||
'Please set env var NYM_PROJECT_NAME with the project name for displaying in notification messages',
|
||||
);
|
||||
}
|
||||
if (!context.env.KEYBASE_NYM_CHANNEL) {
|
||||
throw new Error(
|
||||
'Please set env var KEYBASE_NYM_CHANNEL with the channel name for the notification message',
|
||||
);
|
||||
}
|
||||
if (!context.env.KEYBASE_NYMBOT_USERNAME) {
|
||||
throw new Error(
|
||||
'Username is not defined. Please set env var KEYBASE_NYMBOT_USERNAME',
|
||||
);
|
||||
}
|
||||
if (!context.env.KEYBASE_NYMBOT_PAPERKEY) {
|
||||
throw new Error(
|
||||
'Paperkey is not defined. Please set env var KEYBASE_NYMBOT_PAPERKEY',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a context that will be available in the templates for rendering notifications
|
||||
*/
|
||||
function createTemplateContext() {
|
||||
const options = { dateStyle: 'full', timeStyle: 'long' };
|
||||
context.timestamp = new Date().toLocaleString(undefined, options);
|
||||
|
||||
// add environment to template context and validate
|
||||
context.env = process.env;
|
||||
try {
|
||||
validateContext();
|
||||
} catch (e) {
|
||||
if(process.env.SHOW_DEBUG) {
|
||||
// recursively print the context for easy debugging and rethrow the error
|
||||
console.dir({ context }, { depth: null });
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
context.kind = context.env.NYM_NOTIFICATION_KIND;
|
||||
|
||||
context.keybase = {
|
||||
channel: context.env.KEYBASE_NYM_CHANNEL,
|
||||
username: context.env.KEYBASE_NYMBOT_USERNAME,
|
||||
paperkey: context.env.KEYBASE_NYMBOT_PAPERKEY,
|
||||
};
|
||||
|
||||
if (!context.env.GIT_BRANCH_NAME) {
|
||||
context.env.GIT_BRANCH_NAME = context.env.GITHUB_REF.split('/')
|
||||
.slice(2)
|
||||
.join('/');
|
||||
}
|
||||
|
||||
context.status = process.env.IS_SUCCESS === 'true' ? 'success' : 'failure';
|
||||
}
|
||||
|
||||
async function sendKeybaseMessage(messageBody) {
|
||||
const bot = new Bot();
|
||||
try {
|
||||
console.log(
|
||||
`Initialising keybase with user "${
|
||||
context.keybase.username
|
||||
}" and key: "${'*'.repeat(context.keybase.paperkey.length)}"...`,
|
||||
);
|
||||
await bot.init(context.keybase.username, context.keybase.paperkey, {
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
const channel = {
|
||||
name: 'nymtech_bot',
|
||||
membersType: 'team',
|
||||
topicName: context.keybase.channel,
|
||||
topic_type: 'CHAT',
|
||||
};
|
||||
const message = {
|
||||
body: messageBody,
|
||||
};
|
||||
|
||||
console.log(`Sending to ${channel.name}#${channel.topicName}...`);
|
||||
await bot.chat.send(channel, message);
|
||||
|
||||
console.log('Message sent!');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exitCode = -1;
|
||||
} finally {
|
||||
await bot.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the `kind` set in the context to process the context and generate a notification message
|
||||
* @returns {Promise<string>} A string notification message body
|
||||
*/
|
||||
async function processKindScript() {
|
||||
const script = require(`../${context.kind}`);
|
||||
if (!script.addToContextAndValidate) {
|
||||
throw new Error(
|
||||
`"./${context.kind}/index.js" does not export a method called "async addToContextAndValidate(context)"`,
|
||||
);
|
||||
}
|
||||
if (!script.getMessageBody) {
|
||||
throw new Error(
|
||||
`"./${context.kind}/index.js" does not export a method called "async getMessageBody(context)"`,
|
||||
);
|
||||
}
|
||||
|
||||
// call the script to modify and validate the context
|
||||
await script.addToContextAndValidate(context);
|
||||
|
||||
// let the script create a message body and return the result as a string for sending
|
||||
return await script.getMessageBody(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* The main function, as async so that await syntax is available
|
||||
*/
|
||||
async function main() {
|
||||
createTemplateContext();
|
||||
console.log(`Sending notification for kind "${context.kind}"...`);
|
||||
const messageBody = await processKindScript();
|
||||
if(process.env.SHOW_DEBUG) {
|
||||
console.log('-----------------------------------------');
|
||||
console.log(messageBody);
|
||||
console.log('-----------------------------------------');
|
||||
}
|
||||
await sendKeybaseMessage(messageBody);
|
||||
}
|
||||
|
||||
// call main function and let NodeJS handle the promise
|
||||
main();
|
||||
@@ -19,27 +19,30 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
|
||||
# token credentials (non-coconut) don't work for wasm right now
|
||||
# - uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: build
|
||||
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --features=coconut
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --manifest-path clients/webassembly/Cargo.toml
|
||||
# for some reason this does not seem to work correctly, leave it for later, building is good enough for now
|
||||
# - uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: test
|
||||
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path clients/webassembly/Cargo.toml -- --check
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
|
||||
# for some reason this does not seem to work correctly, leave it for later, building is good enough for now
|
||||
# - uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: clippy
|
||||
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
unreleased=true
|
||||
future-release=v0.12.0
|
||||
since-tag=v0.11.0
|
||||
@@ -1 +0,0 @@
|
||||
2.7.5
|
||||
+879
-228
File diff suppressed because it is too large
Load Diff
Generated
+208
-663
File diff suppressed because it is too large
Load Diff
+5
-7
@@ -11,13 +11,13 @@ panic = "abort"
|
||||
|
||||
[workspace]
|
||||
|
||||
resolver = "2"
|
||||
members = [
|
||||
"clients/client-core",
|
||||
"clients/native",
|
||||
"clients/native/websocket-requests",
|
||||
"clients/socks5",
|
||||
"clients/tauri-client/src-tauri",
|
||||
"clients/webassembly",
|
||||
"common/client-libs/gateway-client",
|
||||
"common/client-libs/mixnet-client",
|
||||
"common/client-libs/validator-client",
|
||||
@@ -25,10 +25,8 @@ members = [
|
||||
"common/config",
|
||||
"common/credentials",
|
||||
"common/crypto",
|
||||
"common/bandwidth-claim-contract",
|
||||
"common/cosmwasm-smart-contracts/contracts-common",
|
||||
"common/cosmwasm-smart-contracts/mixnet-contract",
|
||||
"common/cosmwasm-smart-contracts/vesting-contract",
|
||||
"common/erc20-bridge-contract",
|
||||
"common/mixnet-contract",
|
||||
"common/mixnode-common",
|
||||
"common/network-defaults",
|
||||
"common/nonexhaustive-delayqueue",
|
||||
@@ -54,12 +52,12 @@ members = [
|
||||
"mixnode",
|
||||
"service-providers/network-requester",
|
||||
"validator-api",
|
||||
"validator-api/validator-api-requests",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
"clients/native",
|
||||
"clients/socks5",
|
||||
# "clients/webassembly",
|
||||
"gateway",
|
||||
"service-providers/network-requester",
|
||||
"mixnode",
|
||||
@@ -67,4 +65,4 @@ default-members = [
|
||||
"explorer-api",
|
||||
]
|
||||
|
||||
exclude = ["explorer", "contracts", "tokenomics-py", "clients/webassembly"]
|
||||
exclude = ["explorer", "contracts", "tokenomics-py"]
|
||||
|
||||
@@ -1,46 +1,25 @@
|
||||
test: build clippy-all cargo-test wasm fmt
|
||||
happy: fmt clippy-happy test
|
||||
clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet
|
||||
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet
|
||||
cargo-test: test-main test-contracts test-wallet
|
||||
build: build-main build-contracts build-wallet
|
||||
all: clippy test fmt
|
||||
clippy: clippy-main clippy-contracts clippy-wallet
|
||||
test: test-main test-contracts test-wallet
|
||||
fmt: fmt-main fmt-contracts fmt-wallet
|
||||
|
||||
clippy-happy-main:
|
||||
clippy-main:
|
||||
cargo clippy
|
||||
|
||||
clippy-happy-contracts:
|
||||
cargo clippy --manifest-path contracts/Cargo.toml --target wasm32-unknown-unknown
|
||||
clippy-contracts:
|
||||
cargo clippy --manifest-path contracts/Cargo.toml
|
||||
|
||||
clippy-happy-wallet:
|
||||
clippy-wallet:
|
||||
cargo clippy --manifest-path nym-wallet/Cargo.toml
|
||||
|
||||
clippy-all-main:
|
||||
cargo clippy --all-features -- -D warnings
|
||||
|
||||
clippy-all-contracts:
|
||||
cargo clippy --manifest-path contracts/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings
|
||||
|
||||
clippy-all-wallet:
|
||||
cargo clippy --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings
|
||||
|
||||
test-main:
|
||||
cargo test --all-features
|
||||
cargo test
|
||||
|
||||
test-contracts:
|
||||
cargo test --manifest-path contracts/Cargo.toml --all-features
|
||||
cargo test --manifest-path contracts/Cargo.toml
|
||||
|
||||
test-wallet:
|
||||
cargo test --manifest-path nym-wallet/Cargo.toml --all-features
|
||||
|
||||
build-main:
|
||||
cargo build --all
|
||||
|
||||
build-contracts:
|
||||
cargo build --manifest-path contracts/Cargo.toml --all
|
||||
|
||||
build-wallet:
|
||||
cargo build --manifest-path nym-wallet/Cargo.toml --all
|
||||
cargo test --manifest-path nym-wallet/Cargo.toml
|
||||
|
||||
fmt-main:
|
||||
cargo fmt --all
|
||||
@@ -50,6 +29,3 @@ fmt-contracts:
|
||||
|
||||
fmt-wallet:
|
||||
cargo fmt --manifest-path nym-wallet/Cargo.toml --all
|
||||
|
||||
wasm:
|
||||
RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
|
||||
|
||||
@@ -21,8 +21,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
||||
|
||||
### Building
|
||||
|
||||
Platform build instructions are available on [our docs site](https://nymtech.net/docs/stable/run-nym-nodes/build-nym).
|
||||
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers).
|
||||
Platform build instructions are available on [our docs site](https://nymtech.net/docs/0.11.0/overview/index/).
|
||||
|
||||
### Developing
|
||||
|
||||
@@ -41,13 +40,13 @@ Node, node operator and delegator rewards are determined according to the princi
|
||||
|<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}">|node reward for mixnode `i`.
|
||||
|<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}">|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}">|fraction of total effort undertaken by node `i`, set to `1/k`.
|
||||
|<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=\lambda_{i}">|ratio of stake operator has plaged to their node to the token circulating supply.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=\omega_{i}">|fraction of total effort undertaken by node `i`, set to `1/k` in testnet Milhon.
|
||||
|<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 `active set` size, and set to 5000 in testnet Milhon.
|
||||
|<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}">|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 testnet Milhon.
|
||||
|<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}">|cost of operating node `i` for the duration of the rewarding eopoch, 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 Nym for testnet Milhon.
|
||||
|
||||
Node reward for node `i` is determined as:
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "client-core"
|
||||
version = "0.12.0"
|
||||
version = "0.11.0"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ use nymsphinx::utils::sample_poisson_duration;
|
||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::time;
|
||||
|
||||
@@ -164,8 +165,8 @@ impl LoopCoverTrafficStream<OsRng> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(mut self) -> JoinHandle<()> {
|
||||
tokio::spawn(async move {
|
||||
pub fn start(mut self, handle: &Handle) -> JoinHandle<()> {
|
||||
handle.spawn(async move {
|
||||
self.run().await;
|
||||
})
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@ impl KeyManager {
|
||||
))?;
|
||||
|
||||
let gateway_shared_key: SharedKeys =
|
||||
pemstore::load_key(client_pathfinder.gateway_shared_key())?;
|
||||
pemstore::load_key(&client_pathfinder.gateway_shared_key().to_owned())?;
|
||||
|
||||
let ack_key: AckKey = pemstore::load_key(client_pathfinder.ack_key())?;
|
||||
let ack_key: AckKey = pemstore::load_key(&client_pathfinder.ack_key().to_owned())?;
|
||||
|
||||
// TODO: ack key is never stored so it is generated now. But perhaps it should be stored
|
||||
// after all for consistency sake?
|
||||
|
||||
@@ -6,6 +6,7 @@ use futures::StreamExt;
|
||||
use gateway_client::GatewayClient;
|
||||
use log::*;
|
||||
use nymsphinx::forwarding::packet::MixPacket;
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
pub type BatchMixMessageSender = mpsc::UnboundedSender<Vec<MixPacket>>;
|
||||
@@ -71,8 +72,8 @@ impl MixTrafficController {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(mut self) -> JoinHandle<()> {
|
||||
tokio::spawn(async move {
|
||||
pub fn start(mut self, handle: &Handle) -> JoinHandle<()> {
|
||||
handle.spawn(async move {
|
||||
self.run().await;
|
||||
})
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ use nymsphinx::addressing::clients::Recipient;
|
||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
mod acknowledgement_control;
|
||||
@@ -169,8 +170,10 @@ impl RealMessagesController<OsRng> {
|
||||
self.ack_control = Some(ack_control_fut.await.unwrap());
|
||||
}
|
||||
|
||||
pub fn start(mut self) -> JoinHandle<Self> {
|
||||
tokio::spawn(async move {
|
||||
// &Handle is only passed for consistency sake with other client modules, but I think
|
||||
// when we get to refactoring, we should apply gateway approach and make it implicit
|
||||
pub fn start(mut self, handle: &Handle) -> JoinHandle<Self> {
|
||||
handle.spawn(async move {
|
||||
self.run().await;
|
||||
self
|
||||
})
|
||||
|
||||
@@ -15,6 +15,7 @@ use nymsphinx::params::{ReplySurbEncryptionAlgorithm, ReplySurbKeyDigestAlgorith
|
||||
use nymsphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
// Buffer Requests to say "hey, send any reconstructed messages to this channel"
|
||||
@@ -290,8 +291,8 @@ impl RequestReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
fn start(mut self) -> JoinHandle<()> {
|
||||
tokio::spawn(async move {
|
||||
fn start(mut self, handle: &Handle) -> JoinHandle<()> {
|
||||
handle.spawn(async move {
|
||||
while let Some(request) = self.query_receiver.next().await {
|
||||
match request {
|
||||
ReceivedBufferMessage::ReceiverAnnounce(sender) => {
|
||||
@@ -321,8 +322,8 @@ impl FragmentedMessageReceiver {
|
||||
mixnet_packet_receiver,
|
||||
}
|
||||
}
|
||||
fn start(mut self) -> JoinHandle<()> {
|
||||
tokio::spawn(async move {
|
||||
fn start(mut self, handle: &Handle) -> JoinHandle<()> {
|
||||
handle.spawn(async move {
|
||||
while let Some(new_messages) = self.mixnet_packet_receiver.next().await {
|
||||
self.received_buffer.handle_new_received(new_messages).await;
|
||||
}
|
||||
@@ -354,9 +355,9 @@ impl ReceivedMessagesBufferController {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(self) {
|
||||
pub fn start(self, handle: &Handle) {
|
||||
// TODO: should we do anything with JoinHandle(s) returned by start methods?
|
||||
self.fragmented_message_receiver.start();
|
||||
self.request_receiver.start();
|
||||
self.fragmented_message_receiver.start(handle);
|
||||
self.request_receiver.start(handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ impl ReplyKeyStorage {
|
||||
) -> Result<(), ReplyKeyStorageError> {
|
||||
let digest = encryption_key.compute_digest();
|
||||
|
||||
let insertion_result = match self.db.insert(digest, encryption_key.to_bytes()) {
|
||||
let insertion_result = match self.db.insert(digest.to_vec(), encryption_key.to_bytes()) {
|
||||
Err(e) => Err(ReplyKeyStorageError::DbWriteError(e)),
|
||||
Ok(existing_key) => {
|
||||
if existing_key.is_some() {
|
||||
@@ -79,7 +79,7 @@ impl ReplyKeyStorage {
|
||||
&self,
|
||||
key_digest: EncryptionKeyDigest,
|
||||
) -> Result<Option<SurbEncryptionKey>, ReplyKeyStorageError> {
|
||||
let removal_result = match self.db.remove(key_digest) {
|
||||
let removal_result = match self.db.remove(&key_digest.to_vec()) {
|
||||
Err(e) => Err(ReplyKeyStorageError::DbReadError(e)),
|
||||
Ok(existing_key) => {
|
||||
Ok(existing_key.map(|existing_key| self.read_encryption_key(existing_key)))
|
||||
|
||||
@@ -10,6 +10,7 @@ use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::time;
|
||||
use std::time::Duration;
|
||||
use tokio::runtime::Handle;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||
use tokio::task::JoinHandle;
|
||||
use topology::{nym_topology_from_bonds, NymTopology};
|
||||
@@ -303,8 +304,8 @@ impl TopologyRefresher {
|
||||
self.topology_accessor.is_routable().await
|
||||
}
|
||||
|
||||
pub fn start(mut self) -> JoinHandle<()> {
|
||||
tokio::spawn(async move {
|
||||
pub fn start(mut self, handle: &Handle) -> JoinHandle<()> {
|
||||
handle.spawn(async move {
|
||||
loop {
|
||||
tokio::time::sleep(self.refresh_rate).await;
|
||||
self.refresh().await;
|
||||
|
||||
@@ -117,20 +117,12 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.id = id;
|
||||
}
|
||||
|
||||
pub fn with_testnet_mode(&mut self, testnet_mode: bool) {
|
||||
self.client.testnet_mode = testnet_mode;
|
||||
}
|
||||
|
||||
pub fn with_gateway_endpoint<S: Into<String>>(&mut self, id: S, owner: S, listener: S) {
|
||||
self.client.gateway_endpoint = GatewayEndpoint {
|
||||
gateway_id: id.into(),
|
||||
gateway_owner: owner.into(),
|
||||
gateway_listener: listener.into(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
|
||||
self.client.gateway_endpoint.gateway_id = id.into();
|
||||
self.client.gateway_id = id.into();
|
||||
}
|
||||
|
||||
pub fn with_gateway_listener<S: Into<String>>(&mut self, gateway_listener: S) {
|
||||
self.client.gateway_listener = gateway_listener.into();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
@@ -161,10 +153,6 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.id.clone()
|
||||
}
|
||||
|
||||
pub fn get_testnet_mode(&self) -> bool {
|
||||
self.client.testnet_mode
|
||||
}
|
||||
|
||||
pub fn get_nym_root_directory(&self) -> PathBuf {
|
||||
self.client.nym_root_directory.clone()
|
||||
}
|
||||
@@ -202,15 +190,11 @@ impl<T: NymConfig> Config<T> {
|
||||
}
|
||||
|
||||
pub fn get_gateway_id(&self) -> String {
|
||||
self.client.gateway_endpoint.gateway_id.clone()
|
||||
}
|
||||
|
||||
pub fn get_gateway_owner(&self) -> String {
|
||||
self.client.gateway_endpoint.gateway_owner.clone()
|
||||
self.client.gateway_id.clone()
|
||||
}
|
||||
|
||||
pub fn get_gateway_listener(&self) -> String {
|
||||
self.client.gateway_endpoint.gateway_listener.clone()
|
||||
self.client.gateway_listener.clone()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
@@ -280,19 +264,6 @@ impl<T: NymConfig> Default for Config<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
struct GatewayEndpoint {
|
||||
/// gateway_id specifies ID of the gateway to which the client should send messages.
|
||||
/// If initially omitted, a random gateway will be chosen from the available topology.
|
||||
gateway_id: String,
|
||||
|
||||
/// Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner: String,
|
||||
|
||||
/// Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct Client<T> {
|
||||
/// Version of the client for which this configuration was created.
|
||||
@@ -302,11 +273,6 @@ pub struct Client<T> {
|
||||
/// ID specifies the human readable ID of this particular client.
|
||||
id: String,
|
||||
|
||||
/// Indicates whether this client is running in a testnet mode, thus attempting
|
||||
/// to claim bandwidth without presenting bandwidth credentials.
|
||||
#[serde(default)]
|
||||
testnet_mode: bool,
|
||||
|
||||
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
||||
validator_api_urls: Vec<Url>,
|
||||
|
||||
@@ -334,8 +300,12 @@ pub struct Client<T> {
|
||||
/// sent but not received back.
|
||||
reply_encryption_key_store_path: PathBuf,
|
||||
|
||||
/// Information regarding how the client should send data to gateway.
|
||||
gateway_endpoint: GatewayEndpoint,
|
||||
/// gateway_id specifies ID of the gateway to which the client should send messages.
|
||||
/// If initially omitted, a random gateway will be chosen from the available topology.
|
||||
gateway_id: String,
|
||||
|
||||
/// Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener: String,
|
||||
|
||||
/// Path to directory containing public/private keys used for bandwidth token purchase.
|
||||
/// Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
|
||||
@@ -365,7 +335,6 @@ impl<T: NymConfig> Default for Client<T> {
|
||||
Client {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
id: "".to_string(),
|
||||
testnet_mode: false,
|
||||
validator_api_urls: default_api_endpoints(),
|
||||
private_identity_key_file: Default::default(),
|
||||
public_identity_key_file: Default::default(),
|
||||
@@ -374,7 +343,8 @@ impl<T: NymConfig> Default for Client<T> {
|
||||
gateway_shared_key_file: Default::default(),
|
||||
ack_key_file: Default::default(),
|
||||
reply_encryption_key_store_path: Default::default(),
|
||||
gateway_endpoint: Default::default(),
|
||||
gateway_id: "".to_string(),
|
||||
gateway_listener: "".to_string(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
backup_bandwidth_token_keys_dir: Default::default(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "0.12.1"
|
||||
version = "0.11.0"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
rust-version = "1.56"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -48,7 +48,6 @@ network-defaults = { path = "../../common/network-defaults" }
|
||||
|
||||
[features]
|
||||
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
|
||||
eth = []
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0" # for the "textsend" example
|
||||
|
||||
+24
-24
@@ -2441,9 +2441,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.8",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
|
||||
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
|
||||
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3806,9 +3806,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
|
||||
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
|
||||
"version": "3.1.23",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
|
||||
"integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
@@ -3943,9 +3943,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nth-check": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
|
||||
"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
|
||||
"integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==",
|
||||
"dependencies": {
|
||||
"boolbase": "^1.0.0"
|
||||
},
|
||||
@@ -6085,9 +6085,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
|
||||
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
@@ -8853,9 +8853,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.8",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
|
||||
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
|
||||
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
|
||||
"dev": true
|
||||
},
|
||||
"for-in": {
|
||||
@@ -9871,9 +9871,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
|
||||
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
|
||||
"version": "3.1.23",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
|
||||
"integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
@@ -9984,9 +9984,9 @@
|
||||
}
|
||||
},
|
||||
"nth-check": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
|
||||
"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
|
||||
"integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==",
|
||||
"requires": {
|
||||
"boolbase": "^1.0.0"
|
||||
}
|
||||
@@ -11733,9 +11733,9 @@
|
||||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
|
||||
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
|
||||
@@ -35,7 +35,7 @@ async fn send_file_with_reply() {
|
||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||
|
||||
let recipient = get_self_address(&mut ws_stream).await;
|
||||
println!("our full address is: {}", recipient);
|
||||
println!("our full address is: {}", recipient.to_string());
|
||||
|
||||
let read_data = std::fs::read("examples/dummy_file").unwrap();
|
||||
|
||||
@@ -83,7 +83,7 @@ async fn send_file_without_reply() {
|
||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||
|
||||
let recipient = get_self_address(&mut ws_stream).await;
|
||||
println!("our full address is: {}", recipient);
|
||||
println!("our full address is: {}", recipient.to_string());
|
||||
|
||||
let read_data = std::fs::read("examples/dummy_file").unwrap();
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ async fn send_text_with_reply() {
|
||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||
|
||||
let recipient = get_self_address(&mut ws_stream).await;
|
||||
println!("our full address is: {}", recipient);
|
||||
println!("our full address is: {}", recipient.to_string());
|
||||
|
||||
let send_request = json!({
|
||||
"type" : "send",
|
||||
@@ -76,7 +76,7 @@ async fn send_text_without_reply() {
|
||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||
|
||||
let recipient = get_self_address(&mut ws_stream).await;
|
||||
println!("our full address is: {}", recipient);
|
||||
println!("our full address is: {}", recipient.to_string());
|
||||
|
||||
let send_request = json!({
|
||||
"type" : "send",
|
||||
|
||||
@@ -5,7 +5,7 @@ pub(crate) fn config_template() -> &'static str {
|
||||
// While using normal toml marshalling would have been way simpler with less overhead,
|
||||
// I think it's useful to have comments attached to the saved config file to explain behaviour of
|
||||
// particular fields.
|
||||
// Note: any changes to the template must be reflected in the appropriate structs.
|
||||
// Note: any changes to the template must be reflected in the appropriate structs in verloc.
|
||||
r#"
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
@@ -19,10 +19,6 @@ version = '{{ client.version }}'
|
||||
# Human readable ID of this particular client.
|
||||
id = '{{ client.id }}'
|
||||
|
||||
# Indicates whether this client is running in a testnet mode, thus attempting
|
||||
# to claim bandwidth without presenting bandwidth credentials.
|
||||
testnet_mode = {{ client.testnet_mode }}
|
||||
|
||||
# Addresses to APIs running on validator from which the client gets the view of the network.
|
||||
validator_api_urls = [
|
||||
{{#each client.validator_api_urls }}
|
||||
@@ -59,6 +55,12 @@ eth_endpoint = '{{ client.eth_endpoint }}'
|
||||
|
||||
##### additional client config options #####
|
||||
|
||||
# ID of the gateway from which the client should be fetching messages.
|
||||
gateway_id = '{{ client.gateway_id }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ client.gateway_listener }}'
|
||||
|
||||
# A gateway specific, optional, base58 stringified shared key used for
|
||||
# communication with particular gateway.
|
||||
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
||||
@@ -72,17 +74,6 @@ ack_key_file = '{{ client.ack_key_file }}'
|
||||
# Absolute path to the home Nym Clients directory.
|
||||
nym_root_directory = '{{ client.nym_root_directory }}'
|
||||
|
||||
[client.gateway_endpoint]
|
||||
# ID of the gateway from which the client should be fetching messages.
|
||||
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
|
||||
|
||||
# Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
|
||||
|
||||
|
||||
|
||||
##### socket config options #####
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||
use nymsphinx::anonymous_replies::ReplySurb;
|
||||
use nymsphinx::receiver::ReconstructedMessage;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::client::config::{Config, SocketType};
|
||||
use crate::websocket;
|
||||
@@ -43,6 +44,11 @@ pub struct NymClient {
|
||||
/// key filepaths, etc.
|
||||
config: Config,
|
||||
|
||||
/// Tokio runtime used for futures execution.
|
||||
// TODO: JS: Personally I think I prefer the implicit way of using it that we've done with the
|
||||
// gateway.
|
||||
runtime: Runtime,
|
||||
|
||||
/// KeyManager object containing smart pointers to all relevant keys used by the client.
|
||||
key_manager: KeyManager,
|
||||
|
||||
@@ -62,6 +68,7 @@ impl NymClient {
|
||||
let key_manager = KeyManager::load_keys(&pathfinder).expect("failed to load stored keys");
|
||||
|
||||
NymClient {
|
||||
runtime: Runtime::new().unwrap(),
|
||||
config,
|
||||
key_manager,
|
||||
input_tx: None,
|
||||
@@ -87,6 +94,9 @@ impl NymClient {
|
||||
mix_tx: BatchMixMessageSender,
|
||||
) {
|
||||
info!("Starting loop cover traffic stream...");
|
||||
// we need to explicitly enter runtime due to "next_delay: time::delay_for(Default::default())"
|
||||
// set in the constructor which HAS TO be called within context of a tokio runtime
|
||||
let _guard = self.runtime.enter();
|
||||
|
||||
LoopCoverTrafficStream::new(
|
||||
self.key_manager.ack_key(),
|
||||
@@ -99,7 +109,7 @@ impl NymClient {
|
||||
self.as_mix_recipient(),
|
||||
topology_accessor,
|
||||
)
|
||||
.start();
|
||||
.start(self.runtime.handle());
|
||||
}
|
||||
|
||||
fn start_real_traffic_controller(
|
||||
@@ -121,6 +131,10 @@ impl NymClient {
|
||||
);
|
||||
|
||||
info!("Starting real traffic stream...");
|
||||
// we need to explicitly enter runtime due to "next_delay: time::delay_for(Default::default())"
|
||||
// set in the constructor [of OutQueueControl] which HAS TO be called within context of a tokio runtime
|
||||
// When refactoring this restriction should definitely be removed.
|
||||
let _guard = self.runtime.enter();
|
||||
|
||||
RealMessagesController::new(
|
||||
controller_config,
|
||||
@@ -130,7 +144,7 @@ impl NymClient {
|
||||
topology_accessor,
|
||||
reply_key_storage,
|
||||
)
|
||||
.start();
|
||||
.start(self.runtime.handle());
|
||||
}
|
||||
|
||||
// buffer controlling all messages fetched from provider
|
||||
@@ -148,10 +162,10 @@ impl NymClient {
|
||||
mixnet_receiver,
|
||||
reply_key_storage,
|
||||
)
|
||||
.start()
|
||||
.start(self.runtime.handle())
|
||||
}
|
||||
|
||||
async fn start_gateway_client(
|
||||
fn start_gateway_client(
|
||||
&mut self,
|
||||
mixnet_message_sender: MixnetMessageSender,
|
||||
ack_sender: AcknowledgementSender,
|
||||
@@ -160,10 +174,6 @@ impl NymClient {
|
||||
if gateway_id.is_empty() {
|
||||
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
|
||||
}
|
||||
let gateway_owner = self.config.get_base().get_gateway_owner();
|
||||
if gateway_owner.is_empty() {
|
||||
panic!("The owner of the gateway is unknown - did you run `nym-client` init?")
|
||||
}
|
||||
let gateway_address = self.config.get_base().get_gateway_listener();
|
||||
if gateway_address.is_empty() {
|
||||
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
|
||||
@@ -172,45 +182,43 @@ impl NymClient {
|
||||
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
||||
.expect("provided gateway id is invalid!");
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
self.config.get_base().get_validator_api_endpoints(),
|
||||
*self.key_manager.identity_keypair().public_key(),
|
||||
);
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
self.config.get_base().get_eth_endpoint(),
|
||||
self.config.get_base().get_eth_private_key(),
|
||||
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
|
||||
)
|
||||
.expect("Could not create bandwidth controller");
|
||||
self.runtime.block_on(async {
|
||||
#[cfg(feature = "coconut")]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
self.config.get_base().get_validator_api_endpoints(),
|
||||
*self.key_manager.identity_keypair().public_key(),
|
||||
);
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
self.config.get_base().get_eth_endpoint(),
|
||||
self.config.get_base().get_eth_private_key(),
|
||||
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
|
||||
)
|
||||
.expect("Could not create bandwidth controller");
|
||||
|
||||
let mut gateway_client = GatewayClient::new(
|
||||
gateway_address,
|
||||
self.key_manager.identity_keypair(),
|
||||
gateway_identity,
|
||||
gateway_owner,
|
||||
Some(self.key_manager.gateway_shared_key()),
|
||||
mixnet_message_sender,
|
||||
ack_sender,
|
||||
self.config.get_base().get_gateway_response_timeout(),
|
||||
Some(bandwidth_controller),
|
||||
);
|
||||
let mut gateway_client = GatewayClient::new(
|
||||
gateway_address,
|
||||
self.key_manager.identity_keypair(),
|
||||
gateway_identity,
|
||||
Some(self.key_manager.gateway_shared_key()),
|
||||
mixnet_message_sender,
|
||||
ack_sender,
|
||||
self.config.get_base().get_gateway_response_timeout(),
|
||||
Some(bandwidth_controller),
|
||||
);
|
||||
|
||||
if self.config.get_base().get_testnet_mode() {
|
||||
gateway_client.set_testnet_mode(true)
|
||||
}
|
||||
gateway_client
|
||||
.authenticate_and_start()
|
||||
.await
|
||||
.expect("could not authenticate and start up the gateway connection");
|
||||
gateway_client
|
||||
.authenticate_and_start()
|
||||
.await
|
||||
.expect("could not authenticate and start up the gateway connection");
|
||||
|
||||
gateway_client
|
||||
gateway_client
|
||||
})
|
||||
}
|
||||
|
||||
// future responsible for periodically polling directory server and updating
|
||||
// the current global view of topology
|
||||
async fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
||||
fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||
self.config.get_base().get_validator_api_endpoints(),
|
||||
self.config.get_base().get_topology_refresh_rate(),
|
||||
@@ -221,10 +229,13 @@ impl NymClient {
|
||||
// before returning, block entire runtime to refresh the current network view so that any
|
||||
// components depending on topology would see a non-empty view
|
||||
info!("Obtaining initial network topology");
|
||||
topology_refresher.refresh().await;
|
||||
self.runtime.block_on(topology_refresher.refresh());
|
||||
|
||||
// TODO: a slightly more graceful termination here
|
||||
if !topology_refresher.is_topology_routable().await {
|
||||
if !self
|
||||
.runtime
|
||||
.block_on(topology_refresher.is_topology_routable())
|
||||
{
|
||||
panic!(
|
||||
"The current network topology seem to be insufficient to route any packets through\
|
||||
- check if enough nodes and a gateway are online"
|
||||
@@ -232,7 +243,7 @@ impl NymClient {
|
||||
}
|
||||
|
||||
info!("Starting topology refresher...");
|
||||
topology_refresher.start();
|
||||
topology_refresher.start(self.runtime.handle());
|
||||
}
|
||||
|
||||
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
|
||||
@@ -245,7 +256,7 @@ impl NymClient {
|
||||
gateway_client: GatewayClient,
|
||||
) {
|
||||
info!("Starting mix traffic controller...");
|
||||
MixTrafficController::new(mix_rx, gateway_client).start();
|
||||
MixTrafficController::new(mix_rx, gateway_client).start(self.runtime.handle());
|
||||
}
|
||||
|
||||
fn start_websocket_listener(
|
||||
@@ -258,7 +269,8 @@ impl NymClient {
|
||||
let websocket_handler =
|
||||
websocket::Handler::new(msg_input, buffer_requester, self.as_mix_recipient());
|
||||
|
||||
websocket::Listener::new(self.config.get_listening_port()).start(websocket_handler);
|
||||
websocket::Listener::new(self.config.get_listening_port())
|
||||
.start(self.runtime.handle(), websocket_handler);
|
||||
}
|
||||
|
||||
/// EXPERIMENTAL DIRECT RUST API
|
||||
@@ -305,9 +317,9 @@ impl NymClient {
|
||||
}
|
||||
|
||||
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
|
||||
pub async fn run_forever(&mut self) {
|
||||
self.start().await;
|
||||
if let Err(e) = tokio::signal::ctrl_c().await {
|
||||
pub fn run_forever(&mut self) {
|
||||
self.start();
|
||||
if let Err(e) = self.runtime.block_on(tokio::signal::ctrl_c()) {
|
||||
error!(
|
||||
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
|
||||
e
|
||||
@@ -319,7 +331,7 @@ impl NymClient {
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn start(&mut self) {
|
||||
pub fn start(&mut self) {
|
||||
info!("Starting nym client");
|
||||
// channels for inter-component communication
|
||||
// TODO: make the channels be internally created by the relevant components
|
||||
@@ -351,17 +363,14 @@ impl NymClient {
|
||||
|
||||
// the components are started in very specific order. Unless you know what you are doing,
|
||||
// do not change that.
|
||||
self.start_topology_refresher(shared_topology_accessor.clone())
|
||||
.await;
|
||||
self.start_topology_refresher(shared_topology_accessor.clone());
|
||||
self.start_received_messages_buffer_controller(
|
||||
received_buffer_request_receiver,
|
||||
mixnet_messages_receiver,
|
||||
reply_key_storage.clone(),
|
||||
);
|
||||
|
||||
let gateway_client = self
|
||||
.start_gateway_client(mixnet_messages_sender, ack_sender)
|
||||
.await;
|
||||
let gateway_client = self.start_gateway_client(mixnet_messages_sender, ack_sender);
|
||||
|
||||
self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
|
||||
self.start_real_traffic_controller(
|
||||
|
||||
@@ -31,12 +31,6 @@ use url::Url;
|
||||
|
||||
use crate::client::config::Config;
|
||||
use crate::commands::override_config;
|
||||
#[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,
|
||||
};
|
||||
|
||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
let app = App::new("init")
|
||||
@@ -72,28 +66,18 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
.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")
|
||||
.arg(Arg::with_name("eth_endpoint")
|
||||
.long("eth_endpoint")
|
||||
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
|
||||
.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")
|
||||
.arg(Arg::with_name("eth_private_key")
|
||||
.long("eth_private_key")
|
||||
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
|
||||
.takes_value(true)
|
||||
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
|
||||
.required(true)
|
||||
);
|
||||
.required(true));
|
||||
|
||||
app
|
||||
}
|
||||
@@ -136,7 +120,6 @@ async fn register_with_gateway(
|
||||
let mut gateway_client = GatewayClient::new_init(
|
||||
gateway.clients_address(),
|
||||
gateway.identity_key,
|
||||
gateway.owner.clone(),
|
||||
our_identity.clone(),
|
||||
timeout,
|
||||
);
|
||||
@@ -220,7 +203,7 @@ fn show_address(config: &Config) {
|
||||
println!("\nThe address of this client is: {}", client_recipient);
|
||||
}
|
||||
|
||||
pub async fn execute(matches: ArgMatches<'static>) {
|
||||
pub fn execute(matches: &ArgMatches) {
|
||||
println!("Initialising client...");
|
||||
|
||||
let id = matches.value_of("id").unwrap(); // required for now
|
||||
@@ -238,7 +221,7 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
|
||||
// TODO: ideally that should be the last thing that's being done to config.
|
||||
// However, we are later further overriding it with gateway id
|
||||
config = override_config(config, &matches);
|
||||
config = override_config(config, matches);
|
||||
if matches.is_present("fastmode") {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
@@ -251,19 +234,26 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
|
||||
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;
|
||||
let registration_fut = async {
|
||||
let gate_details = gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_id(gate_details.identity_key.to_base58_string());
|
||||
let shared_keys =
|
||||
register_with_gateway(&gate_details, key_manager.identity_keypair()).await;
|
||||
(shared_keys, gate_details.clients_address())
|
||||
};
|
||||
|
||||
config.get_base_mut().with_gateway_endpoint(
|
||||
gateway_details.identity_key.to_base58_string(),
|
||||
gateway_details.owner.clone(),
|
||||
gateway_details.clients_address(),
|
||||
);
|
||||
// TODO: is there perhaps a way to make it work without having to spawn entire runtime?
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
let (shared_keys, gateway_listener) = rt.block_on(registration_fut);
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_listener(gateway_listener);
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
|
||||
@@ -5,18 +5,6 @@ use crate::client::config::{Config, SocketType};
|
||||
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 run;
|
||||
pub(crate) mod upgrade;
|
||||
@@ -32,7 +20,7 @@ fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config {
|
||||
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
|
||||
if let Some(raw_validators) = matches.value_of("validators") {
|
||||
config
|
||||
.get_base_mut()
|
||||
@@ -56,24 +44,12 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
if let Some(eth_endpoint) = matches.value_of(ETH_ENDPOINT_ARG_NAME) {
|
||||
if let Some(eth_endpoint) = matches.value_of("eth_endpoint") {
|
||||
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) {
|
||||
if let Some(eth_private_key) = matches.value_of("eth_private_key") {
|
||||
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
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
use crate::client::config::Config;
|
||||
use crate::client::NymClient;
|
||||
use crate::commands::override_config;
|
||||
#[cfg(feature = "eth")]
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
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 log::*;
|
||||
@@ -42,22 +39,15 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
.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")
|
||||
.arg(Arg::with_name("eth_endpoint")
|
||||
.long("eth_endpoint")
|
||||
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
|
||||
.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")
|
||||
.arg(Arg::with_name("eth_private_key")
|
||||
.long("eth_private_key")
|
||||
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
|
||||
.takes_value(true));
|
||||
|
||||
app
|
||||
@@ -82,7 +72,7 @@ fn version_check(cfg: &Config) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(matches: ArgMatches<'static>) {
|
||||
pub fn execute(matches: &ArgMatches) {
|
||||
let id = matches.value_of("id").unwrap();
|
||||
|
||||
let mut config = match Config::load_from_file(Some(id)) {
|
||||
@@ -93,12 +83,12 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
}
|
||||
};
|
||||
|
||||
config = override_config(config, &matches);
|
||||
config = override_config(config, matches);
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("failed the local version check");
|
||||
return;
|
||||
}
|
||||
|
||||
NymClient::new(config).run_forever().await;
|
||||
NymClient::new(config).run_forever();
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ fn parse_package_version() -> Version {
|
||||
|
||||
fn minor_0_12_upgrade(
|
||||
mut config: Config,
|
||||
_matches: &ArgMatches<'_>,
|
||||
_matches: &ArgMatches,
|
||||
config_version: &Version,
|
||||
package_version: &Version,
|
||||
) -> Config {
|
||||
@@ -131,7 +131,7 @@ fn minor_0_12_upgrade(
|
||||
config
|
||||
}
|
||||
|
||||
fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Version) {
|
||||
fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version) {
|
||||
loop {
|
||||
let config_version = parse_config_version(&config);
|
||||
|
||||
@@ -151,7 +151,7 @@ fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Ver
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(matches: &ArgMatches<'_>) {
|
||||
pub fn execute(matches: &ArgMatches) {
|
||||
let package_version = parse_package_version();
|
||||
|
||||
let id = matches.value_of("id").unwrap();
|
||||
|
||||
@@ -7,8 +7,7 @@ pub mod client;
|
||||
pub mod commands;
|
||||
pub mod websocket;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
fn main() {
|
||||
dotenv::dotenv().ok();
|
||||
setup_logging();
|
||||
println!("{}", banner());
|
||||
@@ -23,13 +22,13 @@ async fn main() {
|
||||
.subcommand(commands::upgrade::command_args())
|
||||
.get_matches();
|
||||
|
||||
execute(arg_matches).await;
|
||||
execute(arg_matches);
|
||||
}
|
||||
|
||||
async fn execute(matches: ArgMatches<'static>) {
|
||||
fn execute(matches: ArgMatches) {
|
||||
match matches.subcommand() {
|
||||
("init", Some(m)) => commands::init::execute(m.clone()).await,
|
||||
("run", Some(m)) => commands::run::execute(m.clone()).await,
|
||||
("init", Some(m)) => commands::init::execute(m),
|
||||
("run", Some(m)) => commands::run::execute(m),
|
||||
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
||||
_ => println!("{}", usage()),
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use super::handler::Handler;
|
||||
use log::*;
|
||||
use std::{net::SocketAddr, process, sync::Arc};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::runtime;
|
||||
use tokio::{sync::Notify, task::JoinHandle};
|
||||
|
||||
enum State {
|
||||
@@ -86,9 +87,9 @@ impl Listener {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start(mut self, handler: Handler) -> JoinHandle<()> {
|
||||
pub(crate) fn start(mut self, rt_handle: &runtime::Handle, handler: Handler) -> JoinHandle<()> {
|
||||
info!("Running websocket on {:?}", self.address.to_string());
|
||||
|
||||
tokio::spawn(async move { self.run(handler).await })
|
||||
rt_handle.spawn(async move { self.run(handler).await })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "websocket-requests"
|
||||
version = "0.1.0"
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "0.12.1"
|
||||
version = "0.11.0"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
rust-version = "1.56"
|
||||
|
||||
[lib]
|
||||
@@ -43,7 +43,6 @@ network-defaults = { path = "../../common/network-defaults" }
|
||||
|
||||
[features]
|
||||
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
|
||||
eth = []
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
@@ -5,7 +5,7 @@ pub(crate) fn config_template() -> &'static str {
|
||||
// While using normal toml marshalling would have been way simpler with less overhead,
|
||||
// I think it's useful to have comments attached to the saved config file to explain behaviour of
|
||||
// particular fields.
|
||||
// Note: any changes to the template must be reflected in the appropriate structs.
|
||||
// Note: any changes to the template must be reflected in the appropriate structs in verloc.
|
||||
r#"
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
@@ -19,10 +19,6 @@ version = '{{ client.version }}'
|
||||
# Human readable ID of this particular client.
|
||||
id = '{{ client.id }}'
|
||||
|
||||
# Indicates whether this client is running in a testnet mode, thus attempting
|
||||
# to claim bandwidth without presenting bandwidth credentials.
|
||||
testnet_mode = {{ client.testnet_mode }}
|
||||
|
||||
# Addresses to APIs running on validator from which the client gets the view of the network.
|
||||
validator_api_urls = [
|
||||
{{#each client.validator_api_urls }}
|
||||
@@ -59,6 +55,12 @@ eth_endpoint = '{{ client.eth_endpoint }}'
|
||||
|
||||
##### additional client config options #####
|
||||
|
||||
# ID of the gateway from which the client should be fetching messages.
|
||||
gateway_id = '{{ client.gateway_id }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ client.gateway_listener }}'
|
||||
|
||||
# A gateway specific, optional, base58 stringified shared key used for
|
||||
# communication with particular gateway.
|
||||
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
||||
@@ -66,22 +68,12 @@ gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
||||
# Path to file containing key used for encrypting and decrypting the content of an
|
||||
# acknowledgement so that nobody besides the client knows which packet it refers to.
|
||||
ack_key_file = '{{ client.ack_key_file }}'
|
||||
|
||||
|
||||
##### advanced configuration options #####
|
||||
|
||||
# Absolute path to the home Nym Clients directory.
|
||||
nym_root_directory = '{{ client.nym_root_directory }}'
|
||||
|
||||
[client.gateway_endpoint]
|
||||
# ID of the gateway from which the client should be fetching messages.
|
||||
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
|
||||
|
||||
# Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
|
||||
|
||||
|
||||
##### socket config options #####
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ use gateway_client::{
|
||||
use log::*;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::client::config::Config;
|
||||
use crate::socks::{
|
||||
@@ -42,6 +43,11 @@ pub struct NymClient {
|
||||
/// key filepaths, etc.
|
||||
config: Config,
|
||||
|
||||
/// Tokio runtime used for futures execution.
|
||||
// TODO: JS: Personally I think I prefer the implicit way of using it that we've done with the
|
||||
// gateway.
|
||||
runtime: Runtime,
|
||||
|
||||
/// KeyManager object containing smart pointers to all relevant keys used by the client.
|
||||
key_manager: KeyManager,
|
||||
}
|
||||
@@ -52,6 +58,7 @@ impl NymClient {
|
||||
let key_manager = KeyManager::load_keys(&pathfinder).expect("failed to load stored keys");
|
||||
|
||||
NymClient {
|
||||
runtime: Runtime::new().unwrap(),
|
||||
config,
|
||||
key_manager,
|
||||
}
|
||||
@@ -75,6 +82,9 @@ impl NymClient {
|
||||
mix_tx: BatchMixMessageSender,
|
||||
) {
|
||||
info!("Starting loop cover traffic stream...");
|
||||
// we need to explicitly enter runtime due to "next_delay: time::delay_for(Default::default())"
|
||||
// set in the constructor which HAS TO be called within context of a tokio runtime
|
||||
let _guard = self.runtime.enter();
|
||||
|
||||
LoopCoverTrafficStream::new(
|
||||
self.key_manager.ack_key(),
|
||||
@@ -87,7 +97,7 @@ impl NymClient {
|
||||
self.as_mix_recipient(),
|
||||
topology_accessor,
|
||||
)
|
||||
.start();
|
||||
.start(self.runtime.handle());
|
||||
}
|
||||
|
||||
fn start_real_traffic_controller(
|
||||
@@ -109,6 +119,10 @@ impl NymClient {
|
||||
);
|
||||
|
||||
info!("Starting real traffic stream...");
|
||||
// we need to explicitly enter runtime due to "next_delay: time::delay_for(Default::default())"
|
||||
// set in the constructor [of OutQueueControl] which HAS TO be called within context of a tokio runtime
|
||||
// When refactoring this restriction should definitely be removed.
|
||||
let _guard = self.runtime.enter();
|
||||
|
||||
RealMessagesController::new(
|
||||
controller_config,
|
||||
@@ -118,7 +132,7 @@ impl NymClient {
|
||||
topology_accessor,
|
||||
reply_key_storage,
|
||||
)
|
||||
.start();
|
||||
.start(self.runtime.handle());
|
||||
}
|
||||
|
||||
// buffer controlling all messages fetched from provider
|
||||
@@ -136,10 +150,10 @@ impl NymClient {
|
||||
mixnet_receiver,
|
||||
reply_key_storage,
|
||||
)
|
||||
.start()
|
||||
.start(self.runtime.handle())
|
||||
}
|
||||
|
||||
async fn start_gateway_client(
|
||||
fn start_gateway_client(
|
||||
&mut self,
|
||||
mixnet_message_sender: MixnetMessageSender,
|
||||
ack_sender: AcknowledgementSender,
|
||||
@@ -148,10 +162,6 @@ impl NymClient {
|
||||
if gateway_id.is_empty() {
|
||||
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
|
||||
}
|
||||
let gateway_owner = self.config.get_base().get_gateway_owner();
|
||||
if gateway_owner.is_empty() {
|
||||
panic!("The owner of the gateway is unknown - did you run `nym-client` init?")
|
||||
}
|
||||
let gateway_address = self.config.get_base().get_gateway_listener();
|
||||
if gateway_address.is_empty() {
|
||||
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
|
||||
@@ -160,45 +170,43 @@ impl NymClient {
|
||||
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
||||
.expect("provided gateway id is invalid!");
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
self.config.get_base().get_validator_api_endpoints(),
|
||||
*self.key_manager.identity_keypair().public_key(),
|
||||
);
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
self.config.get_base().get_eth_endpoint(),
|
||||
self.config.get_base().get_eth_private_key(),
|
||||
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
|
||||
)
|
||||
.expect("Could not create bandwidth controller");
|
||||
self.runtime.block_on(async {
|
||||
#[cfg(feature = "coconut")]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
self.config.get_base().get_validator_api_endpoints(),
|
||||
*self.key_manager.identity_keypair().public_key(),
|
||||
);
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
self.config.get_base().get_eth_endpoint(),
|
||||
self.config.get_base().get_eth_private_key(),
|
||||
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
|
||||
)
|
||||
.expect("Could not create bandwidth controller");
|
||||
|
||||
let mut gateway_client = GatewayClient::new(
|
||||
gateway_address,
|
||||
self.key_manager.identity_keypair(),
|
||||
gateway_identity,
|
||||
gateway_owner,
|
||||
Some(self.key_manager.gateway_shared_key()),
|
||||
mixnet_message_sender,
|
||||
ack_sender,
|
||||
self.config.get_base().get_gateway_response_timeout(),
|
||||
Some(bandwidth_controller),
|
||||
);
|
||||
let mut gateway_client = GatewayClient::new(
|
||||
gateway_address,
|
||||
self.key_manager.identity_keypair(),
|
||||
gateway_identity,
|
||||
Some(self.key_manager.gateway_shared_key()),
|
||||
mixnet_message_sender,
|
||||
ack_sender,
|
||||
self.config.get_base().get_gateway_response_timeout(),
|
||||
Some(bandwidth_controller),
|
||||
);
|
||||
|
||||
if self.config.get_base().get_testnet_mode() {
|
||||
gateway_client.set_testnet_mode(true)
|
||||
}
|
||||
gateway_client
|
||||
.authenticate_and_start()
|
||||
.await
|
||||
.expect("could not authenticate and start up the gateway connection");
|
||||
gateway_client
|
||||
.authenticate_and_start()
|
||||
.await
|
||||
.expect("could not authenticate and start up the gateway connection");
|
||||
|
||||
gateway_client
|
||||
gateway_client
|
||||
})
|
||||
}
|
||||
|
||||
// future responsible for periodically polling directory server and updating
|
||||
// the current global view of topology
|
||||
async fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
||||
fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||
self.config.get_base().get_validator_api_endpoints(),
|
||||
self.config.get_base().get_topology_refresh_rate(),
|
||||
@@ -209,10 +217,13 @@ impl NymClient {
|
||||
// before returning, block entire runtime to refresh the current network view so that any
|
||||
// components depending on topology would see a non-empty view
|
||||
info!("Obtaining initial network topology");
|
||||
topology_refresher.refresh().await;
|
||||
self.runtime.block_on(topology_refresher.refresh());
|
||||
|
||||
// TODO: a slightly more graceful termination here
|
||||
if !topology_refresher.is_topology_routable().await {
|
||||
if !self
|
||||
.runtime
|
||||
.block_on(topology_refresher.is_topology_routable())
|
||||
{
|
||||
panic!(
|
||||
"The current network topology seem to be insufficient to route any packets through\
|
||||
- check if enough nodes and a gateway are online"
|
||||
@@ -220,7 +231,7 @@ impl NymClient {
|
||||
}
|
||||
|
||||
info!("Starting topology refresher...");
|
||||
topology_refresher.start();
|
||||
topology_refresher.start(self.runtime.handle());
|
||||
}
|
||||
|
||||
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
|
||||
@@ -233,7 +244,7 @@ impl NymClient {
|
||||
gateway_client: GatewayClient,
|
||||
) {
|
||||
info!("Starting mix traffic controller...");
|
||||
MixTrafficController::new(mix_rx, gateway_client).start();
|
||||
MixTrafficController::new(mix_rx, gateway_client).start(self.runtime.handle());
|
||||
}
|
||||
|
||||
fn start_socks5_listener(
|
||||
@@ -252,13 +263,14 @@ impl NymClient {
|
||||
self.config.get_provider_mix_address(),
|
||||
self.as_mix_recipient(),
|
||||
);
|
||||
tokio::spawn(async move { sphinx_socks.serve(msg_input, buffer_requester).await });
|
||||
self.runtime
|
||||
.spawn(async move { sphinx_socks.serve(msg_input, buffer_requester).await });
|
||||
}
|
||||
|
||||
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
|
||||
pub async fn run_forever(&mut self) {
|
||||
self.start().await;
|
||||
if let Err(e) = tokio::signal::ctrl_c().await {
|
||||
pub fn run_forever(&mut self) {
|
||||
self.start();
|
||||
if let Err(e) = self.runtime.block_on(tokio::signal::ctrl_c()) {
|
||||
error!(
|
||||
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
|
||||
e
|
||||
@@ -270,7 +282,7 @@ impl NymClient {
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn start(&mut self) {
|
||||
pub fn start(&mut self) {
|
||||
info!("Starting nym client");
|
||||
// channels for inter-component communication
|
||||
// TODO: make the channels be internally created by the relevant components
|
||||
@@ -302,17 +314,14 @@ impl NymClient {
|
||||
|
||||
// the components are started in very specific order. Unless you know what you are doing,
|
||||
// do not change that.
|
||||
self.start_topology_refresher(shared_topology_accessor.clone())
|
||||
.await;
|
||||
self.start_topology_refresher(shared_topology_accessor.clone());
|
||||
self.start_received_messages_buffer_controller(
|
||||
received_buffer_request_receiver,
|
||||
mixnet_messages_receiver,
|
||||
reply_key_storage.clone(),
|
||||
);
|
||||
|
||||
let gateway_client = self
|
||||
.start_gateway_client(mixnet_messages_sender, ack_sender)
|
||||
.await;
|
||||
let gateway_client = self.start_gateway_client(mixnet_messages_sender, ack_sender);
|
||||
|
||||
self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
|
||||
self.start_real_traffic_controller(
|
||||
|
||||
@@ -29,12 +29,6 @@ use url::Url;
|
||||
|
||||
use crate::client::config::Config;
|
||||
use crate::commands::override_config;
|
||||
#[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,
|
||||
};
|
||||
|
||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
let app = App::new("init")
|
||||
@@ -72,28 +66,18 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
.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")
|
||||
.arg(Arg::with_name("eth_endpoint")
|
||||
.long("eth_endpoint")
|
||||
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
|
||||
.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")
|
||||
.arg(Arg::with_name("eth_private_key")
|
||||
.long("eth_private_key")
|
||||
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
|
||||
.takes_value(true)
|
||||
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
|
||||
.required(true)
|
||||
);
|
||||
.required(true));
|
||||
|
||||
app
|
||||
}
|
||||
@@ -136,7 +120,6 @@ async fn register_with_gateway(
|
||||
let mut gateway_client = GatewayClient::new_init(
|
||||
gateway.clients_address(),
|
||||
gateway.identity_key,
|
||||
gateway.owner.clone(),
|
||||
our_identity.clone(),
|
||||
timeout,
|
||||
);
|
||||
@@ -220,7 +203,7 @@ fn show_address(config: &Config) {
|
||||
println!("\nThe address of this client is: {}", client_recipient);
|
||||
}
|
||||
|
||||
pub async fn execute(matches: ArgMatches<'static>) {
|
||||
pub fn execute(matches: &ArgMatches) {
|
||||
println!("Initialising client...");
|
||||
|
||||
let id = matches.value_of("id").unwrap(); // required for now
|
||||
@@ -239,7 +222,7 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
|
||||
// TODO: ideally that should be the last thing that's being done to config.
|
||||
// However, we are later further overriding it with gateway id
|
||||
config = override_config(config, &matches);
|
||||
config = override_config(config, matches);
|
||||
if matches.is_present("fastmode") {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
@@ -252,19 +235,26 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
|
||||
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;
|
||||
let registration_fut = async {
|
||||
let gate_details = gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_id(gate_details.identity_key.to_base58_string());
|
||||
let shared_keys =
|
||||
register_with_gateway(&gate_details, key_manager.identity_keypair()).await;
|
||||
(shared_keys, gate_details.clients_address())
|
||||
};
|
||||
|
||||
config.get_base_mut().with_gateway_endpoint(
|
||||
gateway_details.identity_key.to_base58_string(),
|
||||
gateway_details.owner.clone(),
|
||||
gateway_details.clients_address(),
|
||||
);
|
||||
// TODO: is there perhaps a way to make it work without having to spawn entire runtime?
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
let (shared_keys, gateway_listener) = rt.block_on(registration_fut);
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_listener(gateway_listener);
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
|
||||
@@ -9,18 +9,6 @@ pub(crate) mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
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";
|
||||
|
||||
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
@@ -32,7 +20,7 @@ fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config {
|
||||
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
|
||||
if let Some(raw_validators) = matches.value_of("validators") {
|
||||
config
|
||||
.get_base_mut()
|
||||
@@ -52,24 +40,12 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
if let Some(eth_endpoint) = matches.value_of(ETH_ENDPOINT_ARG_NAME) {
|
||||
if let Some(eth_endpoint) = matches.value_of("eth_endpoint") {
|
||||
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) {
|
||||
if let Some(eth_private_key) = matches.value_of("eth_private_key") {
|
||||
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
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
use crate::client::config::Config;
|
||||
use crate::client::NymClient;
|
||||
use crate::commands::override_config;
|
||||
#[cfg(feature = "eth")]
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
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 log::*;
|
||||
@@ -48,22 +45,15 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
.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")
|
||||
.arg(Arg::with_name("eth_endpoint")
|
||||
.long("eth_endpoint")
|
||||
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
|
||||
.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")
|
||||
.arg(Arg::with_name("eth_private_key")
|
||||
.long("eth_private_key")
|
||||
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
|
||||
.takes_value(true));
|
||||
|
||||
app
|
||||
@@ -88,7 +78,7 @@ fn version_check(cfg: &Config) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(matches: ArgMatches<'static>) {
|
||||
pub fn execute(matches: &ArgMatches) {
|
||||
let id = matches.value_of("id").unwrap();
|
||||
|
||||
let mut config = match Config::load_from_file(Some(id)) {
|
||||
@@ -99,12 +89,12 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
}
|
||||
};
|
||||
|
||||
config = override_config(config, &matches);
|
||||
config = override_config(config, matches);
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("failed the local version check");
|
||||
return;
|
||||
}
|
||||
|
||||
NymClient::new(config).run_forever().await;
|
||||
NymClient::new(config).run_forever();
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ fn parse_package_version() -> Version {
|
||||
|
||||
fn minor_0_12_upgrade(
|
||||
mut config: Config,
|
||||
_matches: &ArgMatches<'_>,
|
||||
_matches: &ArgMatches,
|
||||
config_version: &Version,
|
||||
package_version: &Version,
|
||||
) -> Config {
|
||||
@@ -131,7 +131,7 @@ fn minor_0_12_upgrade(
|
||||
config
|
||||
}
|
||||
|
||||
fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Version) {
|
||||
fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version) {
|
||||
loop {
|
||||
let config_version = parse_config_version(&config);
|
||||
|
||||
@@ -151,7 +151,7 @@ fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Ver
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(matches: &ArgMatches<'_>) {
|
||||
pub fn execute(matches: &ArgMatches) {
|
||||
let package_version = parse_package_version();
|
||||
|
||||
let id = matches.value_of("id").unwrap();
|
||||
|
||||
@@ -7,8 +7,7 @@ pub mod client;
|
||||
mod commands;
|
||||
pub mod socks;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
fn main() {
|
||||
dotenv::dotenv().ok();
|
||||
setup_logging();
|
||||
println!("{}", banner());
|
||||
@@ -23,13 +22,13 @@ async fn main() {
|
||||
.subcommand(commands::upgrade::command_args())
|
||||
.get_matches();
|
||||
|
||||
execute(arg_matches).await;
|
||||
execute(arg_matches);
|
||||
}
|
||||
|
||||
async fn execute(matches: ArgMatches<'static>) {
|
||||
fn execute(matches: ArgMatches) {
|
||||
match matches.subcommand() {
|
||||
("init", Some(m)) => commands::init::execute(m.clone()).await,
|
||||
("run", Some(m)) => commands::run::execute(m.clone()).await,
|
||||
("init", Some(m)) => commands::init::execute(m),
|
||||
("run", Some(m)) => commands::run::execute(m),
|
||||
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
||||
_ => println!("{}", usage()),
|
||||
}
|
||||
|
||||
Generated
+7
-17
@@ -11,7 +11,6 @@
|
||||
"@tauri-apps/api": "^1.0.0-beta.4",
|
||||
"compression": "^1.7.1",
|
||||
"polka": "next",
|
||||
"qrious": "^4.0.2",
|
||||
"sirv": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -6759,11 +6758,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/qrious": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/qrious/-/qrious-4.0.2.tgz",
|
||||
"integrity": "sha512-xWPJIrK1zu5Ypn898fBp8RHkT/9ibquV2Kv24S/JY9VYEhMBMKur1gHVsOiNUh7PHP9uCgejjpZUHUIXXKoU/g=="
|
||||
},
|
||||
"node_modules/query-string": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
||||
@@ -7585,10 +7579,11 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/simple-get": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
|
||||
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decompress-response": "^4.2.0",
|
||||
"once": "^1.3.1",
|
||||
@@ -13479,11 +13474,6 @@
|
||||
"escape-goat": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"qrious": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/qrious/-/qrious-4.0.2.tgz",
|
||||
"integrity": "sha512-xWPJIrK1zu5Ypn898fBp8RHkT/9ibquV2Kv24S/JY9VYEhMBMKur1gHVsOiNUh7PHP9uCgejjpZUHUIXXKoU/g=="
|
||||
},
|
||||
"query-string": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
||||
@@ -14046,9 +14036,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
|
||||
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"decompress-response": "^4.2.0",
|
||||
|
||||
@@ -6,7 +6,7 @@ authors = ["you"]
|
||||
license = ""
|
||||
repository = ""
|
||||
default-run = "app"
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
build = "src/build.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -1813,7 +1813,7 @@ decompress-response@^3.2.0, decompress-response@^3.3.0:
|
||||
|
||||
decompress-response@^4.2.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986"
|
||||
resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz"
|
||||
integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
|
||||
dependencies:
|
||||
mimic-response "^2.0.0"
|
||||
@@ -3130,7 +3130,7 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
|
||||
|
||||
mimic-response@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
|
||||
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz"
|
||||
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
|
||||
|
||||
mimic-response@^3.1.0:
|
||||
@@ -4056,13 +4056,13 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
|
||||
simple-concat@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
|
||||
resolved "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz"
|
||||
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
|
||||
|
||||
simple-get@^3.0.3, simple-get@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55"
|
||||
integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz"
|
||||
integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==
|
||||
dependencies:
|
||||
decompress-response "^4.2.0"
|
||||
once "^1.3.1"
|
||||
@@ -4673,7 +4673,7 @@ wrap-ansi@^7.0.0:
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
write-file-atomic@^3.0.0:
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# CLIENT INIT
|
||||
NYMD_URL=https://sandbox-validator.nymtech.net
|
||||
VALIDATOR_API=https://sandbox-validator.nymtech.net/api
|
||||
MIXNET_CONTRACT=nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j
|
||||
VESTING_CONTRACT=nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s
|
||||
CURRENCY_PREFIX=nymt
|
||||
CHAIN_ID=nym-sandbox
|
||||
|
||||
# USER DETAILS
|
||||
USER_MNEMONIC=
|
||||
USER_WALLET_ADDRESS=
|
||||
|
||||
# MIXNODE DETAILS
|
||||
MIXNODE_IDENTITY=
|
||||
MIXNODE_SPHINX_KEY=
|
||||
MIXNODE_SIGNATURE=
|
||||
MIXNODE_HOST="1.1.1.1"
|
||||
MIXNODE_VERSION="0.12.1"
|
||||
|
||||
# GATEWAY DETAILS
|
||||
GATEWAY_IDENTITY=
|
||||
GATEWAY_SPHINX=
|
||||
GATEAWAY_LOCATION=
|
||||
GATEWAY_HOST="1.1.1.1"
|
||||
GATEWAY_VERSION="0.12.1"
|
||||
@@ -3,20 +3,26 @@ Nym Validator Client
|
||||
|
||||
A TypeScript client for interacting with CosmWasm smart contracts in Nym validators.
|
||||
|
||||
Running examples
|
||||
-----------------
|
||||
|
||||
With the code checked out, `cd examples`. This folder contains runnable example code that will set up a blockchain and allow you to interact with it through the client.
|
||||
|
||||
Running tests
|
||||
-------------
|
||||
|
||||
The tests will be separated into three categories: unit, integration and mock.
|
||||
|
||||
Currently the command to run all tests:
|
||||
|
||||
```
|
||||
npm test
|
||||
```
|
||||
|
||||
The tests require `.env.example` being renamed to `.env`. The variables and their values for these tests are currently pointing to the `nym-sandbox` environment.
|
||||
You can also trigger test execution with a test watcher. I don't have the centuries of life left to me that are needed to fight through the arcana of wiring up a working TypeScript mocha triggered execution setup, so for now my Cargo-based hack is:
|
||||
|
||||
`Tests are still in development` - the test libary is `jest` and the test script will execute currently with: `--coverage --verbosity false`
|
||||
|
||||
```
|
||||
cargo watch -s "cd clients/validator && npm test"
|
||||
```
|
||||
|
||||
It's ugly but works fine if you have Cargo installed. TypeScript setup help happily accepted here.
|
||||
|
||||
Generating Documentation
|
||||
------------------------
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
||||
setupFiles: ["dotenv/config"],
|
||||
testTimeout: 20000
|
||||
};
|
||||
@@ -6,7 +6,9 @@
|
||||
"main": "./dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"test": "jest --verbose false",
|
||||
"build": "tsc",
|
||||
"test": "ts-mocha tests/**/*.test.ts",
|
||||
"coverage": "nyc npm test",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint src --fix",
|
||||
"docs": "typedoc --out docs src/index.ts"
|
||||
@@ -19,21 +21,27 @@
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.4.0",
|
||||
"@types/chai": "^4.2.15",
|
||||
"@types/expect": "^24.3.0",
|
||||
"@types/mocha": "^8.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.7.0",
|
||||
"@typescript-eslint/parser": "^5.7.0",
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^7.18.0",
|
||||
"eslint-config-airbnb": "^19.0.2",
|
||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-import-resolver-root-import": "^1.0.4",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-mocha": "^10.0.3",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.4.5",
|
||||
"mocha": "^8.2.1",
|
||||
"moq.ts": "^7.2.0",
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "^2.5.1",
|
||||
"ts-jest": "^27.1.2",
|
||||
"ts-mocha": "^8.0.0",
|
||||
"typedoc": "^0.20.27",
|
||||
"typescript": "^4.5.4"
|
||||
"typescript": "^4.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cosmjs/cosmwasm-stargate": "^0.27.0-rc2",
|
||||
@@ -43,8 +51,6 @@
|
||||
"@cosmjs/stargate": "^0.27.0-rc2",
|
||||
"@cosmjs/tendermint-rpc": "^0.27.0-rc2",
|
||||
"axios": "^0.21.1",
|
||||
"cosmjs-types": "^0.4.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"moq.ts": "^7.3.4"
|
||||
"cosmjs-types": "^0.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,8 +180,8 @@ export default class ValidatorClient implements INymClient {
|
||||
return this.client.getSybilResistancePercent(this.mixnetContract);
|
||||
}
|
||||
|
||||
public async getIntervalRewardPercent(): Promise<number> {
|
||||
return this.client.getIntervalRewardPercent(this.mixnetContract);
|
||||
public async getEpochRewardPercent(): Promise<number> {
|
||||
return this.client.getEpochRewardPercent(this.mixnetContract);
|
||||
}
|
||||
|
||||
public async getAllNymdMixnodes(): Promise<MixNodeBond[]> {
|
||||
@@ -433,14 +433,6 @@ export default class ValidatorClient implements INymClient {
|
||||
return (this.client as ISigningClient).undelegateFromMixNode(this.mixnetContract, mixIdentity, fee, memo);
|
||||
}
|
||||
|
||||
public async updateMixnodeConfig(
|
||||
mixIdentity: string,
|
||||
fee: StdFee | 'auto' | number,
|
||||
profitPercentage: number,
|
||||
): Promise<ExecuteResult> {
|
||||
return (this.client as ISigningClient).updateMixnodeConfig(this.mixnetContract, mixIdentity, profitPercentage, fee);
|
||||
}
|
||||
|
||||
public async updateContractStateParams(
|
||||
newParams: ContractStateParams,
|
||||
fee?: StdFee | 'auto' | number,
|
||||
@@ -449,4 +441,4 @@ export default class ValidatorClient implements INymClient {
|
||||
this.assertSigning();
|
||||
return (this.client as ISigningClient).updateContractStateParams(this.mixnetContract, newParams, fee, memo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
PagedGatewayResponse,
|
||||
PagedMixDelegationsResponse,
|
||||
PagedMixnodeResponse,
|
||||
RewardingIntervalResponse,
|
||||
RewardingStatus,
|
||||
} from './types';
|
||||
|
||||
@@ -78,6 +79,12 @@ export default class NymdQuerier implements INymdQuery {
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse> {
|
||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||
current_rewarding_interval: {},
|
||||
});
|
||||
}
|
||||
|
||||
getAllNetworkDelegationsPaged(
|
||||
mixnetContractAddress: string,
|
||||
limit?: number,
|
||||
@@ -148,9 +155,9 @@ export default class NymdQuerier implements INymdQuery {
|
||||
});
|
||||
}
|
||||
|
||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
getEpochRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||
get_interval_reward_percent: {},
|
||||
get_epoch_reward_percent: {},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
PagedGatewayResponse,
|
||||
PagedMixDelegationsResponse,
|
||||
PagedMixnodeResponse,
|
||||
RewardingIntervalResponse,
|
||||
RewardingStatus,
|
||||
} from './types';
|
||||
import ValidatorApiQuerier, { IValidatorApiQuery } from './validator-api-querier';
|
||||
@@ -62,6 +63,7 @@ export interface INymdQuery {
|
||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
|
||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
|
||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams>;
|
||||
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse>;
|
||||
|
||||
getAllNetworkDelegationsPaged(
|
||||
mixnetContractAddress: string,
|
||||
@@ -85,7 +87,7 @@ export interface INymdQuery {
|
||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution>;
|
||||
getRewardPool(mixnetContractAddress: string): Promise<string>;
|
||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string>;
|
||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number>;
|
||||
getEpochRewardPercent(mixnetContractAddress: string): Promise<number>;
|
||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number>;
|
||||
getRewardingStatus(
|
||||
mixnetContractAddress: string,
|
||||
@@ -136,6 +138,10 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
||||
return this.nymdQuerier.getStateParams(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse> {
|
||||
return this.nymdQuerier.getCurrentRewardingInterval(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getAllNetworkDelegationsPaged(
|
||||
mixnetContractAddress: string,
|
||||
limit?: number,
|
||||
@@ -178,8 +184,8 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
||||
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
||||
getEpochRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getEpochRewardPercent(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
PagedGatewayResponse,
|
||||
PagedMixDelegationsResponse,
|
||||
PagedMixnodeResponse,
|
||||
RewardingIntervalResponse,
|
||||
RewardingStatus,
|
||||
} from './types';
|
||||
import ValidatorApiQuerier from './validator-api-querier';
|
||||
@@ -177,13 +178,6 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
|
||||
memo?: string,
|
||||
): Promise<ExecuteResult>;
|
||||
|
||||
updateMixnodeConfig(
|
||||
mixnetContractAddress: string,
|
||||
mixIdentity: string,
|
||||
profitMarginPercent: number,
|
||||
fee: StdFee | 'auto' | number,
|
||||
): Promise<ExecuteResult>;
|
||||
|
||||
updateContractStateParams(
|
||||
mixnetContractAddress: string,
|
||||
newParams: ContractStateParams,
|
||||
@@ -256,6 +250,10 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
return this.nymdQuerier.getStateParams(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse> {
|
||||
return this.nymdQuerier.getCurrentRewardingInterval(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getAllNetworkDelegationsPaged(
|
||||
mixnetContractAddress: string,
|
||||
limit?: number,
|
||||
@@ -298,8 +296,8 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
||||
getEpochRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getEpochRewardPercent(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
||||
@@ -450,20 +448,6 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
);
|
||||
}
|
||||
|
||||
updateMixnodeConfig(
|
||||
mixnetContractAddress: string,
|
||||
mixIdentity: string,
|
||||
profitMarginPercent: number,
|
||||
fee: StdFee | 'auto' | number,
|
||||
): Promise<ExecuteResult> {
|
||||
return this.execute(
|
||||
this.clientAddress,
|
||||
mixnetContractAddress,
|
||||
{ update_mixnode_config: { profit_margin_percent: profitMarginPercent, mix_identity: mixIdentity } },
|
||||
fee,
|
||||
);
|
||||
}
|
||||
|
||||
updateContractStateParams(
|
||||
mixnetContractAddress: string,
|
||||
newParams: ContractStateParams,
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
import axios from 'axios';
|
||||
import { GasPrice } from '@cosmjs/stargate';
|
||||
|
||||
const mainnetPrefix = 'n';
|
||||
const mainnetDenom = 'nym';
|
||||
|
||||
export function nymGasPrice(prefix: string): GasPrice {
|
||||
if (typeof prefix === 'string') {
|
||||
if (prefix === mainnetPrefix) {
|
||||
prefix = mainnetDenom;
|
||||
}
|
||||
return GasPrice.fromString(`0.025u${prefix}`); // TODO: ideally this ugly conversion shouldn't be hardcoded here.
|
||||
}
|
||||
else {
|
||||
throw new Error(`${prefix} is not of type string`);
|
||||
}
|
||||
return GasPrice.fromString(`0.025u${prefix}`); // TODO: ideally this ugly conversion shouldn't be hardcoded here.
|
||||
}
|
||||
|
||||
export const downloadWasm = async (url: string): Promise<Uint8Array> => {
|
||||
|
||||
@@ -43,6 +43,12 @@ export type ContractStateParams = {
|
||||
mixnode_active_set_size: number;
|
||||
};
|
||||
|
||||
export type RewardingIntervalResponse = {
|
||||
current_rewarding_interval_starting_block: number;
|
||||
current_rewarding_interval_nonce: number;
|
||||
rewarding_in_progress: boolean;
|
||||
};
|
||||
|
||||
export type LayerDistribution = {
|
||||
gateways: number;
|
||||
layer1: number;
|
||||
@@ -129,7 +135,6 @@ export type MixNode = {
|
||||
sphinx_key: string;
|
||||
identity_key: string;
|
||||
version: string;
|
||||
profit_margin_percent: number;
|
||||
};
|
||||
|
||||
export type GatewayBond = {
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
import validator from "../../src/index";
|
||||
import { ExecuteResult } from "@cosmjs/cosmwasm-stargate";
|
||||
import { config } from "../test-utils/config";
|
||||
import {buildCoin, buildWallet, profitPercentage} from "../test-utils/utils"
|
||||
import {
|
||||
Gateway,
|
||||
GatewayOwnershipResponse,
|
||||
MixNode,
|
||||
MixOwnershipResponse,
|
||||
} from "../../src/types";
|
||||
|
||||
let response: ExecuteResult;
|
||||
let validatorClient: validator;
|
||||
let ownsMixNode: MixOwnershipResponse;
|
||||
let ownsGateway: GatewayOwnershipResponse;
|
||||
|
||||
beforeEach(async () => {
|
||||
validatorClient = await validator.connect(
|
||||
config.USER_MNEMONIC,
|
||||
config.NYMD_URL,
|
||||
config.VALIDATOR_API,
|
||||
config.NETWORK_BECH,
|
||||
config.MIXNET_CONTRACT,
|
||||
config.VESTING_CONTRACT
|
||||
);
|
||||
});
|
||||
|
||||
describe("long running e2e tests", () => {
|
||||
test.skip("token transfer", async () => {
|
||||
try {
|
||||
//make sure there's enough balance in the wallet
|
||||
|
||||
let coin = buildCoin("50000", "nymt");
|
||||
let userAddress = await buildWallet();
|
||||
let send = await validatorClient.send(
|
||||
userAddress,
|
||||
Array(coin),
|
||||
"auto",
|
||||
"send-tokens"
|
||||
);
|
||||
let jsonParse = JSON.parse(send.rawLog as string);
|
||||
|
||||
//check successful network broadcast - via events
|
||||
//1 - get key attributes values for sender an assert them
|
||||
//2 - get key attributes for receiver assert they match
|
||||
//3 - transaction hash present in response
|
||||
|
||||
// { array of events -> attribute -> event information }
|
||||
expect(jsonParse[0].events[1].attributes[1].value).toStrictEqual(
|
||||
config.USER_WALLET_ADDRESS
|
||||
);
|
||||
expect(jsonParse[0].events[1].attributes[0].value).toStrictEqual(
|
||||
userAddress
|
||||
);
|
||||
expect(jsonParse[0].events[1].type).toStrictEqual(
|
||||
"transfer"
|
||||
);
|
||||
expect(send.transactionHash).toStrictEqual(expect.any(String));
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
test.skip("update mixnode profit percentage", async () => {
|
||||
const nodeIdentity = config.MIXNODE_IDENTITY;
|
||||
const profitPercent = profitPercentage();
|
||||
|
||||
try {
|
||||
//use auto fees - simulated gas
|
||||
response = await validatorClient.updateMixnodeConfig(nodeIdentity, 'auto', profitPercent);
|
||||
}
|
||||
catch (error) {
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
ownsMixNode = await validatorClient.client.ownsMixNode(config.MIXNET_CONTRACT, config.USER_WALLET_ADDRESS);
|
||||
}
|
||||
catch (error) {
|
||||
throw error;
|
||||
}
|
||||
expect(ownsMixNode.mixnode?.mix_node.profit_margin_percent).toStrictEqual(profitPercent);
|
||||
});
|
||||
|
||||
test.skip("unbond and bond mixnode", async () => {
|
||||
|
||||
try {
|
||||
await validatorClient.unbondMixNode("auto", "unbond-mixnode");
|
||||
}
|
||||
catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const profitPercent = profitPercentage();
|
||||
|
||||
const mixnodeDetails = <MixNode>{
|
||||
host: config.MIXNODE_HOST,
|
||||
mix_port: 1789,
|
||||
verloc_port: 1790,
|
||||
http_api_port: 8080,
|
||||
identity_key: config.MIXNODE_IDENTITY,
|
||||
sphinx_key: config.MIXNODE_SPHINX_KEY,
|
||||
version: config.MIXNODE_VERSION,
|
||||
profit_margin_percent: profitPercent
|
||||
};
|
||||
|
||||
const bond = buildCoin("100000000", config.CURRENCY_DENOM)
|
||||
|
||||
try {
|
||||
response = await validatorClient.bondMixNode(
|
||||
mixnodeDetails,
|
||||
config.MIXNODE_SIGNATURE,
|
||||
bond,
|
||||
"auto"
|
||||
);
|
||||
}
|
||||
catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
ownsMixNode = await validatorClient.client.ownsMixNode(config.MIXNET_CONTRACT, config.USER_WALLET_ADDRESS);
|
||||
expect(ownsMixNode.mixnode?.mix_node.profit_margin_percent).toStrictEqual(profitPercent);
|
||||
});
|
||||
|
||||
test.skip("unbond and bond gateway", async () => {
|
||||
//gateway requires different user wallet
|
||||
//init inside test
|
||||
//todo
|
||||
|
||||
try {
|
||||
await validatorClient.unbondGateway("auto", "unbonding gateway");
|
||||
}
|
||||
catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const gateway = <Gateway>{
|
||||
host: config.GATEWAY_HOST,
|
||||
mix_port: 1789,
|
||||
clients_port: 9000,
|
||||
version: config.GATEWAY_VERSION,
|
||||
sphinx_key: config.GATEWAY_SPHINX,
|
||||
identity_key: config.GATEWAY_IDENTITY,
|
||||
location: "earth"
|
||||
};
|
||||
|
||||
const bond = buildCoin("100000000", config.CURRENCY_DENOM)
|
||||
|
||||
try {
|
||||
response = await validatorClient.bondGateway(
|
||||
gateway,
|
||||
config.GATEWAY_SIGNATURE,
|
||||
bond,
|
||||
"auto"
|
||||
);
|
||||
}
|
||||
catch (error) {
|
||||
throw error;
|
||||
}
|
||||
ownsGateway = await validatorClient.client.ownsGateway(config.MIXNET_CONTRACT, config.USER_WALLET_ADDRESS);
|
||||
expect(ownsGateway.gateway?.bond_amount).toStrictEqual(bond.amount);
|
||||
expect(ownsGateway.address).toStrictEqual(config.USER_WALLET_ADDRESS);
|
||||
});
|
||||
|
||||
test.skip("delegate to mixnode, then undelegate", async () => {
|
||||
|
||||
const pledge = buildCoin("100000000", config.CURRENCY_DENOM)
|
||||
|
||||
try {
|
||||
response = await validatorClient.delegateToMixNode(
|
||||
config.MIXNODE_IDENTITY,
|
||||
pledge,
|
||||
"auto"
|
||||
);
|
||||
//todo - we can assert the events for responses
|
||||
response.logs.forEach((log) => {
|
||||
console.log(log.events);
|
||||
console.log(log.log);
|
||||
console.log(log.msg_index);
|
||||
})
|
||||
}
|
||||
catch (error) {
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
const unbond = await validatorClient.undelegateFromMixNode(
|
||||
config.MIXNODE_IDENTITY,
|
||||
"auto"
|
||||
);
|
||||
|
||||
//todo - we can assert the events for responses
|
||||
unbond.logs.forEach((logs) => {
|
||||
logs.events.forEach((events) => {
|
||||
console.log(events.type);
|
||||
console.log(events.attributes);
|
||||
})
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,46 +0,0 @@
|
||||
import { Mock, Times } from "moq.ts";
|
||||
import { Block, BlockHeader } from "@cosmjs/stargate";
|
||||
import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";
|
||||
|
||||
describe("implement cosmwasm client test", () => {
|
||||
test.only("get height of a block then search for it", async () => {
|
||||
let height = Promise.resolve(200);
|
||||
|
||||
let blockHeader = <BlockHeader>{
|
||||
version: {
|
||||
block: "200",
|
||||
app: "testing",
|
||||
},
|
||||
height: 200,
|
||||
chainId: "nym",
|
||||
time: "today",
|
||||
};
|
||||
|
||||
let block = Promise.resolve(<Block>{
|
||||
header: blockHeader,
|
||||
id: "test",
|
||||
txs: [],
|
||||
});
|
||||
|
||||
const getheight = new Mock<CosmWasmClient>()
|
||||
.setup((nym) => nym.getHeight())
|
||||
.returns(height);
|
||||
|
||||
const getblock = new Mock<CosmWasmClient>()
|
||||
.setup((nym) => nym.getBlock(200))
|
||||
.returns(block);
|
||||
|
||||
let heightC = getheight.object();
|
||||
let blockC = getblock.object();
|
||||
|
||||
let executeHeight = await heightC.getHeight();
|
||||
let executeBlock = await blockC.getBlock(200);
|
||||
|
||||
getheight.verify((nym) => nym.getHeight(), Times.Exactly(1));
|
||||
getblock.verify((nym) => nym.getBlock(200), Times.Exactly(1));
|
||||
|
||||
expect(executeHeight).toStrictEqual(await height);
|
||||
expect(executeBlock.header.height).toStrictEqual(await height);
|
||||
expect(executeBlock.header.chainId).toStrictEqual("nym");
|
||||
});
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Mock, Times } from "moq.ts";
|
||||
import { INymdQuery } from "../../src/query-client";
|
||||
|
||||
describe("nym-client mocks", () => {
|
||||
test.only("gets interval rewarding percent", async () => {
|
||||
let contract = "mixnet_contract";
|
||||
let response = Promise.resolve(Number(2));
|
||||
|
||||
const client = new Mock<INymdQuery>()
|
||||
.setup((nym) => nym.getIntervalRewardPercent(contract))
|
||||
.returns(response);
|
||||
|
||||
const obj = client.object();
|
||||
|
||||
let execute = await obj.getIntervalRewardPercent(contract);
|
||||
|
||||
client.verify(
|
||||
(nym) => nym.getIntervalRewardPercent(contract),
|
||||
Times.Exactly(1)
|
||||
);
|
||||
|
||||
expect(execute).toStrictEqual(await response);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
import { Coin } from "@cosmjs/proto-signing";
|
||||
import { Mock, Times } from "moq.ts";
|
||||
import ValidatorClient from "../../src/index";
|
||||
import { Gateway, MixNode } from "../../src/types";
|
||||
import { config } from "../test-utils/config";
|
||||
import { buildWallet, buildCoin, profitPercentage } from "../test-utils/utils";
|
||||
import { promiseExecuteResult } from "../test-utils/expectedResults";
|
||||
import { promiseTxResult } from "../test-utils/expectedResults"
|
||||
|
||||
describe("mock validator client tests", () => {
|
||||
test.skip("token transfer", async () => {
|
||||
|
||||
//arrange
|
||||
//todo -- add more here
|
||||
let recipientAddress = "nymt14ev4p8qaa7ayr06cg3z7y2u2kxc9a8f4h9gkch";
|
||||
let sender = "nymt1cv59jumgvz2chn7ffst8tzvnapqzp282m5vat2";
|
||||
|
||||
const coin = buildCoin("50000", "nymt");
|
||||
|
||||
let transaction = promiseTxResult();
|
||||
|
||||
let mockClient = new Mock<ValidatorClient>()
|
||||
.setup((nym) => nym.send(recipientAddress, [coin], "auto", "test")).returns(transaction);
|
||||
|
||||
let token = mockClient.object();
|
||||
|
||||
//act
|
||||
let response = await token.send(recipientAddress, [coin], "auto", "test");
|
||||
|
||||
//assert
|
||||
mockClient.verify(cl => cl.send(recipientAddress, [coin], "auto"), Times.Exactly(1));
|
||||
});
|
||||
|
||||
test.only("bond mixnode test", async () => {
|
||||
//arrange
|
||||
let ownerSignature = "ownersignature";
|
||||
let coin = buildCoin("50000", "nymt");
|
||||
let expectedResult = promiseExecuteResult();
|
||||
|
||||
const profitPercent = profitPercentage();
|
||||
|
||||
const mixnode = <MixNode>{
|
||||
host: "1.1.1.1",
|
||||
mix_port: 1789,
|
||||
verloc_port: 1790,
|
||||
http_api_port: 8080,
|
||||
identity_key: "identity",
|
||||
sphinx_key: "identity",
|
||||
version: "0.12.1",
|
||||
profit_margin_percent: profitPercent,
|
||||
};
|
||||
|
||||
let client = new Mock<ValidatorClient>()
|
||||
.setup((client) =>
|
||||
client.bondMixNode(mixnode, ownerSignature, coin, "auto")
|
||||
)
|
||||
.returns(expectedResult);
|
||||
|
||||
let mixnodeBond = client.object();
|
||||
|
||||
//act
|
||||
let response = await mixnodeBond.bondMixNode(
|
||||
mixnode,
|
||||
ownerSignature,
|
||||
coin,
|
||||
"auto"
|
||||
);
|
||||
client.verify((cl) =>
|
||||
cl.bondMixNode(mixnode, ownerSignature, coin, "auto")
|
||||
);
|
||||
|
||||
//assert
|
||||
expect(response.logs[0].log).toStrictEqual("test");
|
||||
expect(response.transactionHash).toStrictEqual(
|
||||
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E"
|
||||
);
|
||||
});
|
||||
|
||||
test.only("un-bond mixnode", async () => {
|
||||
//arrange
|
||||
let expectedResult = promiseExecuteResult();
|
||||
|
||||
let client = new Mock<ValidatorClient>()
|
||||
.setup((client) => client.unbondMixNode("auto"))
|
||||
.returns(expectedResult);
|
||||
|
||||
let unbondNode = client.object();
|
||||
|
||||
//act
|
||||
let response = await unbondNode.unbondMixNode("auto");
|
||||
client.verify((cl) => cl.unbondMixNode("auto"));
|
||||
|
||||
//assert
|
||||
expect(response.logs[0].log).toStrictEqual("test");
|
||||
expect(response.transactionHash).toStrictEqual(
|
||||
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E"
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
test.only("bond gateway", async () => {
|
||||
//arrange
|
||||
let expectedResult = promiseExecuteResult();
|
||||
let ownerSignature = "ownersigntature";
|
||||
let coin = buildCoin("50000", "nymt");
|
||||
|
||||
const gateway = <Gateway>{
|
||||
host: '1.2.3.4',
|
||||
mix_port: 1789,
|
||||
clients_port: 9000,
|
||||
version: "0.12.1",
|
||||
sphinx_key: "sphinx_key",
|
||||
identity_key: "identity_key",
|
||||
location: "earth"
|
||||
};
|
||||
|
||||
let client = new Mock<ValidatorClient>()
|
||||
.setup((client) => client.bondGateway(gateway, ownerSignature, coin, "auto", "memo"))
|
||||
.returns(expectedResult);
|
||||
|
||||
let mock = client.object();
|
||||
|
||||
//act
|
||||
let response = await mock.bondGateway(gateway, ownerSignature, coin, "auto", "memo");
|
||||
client.verify((cl) => cl.bondGateway(gateway, ownerSignature, coin, "auto", "memo"));
|
||||
|
||||
//assert
|
||||
expect(response.logs[0].log).toStrictEqual("test");
|
||||
expect(response.transactionHash).toStrictEqual(
|
||||
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E"
|
||||
);
|
||||
});
|
||||
|
||||
test.only("unbond gateway", async () => {
|
||||
//arrange
|
||||
let expectedResult = promiseExecuteResult();
|
||||
let client = new Mock<ValidatorClient>()
|
||||
.setup((client) => client.unbondGateway())
|
||||
.returns(expectedResult);
|
||||
|
||||
let mock = client.object();
|
||||
|
||||
//act
|
||||
let response = await mock.unbondGateway();
|
||||
client.verify((cl) => cl.unbondGateway());
|
||||
|
||||
//assert
|
||||
expect(response.logs[0].log).toStrictEqual("test");
|
||||
expect(response.transactionHash).toStrictEqual(
|
||||
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E"
|
||||
);
|
||||
});
|
||||
|
||||
test.only("retrieve a newly created account and the balance should be empty", async () => {
|
||||
let nymWallet = await buildWallet();
|
||||
|
||||
let coin = Promise.resolve(<Coin>{
|
||||
denom: `${config.CURRENCY_DENOM}`,
|
||||
amount: "0",
|
||||
});
|
||||
|
||||
let client = new Mock<ValidatorClient>()
|
||||
.setup((nym) => nym.getBalance(nymWallet))
|
||||
.returns(coin);
|
||||
|
||||
let obj = client.object();
|
||||
|
||||
let execute = await obj.getBalance(nymWallet);
|
||||
|
||||
client.verify((nym) => nym.getBalance(nymWallet), Times.Exactly(1));
|
||||
|
||||
expect(execute).toStrictEqual(await coin);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
export const config = {
|
||||
NYMD_URL: process.env.NYMD_URL as string,
|
||||
VALIDATOR_API: process.env.VALIDATOR_API as string,
|
||||
MIXNET_CONTRACT: process.env.MIXNET_CONTRACT as string,
|
||||
VESTING_CONTRACT: process.env.VESTING_CONTRACT as string,
|
||||
USER_MNEMONIC: process.env.USER_MNEMONIC as string,
|
||||
USER_WALLET_ADDRESS: process.env.USER_WALLET_ADDRESS as string,
|
||||
CURRENCY_DENOM: process.env.CURRENCY_DENOM as string,
|
||||
CHAIN_ID: process.env.CHAIN_ID as string,
|
||||
MIXNODE_IDENTITY: process.env.MIXNODE_IDENTITY as string,
|
||||
MIXNODE_SPHINX_KEY: process.env.MIXNODE_SPHINX_KEY as string,
|
||||
MIXNODE_SIGNATURE: process.env.MIXNODE_SIGNATURE as string,
|
||||
MIXNODE_HOST: process.env.MIXNODE_HOST as string,
|
||||
MIXNODE_VERSION: process.env.MIXNODE_VERSION as string,
|
||||
GATEWAY_IDENTITY: process.env.GATEWAY_IDENTITY as string,
|
||||
GATEWAY_SIGNATURE: process.env.GATEWAY_SIGNATURE as string,
|
||||
GATEWAY_SPHINX: process.env.GATEWAY_SPHINX as string,
|
||||
GATEWAY_LOCATION: process.env.GATEWAY_LOCATION as string,
|
||||
GATEWAY_HOST: process.env.GATEWAY_HOST as string,
|
||||
GATEWAY_VERSION: process.env.GATEWAY_VERSION as string,
|
||||
NETWORK_BECH: process.env.NETWORK_BECH as string,
|
||||
};
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { ExecuteResult } from "@cosmjs/cosmwasm-stargate";
|
||||
import { DeliverTxResponse, logs } from "@cosmjs/stargate";
|
||||
|
||||
export const promiseExecuteResult = (): Promise<ExecuteResult> => {
|
||||
let log = <logs.Log>{
|
||||
msg_index: 0,
|
||||
log: "test",
|
||||
events: [],
|
||||
};
|
||||
return Promise.resolve(<ExecuteResult>{
|
||||
logs: [log],
|
||||
transactionHash:
|
||||
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E",
|
||||
});
|
||||
};
|
||||
|
||||
export const promiseTxResult = (): Promise<DeliverTxResponse> => {
|
||||
return Promise.resolve(<DeliverTxResponse>{
|
||||
code: 0,
|
||||
height: 1208302,
|
||||
rawLog: "[]",
|
||||
transactionHash:
|
||||
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E",
|
||||
gasUsed: 65042,
|
||||
gasWanted: 67977,
|
||||
});
|
||||
};
|
||||
@@ -1,46 +0,0 @@
|
||||
// timer actions
|
||||
|
||||
import ValidatorClient, { Coin } from "../../src";
|
||||
import { config } from "./config";
|
||||
|
||||
// Store current time as `start`
|
||||
export const now = (eventName = null) => {
|
||||
if (eventName) {
|
||||
console.log(`Started ${eventName}..`);
|
||||
}
|
||||
return new Date().getTime();
|
||||
};
|
||||
|
||||
//takes arg of start time
|
||||
export const elapsed = (beginning: number, log = false) => {
|
||||
const duration = new Date().getTime() - beginning;
|
||||
if (log) {
|
||||
console.log(`${duration / 1000}s`);
|
||||
}
|
||||
return duration;
|
||||
};
|
||||
|
||||
export const profitPercentage = (): number => {
|
||||
return Math.floor(Math.random() * 100) + 1;
|
||||
};
|
||||
|
||||
|
||||
export const buildCoin = (amount: string, denomination: string): Coin => {
|
||||
return {
|
||||
denom: `u${denomination}`,
|
||||
amount: amount,
|
||||
};
|
||||
};
|
||||
|
||||
export const buildWallet = async (): Promise<string> => {
|
||||
let mnemonic = ValidatorClient.randomMnemonic();
|
||||
|
||||
const randomAddress = await ValidatorClient.buildWallet(
|
||||
mnemonic,
|
||||
config.NETWORK_BECH
|
||||
);
|
||||
let accountdetails = await randomAddress.getAccounts();
|
||||
let nymWallet = accountdetails[0].address;
|
||||
return nymWallet;
|
||||
};
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
const currency = require("../../src/currency");
|
||||
|
||||
describe("currency module tests", () => {
|
||||
test.skip("convert to native balance", () => {
|
||||
const decimal = "12.0346";
|
||||
const value = currency.printableBalanceToNative(decimal);
|
||||
|
||||
expect(value).toStrictEqual("12034600");
|
||||
});
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
const stargate = require("../../src/stargate-helper");
|
||||
import { config } from "../test-utils/config";
|
||||
|
||||
describe("test the stargate functions within the project", () => {
|
||||
test.skip("gas price is returned correctly", () => {
|
||||
const nymCurrency = config.CURRENCY_DENOM;
|
||||
const getGasPrice = stargate.nymGasPrice(nymCurrency);
|
||||
|
||||
expect(getGasPrice.denom).toBe(`u${nymCurrency}`);
|
||||
});
|
||||
|
||||
test.skip("provide invalid type returns an error message", () => {
|
||||
//pass invalid type
|
||||
expect(() => {
|
||||
const nymCurrency = 13;
|
||||
stargate.nymGasPrice(nymCurrency);
|
||||
}).toThrow("13 is not of type string");
|
||||
});
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
import validator from "../../src/index";
|
||||
|
||||
describe("validator build network mnemonic", () => {
|
||||
test.skip("get mnemonic", async () => {
|
||||
const mnemonic = validator.randomMnemonic();
|
||||
const mnemonicCount = mnemonic.split(" ").length;
|
||||
|
||||
expect(mnemonicCount).toStrictEqual(24);
|
||||
});
|
||||
});
|
||||
@@ -5,11 +5,7 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
]
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"typedocOptions": {
|
||||
"entryPoints": [
|
||||
@@ -20,11 +16,7 @@
|
||||
"exclude": [
|
||||
"dist",
|
||||
"examples",
|
||||
"node_modules"
|
||||
],
|
||||
"include": [
|
||||
"tests",
|
||||
"./tests/*/*.tsx",
|
||||
"./tests/*/*.ts"
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
+2895
-3590
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "nym-client-wasm"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
version = "0.12.0"
|
||||
edition = "2021"
|
||||
version = "0.11.0"
|
||||
edition = "2018"
|
||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/nymtech/nym"
|
||||
@@ -20,7 +20,7 @@ coconut = ["coconut-interface", "credentials", "gateway-client/coconut"]
|
||||
[dependencies]
|
||||
futures = "0.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
wasm-bindgen = { version = "=0.2.78", features = ["serde-serialize"] }
|
||||
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
|
||||
wasm-bindgen-futures = "0.4"
|
||||
js-sys = "0.3"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
@@ -32,7 +32,7 @@ credentials = { path = "../../common/credentials", optional = true }
|
||||
crypto = { path = "../../common/crypto" }
|
||||
nymsphinx = { path = "../../common/nymsphinx" }
|
||||
topology = { path = "../../common/topology" }
|
||||
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
|
||||
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
|
||||
wasm-utils = { path = "../../common/wasm-utils" }
|
||||
|
||||
|
||||
+16
-19
@@ -22,10 +22,7 @@
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
}
|
||||
},
|
||||
"../pkg": {
|
||||
"name": "@nymproject/nym-client-wasm",
|
||||
"version": "0.0.1"
|
||||
},
|
||||
"../pkg": {},
|
||||
"node_modules/@nymproject/nym-client-wasm": {
|
||||
"resolved": "../pkg",
|
||||
"link": true
|
||||
@@ -2354,9 +2351,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.8",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
|
||||
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
|
||||
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -5862,9 +5859,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
|
||||
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
@@ -6503,9 +6500,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
|
||||
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
||||
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"async-limiter": "~1.0.0"
|
||||
@@ -8545,9 +8542,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.8",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
|
||||
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
|
||||
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
|
||||
"dev": true
|
||||
},
|
||||
"for-in": {
|
||||
@@ -11402,9 +11399,9 @@
|
||||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
|
||||
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
|
||||
+627
-508
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
||||
"@testing-library/react": "^9.5.0",
|
||||
"@testing-library/user-event": "^7.2.1",
|
||||
"bootstrap": "^4.5.0",
|
||||
"node-sass": "^7.0.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-scripts": "^4.0.0",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use futures::channel::mpsc;
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
use gateway_client::GatewayClient;
|
||||
use nymsphinx::acknowledgements::AckKey;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
@@ -26,7 +27,6 @@ const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_millis(1_500);
|
||||
#[wasm_bindgen]
|
||||
pub struct NymClient {
|
||||
validator_server: Url,
|
||||
testnet_mode: bool,
|
||||
|
||||
// TODO: technically this doesn't need to be an Arc since wasm is run on a single thread
|
||||
// however, once we eventually combine this code with the native-client's, it will make things
|
||||
@@ -72,7 +72,6 @@ impl NymClient {
|
||||
|
||||
on_message: None,
|
||||
on_gateway_connect: None,
|
||||
testnet_mode: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,11 +84,6 @@ impl NymClient {
|
||||
self.on_gateway_connect = Some(on_connect)
|
||||
}
|
||||
|
||||
pub fn set_testnet_mode(&mut self, testnet_mode: bool) {
|
||||
console_log!("Setting testnet mode to {}", testnet_mode);
|
||||
self.testnet_mode = testnet_mode;
|
||||
}
|
||||
|
||||
fn self_recipient(&self) -> Recipient {
|
||||
Recipient::new(
|
||||
*self.identity.public_key(),
|
||||
@@ -107,10 +101,8 @@ impl NymClient {
|
||||
|
||||
// Right now it's impossible to have async exported functions to take `&self` rather than self
|
||||
pub async fn initial_setup(self) -> Self {
|
||||
let testnet_mode = self.testnet_mode;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let bandwidth_controller = Some(gateway_client::bandwidth::BandwidthController::new(
|
||||
let bandwidth_controller = Some(BandwidthController::new(
|
||||
vec![self.validator_server.clone()],
|
||||
*self.identity.public_key(),
|
||||
));
|
||||
@@ -127,7 +119,6 @@ impl NymClient {
|
||||
gateway.clients_address(),
|
||||
Arc::clone(&client.identity),
|
||||
gateway.identity_key,
|
||||
gateway.owner.clone(),
|
||||
None,
|
||||
mixnet_messages_sender,
|
||||
ack_sender,
|
||||
@@ -135,10 +126,6 @@ impl NymClient {
|
||||
bandwidth_controller,
|
||||
);
|
||||
|
||||
if testnet_mode {
|
||||
gateway_client.set_testnet_mode(true)
|
||||
}
|
||||
|
||||
gateway_client
|
||||
.authenticate_and_start()
|
||||
.await
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "gateway-client"
|
||||
version = "0.1.0"
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -15,15 +15,12 @@ log = "0.4"
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
secp256k1 = "0.20.3"
|
||||
web3 = { version = "0.17.0", default-features = false }
|
||||
|
||||
# internal
|
||||
credentials = { path = "../../credentials" }
|
||||
crypto = { path = "../../crypto" }
|
||||
gateway-requests = { path = "../../../gateway/gateway-requests" }
|
||||
nymsphinx = { path = "../../nymsphinx" }
|
||||
pemstore = { path = "../../pemstore" }
|
||||
coconut-interface = { path = "../../coconut-interface", optional = true }
|
||||
network-defaults = { path = "../../network-defaults" }
|
||||
|
||||
@@ -39,6 +36,12 @@ features = ["macros", "rt", "net", "sync", "time"]
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
version = "0.14"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.secp256k1]
|
||||
version = "0.20.3"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.web3]
|
||||
version = "0.17.0"
|
||||
|
||||
# wasm-only dependencies
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
||||
version = "0.2"
|
||||
@@ -67,6 +70,4 @@ features = ["js"]
|
||||
#url = "2.1"
|
||||
|
||||
[features]
|
||||
coconut = ["gateway-requests/coconut", "coconut-interface"]
|
||||
wasm = ["web3/wasm", "web3/http", "web3/signing"]
|
||||
default = ["web3/default"]
|
||||
coconut = ["gateway-requests/coconut", "coconut-interface"]
|
||||
@@ -16,27 +16,22 @@ use crypto::asymmetric::identity::PublicKey;
|
||||
use network_defaults::BANDWIDTH_VALUE;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use network_defaults::{
|
||||
eth_contract::ETH_ERC20_JSON_ABI, eth_contract::ETH_JSON_ABI, ETH_BURN_FUNCTION_NAME,
|
||||
ETH_CONTRACT_ADDRESS, ETH_ERC20_APPROVE_FUNCTION_NAME, ETH_ERC20_CONTRACT_ADDRESS,
|
||||
ETH_MIN_BLOCK_DEPTH, TOKENS_TO_BURN, UTOKENS_TO_BURN,
|
||||
eth_contract::ETH_JSON_ABI, ETH_BURN_FUNCTION_NAME, ETH_CONTRACT_ADDRESS, ETH_MIN_BLOCK_DEPTH,
|
||||
TOKENS_TO_BURN,
|
||||
};
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use pemstore::traits::PemStorableKeyPair;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use rand::rngs::OsRng;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use secp256k1::SecretKey;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use std::io::{Read, Write};
|
||||
use std::io::Write;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use std::str::FromStr;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use web3::{
|
||||
contract::{Contract, Options},
|
||||
ethabi::Token,
|
||||
signing::{Key, SecretKeyRef},
|
||||
transports::Http,
|
||||
types::{Address, U256, U64},
|
||||
types::{Address, Bytes, U256, U64},
|
||||
Web3,
|
||||
};
|
||||
|
||||
@@ -55,19 +50,6 @@ pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
.expect("Invalid json abi")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn eth_erc20_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
Contract::from_json(
|
||||
web3.eth(),
|
||||
Address::from(ETH_ERC20_CONTRACT_ADDRESS),
|
||||
json::parse(ETH_ERC20_JSON_ABI)
|
||||
.expect("Invalid json abi")
|
||||
.dump()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect("Invalid json abi")
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BandwidthController {
|
||||
#[cfg(feature = "coconut")]
|
||||
@@ -77,8 +59,6 @@ pub struct BandwidthController {
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
contract: Contract<Http>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_contract: Contract<Http>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_private_key: SecretKey,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
backup_bandwidth_token_keys_dir: std::path::PathBuf,
|
||||
@@ -104,14 +84,12 @@ impl BandwidthController {
|
||||
Http::new(ð_endpoint).map_err(|_| GatewayClientError::InvalidURL(eth_endpoint))?;
|
||||
let web3 = web3::Web3::new(transport);
|
||||
// Fail early, on invalid abi
|
||||
let contract = eth_contract(web3.clone());
|
||||
let erc20_contract = eth_erc20_contract(web3);
|
||||
let contract = eth_contract(web3);
|
||||
let eth_private_key = secp256k1::SecretKey::from_str(ð_private_key)
|
||||
.map_err(|_| GatewayClientError::InvalidEthereumPrivateKey)?;
|
||||
|
||||
Ok(BandwidthController {
|
||||
contract,
|
||||
erc20_contract,
|
||||
eth_private_key,
|
||||
backup_bandwidth_token_keys_dir,
|
||||
})
|
||||
@@ -129,45 +107,6 @@ impl BandwidthController {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
fn restore_keypair(&self) -> Result<identity::KeyPair, GatewayClientError> {
|
||||
std::fs::create_dir_all(&self.backup_bandwidth_token_keys_dir)?;
|
||||
let file = std::fs::read_dir(&self.backup_bandwidth_token_keys_dir)?
|
||||
.find(|entry| {
|
||||
entry
|
||||
.as_ref()
|
||||
.map(|entry| entry.path().is_file())
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.unwrap_or_else(|| Err(std::io::Error::from(std::io::ErrorKind::NotFound)))?;
|
||||
let file_path = file.path();
|
||||
let pub_key = file_path
|
||||
.file_name()
|
||||
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotFound))?
|
||||
.to_str()
|
||||
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotFound))?;
|
||||
let mut priv_key = vec![];
|
||||
std::fs::File::open(file_path.clone())?.read_to_end(&mut priv_key)?;
|
||||
Ok(identity::KeyPair::from_keys(
|
||||
identity::PrivateKey::from_bytes(&priv_key).unwrap(),
|
||||
identity::PublicKey::from_base58_string(pub_key).unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
fn mark_keypair_as_spent(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
|
||||
let mut spent_dir = self.backup_bandwidth_token_keys_dir.clone();
|
||||
spent_dir.push("spent");
|
||||
std::fs::create_dir_all(&spent_dir)?;
|
||||
let file_path_old = self
|
||||
.backup_bandwidth_token_keys_dir
|
||||
.join(keypair.public_key().to_base58_string());
|
||||
let file_path_new = spent_dir.join(keypair.public_key().to_base58_string());
|
||||
std::fs::rename(file_path_old, file_path_new)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub async fn prepare_coconut_credential(
|
||||
&self,
|
||||
@@ -206,25 +145,17 @@ impl BandwidthController {
|
||||
pub async fn prepare_token_credential(
|
||||
&self,
|
||||
gateway_identity: PublicKey,
|
||||
gateway_owner: String,
|
||||
) -> Result<TokenCredential, GatewayClientError> {
|
||||
let kp = match self.restore_keypair() {
|
||||
Ok(kp) => kp,
|
||||
Err(_) => {
|
||||
let mut rng = OsRng;
|
||||
let kp = identity::KeyPair::new(&mut rng);
|
||||
self.backup_keypair(&kp)?;
|
||||
kp
|
||||
}
|
||||
};
|
||||
let mut rng = OsRng;
|
||||
|
||||
let kp = identity::KeyPair::new(&mut rng);
|
||||
self.backup_keypair(&kp)?;
|
||||
|
||||
let verification_key = *kp.public_key();
|
||||
let signed_verification_key = kp.private_key().sign(&verification_key.to_bytes());
|
||||
self.buy_token_credential(verification_key, signed_verification_key, gateway_owner)
|
||||
self.buy_token_credential(verification_key, signed_verification_key)
|
||||
.await?;
|
||||
|
||||
self.mark_keypair_as_spent(&kp)?;
|
||||
|
||||
let message: Vec<u8> = verification_key
|
||||
.to_bytes()
|
||||
.iter()
|
||||
@@ -246,114 +177,33 @@ impl BandwidthController {
|
||||
&self,
|
||||
verification_key: PublicKey,
|
||||
signed_verification_key: identity::Signature,
|
||||
gateway_owner: String,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
// 0 means a transaction failure, 1 means success
|
||||
let confirmations = if cfg!(debug_assertions) {
|
||||
1
|
||||
} else {
|
||||
ETH_MIN_BLOCK_DEPTH
|
||||
};
|
||||
// 15 seconds per confirmation block + 10 seconds of network overhead + 20 seconds of wait for kill
|
||||
// 15 seconds per confirmation block + 10 seconds of network overhead
|
||||
log::info!(
|
||||
"Waiting for Ethereum transaction. This should take about {} seconds",
|
||||
(confirmations + 1) * 15 + 30
|
||||
confirmations * 15 + 10
|
||||
);
|
||||
let mut options = Options::default();
|
||||
let estimation = self
|
||||
.erc20_contract
|
||||
.estimate_gas(
|
||||
ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
(
|
||||
Token::Address(Address::from(ETH_CONTRACT_ADDRESS)),
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
),
|
||||
SecretKeyRef::from(&self.eth_private_key).address(),
|
||||
options.clone(),
|
||||
)
|
||||
.await?;
|
||||
options.gas = Some(estimation);
|
||||
log::info!("Calling ERC20 approve in 10 seconds with an estimated gas of {}. Kill the process if you want to abort", estimation);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if let Err(err) = fluvio_wasm_timer::Delay::new(std::time::Duration::from_secs(10)).await {
|
||||
log::error!(
|
||||
"the timer has gone away while waiting for possible kill! - {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
let recipt = self
|
||||
.erc20_contract
|
||||
.signed_call_with_confirmations(
|
||||
ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
(
|
||||
Token::Address(Address::from(ETH_CONTRACT_ADDRESS)),
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
),
|
||||
options,
|
||||
1, // One confirmation is enough, as we'll be consuming the approved token next anyway
|
||||
&self.eth_private_key,
|
||||
)
|
||||
.await?;
|
||||
if Some(U64::from(0u64)) == recipt.status {
|
||||
return Err(GatewayClientError::BurnTokenError(
|
||||
web3::Error::InvalidResponse(format!(
|
||||
"Approve transaction status is 0 (failure): {:?}",
|
||||
recipt.logs,
|
||||
)),
|
||||
));
|
||||
} else {
|
||||
log::info!(
|
||||
"Approved {} tokens for bandwidth use on Ethereum",
|
||||
TOKENS_TO_BURN
|
||||
);
|
||||
}
|
||||
|
||||
let mut options = Options::default();
|
||||
let estimation = self
|
||||
.contract
|
||||
.estimate_gas(
|
||||
ETH_BURN_FUNCTION_NAME,
|
||||
(
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
Token::Uint(U256::from(&verification_key.to_bytes())),
|
||||
Token::Bytes(signed_verification_key.to_bytes().to_vec()),
|
||||
Token::String(gateway_owner.clone()),
|
||||
),
|
||||
SecretKeyRef::from(&self.eth_private_key).address(),
|
||||
options.clone(),
|
||||
)
|
||||
.await?;
|
||||
options.gas = Some(estimation);
|
||||
log::info!("Generating bandwidth on ETH contract in 10 seconds with an estimated gas of {}. \
|
||||
Kill the process if you want to abort. Keep in mind that if you abort now, you'll still have \
|
||||
some tokens approved for bandwidth spending from the previous action. \
|
||||
If you don't want that, you'll need to manually decreaseAllowance to revert the approval.", estimation);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if let Err(err) = fluvio_wasm_timer::Delay::new(std::time::Duration::from_secs(10)).await {
|
||||
log::error!(
|
||||
"the timer has gone away while waiting for possible kill! - {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
let recipt = self
|
||||
.contract
|
||||
.signed_call_with_confirmations(
|
||||
ETH_BURN_FUNCTION_NAME,
|
||||
(
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
Token::Uint(U256::from(&verification_key.to_bytes())),
|
||||
Token::Bytes(signed_verification_key.to_bytes().to_vec()),
|
||||
Token::String(gateway_owner),
|
||||
U256::from(TOKENS_TO_BURN),
|
||||
U256::from(&verification_key.to_bytes()),
|
||||
Bytes(signed_verification_key.to_bytes().to_vec()),
|
||||
),
|
||||
options,
|
||||
Options::default(),
|
||||
confirmations,
|
||||
&self.eth_private_key,
|
||||
)
|
||||
.await?;
|
||||
if Some(U64::from(0u64)) == recipt.status {
|
||||
if Some(U64::from(0)) == recipt.status {
|
||||
Err(GatewayClientError::BurnTokenError(
|
||||
web3::Error::InvalidResponse(format!(
|
||||
"Transaction status is 0 (failure): {:?}",
|
||||
@@ -386,15 +236,6 @@ mod tests {
|
||||
eth_contract(web3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_erc20_contract() {
|
||||
let transport =
|
||||
Http::new("https://rinkeby.infura.io/v3/00000000000000000000000000000000").unwrap();
|
||||
let web3 = web3::Web3::new(transport);
|
||||
// test no panic occurs
|
||||
eth_erc20_contract(web3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_event_name_constant_against_abi() {
|
||||
let transport =
|
||||
|
||||
@@ -20,7 +20,6 @@ use gateway_requests::iv::IV;
|
||||
use gateway_requests::registration::handshake::{client_handshake, SharedKeys};
|
||||
use gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse};
|
||||
use log::*;
|
||||
use network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
|
||||
use nymsphinx::forwarding::packet::MixPacket;
|
||||
use rand::rngs::OsRng;
|
||||
use std::convert::TryFrom;
|
||||
@@ -41,11 +40,9 @@ const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
|
||||
|
||||
pub struct GatewayClient {
|
||||
authenticated: bool,
|
||||
testnet_mode: bool,
|
||||
bandwidth_remaining: i64,
|
||||
gateway_address: String,
|
||||
gateway_identity: identity::PublicKey,
|
||||
gateway_owner: String,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
shared_key: Option<Arc<SharedKeys>>,
|
||||
connection: SocketState,
|
||||
@@ -70,7 +67,6 @@ impl GatewayClient {
|
||||
gateway_address: String,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
gateway_identity: identity::PublicKey,
|
||||
gateway_owner: String,
|
||||
shared_key: Option<Arc<SharedKeys>>,
|
||||
mixnet_message_sender: MixnetMessageSender,
|
||||
ack_sender: AcknowledgementSender,
|
||||
@@ -79,11 +75,9 @@ impl GatewayClient {
|
||||
) -> Self {
|
||||
GatewayClient {
|
||||
authenticated: false,
|
||||
testnet_mode: false,
|
||||
bandwidth_remaining: 0,
|
||||
gateway_address,
|
||||
gateway_identity,
|
||||
gateway_owner,
|
||||
local_identity,
|
||||
shared_key,
|
||||
connection: SocketState::NotConnected,
|
||||
@@ -96,10 +90,6 @@ impl GatewayClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_testnet_mode(&mut self, testnet_mode: bool) {
|
||||
self.testnet_mode = testnet_mode
|
||||
}
|
||||
|
||||
// TODO: later convert into proper builder methods
|
||||
pub fn with_reconnection_on_failure(&mut self, should_reconnect_on_failure: bool) {
|
||||
self.should_reconnect_on_failure = should_reconnect_on_failure
|
||||
@@ -116,7 +106,6 @@ impl GatewayClient {
|
||||
pub fn new_init(
|
||||
gateway_address: String,
|
||||
gateway_identity: identity::PublicKey,
|
||||
gateway_owner: String,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
response_timeout_duration: Duration,
|
||||
) -> Self {
|
||||
@@ -130,11 +119,9 @@ impl GatewayClient {
|
||||
|
||||
GatewayClient {
|
||||
authenticated: false,
|
||||
testnet_mode: false,
|
||||
bandwidth_remaining: 0,
|
||||
gateway_address,
|
||||
gateway_identity,
|
||||
gateway_owner,
|
||||
local_identity,
|
||||
shared_key: None,
|
||||
connection: SocketState::NotConnected,
|
||||
@@ -526,17 +513,6 @@ impl GatewayClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn try_claim_testnet_bandwidth(&mut self) -> Result<(), GatewayClientError> {
|
||||
let msg = ClientControlRequest::ClaimFreeTestnetBandwidth.into();
|
||||
self.bandwidth_remaining = match self.send_websocket_message(msg).await? {
|
||||
ServerResponse::Bandwidth { available_total } => Ok(available_total),
|
||||
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
|
||||
_ => Err(GatewayClientError::UnexpectedResponse),
|
||||
}?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn claim_bandwidth(&mut self) -> Result<(), GatewayClientError> {
|
||||
if !self.authenticated {
|
||||
return Err(GatewayClientError::NotAuthenticated);
|
||||
@@ -549,12 +525,6 @@ impl GatewayClient {
|
||||
}
|
||||
|
||||
warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while");
|
||||
if self.testnet_mode {
|
||||
info!("The client is running in testnet mode - attempting to claim bandwidth without a credential");
|
||||
return self.try_claim_testnet_bandwidth().await;
|
||||
}
|
||||
|
||||
let _gateway_owner = self.gateway_owner.clone();
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let credential = self
|
||||
@@ -568,7 +538,7 @@ impl GatewayClient {
|
||||
.bandwidth_controller
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.prepare_token_credential(self.gateway_identity, _gateway_owner)
|
||||
.prepare_token_credential(self.gateway_identity)
|
||||
.await?;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
@@ -592,10 +562,15 @@ impl GatewayClient {
|
||||
return Err(GatewayClientError::NotAuthenticated);
|
||||
}
|
||||
if self.estimate_required_bandwidth(&packets) > self.bandwidth_remaining {
|
||||
return Err(GatewayClientError::NotEnoughBandwidth(
|
||||
self.estimate_required_bandwidth(&packets),
|
||||
self.bandwidth_remaining,
|
||||
));
|
||||
// Try to claim more bandwidth first, and return an error only if that is still not
|
||||
// enough (the current granularity for bandwidth should be sufficient)
|
||||
self.claim_bandwidth().await?;
|
||||
if self.estimate_required_bandwidth(&packets) > self.bandwidth_remaining {
|
||||
return Err(GatewayClientError::NotEnoughBandwidth(
|
||||
self.estimate_required_bandwidth(&packets),
|
||||
self.bandwidth_remaining,
|
||||
));
|
||||
}
|
||||
}
|
||||
if !self.connection.is_established() {
|
||||
return Err(GatewayClientError::ConnectionNotEstablished);
|
||||
@@ -662,10 +637,15 @@ impl GatewayClient {
|
||||
return Err(GatewayClientError::NotAuthenticated);
|
||||
}
|
||||
if (mix_packet.sphinx_packet().len() as i64) > self.bandwidth_remaining {
|
||||
return Err(GatewayClientError::NotEnoughBandwidth(
|
||||
mix_packet.sphinx_packet().len() as i64,
|
||||
self.bandwidth_remaining,
|
||||
));
|
||||
// Try to claim more bandwidth first, and return an error only if that is still not
|
||||
// enough
|
||||
self.claim_bandwidth().await?;
|
||||
if (mix_packet.sphinx_packet().len() as i64) > self.bandwidth_remaining {
|
||||
return Err(GatewayClientError::NotEnoughBandwidth(
|
||||
mix_packet.sphinx_packet().len() as i64,
|
||||
self.bandwidth_remaining,
|
||||
));
|
||||
}
|
||||
}
|
||||
if !self.connection.is_established() {
|
||||
return Err(GatewayClientError::ConnectionNotEstablished);
|
||||
@@ -735,12 +715,6 @@ impl GatewayClient {
|
||||
}
|
||||
let shared_key = self.perform_initial_authentication().await?;
|
||||
|
||||
if self.bandwidth_remaining < REMAINING_BANDWIDTH_THRESHOLD {
|
||||
info!("Claiming more bandwidth for your tokens. This will use {} token(s) from your wallet. \
|
||||
Stop the process now if you don't want that to happen.", TOKENS_TO_BURN);
|
||||
self.claim_bandwidth().await?;
|
||||
}
|
||||
|
||||
// this call is NON-blocking
|
||||
self.start_listening_for_mixnet_messages()?;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use tungstenite::Error as WsError;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::JsValue;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use web3::{contract::Error as Web3ContractError, Error as Web3Error};
|
||||
use web3::Error as Web3Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GatewayClientError {
|
||||
@@ -27,17 +27,13 @@ pub enum GatewayClientError {
|
||||
NetworkErrorWasm(JsValue),
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[error("Keypair IO error - {0}")]
|
||||
#[error("Could not backup keypair - {0}")]
|
||||
IOError(#[from] std::io::Error),
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[error("Could not burn ERC20 token in Ethereum smart contract - {0}")]
|
||||
BurnTokenError(#[from] Web3Error),
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[error("Could not run web3 contract - {0}")]
|
||||
Web3ContractError(#[from] Web3ContractError),
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[error("Invalid Ethereum private key")]
|
||||
InvalidEthereumPrivateKey,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "mixnet-client"
|
||||
version = "0.1.0"
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user