Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8764ddbb6c | |||
| 7338640f36 |
+3
-3
@@ -15,8 +15,8 @@
|
|||||||
* @futurechimp @mmsinclair
|
* @futurechimp @mmsinclair
|
||||||
|
|
||||||
# Rust rules:
|
# Rust rules:
|
||||||
*.rs @durch @futurechimp @jstuczyn @neacsu @octol
|
*.rs @durch @futurechimp @jstuczyn @neacsu
|
||||||
Cargo.* @durch @futurechimp @jstuczyn @neacsu @octol
|
Cargo.* @durch @futurechimp @jstuczyn @neacsu
|
||||||
|
|
||||||
# JS rules:
|
# JS rules:
|
||||||
*.js @mmsinclair @fmtabbara @Aid19801
|
*.js @mmsinclair @fmtabbara @Aid19801
|
||||||
@@ -36,5 +36,5 @@ Cargo.* @durch @futurechimp @jstuczyn @neacsu @octol
|
|||||||
|
|
||||||
# Explorer and wallet should probably get looked by the product team
|
# Explorer and wallet should probably get looked by the product team
|
||||||
/explorer/ @nymtech/product
|
/explorer/ @nymtech/product
|
||||||
/nym-wallet/ @nymtech/product
|
/tauri-wallet/ @nymtech/product
|
||||||
/wallet-web/ @nymtech/product
|
/wallet-web/ @nymtech/product
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an enhancement to the product
|
|
||||||
title: "[Feature Request]"
|
|
||||||
labels: enhancement
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is...
|
|
||||||
|
|
||||||
**Is your request a feature not related to an existing problem? A new feature.**
|
|
||||||
For example.
|
|
||||||
- Given I am using the nym wallet
|
|
||||||
- When I transfer nym tokens across the network
|
|
||||||
- Then I want to have an url link in the wallet which navigates outside the application to the nym-explorer
|
|
||||||
|
|
||||||
**Where does the feature fit in the Nym real estate?**
|
|
||||||
- Application / UI
|
|
||||||
|
|
||||||
**What is this solving?**
|
|
||||||
How will this improve the product...
|
|
||||||
|
|
||||||
**Is this an update to packages or libraries?**
|
|
||||||
If so, please list them. If not, please ignore this section.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
---
|
|
||||||
name: Report
|
|
||||||
about: To help identify and reproduce issues
|
|
||||||
title: "[Issue]"
|
|
||||||
labels: bug, bug-needs-triage, qa
|
|
||||||
assignees: tommyv1987
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the issue**
|
|
||||||
A clear and concise description of what the issue is...
|
|
||||||
|
|
||||||
**Expected behaviour**
|
|
||||||
A clear and concise description of what you expected to happen...
|
|
||||||
|
|
||||||
**Stack Traces**
|
|
||||||
If there are stack traces or logs, please provide them here...
|
|
||||||
|
|
||||||
**Steps to Reproduce**
|
|
||||||
Steps to reproduce the behaviour, if you're familiar with BDD syntax, please write it in this style:
|
|
||||||
- Given I was doing X
|
|
||||||
- And I installed Y
|
|
||||||
- When I actioned Y
|
|
||||||
- Then I expect Z
|
|
||||||
|
|
||||||
*An example:*
|
|
||||||
- Given I was setting up a mix-node following the instructions in the docs
|
|
||||||
- And I successfully bonded my node via the the wallet
|
|
||||||
- When I went to start my mixnode
|
|
||||||
- Then I was presented with an error
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem...
|
|
||||||
|
|
||||||
**Which area of Nym were you using?**
|
|
||||||
- UI: [e.g. Websites - network-explorer, nym-website]
|
|
||||||
- Application: [e.g Gateway, Client, Wallet]
|
|
||||||
- OS: [e.g. Ubuntu 20.x, MacOs Big Sur, Windows 10]
|
|
||||||
- Browser: [e.g Chrome (if applicable)]
|
|
||||||
- Version: [e.g. nym binary(0.11.0), browser(94.0)]
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Please provide any other information
|
|
||||||
+83
-25
@@ -1,22 +1,20 @@
|
|||||||
name: Continuous integration
|
name: Continuous integration
|
||||||
|
|
||||||
on:
|
on: [push, pull_request]
|
||||||
push:
|
|
||||||
paths-ignore:
|
|
||||||
- 'explorer/**'
|
|
||||||
pull_request:
|
|
||||||
paths-ignore:
|
|
||||||
- 'explorer/**'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: [ self-hosted, custom-linux-exoscale ]
|
runs-on: ${{ matrix.os }}
|
||||||
# Enable sccache via environment variable
|
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.os == 'windows-latest' }}
|
||||||
env:
|
strategy:
|
||||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
matrix:
|
||||||
|
rust: [stable, beta, nightly]
|
||||||
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install Dependencies (Linux)
|
- 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
|
- name: Check out repository code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -25,7 +23,7 @@ jobs:
|
|||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: stable
|
toolchain: ${{ matrix.rust }}
|
||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
@@ -47,32 +45,92 @@ jobs:
|
|||||||
command: fmt
|
command: fmt
|
||||||
args: --all -- --check
|
args: --all -- --check
|
||||||
|
|
||||||
- uses: actions-rs/clippy-check@v1
|
|
||||||
name: Clippy checks
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
args: --all-features
|
|
||||||
|
|
||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
|
if: ${{ matrix.rust != 'nightly' }}
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: -- -D warnings
|
args: -- -D warnings
|
||||||
|
|
||||||
- name: Build all binaries with coconut enabled
|
# 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
|
||||||
|
|
||||||
|
# BUILD
|
||||||
|
- name: Build gateway with coconut feature
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --all --features=coconut
|
args: --bin nym-gateway --features=coconut
|
||||||
|
|
||||||
- name: Run all tests with coconut enabled
|
- name: Build native client with coconut feature
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --bin nym-client --features=coconut
|
||||||
|
|
||||||
|
- name: Build socks5 client with coconut feature
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --bin nym-socks5-client --features=coconut
|
||||||
|
|
||||||
|
- name: Build validator-api with coconut feature
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --bin nym-validator-api --features=coconut
|
||||||
|
|
||||||
|
# TEST
|
||||||
|
- name: Test gateway with coconut feature
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --all --features=coconut
|
args: --bin nym-gateway --features=coconut
|
||||||
|
|
||||||
- name: Run clippy with coconut enabled
|
- name: Test native client with coconut feature
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --bin nym-client --features=coconut
|
||||||
|
|
||||||
|
- name: Test socks5 client with coconut feature
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --bin nym-socks5-client --features=coconut
|
||||||
|
|
||||||
|
- name: Test validator-api with coconut feature
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --bin nym-validator-api --features=coconut
|
||||||
|
|
||||||
|
# CLIPPY
|
||||||
|
|
||||||
|
- name: Run clippy on gateway with coconut feature
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --features=coconut -- -D warnings
|
args: --bin nym-gateway --features=coconut -- -D warnings
|
||||||
|
|
||||||
|
- name: Run clippy on native client with coconut feature
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --bin nym-client --features=coconut -- -D warnings
|
||||||
|
|
||||||
|
- name: Run clippy on socks5 client with coconut feature
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --bin nym-socks5-client --features=coconut -- -D warnings
|
||||||
|
|
||||||
|
- name: Run clippy on validator-api with coconut feature
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --bin nym-validator-api --features=coconut -- -D warnings
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"os":"ubuntu-latest",
|
|
||||||
"rust":"stable",
|
|
||||||
"runOnEvent":"always"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"os":"windows-latest",
|
|
||||||
"rust":"stable",
|
|
||||||
"runOnEvent":"pull_request"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"os":"macos-latest",
|
|
||||||
"rust":"stable",
|
|
||||||
"runOnEvent":"pull_request"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
name: Clippy check
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
clippy_check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- run: rustup component add clippy
|
||||||
|
- uses: actions-rs/clippy-check@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
args: --all-features
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"rust":"stable",
|
|
||||||
"runOnEvent":"always"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rust":"beta",
|
|
||||||
"runOnEvent":"pull_request"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"rust":"nightly",
|
|
||||||
"runOnEvent":"pull_request"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
name: Contracts
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths-ignore:
|
|
||||||
- 'explorer/**'
|
|
||||||
pull_request:
|
|
||||||
paths-ignore:
|
|
||||||
- '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/contract_matrix_includes.json'
|
|
||||||
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
|
|
||||||
contracts:
|
|
||||||
# since it's going to be compiled into wasm, there's absolutely
|
|
||||||
# no point in running CI on different OS-es
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
continue-on-error: ${{ matrix.rust == 'nightly' }}
|
|
||||||
needs: matrix_prep
|
|
||||||
strategy:
|
|
||||||
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: ${{ matrix.rust }}
|
|
||||||
target: wasm32-unknown-unknown
|
|
||||||
override: true
|
|
||||||
components: rustfmt, clippy
|
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
env:
|
|
||||||
RUSTFLAGS: '-C link-arg=-s'
|
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
args: --manifest-path contracts/Cargo.toml --all --target wasm32-unknown-unknown
|
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: --manifest-path contracts/Cargo.toml
|
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: fmt
|
|
||||||
args: --manifest-path contracts/Cargo.toml --all -- --check
|
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
if: ${{ matrix.rust != 'nightly' }}
|
|
||||||
with:
|
|
||||||
command: clippy
|
|
||||||
args: --manifest-path contracts/Cargo.toml --all -- -D warnings
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
name: Mixnet Contract
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
mixnet-contract:
|
||||||
|
# since it's going to be compiled into wasm, there's absolutely
|
||||||
|
# no point in running CI on different OS-es
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: ${{ matrix.rust == 'nightly' }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
rust: [ stable, beta, nightly ]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
target: wasm32-unknown-unknown
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --manifest-path contracts/mixnet/Cargo.toml --target wasm32-unknown-unknown
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --manifest-path contracts/mixnet/Cargo.toml
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --manifest-path contracts/mixnet/Cargo.toml -- --check
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
if: ${{ matrix.rust != 'nightly' }}
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --manifest-path contracts/mixnet/Cargo.toml -- -D warnings
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
name: Linting for Network Explorer (eslint/prettier)
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'explorer/**'
|
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: explorer
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: custom-runner-linux
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: '14'
|
|
||||||
- run: npm install
|
|
||||||
- name: Run ESLint
|
|
||||||
# GitHub should automatically annotate the PR
|
|
||||||
run: npm run lint
|
|
||||||
@@ -11,7 +11,7 @@ defaults:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: custom-runner-linux
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install rsync
|
- name: Install rsync
|
||||||
@@ -22,8 +22,6 @@ jobs:
|
|||||||
node-version: '14'
|
node-version: '14'
|
||||||
- run: npm install
|
- run: npm install
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- name: Set environment from the example
|
|
||||||
run: cp .env.prod .env
|
|
||||||
- run: npm run test
|
- run: npm run test
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
- run: npm run build
|
- run: npm run build
|
||||||
@@ -37,17 +35,15 @@ jobs:
|
|||||||
SOURCE: "explorer/dist/"
|
SOURCE: "explorer/dist/"
|
||||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
||||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
|
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/${{ env.GITHUB_REF_SLUG }}
|
||||||
EXCLUDE: "/dist/, /node_modules/"
|
EXCLUDE: "/dist/, /node_modules/"
|
||||||
- name: Keybase - Node Install
|
- name: Keybase - Node Install
|
||||||
run: npm install
|
run: npm install
|
||||||
working-directory: .github/workflows/support-files
|
working-directory: .github/workflows/support-files/messages
|
||||||
- name: Keybase - Send Notification
|
- name: Keybase - Send Notification
|
||||||
env:
|
env:
|
||||||
NYM_NOTIFICATION_KIND: network-explorer
|
|
||||||
NYM_PROJECT_NAME: "Network Explorer"
|
NYM_PROJECT_NAME: "Network Explorer"
|
||||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
||||||
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
|
|
||||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||||
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
||||||
@@ -57,4 +53,4 @@ jobs:
|
|||||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||||
uses: docker://keybaseio/client:stable-node
|
uses: docker://keybaseio/client:stable-node
|
||||||
with:
|
with:
|
||||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
args: .github/workflows/support-files/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
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
name: Generate TS types
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths-ignore:
|
|
||||||
- "explorer/**"
|
|
||||||
pull_request:
|
|
||||||
paths-ignore:
|
|
||||||
- "explorer/**"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
nym-wallet-types:
|
|
||||||
runs-on: [ self-hosted, custom-linux-exoscale ]
|
|
||||||
# Enable sccache
|
|
||||||
env:
|
|
||||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
|
||||||
steps:
|
|
||||||
- name: Prepare
|
|
||||||
run: sudo apt-get update && sudo apt-get install -y libpango1.0-dev libatk1.0-dev libgdk-pixbuf2.0-dev libsoup2.4-dev librust-gdk-dev libwebkit2gtk-4.0-dev
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
- name: Generate TS
|
|
||||||
run: cd nym-wallet/src-tauri && cargo test
|
|
||||||
- uses: EndBug/add-and-commit@v7.2.1 # https://github.com/marketplace/actions/add-commit
|
|
||||||
with:
|
|
||||||
add: '["nym-wallet"]'
|
|
||||||
message: "[ci skip] Generate TS types"
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
name: Webdriverio tests for nym wallet
|
name: Webdriverio tests for nym wallet
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- "nym-wallet/**"
|
- 'tauri-wallet/**'
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: nym-wallet
|
working-directory: tauri-wallet
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: wallet tests
|
name: wallet tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
@@ -30,8 +30,8 @@ jobs:
|
|||||||
- name: Install minimal stable
|
- name: Install minimal stable
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
|
|
||||||
- name: Node v16
|
- name: Node v16
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
@@ -39,32 +39,27 @@ jobs:
|
|||||||
node-version: 16.x
|
node-version: 16.x
|
||||||
|
|
||||||
- name: Install yarn for building application
|
- name: Install yarn for building application
|
||||||
run: yarn install
|
run: yarn install
|
||||||
|
|
||||||
- name: Build application
|
- name: Build application
|
||||||
run: yarn run webpack:build & yarn run tauri:build
|
run: yarn run webpack:build & yarn run tauri:build
|
||||||
|
|
||||||
- name: Check binary exists
|
|
||||||
run: |
|
|
||||||
cd target/release/
|
|
||||||
(test -f nym-wallet && echo nym binary exists) || echo wallet does not exist
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
working-directory: nym-wallet/webdriver
|
working-directory: tauri-wallet/webdriver
|
||||||
|
|
||||||
- name: Remove existing user datafile
|
- name: Remove existing user datafile
|
||||||
uses: JesseTG/rm@v1.0.2
|
uses: JesseTG/rm@v1.0.2
|
||||||
with:
|
with:
|
||||||
path: nym-wallet/webdriver/common/data/user-data.json
|
path: tauri-wallet/webdriver/common/data/user-data.json
|
||||||
|
|
||||||
- name: Create user data json file
|
- name: Create user data json file
|
||||||
id: create-json
|
id: create-json
|
||||||
uses: jsdaniell/create-json@1.1.2
|
uses: jsdaniell/create-json@1.1.2
|
||||||
with:
|
with:
|
||||||
name: "user-data.json"
|
name: "user-data.json"
|
||||||
json: ${{ secrets.WALLET_USERDATA }}
|
json: ${{ secrets.WALLET_USERDATA }}
|
||||||
dir: "nym-wallet/webdriver/common/data/"
|
dir: 'tauri-wallet/webdriver/common/data/'
|
||||||
|
|
||||||
- name: Install tauri-driver
|
- name: Install tauri-driver
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
@@ -73,5 +68,5 @@ jobs:
|
|||||||
args: tauri-driver
|
args: tauri-driver
|
||||||
|
|
||||||
- name: Launch tests
|
- name: Launch tests
|
||||||
run: xvfb-run yarn test:runall
|
run: xvfb-run yarn test:newuser
|
||||||
working-directory: nym-wallet/webdriver
|
working-directory: tauri-wallet/webdriver
|
||||||
|
|||||||
@@ -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",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node dev.js",
|
"format": "prettier --write send_message.js"
|
||||||
"format": "prettier --write **/*.js"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^16.0.0",
|
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"keybase-bot": "^3.6.1",
|
"keybase-bot": "^3.6.1"
|
||||||
"octokit": "^1.7.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "2.3.2"
|
"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();
|
||||||
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
|
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
|
||||||
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
|
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.GITHUB_REF_SLUG }}.{{ env.NYM_CI_WWW_BASE }}/
|
||||||
> ✅ **SUCCESS**
|
> ✅ **SUCCESS**
|
||||||
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
|
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
|
||||||
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
|
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
|
||||||
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
|
> `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 }}
|
{{ 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();
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
name: Generate TS types
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tauri-wallet-types:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y libpango1.0-dev libatk1.0-dev libgdk-pixbuf2.0-dev libsoup2.4-dev librust-gdk-dev libwebkit2gtk-4.0-dev
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
- name: Generate TS
|
||||||
|
run: cd tauri-wallet/src-tauri && cargo test
|
||||||
|
- uses: EndBug/add-and-commit@v7.2.1 # https://github.com/marketplace/actions/add-commit
|
||||||
|
with:
|
||||||
|
add: '["tauri-wallet"]'
|
||||||
|
message: '[ci skip] Generate TS types'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
name: Wasm Client
|
name: Wasm Client
|
||||||
|
|
||||||
on:
|
on: [push, pull_request]
|
||||||
pull_request:
|
|
||||||
paths-ignore:
|
|
||||||
- 'explorer/**'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
wasm:
|
wasm:
|
||||||
@@ -29,17 +26,19 @@ jobs:
|
|||||||
command: build
|
command: build
|
||||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --features=coconut
|
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --features=coconut
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
# for some reason this does not seem to work correctly, leave it for later, building is good enough for now
|
||||||
with:
|
# - uses: actions-rs/cargo@v1
|
||||||
command: test
|
# with:
|
||||||
args: --manifest-path clients/webassembly/Cargo.toml
|
# command: test
|
||||||
|
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: fmt
|
command: fmt
|
||||||
args: --manifest-path clients/webassembly/Cargo.toml -- --check
|
args: --manifest-path clients/webassembly/Cargo.toml -- --check
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
# for some reason this does not seem to work correctly, leave it for later, building is good enough for now
|
||||||
with:
|
# - uses: actions-rs/cargo@v1
|
||||||
command: clippy
|
# with:
|
||||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
|
# 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
-7
@@ -11,6 +11,7 @@ target
|
|||||||
/.vscode/settings.json
|
/.vscode/settings.json
|
||||||
validator/.vscode
|
validator/.vscode
|
||||||
sample-configs/validator-config.toml
|
sample-configs/validator-config.toml
|
||||||
|
.vscode
|
||||||
scripts/deploy_qa.sh
|
scripts/deploy_qa.sh
|
||||||
scripts/run_gate.sh
|
scripts/run_gate.sh
|
||||||
scripts/run_mix.sh
|
scripts/run_mix.sh
|
||||||
@@ -24,15 +25,8 @@ v6-topology.json
|
|||||||
/explorer/downloads/topology.json
|
/explorer/downloads/topology.json
|
||||||
/explorer/public/downloads/mixmining.json
|
/explorer/public/downloads/mixmining.json
|
||||||
/explorer/public/downloads/topology.json
|
/explorer/public/downloads/topology.json
|
||||||
/nym-wallet/dist/*
|
|
||||||
/clients/validator/examples/nym-driver-example/current-contract.txt
|
/clients/validator/examples/nym-driver-example/current-contract.txt
|
||||||
validator-api/v4.json
|
validator-api/v4.json
|
||||||
validator-api/v6.json
|
validator-api/v6.json
|
||||||
**/node_modules
|
**/node_modules
|
||||||
validator-api/keypair
|
validator-api/keypair
|
||||||
contracts/mixnet/code_id
|
|
||||||
contracts/mixnet/Justfile
|
|
||||||
contracts/mixnet/Makefile
|
|
||||||
validator-config
|
|
||||||
*.patch
|
|
||||||
validator-api-config.toml
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
2.7.5
|
|
||||||
+879
-228
File diff suppressed because it is too large
Load Diff
Generated
+292
-1363
File diff suppressed because it is too large
Load Diff
+4
-10
@@ -4,20 +4,19 @@
|
|||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
opt-level = "s"
|
opt-level = "s"
|
||||||
overflow-checks = true
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
resolver = "2"
|
|
||||||
members = [
|
members = [
|
||||||
"clients/client-core",
|
"clients/client-core",
|
||||||
"clients/native",
|
"clients/native",
|
||||||
"clients/native/websocket-requests",
|
"clients/native/websocket-requests",
|
||||||
"clients/socks5",
|
"clients/socks5",
|
||||||
"clients/tauri-client/src-tauri",
|
"clients/tauri-client/src-tauri",
|
||||||
|
"clients/webassembly",
|
||||||
"common/client-libs/gateway-client",
|
"common/client-libs/gateway-client",
|
||||||
"common/client-libs/mixnet-client",
|
"common/client-libs/mixnet-client",
|
||||||
"common/client-libs/validator-client",
|
"common/client-libs/validator-client",
|
||||||
@@ -25,14 +24,10 @@ members = [
|
|||||||
"common/config",
|
"common/config",
|
||||||
"common/credentials",
|
"common/credentials",
|
||||||
"common/crypto",
|
"common/crypto",
|
||||||
"common/bandwidth-claim-contract",
|
"common/mixnet-contract",
|
||||||
"common/cosmwasm-smart-contracts/contracts-common",
|
|
||||||
"common/cosmwasm-smart-contracts/mixnet-contract",
|
|
||||||
"common/cosmwasm-smart-contracts/vesting-contract",
|
|
||||||
"common/mixnode-common",
|
"common/mixnode-common",
|
||||||
"common/network-defaults",
|
"common/network-defaults",
|
||||||
"common/nonexhaustive-delayqueue",
|
"common/nonexhaustive-delayqueue",
|
||||||
"common/nymcoconut",
|
|
||||||
"common/nymsphinx",
|
"common/nymsphinx",
|
||||||
"common/nymsphinx/acknowledgements",
|
"common/nymsphinx/acknowledgements",
|
||||||
"common/nymsphinx/addressing",
|
"common/nymsphinx/addressing",
|
||||||
@@ -54,17 +49,16 @@ members = [
|
|||||||
"mixnode",
|
"mixnode",
|
||||||
"service-providers/network-requester",
|
"service-providers/network-requester",
|
||||||
"validator-api",
|
"validator-api",
|
||||||
"validator-api/validator-api-requests",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
default-members = [
|
default-members = [
|
||||||
"clients/native",
|
"clients/native",
|
||||||
"clients/socks5",
|
"clients/socks5",
|
||||||
|
# "clients/webassembly",
|
||||||
"gateway",
|
"gateway",
|
||||||
"service-providers/network-requester",
|
"service-providers/network-requester",
|
||||||
"mixnode",
|
"mixnode",
|
||||||
"validator-api",
|
"validator-api",
|
||||||
"explorer-api",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
exclude = ["explorer", "contracts", "tokenomics-py", "clients/webassembly"]
|
exclude = ["explorer", "contracts"]
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
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
|
|
||||||
fmt: fmt-main fmt-contracts fmt-wallet
|
|
||||||
|
|
||||||
clippy-happy-main:
|
|
||||||
cargo clippy
|
|
||||||
|
|
||||||
clippy-happy-contracts:
|
|
||||||
cargo clippy --manifest-path contracts/Cargo.toml --target wasm32-unknown-unknown
|
|
||||||
|
|
||||||
clippy-happy-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
|
|
||||||
|
|
||||||
test-contracts:
|
|
||||||
cargo test --manifest-path contracts/Cargo.toml --all-features
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
fmt-main:
|
|
||||||
cargo fmt --all
|
|
||||||
|
|
||||||
fmt-contracts:
|
|
||||||
cargo fmt --manifest-path contracts/Cargo.toml --all
|
|
||||||
|
|
||||||
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
|
|
||||||
@@ -5,6 +5,8 @@ SPDX-License-Identifier: Apache-2.0
|
|||||||
|
|
||||||
## The Nym Privacy Platform
|
## The Nym Privacy Platform
|
||||||
|
|
||||||
|
This repository contains the Nym mixnet.
|
||||||
|
|
||||||
The platform is composed of multiple Rust crates. Top-level executable binary crates include:
|
The platform is composed of multiple Rust crates. Top-level executable binary crates include:
|
||||||
|
|
||||||
* nym-mixnode - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers.
|
* nym-mixnode - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers.
|
||||||
@@ -13,7 +15,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
|||||||
* nym-gateway - acts sort of like a mailbox for mixnet messages, removing the need for directly delivery to potentially offline or firewalled devices.
|
* nym-gateway - acts sort of like a mailbox for mixnet messages, removing the need for directly delivery to potentially offline or firewalled devices.
|
||||||
* nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
|
* nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
|
||||||
* nym-explorer - a (projected) block explorer and (existing) mixnet viewer.
|
* nym-explorer - a (projected) block explorer and (existing) mixnet viewer.
|
||||||
* nym-wallet - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
|
* nym-wallet (currently in development)- a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
|
||||||
|
|
||||||
[](https://opensource.org/licenses/Apache-2.0)
|
[](https://opensource.org/licenses/Apache-2.0)
|
||||||
[](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
|
[](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
|
||||||
@@ -21,8 +23,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
|||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
Platform build instructions are available on [our docs site](https://nymtech.net/docs/stable/run-nym-nodes/build-nym).
|
Platform build instructions are available on [our docs site](https://nymtech.net/docs/0.11.0/overview/index/).
|
||||||
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers).
|
|
||||||
|
|
||||||
### Developing
|
### Developing
|
||||||
|
|
||||||
@@ -32,45 +33,6 @@ There's a `.env.sample-dev` file provided which you can rename to `.env` if you
|
|||||||
|
|
||||||
You can chat to us in [Keybase](https://keybase.io). Download their chat app, then click **Teams -> Join a team**. Type **nymtech.friends** into the team name and hit **continue**. For general chat, hang out in the **#general** channel. Our development takes places in the **#dev** channel. Node operators should be in the **#node-operators** channel.
|
You can chat to us in [Keybase](https://keybase.io). Download their chat app, then click **Teams -> Join a team**. Type **nymtech.friends** into the team name and hit **continue**. For general chat, hang out in the **#general** channel. Our development takes places in the **#dev** channel. Node operators should be in the **#node-operators** channel.
|
||||||
|
|
||||||
### Rewards
|
|
||||||
|
|
||||||
Node, node operator and delegator rewards are determined according to the principles laid out in the section 6 of [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf). Below is a TLDR of the variables and formulas involved in calculating the epoch rewards. Initial reward pool is set to 250 million Nym, making the circulating supply 750 million Nym.
|
|
||||||
|
|
||||||
|Symbol|Definition|
|
|
||||||
|---|---|
|
|
||||||
|<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=\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=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.
|
|
||||||
|
|
||||||
Node reward for node `i` is determined as:
|
|
||||||
|
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)">
|
|
||||||
|
|
||||||
where:
|
|
||||||
|
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=\sigma^'_{i} = min\{\sigma_{i}, 1/k\}">
|
|
||||||
|
|
||||||
and
|
|
||||||
|
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=\lambda^'_{i} = min\{\lambda_{i}, 1/k\}">
|
|
||||||
|
|
||||||
Operator of node `i` is credited with the following amount:
|
|
||||||
|
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}">
|
|
||||||
|
|
||||||
Delegate with stake `s` recieves:
|
|
||||||
|
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}">
|
|
||||||
|
|
||||||
where `s'` is stake `s` scaled over total token circulating supply.
|
|
||||||
|
|
||||||
### Licensing and copyright information
|
### Licensing and copyright information
|
||||||
|
|
||||||
This program is available as open source under the terms of the Apache 2.0 license. However, some elements are being licensed under CC0-1.0 and MIT. For accurate information, please check individual files.
|
This program is available as open source under the terms of the Apache 2.0 license. However, some elements are being licensed under CC0-1.0 and MIT. For accurate information, please check individual files.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "client-core"
|
name = "client-core"
|
||||||
version = "0.12.0"
|
version = "0.11.0"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@@ -30,6 +30,3 @@ validator-client = { path = "../../common/client-libs/validator-client" }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
[features]
|
|
||||||
coconut = []
|
|
||||||
@@ -13,6 +13,7 @@ use nymsphinx::utils::sample_poisson_duration;
|
|||||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tokio::runtime::Handle;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
|
|
||||||
@@ -164,8 +165,8 @@ impl LoopCoverTrafficStream<OsRng> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(mut self) -> JoinHandle<()> {
|
pub fn start(mut self, handle: &Handle) -> JoinHandle<()> {
|
||||||
tokio::spawn(async move {
|
handle.spawn(async move {
|
||||||
self.run().await;
|
self.run().await;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,9 +79,9 @@ impl KeyManager {
|
|||||||
))?;
|
))?;
|
||||||
|
|
||||||
let gateway_shared_key: SharedKeys =
|
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
|
// TODO: ack key is never stored so it is generated now. But perhaps it should be stored
|
||||||
// after all for consistency sake?
|
// after all for consistency sake?
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use futures::StreamExt;
|
|||||||
use gateway_client::GatewayClient;
|
use gateway_client::GatewayClient;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nymsphinx::forwarding::packet::MixPacket;
|
use nymsphinx::forwarding::packet::MixPacket;
|
||||||
|
use tokio::runtime::Handle;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
pub type BatchMixMessageSender = mpsc::UnboundedSender<Vec<MixPacket>>;
|
pub type BatchMixMessageSender = mpsc::UnboundedSender<Vec<MixPacket>>;
|
||||||
@@ -71,8 +72,8 @@ impl MixTrafficController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(mut self) -> JoinHandle<()> {
|
pub fn start(mut self, handle: &Handle) -> JoinHandle<()> {
|
||||||
tokio::spawn(async move {
|
handle.spawn(async move {
|
||||||
self.run().await;
|
self.run().await;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use nymsphinx::addressing::clients::Recipient;
|
|||||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use tokio::runtime::Handle;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
mod acknowledgement_control;
|
mod acknowledgement_control;
|
||||||
@@ -169,8 +170,10 @@ impl RealMessagesController<OsRng> {
|
|||||||
self.ack_control = Some(ack_control_fut.await.unwrap());
|
self.ack_control = Some(ack_control_fut.await.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(mut self) -> JoinHandle<Self> {
|
// &Handle is only passed for consistency sake with other client modules, but I think
|
||||||
tokio::spawn(async move {
|
// 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.run().await;
|
||||||
self
|
self
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use nymsphinx::params::{ReplySurbEncryptionAlgorithm, ReplySurbKeyDigestAlgorith
|
|||||||
use nymsphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage};
|
use nymsphinx::receiver::{MessageReceiver, MessageRecoveryError, ReconstructedMessage};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tokio::runtime::Handle;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
// Buffer Requests to say "hey, send any reconstructed messages to this channel"
|
// Buffer Requests to say "hey, send any reconstructed messages to this channel"
|
||||||
@@ -290,8 +291,8 @@ impl RequestReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(mut self) -> JoinHandle<()> {
|
fn start(mut self, handle: &Handle) -> JoinHandle<()> {
|
||||||
tokio::spawn(async move {
|
handle.spawn(async move {
|
||||||
while let Some(request) = self.query_receiver.next().await {
|
while let Some(request) = self.query_receiver.next().await {
|
||||||
match request {
|
match request {
|
||||||
ReceivedBufferMessage::ReceiverAnnounce(sender) => {
|
ReceivedBufferMessage::ReceiverAnnounce(sender) => {
|
||||||
@@ -321,8 +322,8 @@ impl FragmentedMessageReceiver {
|
|||||||
mixnet_packet_receiver,
|
mixnet_packet_receiver,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn start(mut self) -> JoinHandle<()> {
|
fn start(mut self, handle: &Handle) -> JoinHandle<()> {
|
||||||
tokio::spawn(async move {
|
handle.spawn(async move {
|
||||||
while let Some(new_messages) = self.mixnet_packet_receiver.next().await {
|
while let Some(new_messages) = self.mixnet_packet_receiver.next().await {
|
||||||
self.received_buffer.handle_new_received(new_messages).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?
|
// TODO: should we do anything with JoinHandle(s) returned by start methods?
|
||||||
self.fragmented_message_receiver.start();
|
self.fragmented_message_receiver.start(handle);
|
||||||
self.request_receiver.start();
|
self.request_receiver.start(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ impl ReplyKeyStorage {
|
|||||||
) -> Result<(), ReplyKeyStorageError> {
|
) -> Result<(), ReplyKeyStorageError> {
|
||||||
let digest = encryption_key.compute_digest();
|
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)),
|
Err(e) => Err(ReplyKeyStorageError::DbWriteError(e)),
|
||||||
Ok(existing_key) => {
|
Ok(existing_key) => {
|
||||||
if existing_key.is_some() {
|
if existing_key.is_some() {
|
||||||
@@ -79,7 +79,7 @@ impl ReplyKeyStorage {
|
|||||||
&self,
|
&self,
|
||||||
key_digest: EncryptionKeyDigest,
|
key_digest: EncryptionKeyDigest,
|
||||||
) -> Result<Option<SurbEncryptionKey>, ReplyKeyStorageError> {
|
) -> 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)),
|
Err(e) => Err(ReplyKeyStorageError::DbReadError(e)),
|
||||||
Ok(existing_key) => {
|
Ok(existing_key) => {
|
||||||
Ok(existing_key.map(|existing_key| self.read_encryption_key(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::sync::Arc;
|
||||||
use std::time;
|
use std::time;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use tokio::runtime::Handle;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use topology::{nym_topology_from_bonds, NymTopology};
|
use topology::{nym_topology_from_bonds, NymTopology};
|
||||||
@@ -256,7 +257,7 @@ impl TopologyRefresher {
|
|||||||
Ok(mixes) => mixes,
|
Ok(mixes) => mixes,
|
||||||
};
|
};
|
||||||
|
|
||||||
let gateways = match self.validator_client.get_cached_gateways().await {
|
let gateways = match self.validator_client.get_cached_active_gateways().await {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("failed to get network gateways - {}", err);
|
error!("failed to get network gateways - {}", err);
|
||||||
return None;
|
return None;
|
||||||
@@ -303,8 +304,8 @@ impl TopologyRefresher {
|
|||||||
self.topology_accessor.is_routable().await
|
self.topology_accessor.is_routable().await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(mut self) -> JoinHandle<()> {
|
pub fn start(mut self, handle: &Handle) -> JoinHandle<()> {
|
||||||
tokio::spawn(async move {
|
handle.spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
tokio::time::sleep(self.refresh_rate).await;
|
tokio::time::sleep(self.refresh_rate).await;
|
||||||
self.refresh().await;
|
self.refresh().await;
|
||||||
|
|||||||
@@ -22,10 +22,7 @@ const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20)
|
|||||||
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
|
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
|
||||||
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
|
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
|
||||||
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
|
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
|
||||||
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
|
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_millis(1_500);
|
||||||
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
|
|
||||||
// bandwidth bridging protocol, we can come back to a smaller timeout value
|
|
||||||
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
|
|
||||||
|
|
||||||
pub fn missing_string_value() -> String {
|
pub fn missing_string_value() -> String {
|
||||||
MISSING_VALUE.to_string()
|
MISSING_VALUE.to_string()
|
||||||
@@ -103,44 +100,15 @@ impl<T: NymConfig> Config<T> {
|
|||||||
self::Client::<T>::default_reply_encryption_key_store_path(&id);
|
self::Client::<T>::default_reply_encryption_key_store_path(&id);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
if self
|
|
||||||
.client
|
|
||||||
.backup_bandwidth_token_keys_dir
|
|
||||||
.as_os_str()
|
|
||||||
.is_empty()
|
|
||||||
{
|
|
||||||
self.client.backup_bandwidth_token_keys_dir =
|
|
||||||
self::Client::<T>::default_backup_bandwidth_token_keys_dir(&id);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.client.id = id;
|
self.client.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_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) {
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
pub fn with_gateway_listener<S: Into<String>>(&mut self, gateway_listener: S) {
|
||||||
pub fn with_eth_private_key<S: Into<String>>(&mut self, eth_private_key: S) {
|
self.client.gateway_listener = gateway_listener.into();
|
||||||
self.client.eth_private_key = eth_private_key.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
pub fn with_eth_endpoint<S: Into<String>>(&mut self, eth_endpoint: S) {
|
|
||||||
self.client.eth_endpoint = eth_endpoint.into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
|
pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
|
||||||
@@ -161,10 +129,6 @@ impl<T: NymConfig> Config<T> {
|
|||||||
self.client.id.clone()
|
self.client.id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_testnet_mode(&self) -> bool {
|
|
||||||
self.client.testnet_mode
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_nym_root_directory(&self) -> PathBuf {
|
pub fn get_nym_root_directory(&self) -> PathBuf {
|
||||||
self.client.nym_root_directory.clone()
|
self.client.nym_root_directory.clone()
|
||||||
}
|
}
|
||||||
@@ -202,30 +166,11 @@ impl<T: NymConfig> Config<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_gateway_id(&self) -> String {
|
pub fn get_gateway_id(&self) -> String {
|
||||||
self.client.gateway_endpoint.gateway_id.clone()
|
self.client.gateway_id.clone()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_gateway_owner(&self) -> String {
|
|
||||||
self.client.gateway_endpoint.gateway_owner.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_gateway_listener(&self) -> String {
|
pub fn get_gateway_listener(&self) -> String {
|
||||||
self.client.gateway_endpoint.gateway_listener.clone()
|
self.client.gateway_listener.clone()
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
pub fn get_backup_bandwidth_token_keys_dir(&self) -> PathBuf {
|
|
||||||
self.client.backup_bandwidth_token_keys_dir.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
pub fn get_eth_endpoint(&self) -> String {
|
|
||||||
self.client.eth_endpoint.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
pub fn get_eth_private_key(&self) -> String {
|
|
||||||
self.client.eth_private_key.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug getters
|
// Debug getters
|
||||||
@@ -280,19 +225,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)]
|
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||||
pub struct Client<T> {
|
pub struct Client<T> {
|
||||||
/// Version of the client for which this configuration was created.
|
/// Version of the client for which this configuration was created.
|
||||||
@@ -302,11 +234,6 @@ pub struct Client<T> {
|
|||||||
/// ID specifies the human readable ID of this particular client.
|
/// ID specifies the human readable ID of this particular client.
|
||||||
id: String,
|
id: String,
|
||||||
|
|
||||||
/// Indicates whether this client is running in a 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.
|
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
||||||
validator_api_urls: Vec<Url>,
|
validator_api_urls: Vec<Url>,
|
||||||
|
|
||||||
@@ -334,22 +261,12 @@ pub struct Client<T> {
|
|||||||
/// sent but not received back.
|
/// sent but not received back.
|
||||||
reply_encryption_key_store_path: PathBuf,
|
reply_encryption_key_store_path: PathBuf,
|
||||||
|
|
||||||
/// Information regarding how the client should send data to gateway.
|
/// gateway_id specifies ID of the gateway to which the client should send messages.
|
||||||
gateway_endpoint: GatewayEndpoint,
|
/// If initially omitted, a random gateway will be chosen from the available topology.
|
||||||
|
gateway_id: String,
|
||||||
|
|
||||||
/// Path to directory containing public/private keys used for bandwidth token purchase.
|
/// Address of the gateway listener to which all client requests should be sent.
|
||||||
/// Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
|
gateway_listener: String,
|
||||||
/// The public key is the name of the file, while the private key is the content.
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
backup_bandwidth_token_keys_dir: PathBuf,
|
|
||||||
|
|
||||||
/// Ethereum private key.
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
eth_private_key: String,
|
|
||||||
|
|
||||||
/// Address to an Ethereum full node.
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
eth_endpoint: String,
|
|
||||||
|
|
||||||
/// nym_home_directory specifies absolute path to the home nym Clients directory.
|
/// nym_home_directory specifies absolute path to the home nym Clients directory.
|
||||||
/// It is expected to use default value and hence .toml file should not redefine this field.
|
/// It is expected to use default value and hence .toml file should not redefine this field.
|
||||||
@@ -365,7 +282,6 @@ impl<T: NymConfig> Default for Client<T> {
|
|||||||
Client {
|
Client {
|
||||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
id: "".to_string(),
|
id: "".to_string(),
|
||||||
testnet_mode: false,
|
|
||||||
validator_api_urls: default_api_endpoints(),
|
validator_api_urls: default_api_endpoints(),
|
||||||
private_identity_key_file: Default::default(),
|
private_identity_key_file: Default::default(),
|
||||||
public_identity_key_file: Default::default(),
|
public_identity_key_file: Default::default(),
|
||||||
@@ -374,13 +290,8 @@ impl<T: NymConfig> Default for Client<T> {
|
|||||||
gateway_shared_key_file: Default::default(),
|
gateway_shared_key_file: Default::default(),
|
||||||
ack_key_file: Default::default(),
|
ack_key_file: Default::default(),
|
||||||
reply_encryption_key_store_path: Default::default(),
|
reply_encryption_key_store_path: Default::default(),
|
||||||
gateway_endpoint: Default::default(),
|
gateway_id: "".to_string(),
|
||||||
#[cfg(not(feature = "coconut"))]
|
gateway_listener: "".to_string(),
|
||||||
backup_bandwidth_token_keys_dir: Default::default(),
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
eth_private_key: "".to_string(),
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
eth_endpoint: "".to_string(),
|
|
||||||
nym_root_directory: T::default_root_directory(),
|
nym_root_directory: T::default_root_directory(),
|
||||||
super_struct: Default::default(),
|
super_struct: Default::default(),
|
||||||
}
|
}
|
||||||
@@ -415,11 +326,6 @@ impl<T: NymConfig> Client<T> {
|
|||||||
fn default_reply_encryption_key_store_path(id: &str) -> PathBuf {
|
fn default_reply_encryption_key_store_path(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(Some(id)).join("reply_key_store")
|
T::default_data_directory(Some(id)).join("reply_key_store")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
fn default_backup_bandwidth_token_keys_dir(id: &str) -> PathBuf {
|
|
||||||
T::default_data_directory(Some(id)).join("backup_bandwidth_token_keys")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client"
|
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>"]
|
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ dirs = "3.0" # for determining default store directories in config
|
|||||||
dotenv = "0.15.0" # for obtaining environmental variables (only used for RUST_LOG for time being)
|
dotenv = "0.15.0" # for obtaining environmental variables (only used for RUST_LOG for time being)
|
||||||
log = "0.4" # self explanatory
|
log = "0.4" # self explanatory
|
||||||
pretty_env_logger = "0.4" # for formatting log messages
|
pretty_env_logger = "0.4" # for formatting log messages
|
||||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use
|
rand = {version = "0.7.3", features = ["wasm-bindgen"]} # rng-related traits + some rng implementation to use
|
||||||
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
|
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
|
||||||
sled = "0.34" # for storage of replySURB decryption keys
|
sled = "0.34" # for storage of replySURB decryption keys
|
||||||
tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
||||||
@@ -44,14 +43,9 @@ topology = { path = "../../common/topology" }
|
|||||||
websocket-requests = { path = "websocket-requests" }
|
websocket-requests = { path = "websocket-requests" }
|
||||||
validator-client = { path = "../../common/client-libs/validator-client" }
|
validator-client = { path = "../../common/client-libs/validator-client" }
|
||||||
version-checker = { path = "../../common/version-checker" }
|
version-checker = { path = "../../common/version-checker" }
|
||||||
network-defaults = { path = "../../common/network-defaults" }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
|
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
|
||||||
eth = []
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json = "1.0" # for the "textsend" example
|
serde_json = "1.0" # for the "textsend" example
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use vergen::{vergen, Config};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
vergen(Config::default()).expect("failed to extract build metadata")
|
|
||||||
}
|
|
||||||
@@ -3,5 +3,6 @@ module github.com/nymtech/nym/clients/native/examples/go
|
|||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/btcsuite/btcutil v1.0.2 // indirect
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
)
|
)
|
||||||
|
|||||||
+24
-24
@@ -2441,9 +2441,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.14.8",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
|
||||||
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
|
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -3806,9 +3806,9 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.2.0",
|
"version": "3.1.23",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
|
||||||
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
|
"integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -3943,9 +3943,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nth-check": {
|
"node_modules/nth-check": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
|
||||||
"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
|
"integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"boolbase": "^1.0.0"
|
"boolbase": "^1.0.0"
|
||||||
},
|
},
|
||||||
@@ -6085,9 +6085,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/url-parse": {
|
"node_modules/url-parse": {
|
||||||
"version": "1.5.7",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||||
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
|
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"querystringify": "^2.1.1",
|
"querystringify": "^2.1.1",
|
||||||
@@ -8853,9 +8853,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.14.8",
|
"version": "1.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
|
||||||
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
|
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"for-in": {
|
"for-in": {
|
||||||
@@ -9871,9 +9871,9 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"nanoid": {
|
"nanoid": {
|
||||||
"version": "3.2.0",
|
"version": "3.1.23",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
|
||||||
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
|
"integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
@@ -9984,9 +9984,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nth-check": {
|
"nth-check": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
|
||||||
"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
|
"integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"boolbase": "^1.0.0"
|
"boolbase": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -11733,9 +11733,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"url-parse": {
|
"url-parse": {
|
||||||
"version": "1.5.7",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||||
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
|
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"querystringify": "^2.1.1",
|
"querystringify": "^2.1.1",
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ async fn send_file_with_reply() {
|
|||||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||||
|
|
||||||
let recipient = get_self_address(&mut ws_stream).await;
|
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();
|
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 (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||||
|
|
||||||
let recipient = get_self_address(&mut ws_stream).await;
|
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();
|
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 (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||||
|
|
||||||
let recipient = get_self_address(&mut ws_stream).await;
|
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!({
|
let send_request = json!({
|
||||||
"type" : "send",
|
"type" : "send",
|
||||||
@@ -76,7 +76,7 @@ async fn send_text_without_reply() {
|
|||||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||||
|
|
||||||
let recipient = get_self_address(&mut ws_stream).await;
|
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!({
|
let send_request = json!({
|
||||||
"type" : "send",
|
"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,
|
// 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
|
// I think it's useful to have comments attached to the saved config file to explain behaviour of
|
||||||
// particular fields.
|
// 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#"
|
r#"
|
||||||
# This is a TOML config file.
|
# This is a TOML config file.
|
||||||
# For more information, see https://github.com/toml-lang/toml
|
# For more information, see https://github.com/toml-lang/toml
|
||||||
@@ -19,10 +19,6 @@ version = '{{ client.version }}'
|
|||||||
# Human readable ID of this particular client.
|
# Human readable ID of this particular client.
|
||||||
id = '{{ client.id }}'
|
id = '{{ client.id }}'
|
||||||
|
|
||||||
# Indicates whether this client is running in a 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.
|
# Addresses to APIs running on validator from which the client gets the view of the network.
|
||||||
validator_api_urls = [
|
validator_api_urls = [
|
||||||
{{#each client.validator_api_urls }}
|
{{#each client.validator_api_urls }}
|
||||||
@@ -46,19 +42,14 @@ public_encryption_key_file = '{{ client.public_encryption_key_file }}'
|
|||||||
# sent but not received back.
|
# sent but not received back.
|
||||||
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
||||||
|
|
||||||
# Path to directory containing public/private keys used for bandwidth token purchase.
|
|
||||||
# Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
|
|
||||||
# The public key is the name of the file, while the private key is the content.
|
|
||||||
backup_bandwidth_token_keys_dir = '{{ client.backup_bandwidth_token_keys_dir }}'
|
|
||||||
|
|
||||||
# Ethereum private key.
|
|
||||||
eth_private_key = '{{ client.eth_private_key }}'
|
|
||||||
|
|
||||||
# Addess to an Ethereum full node.
|
|
||||||
eth_endpoint = '{{ client.eth_endpoint }}'
|
|
||||||
|
|
||||||
##### additional client config options #####
|
##### additional client config options #####
|
||||||
|
|
||||||
|
# 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
|
# A gateway specific, optional, base58 stringified shared key used for
|
||||||
# communication with particular gateway.
|
# communication with particular gateway.
|
||||||
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
||||||
@@ -72,17 +63,6 @@ ack_key_file = '{{ client.ack_key_file }}'
|
|||||||
# Absolute path to the home Nym Clients directory.
|
# Absolute path to the home Nym Clients directory.
|
||||||
nym_root_directory = '{{ client.nym_root_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 #####
|
##### socket config options #####
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::client::config::{Config, SocketType};
|
||||||
|
use crate::websocket;
|
||||||
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
|
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
|
||||||
use client_core::client::inbound_messages::{
|
use client_core::client::inbound_messages::{
|
||||||
InputMessage, InputMessageReceiver, InputMessageSender,
|
InputMessage, InputMessageReceiver, InputMessageSender,
|
||||||
@@ -22,7 +24,6 @@ use client_core::client::topology_control::{
|
|||||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||||
use crypto::asymmetric::identity;
|
use crypto::asymmetric::identity;
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use gateway_client::bandwidth::BandwidthController;
|
|
||||||
use gateway_client::{
|
use gateway_client::{
|
||||||
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
|
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
|
||||||
MixnetMessageSender,
|
MixnetMessageSender,
|
||||||
@@ -32,9 +33,12 @@ use nymsphinx::addressing::clients::Recipient;
|
|||||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||||
use nymsphinx::anonymous_replies::ReplySurb;
|
use nymsphinx::anonymous_replies::ReplySurb;
|
||||||
use nymsphinx::receiver::ReconstructedMessage;
|
use nymsphinx::receiver::ReconstructedMessage;
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
use crate::client::config::{Config, SocketType};
|
#[cfg(feature = "coconut")]
|
||||||
use crate::websocket;
|
use coconut_interface::Credential;
|
||||||
|
#[cfg(feature = "coconut")]
|
||||||
|
use credentials::{bandwidth::prepare_for_spending, obtain_aggregate_verification_key};
|
||||||
|
|
||||||
pub(crate) mod config;
|
pub(crate) mod config;
|
||||||
|
|
||||||
@@ -43,6 +47,11 @@ pub struct NymClient {
|
|||||||
/// key filepaths, etc.
|
/// key filepaths, etc.
|
||||||
config: Config,
|
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.
|
/// KeyManager object containing smart pointers to all relevant keys used by the client.
|
||||||
key_manager: KeyManager,
|
key_manager: KeyManager,
|
||||||
|
|
||||||
@@ -62,6 +71,7 @@ impl NymClient {
|
|||||||
let key_manager = KeyManager::load_keys(&pathfinder).expect("failed to load stored keys");
|
let key_manager = KeyManager::load_keys(&pathfinder).expect("failed to load stored keys");
|
||||||
|
|
||||||
NymClient {
|
NymClient {
|
||||||
|
runtime: Runtime::new().unwrap(),
|
||||||
config,
|
config,
|
||||||
key_manager,
|
key_manager,
|
||||||
input_tx: None,
|
input_tx: None,
|
||||||
@@ -87,6 +97,9 @@ impl NymClient {
|
|||||||
mix_tx: BatchMixMessageSender,
|
mix_tx: BatchMixMessageSender,
|
||||||
) {
|
) {
|
||||||
info!("Starting loop cover traffic stream...");
|
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(
|
LoopCoverTrafficStream::new(
|
||||||
self.key_manager.ack_key(),
|
self.key_manager.ack_key(),
|
||||||
@@ -99,7 +112,7 @@ impl NymClient {
|
|||||||
self.as_mix_recipient(),
|
self.as_mix_recipient(),
|
||||||
topology_accessor,
|
topology_accessor,
|
||||||
)
|
)
|
||||||
.start();
|
.start(self.runtime.handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_real_traffic_controller(
|
fn start_real_traffic_controller(
|
||||||
@@ -121,6 +134,10 @@ impl NymClient {
|
|||||||
);
|
);
|
||||||
|
|
||||||
info!("Starting real traffic stream...");
|
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(
|
RealMessagesController::new(
|
||||||
controller_config,
|
controller_config,
|
||||||
@@ -130,7 +147,7 @@ impl NymClient {
|
|||||||
topology_accessor,
|
topology_accessor,
|
||||||
reply_key_storage,
|
reply_key_storage,
|
||||||
)
|
)
|
||||||
.start();
|
.start(self.runtime.handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
// buffer controlling all messages fetched from provider
|
// buffer controlling all messages fetched from provider
|
||||||
@@ -148,10 +165,35 @@ impl NymClient {
|
|||||||
mixnet_receiver,
|
mixnet_receiver,
|
||||||
reply_key_storage,
|
reply_key_storage,
|
||||||
)
|
)
|
||||||
.start()
|
.start(self.runtime.handle())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_gateway_client(
|
#[cfg(feature = "coconut")]
|
||||||
|
async fn prepare_coconut_credential(&self) -> Credential {
|
||||||
|
let verification_key = obtain_aggregate_verification_key(
|
||||||
|
&self.config.get_base().get_validator_api_endpoints(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("could not obtain aggregate verification key of validators");
|
||||||
|
|
||||||
|
let bandwidth_credential = credentials::bandwidth::obtain_signature(
|
||||||
|
&self.key_manager.identity_keypair().public_key().to_bytes(),
|
||||||
|
&self.config.get_base().get_validator_api_endpoints(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("could not obtain bandwidth credential");
|
||||||
|
// the above would presumably be loaded from a file
|
||||||
|
|
||||||
|
// the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
|
||||||
|
prepare_for_spending(
|
||||||
|
&self.key_manager.identity_keypair().public_key().to_bytes(),
|
||||||
|
&bandwidth_credential,
|
||||||
|
&verification_key,
|
||||||
|
)
|
||||||
|
.expect("could not prepare out bandwidth credential for spending")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_gateway_client(
|
||||||
&mut self,
|
&mut self,
|
||||||
mixnet_message_sender: MixnetMessageSender,
|
mixnet_message_sender: MixnetMessageSender,
|
||||||
ack_sender: AcknowledgementSender,
|
ack_sender: AcknowledgementSender,
|
||||||
@@ -160,10 +202,6 @@ impl NymClient {
|
|||||||
if gateway_id.is_empty() {
|
if gateway_id.is_empty() {
|
||||||
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
|
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();
|
let gateway_address = self.config.get_base().get_gateway_listener();
|
||||||
if gateway_address.is_empty() {
|
if gateway_address.is_empty() {
|
||||||
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
|
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
|
||||||
@@ -172,45 +210,35 @@ impl NymClient {
|
|||||||
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
||||||
.expect("provided gateway id is invalid!");
|
.expect("provided gateway id is invalid!");
|
||||||
|
|
||||||
#[cfg(feature = "coconut")]
|
self.runtime.block_on(async {
|
||||||
let bandwidth_controller = BandwidthController::new(
|
#[cfg(feature = "coconut")]
|
||||||
self.config.get_base().get_validator_api_endpoints(),
|
let coconut_credential = self.prepare_coconut_credential().await;
|
||||||
*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(
|
let mut gateway_client = GatewayClient::new(
|
||||||
gateway_address,
|
gateway_address,
|
||||||
self.key_manager.identity_keypair(),
|
self.key_manager.identity_keypair(),
|
||||||
gateway_identity,
|
gateway_identity,
|
||||||
gateway_owner,
|
Some(self.key_manager.gateway_shared_key()),
|
||||||
Some(self.key_manager.gateway_shared_key()),
|
mixnet_message_sender,
|
||||||
mixnet_message_sender,
|
ack_sender,
|
||||||
ack_sender,
|
self.config.get_base().get_gateway_response_timeout(),
|
||||||
self.config.get_base().get_gateway_response_timeout(),
|
);
|
||||||
Some(bandwidth_controller),
|
|
||||||
);
|
|
||||||
|
|
||||||
if self.config.get_base().get_testnet_mode() {
|
gateway_client
|
||||||
gateway_client.set_testnet_mode(true)
|
.authenticate_and_start(
|
||||||
}
|
#[cfg(feature = "coconut")]
|
||||||
gateway_client
|
Some(coconut_credential),
|
||||||
.authenticate_and_start()
|
)
|
||||||
.await
|
.await
|
||||||
.expect("could not authenticate and start up the gateway connection");
|
.expect("could not authenticate and start up the gateway connection");
|
||||||
|
|
||||||
gateway_client
|
gateway_client
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// future responsible for periodically polling directory server and updating
|
// future responsible for periodically polling directory server and updating
|
||||||
// the current global view of topology
|
// the current global view of topology
|
||||||
async fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
||||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||||
self.config.get_base().get_validator_api_endpoints(),
|
self.config.get_base().get_validator_api_endpoints(),
|
||||||
self.config.get_base().get_topology_refresh_rate(),
|
self.config.get_base().get_topology_refresh_rate(),
|
||||||
@@ -221,10 +249,13 @@ impl NymClient {
|
|||||||
// before returning, block entire runtime to refresh the current network view so that any
|
// before returning, block entire runtime to refresh the current network view so that any
|
||||||
// components depending on topology would see a non-empty view
|
// components depending on topology would see a non-empty view
|
||||||
info!("Obtaining initial network topology");
|
info!("Obtaining initial network topology");
|
||||||
topology_refresher.refresh().await;
|
self.runtime.block_on(topology_refresher.refresh());
|
||||||
|
|
||||||
// TODO: a slightly more graceful termination here
|
// 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!(
|
panic!(
|
||||||
"The current network topology seem to be insufficient to route any packets through\
|
"The current network topology seem to be insufficient to route any packets through\
|
||||||
- check if enough nodes and a gateway are online"
|
- check if enough nodes and a gateway are online"
|
||||||
@@ -232,7 +263,7 @@ impl NymClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
info!("Starting topology refresher...");
|
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)
|
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
|
||||||
@@ -245,7 +276,7 @@ impl NymClient {
|
|||||||
gateway_client: GatewayClient,
|
gateway_client: GatewayClient,
|
||||||
) {
|
) {
|
||||||
info!("Starting mix traffic controller...");
|
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(
|
fn start_websocket_listener(
|
||||||
@@ -258,7 +289,8 @@ impl NymClient {
|
|||||||
let websocket_handler =
|
let websocket_handler =
|
||||||
websocket::Handler::new(msg_input, buffer_requester, self.as_mix_recipient());
|
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
|
/// EXPERIMENTAL DIRECT RUST API
|
||||||
@@ -305,9 +337,9 @@ impl NymClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
|
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
|
||||||
pub async fn run_forever(&mut self) {
|
pub fn run_forever(&mut self) {
|
||||||
self.start().await;
|
self.start();
|
||||||
if let Err(e) = tokio::signal::ctrl_c().await {
|
if let Err(e) = self.runtime.block_on(tokio::signal::ctrl_c()) {
|
||||||
error!(
|
error!(
|
||||||
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
|
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
|
||||||
e
|
e
|
||||||
@@ -319,7 +351,7 @@ impl NymClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
info!("Starting nym client");
|
info!("Starting nym client");
|
||||||
// channels for inter-component communication
|
// channels for inter-component communication
|
||||||
// TODO: make the channels be internally created by the relevant components
|
// TODO: make the channels be internally created by the relevant components
|
||||||
@@ -351,17 +383,14 @@ impl NymClient {
|
|||||||
|
|
||||||
// the components are started in very specific order. Unless you know what you are doing,
|
// the components are started in very specific order. Unless you know what you are doing,
|
||||||
// do not change that.
|
// do not change that.
|
||||||
self.start_topology_refresher(shared_topology_accessor.clone())
|
self.start_topology_refresher(shared_topology_accessor.clone());
|
||||||
.await;
|
|
||||||
self.start_received_messages_buffer_controller(
|
self.start_received_messages_buffer_controller(
|
||||||
received_buffer_request_receiver,
|
received_buffer_request_receiver,
|
||||||
mixnet_messages_receiver,
|
mixnet_messages_receiver,
|
||||||
reply_key_storage.clone(),
|
reply_key_storage.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let gateway_client = self
|
let gateway_client = self.start_gateway_client(mixnet_messages_sender, ack_sender);
|
||||||
.start_gateway_client(mixnet_messages_sender, ack_sender)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
|
self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
|
||||||
self.start_real_traffic_controller(
|
self.start_real_traffic_controller(
|
||||||
|
|||||||
@@ -1,23 +1,15 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::client::config::Config;
|
||||||
|
use crate::commands::override_config;
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use client_core::client::key_manager::KeyManager;
|
use client_core::client::key_manager::KeyManager;
|
||||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use coconut_interface::{hash_to_scalar, Credential, Parameters};
|
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use credentials::coconut::bandwidth::{
|
|
||||||
obtain_signature, prepare_for_spending, BandwidthVoucherAttributes, TOTAL_ATTRIBUTES,
|
|
||||||
};
|
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use credentials::obtain_aggregate_verification_key;
|
|
||||||
use crypto::asymmetric::{encryption, identity};
|
use crypto::asymmetric::{encryption, identity};
|
||||||
use gateway_client::GatewayClient;
|
use gateway_client::GatewayClient;
|
||||||
use gateway_requests::registration::handshake::SharedKeys;
|
use gateway_requests::registration::handshake::SharedKeys;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use network_defaults::BANDWIDTH_VALUE;
|
|
||||||
use nymsphinx::addressing::clients::Recipient;
|
use nymsphinx::addressing::clients::Recipient;
|
||||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
@@ -29,17 +21,8 @@ use std::time::Duration;
|
|||||||
use topology::{filter::VersionFilterable, gateway};
|
use topology::{filter::VersionFilterable, gateway};
|
||||||
use url::Url;
|
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> {
|
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||||
let app = App::new("init")
|
App::new("init")
|
||||||
.about("Initialise a Nym client. Do this first!")
|
.about("Initialise a Nym client. Do this first!")
|
||||||
.arg(Arg::with_name("id")
|
.arg(Arg::with_name("id")
|
||||||
.long("id")
|
.long("id")
|
||||||
@@ -53,9 +36,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("validators")
|
.arg(Arg::with_name("validators")
|
||||||
.long("validators")
|
.long("validators")
|
||||||
.help("Comma separated list of rest endpoints of the validators")
|
.help("Comma separated list of rest endpoints of the validators")
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("disable-socket")
|
.arg(Arg::with_name("disable-socket")
|
||||||
.long("disable-socket")
|
.long("disable-socket")
|
||||||
@@ -71,61 +54,7 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.long("fastmode")
|
.long("fastmode")
|
||||||
.hidden(true) // this will prevent this flag from being displayed in `--help`
|
.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")
|
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
|
||||||
);
|
|
||||||
#[cfg(feature = "eth")]
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
let app = app
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(TESTNET_MODE_ARG_NAME)
|
|
||||||
.long(TESTNET_MODE_ARG_NAME)
|
|
||||||
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
|
||||||
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
|
||||||
.long(ETH_ENDPOINT_ARG_NAME)
|
|
||||||
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT)
|
|
||||||
.required(true))
|
|
||||||
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
|
|
||||||
.long(ETH_PRIVATE_KEY_ARG_NAME)
|
|
||||||
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
|
|
||||||
.required(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
app
|
|
||||||
}
|
|
||||||
|
|
||||||
// this behaviour should definitely be changed, we shouldn't
|
|
||||||
// need to get bandwidth credential for registration
|
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
async fn _prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential {
|
|
||||||
let verification_key = obtain_aggregate_verification_key(validators)
|
|
||||||
.await
|
|
||||||
.expect("could not obtain aggregate verification key of validators");
|
|
||||||
|
|
||||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
|
||||||
let bandwidth_credential_attributes = BandwidthVoucherAttributes {
|
|
||||||
serial_number: params.random_scalar(),
|
|
||||||
binding_number: params.random_scalar(),
|
|
||||||
voucher_value: hash_to_scalar(BANDWIDTH_VALUE.to_be_bytes()),
|
|
||||||
voucher_info: hash_to_scalar(String::from("BandwidthVoucher").as_bytes()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let bandwidth_credential =
|
|
||||||
obtain_signature(¶ms, &bandwidth_credential_attributes, validators)
|
|
||||||
.await
|
|
||||||
.expect("could not obtain bandwidth credential");
|
|
||||||
|
|
||||||
prepare_for_spending(
|
|
||||||
raw_identity,
|
|
||||||
&bandwidth_credential,
|
|
||||||
&bandwidth_credential_attributes,
|
|
||||||
&verification_key,
|
|
||||||
)
|
|
||||||
.expect("could not prepare out bandwidth credential for spending")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn register_with_gateway(
|
async fn register_with_gateway(
|
||||||
@@ -136,7 +65,6 @@ async fn register_with_gateway(
|
|||||||
let mut gateway_client = GatewayClient::new_init(
|
let mut gateway_client = GatewayClient::new_init(
|
||||||
gateway.clients_address(),
|
gateway.clients_address(),
|
||||||
gateway.identity_key,
|
gateway.identity_key,
|
||||||
gateway.owner.clone(),
|
|
||||||
our_identity.clone(),
|
our_identity.clone(),
|
||||||
timeout,
|
timeout,
|
||||||
);
|
);
|
||||||
@@ -220,7 +148,7 @@ fn show_address(config: &Config) {
|
|||||||
println!("\nThe address of this client is: {}", client_recipient);
|
println!("\nThe address of this client is: {}", client_recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn execute(matches: ArgMatches<'static>) {
|
pub fn execute(matches: &ArgMatches) {
|
||||||
println!("Initialising client...");
|
println!("Initialising client...");
|
||||||
|
|
||||||
let id = matches.value_of("id").unwrap(); // required for now
|
let id = matches.value_of("id").unwrap(); // required for now
|
||||||
@@ -238,7 +166,7 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
|||||||
|
|
||||||
// TODO: ideally that should be the last thing that's being done to config.
|
// TODO: ideally that should be the last thing that's being done to config.
|
||||||
// However, we are later further overriding it with gateway id
|
// 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") {
|
if matches.is_present("fastmode") {
|
||||||
config.get_base_mut().set_high_default_traffic_volume();
|
config.get_base_mut().set_high_default_traffic_volume();
|
||||||
}
|
}
|
||||||
@@ -251,19 +179,26 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
|||||||
|
|
||||||
let chosen_gateway_id = matches.value_of("gateway");
|
let chosen_gateway_id = matches.value_of("gateway");
|
||||||
|
|
||||||
let gateway_details = gateway_details(
|
let registration_fut = async {
|
||||||
config.get_base().get_validator_api_endpoints(),
|
let gate_details = gateway_details(
|
||||||
chosen_gateway_id,
|
config.get_base().get_validator_api_endpoints(),
|
||||||
)
|
chosen_gateway_id,
|
||||||
.await;
|
)
|
||||||
let shared_keys =
|
.await;
|
||||||
register_with_gateway(&gateway_details, key_manager.identity_keypair()).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(
|
// TODO: is there perhaps a way to make it work without having to spawn entire runtime?
|
||||||
gateway_details.identity_key.to_base58_string(),
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
gateway_details.owner.clone(),
|
let (shared_keys, gateway_listener) = rt.block_on(registration_fut);
|
||||||
gateway_details.clients_address(),
|
config
|
||||||
);
|
.get_base_mut()
|
||||||
|
.with_gateway_listener(gateway_listener);
|
||||||
key_manager.insert_gateway_shared_key(shared_keys);
|
key_manager.insert_gateway_shared_key(shared_keys);
|
||||||
|
|
||||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||||
|
|||||||
@@ -5,18 +5,6 @@ use crate::client::config::{Config, SocketType};
|
|||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub(crate) const TESTNET_MODE_ARG_NAME: &str = "testnet-mode";
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
pub(crate) const ETH_ENDPOINT_ARG_NAME: &str = "eth_endpoint";
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
pub(crate) const ETH_PRIVATE_KEY_ARG_NAME: &str = "eth_private_key";
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
pub(crate) const DEFAULT_ETH_ENDPOINT: &str =
|
|
||||||
"https://rinkeby.infura.io/v3/00000000000000000000000000000000";
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
pub(crate) const DEFAULT_ETH_PRIVATE_KEY: &str =
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001";
|
|
||||||
|
|
||||||
pub(crate) mod init;
|
pub(crate) mod init;
|
||||||
pub(crate) mod run;
|
pub(crate) mod run;
|
||||||
pub(crate) mod upgrade;
|
pub(crate) mod upgrade;
|
||||||
@@ -32,7 +20,7 @@ fn parse_validators(raw: &str) -> Vec<Url> {
|
|||||||
.collect()
|
.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") {
|
if let Some(raw_validators) = matches.value_of("validators") {
|
||||||
config
|
config
|
||||||
.get_base_mut()
|
.get_base_mut()
|
||||||
@@ -55,26 +43,5 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
|
|||||||
config = config.with_port(port.unwrap());
|
config = config.with_port(port.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
if let Some(eth_endpoint) = matches.value_of(ETH_ENDPOINT_ARG_NAME) {
|
|
||||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
|
||||||
} else if !cfg!(feature = "eth") {
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT);
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
if let Some(eth_private_key) = matches.value_of(ETH_PRIVATE_KEY_ARG_NAME) {
|
|
||||||
config.get_base_mut().with_eth_private_key(eth_private_key);
|
|
||||||
} else if !cfg!(feature = "eth") {
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cfg!(feature = "eth") || matches.is_present(TESTNET_MODE_ARG_NAME) {
|
|
||||||
config.get_base_mut().with_testnet_mode(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,13 @@
|
|||||||
use crate::client::config::Config;
|
use crate::client::config::Config;
|
||||||
use crate::client::NymClient;
|
use crate::client::NymClient;
|
||||||
use crate::commands::override_config;
|
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 clap::{App, Arg, ArgMatches};
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
use log::*;
|
use log::*;
|
||||||
use version_checker::is_minor_version_compatible;
|
use version_checker::is_minor_version_compatible;
|
||||||
|
|
||||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||||
let app = App::new("run")
|
App::new("run")
|
||||||
.about("Run the Nym client with provided configuration client optionally overriding set parameters")
|
.about("Run the Nym client with provided configuration client optionally overriding set parameters")
|
||||||
.arg(Arg::with_name("id")
|
.arg(Arg::with_name("id")
|
||||||
.long("id")
|
.long("id")
|
||||||
@@ -41,26 +38,7 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.long("port")
|
.long("port")
|
||||||
.help("Port for the socket (if applicable) to listen on")
|
.help("Port for the socket (if applicable) to listen on")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
);
|
|
||||||
#[cfg(feature = "eth")]
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
let app = app
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(TESTNET_MODE_ARG_NAME)
|
|
||||||
.long(TESTNET_MODE_ARG_NAME)
|
|
||||||
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
|
||||||
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
|
||||||
.long(ETH_ENDPOINT_ARG_NAME)
|
|
||||||
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
|
||||||
.takes_value(true))
|
|
||||||
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
|
|
||||||
.long(ETH_PRIVATE_KEY_ARG_NAME)
|
|
||||||
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
|
||||||
.takes_value(true));
|
|
||||||
|
|
||||||
app
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this only checks compatibility between config the binary. It does not take into consideration
|
// this only checks compatibility between config the binary. It does not take into consideration
|
||||||
@@ -82,7 +60,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 id = matches.value_of("id").unwrap();
|
||||||
|
|
||||||
let mut config = match Config::load_from_file(Some(id)) {
|
let mut config = match Config::load_from_file(Some(id)) {
|
||||||
@@ -93,12 +71,12 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
config = override_config(config, &matches);
|
config = override_config(config, matches);
|
||||||
|
|
||||||
if !version_check(&config) {
|
if !version_check(&config) {
|
||||||
error!("failed the local version check");
|
error!("failed the local version check");
|
||||||
return;
|
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(
|
fn minor_0_12_upgrade(
|
||||||
mut config: Config,
|
mut config: Config,
|
||||||
_matches: &ArgMatches<'_>,
|
_matches: &ArgMatches,
|
||||||
config_version: &Version,
|
config_version: &Version,
|
||||||
package_version: &Version,
|
package_version: &Version,
|
||||||
) -> Config {
|
) -> Config {
|
||||||
@@ -131,7 +131,7 @@ fn minor_0_12_upgrade(
|
|||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Version) {
|
fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version) {
|
||||||
loop {
|
loop {
|
||||||
let config_version = parse_config_version(&config);
|
let config_version = parse_config_version(&config);
|
||||||
|
|
||||||
@@ -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 package_version = parse_package_version();
|
||||||
|
|
||||||
let id = matches.value_of("id").unwrap();
|
let id = matches.value_of("id").unwrap();
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use clap::{crate_version, App, ArgMatches};
|
use clap::{App, ArgMatches};
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod websocket;
|
pub mod websocket;
|
||||||
|
|
||||||
#[tokio::main]
|
fn main() {
|
||||||
async fn main() {
|
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
setup_logging();
|
setup_logging();
|
||||||
println!("{}", banner());
|
println!("{}", banner());
|
||||||
|
|
||||||
let arg_matches = App::new("Nym Client")
|
let arg_matches = App::new("Nym Client")
|
||||||
.version(crate_version!())
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
.long_version(&*long_version())
|
|
||||||
.author("Nymtech")
|
.author("Nymtech")
|
||||||
.about("Implementation of the Nym Client")
|
.about("Implementation of the Nym Client")
|
||||||
.subcommand(commands::init::command_args())
|
.subcommand(commands::init::command_args())
|
||||||
@@ -23,13 +21,13 @@ async fn main() {
|
|||||||
.subcommand(commands::upgrade::command_args())
|
.subcommand(commands::upgrade::command_args())
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
execute(arg_matches).await;
|
execute(arg_matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(matches: ArgMatches<'static>) {
|
fn execute(matches: ArgMatches) {
|
||||||
match matches.subcommand() {
|
match matches.subcommand() {
|
||||||
("init", Some(m)) => commands::init::execute(m.clone()).await,
|
("init", Some(m)) => commands::init::execute(m),
|
||||||
("run", Some(m)) => commands::run::execute(m.clone()).await,
|
("run", Some(m)) => commands::run::execute(m),
|
||||||
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
||||||
_ => println!("{}", usage()),
|
_ => println!("{}", usage()),
|
||||||
}
|
}
|
||||||
@@ -52,38 +50,7 @@ fn banner() -> String {
|
|||||||
(client - version {:})
|
(client - version {:})
|
||||||
|
|
||||||
"#,
|
"#,
|
||||||
crate_version!()
|
env!("CARGO_PKG_VERSION")
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn long_version() -> String {
|
|
||||||
format!(
|
|
||||||
r#"
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
"#,
|
|
||||||
"Build Timestamp:",
|
|
||||||
env!("VERGEN_BUILD_TIMESTAMP"),
|
|
||||||
"Build Version:",
|
|
||||||
env!("VERGEN_BUILD_SEMVER"),
|
|
||||||
"Commit SHA:",
|
|
||||||
env!("VERGEN_GIT_SHA"),
|
|
||||||
"Commit Date:",
|
|
||||||
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
|
|
||||||
"Commit Branch:",
|
|
||||||
env!("VERGEN_GIT_BRANCH"),
|
|
||||||
"rustc Version:",
|
|
||||||
env!("VERGEN_RUSTC_SEMVER"),
|
|
||||||
"rustc Channel:",
|
|
||||||
env!("VERGEN_RUSTC_CHANNEL"),
|
|
||||||
"cargo Profile:",
|
|
||||||
env!("VERGEN_CARGO_PROFILE"),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use super::handler::Handler;
|
|||||||
use log::*;
|
use log::*;
|
||||||
use std::{net::SocketAddr, process, sync::Arc};
|
use std::{net::SocketAddr, process, sync::Arc};
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
use tokio::runtime;
|
||||||
use tokio::{sync::Notify, task::JoinHandle};
|
use tokio::{sync::Notify, task::JoinHandle};
|
||||||
|
|
||||||
enum State {
|
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());
|
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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
pub(crate) use handler::Handler;
|
|
||||||
pub(crate) use listener::Listener;
|
|
||||||
|
|
||||||
pub(crate) mod handler;
|
pub(crate) mod handler;
|
||||||
pub(crate) mod listener;
|
pub(crate) mod listener;
|
||||||
|
|
||||||
|
pub(crate) use handler::Handler;
|
||||||
|
pub(crate) use listener::Listener;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
name = "websocket-requests"
|
name = "websocket-requests"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "0.12.1"
|
version = "0.11.0"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
rust-version = "1.56"
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "nym_socks5"
|
name = "nym_socks5"
|
||||||
@@ -32,18 +31,13 @@ crypto = { path = "../../common/crypto" }
|
|||||||
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
||||||
gateway-requests = { path = "../../gateway/gateway-requests" }
|
gateway-requests = { path = "../../gateway/gateway-requests" }
|
||||||
nymsphinx = { path = "../../common/nymsphinx" }
|
nymsphinx = { path = "../../common/nymsphinx" }
|
||||||
ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
|
ordered-buffer = {path = "../../common/socks5/ordered-buffer"}
|
||||||
socks5-requests = { path = "../../common/socks5/requests" }
|
socks5-requests = { path = "../../common/socks5/requests" }
|
||||||
topology = { path = "../../common/topology" }
|
topology = { path = "../../common/topology" }
|
||||||
pemstore = { path = "../../common/pemstore" }
|
pemstore = { path = "../../common/pemstore" }
|
||||||
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
||||||
validator-client = { path = "../../common/client-libs/validator-client" }
|
validator-client = { path = "../../common/client-libs/validator-client" }
|
||||||
version-checker = { path = "../../common/version-checker" }
|
version-checker = { path = "../../common/version-checker" }
|
||||||
network-defaults = { path = "../../common/network-defaults" }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
|
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
|
||||||
eth = []
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use vergen::{vergen, Config};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
vergen(Config::default()).expect("failed to extract build metadata")
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ pub(crate) fn config_template() -> &'static str {
|
|||||||
// While using normal toml marshalling would have been way simpler with less overhead,
|
// 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
|
// I think it's useful to have comments attached to the saved config file to explain behaviour of
|
||||||
// particular fields.
|
// 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#"
|
r#"
|
||||||
# This is a TOML config file.
|
# This is a TOML config file.
|
||||||
# For more information, see https://github.com/toml-lang/toml
|
# For more information, see https://github.com/toml-lang/toml
|
||||||
@@ -19,10 +19,6 @@ version = '{{ client.version }}'
|
|||||||
# Human readable ID of this particular client.
|
# Human readable ID of this particular client.
|
||||||
id = '{{ client.id }}'
|
id = '{{ client.id }}'
|
||||||
|
|
||||||
# Indicates whether this client is running in a 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.
|
# Addresses to APIs running on validator from which the client gets the view of the network.
|
||||||
validator_api_urls = [
|
validator_api_urls = [
|
||||||
{{#each client.validator_api_urls }}
|
{{#each client.validator_api_urls }}
|
||||||
@@ -46,19 +42,14 @@ public_encryption_key_file = '{{ client.public_encryption_key_file }}'
|
|||||||
# sent but not received back.
|
# sent but not received back.
|
||||||
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
||||||
|
|
||||||
# Path to directory containing public/private keys used for bandwidth token purchase.
|
|
||||||
# Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
|
|
||||||
# The public key is the name of the file, while the private key is the content.
|
|
||||||
backup_bandwidth_token_keys_dir = '{{ client.backup_bandwidth_token_keys_dir }}'
|
|
||||||
|
|
||||||
# Ethereum private key.
|
|
||||||
eth_private_key = '{{ client.eth_private_key }}'
|
|
||||||
|
|
||||||
# Addess to an Ethereum full node.
|
|
||||||
eth_endpoint = '{{ client.eth_endpoint }}'
|
|
||||||
|
|
||||||
##### additional client config options #####
|
##### additional client config options #####
|
||||||
|
|
||||||
|
# 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
|
# A gateway specific, optional, base58 stringified shared key used for
|
||||||
# communication with particular gateway.
|
# communication with particular gateway.
|
||||||
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
||||||
@@ -66,22 +57,12 @@ gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
|||||||
# Path to file containing key used for encrypting and decrypting the content of an
|
# Path to file containing key used for encrypting and decrypting the content of an
|
||||||
# acknowledgement so that nobody besides the client knows which packet it refers to.
|
# acknowledgement so that nobody besides the client knows which packet it refers to.
|
||||||
ack_key_file = '{{ client.ack_key_file }}'
|
ack_key_file = '{{ client.ack_key_file }}'
|
||||||
|
|
||||||
##### advanced configuration options #####
|
##### advanced configuration options #####
|
||||||
|
|
||||||
# Absolute path to the home Nym Clients directory.
|
# Absolute path to the home Nym Clients directory.
|
||||||
nym_root_directory = '{{ client.nym_root_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 #####
|
##### socket config options #####
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::client::config::Config;
|
||||||
|
use crate::socks::{
|
||||||
|
authentication::{AuthenticationMethods, Authenticator, User},
|
||||||
|
server::SphinxSocksServer,
|
||||||
|
};
|
||||||
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
|
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
|
||||||
use client_core::client::inbound_messages::{
|
use client_core::client::inbound_messages::{
|
||||||
InputMessage, InputMessageReceiver, InputMessageSender,
|
InputMessage, InputMessageReceiver, InputMessageSender,
|
||||||
@@ -20,7 +25,6 @@ use client_core::client::topology_control::{
|
|||||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||||
use crypto::asymmetric::identity;
|
use crypto::asymmetric::identity;
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use gateway_client::bandwidth::BandwidthController;
|
|
||||||
use gateway_client::{
|
use gateway_client::{
|
||||||
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
|
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
|
||||||
MixnetMessageSender,
|
MixnetMessageSender,
|
||||||
@@ -28,12 +32,12 @@ use gateway_client::{
|
|||||||
use log::*;
|
use log::*;
|
||||||
use nymsphinx::addressing::clients::Recipient;
|
use nymsphinx::addressing::clients::Recipient;
|
||||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
use crate::client::config::Config;
|
#[cfg(feature = "coconut")]
|
||||||
use crate::socks::{
|
use coconut_interface::Credential;
|
||||||
authentication::{AuthenticationMethods, Authenticator, User},
|
#[cfg(feature = "coconut")]
|
||||||
server::SphinxSocksServer,
|
use credentials::{bandwidth::prepare_for_spending, obtain_aggregate_verification_key};
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) mod config;
|
pub(crate) mod config;
|
||||||
|
|
||||||
@@ -42,6 +46,11 @@ pub struct NymClient {
|
|||||||
/// key filepaths, etc.
|
/// key filepaths, etc.
|
||||||
config: Config,
|
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.
|
/// KeyManager object containing smart pointers to all relevant keys used by the client.
|
||||||
key_manager: KeyManager,
|
key_manager: KeyManager,
|
||||||
}
|
}
|
||||||
@@ -52,6 +61,7 @@ impl NymClient {
|
|||||||
let key_manager = KeyManager::load_keys(&pathfinder).expect("failed to load stored keys");
|
let key_manager = KeyManager::load_keys(&pathfinder).expect("failed to load stored keys");
|
||||||
|
|
||||||
NymClient {
|
NymClient {
|
||||||
|
runtime: Runtime::new().unwrap(),
|
||||||
config,
|
config,
|
||||||
key_manager,
|
key_manager,
|
||||||
}
|
}
|
||||||
@@ -75,6 +85,9 @@ impl NymClient {
|
|||||||
mix_tx: BatchMixMessageSender,
|
mix_tx: BatchMixMessageSender,
|
||||||
) {
|
) {
|
||||||
info!("Starting loop cover traffic stream...");
|
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(
|
LoopCoverTrafficStream::new(
|
||||||
self.key_manager.ack_key(),
|
self.key_manager.ack_key(),
|
||||||
@@ -87,7 +100,7 @@ impl NymClient {
|
|||||||
self.as_mix_recipient(),
|
self.as_mix_recipient(),
|
||||||
topology_accessor,
|
topology_accessor,
|
||||||
)
|
)
|
||||||
.start();
|
.start(self.runtime.handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_real_traffic_controller(
|
fn start_real_traffic_controller(
|
||||||
@@ -109,6 +122,10 @@ impl NymClient {
|
|||||||
);
|
);
|
||||||
|
|
||||||
info!("Starting real traffic stream...");
|
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(
|
RealMessagesController::new(
|
||||||
controller_config,
|
controller_config,
|
||||||
@@ -118,7 +135,7 @@ impl NymClient {
|
|||||||
topology_accessor,
|
topology_accessor,
|
||||||
reply_key_storage,
|
reply_key_storage,
|
||||||
)
|
)
|
||||||
.start();
|
.start(self.runtime.handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
// buffer controlling all messages fetched from provider
|
// buffer controlling all messages fetched from provider
|
||||||
@@ -136,10 +153,35 @@ impl NymClient {
|
|||||||
mixnet_receiver,
|
mixnet_receiver,
|
||||||
reply_key_storage,
|
reply_key_storage,
|
||||||
)
|
)
|
||||||
.start()
|
.start(self.runtime.handle())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_gateway_client(
|
#[cfg(feature = "coconut")]
|
||||||
|
async fn prepare_coconut_credential(&self) -> Credential {
|
||||||
|
let verification_key = obtain_aggregate_verification_key(
|
||||||
|
&self.config.get_base().get_validator_api_endpoints(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("could not obtain aggregate verification key of validators");
|
||||||
|
|
||||||
|
let bandwidth_credential = credentials::bandwidth::obtain_signature(
|
||||||
|
&self.key_manager.identity_keypair().public_key().to_bytes(),
|
||||||
|
&self.config.get_base().get_validator_api_endpoints(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("could not obtain bandwidth credential");
|
||||||
|
// the above would presumably be loaded from a file
|
||||||
|
|
||||||
|
// the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
|
||||||
|
prepare_for_spending(
|
||||||
|
&self.key_manager.identity_keypair().public_key().to_bytes(),
|
||||||
|
&bandwidth_credential,
|
||||||
|
&verification_key,
|
||||||
|
)
|
||||||
|
.expect("could not prepare out bandwidth credential for spending")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_gateway_client(
|
||||||
&mut self,
|
&mut self,
|
||||||
mixnet_message_sender: MixnetMessageSender,
|
mixnet_message_sender: MixnetMessageSender,
|
||||||
ack_sender: AcknowledgementSender,
|
ack_sender: AcknowledgementSender,
|
||||||
@@ -148,10 +190,6 @@ impl NymClient {
|
|||||||
if gateway_id.is_empty() {
|
if gateway_id.is_empty() {
|
||||||
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
|
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();
|
let gateway_address = self.config.get_base().get_gateway_listener();
|
||||||
if gateway_address.is_empty() {
|
if gateway_address.is_empty() {
|
||||||
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
|
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
|
||||||
@@ -160,45 +198,35 @@ impl NymClient {
|
|||||||
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
||||||
.expect("provided gateway id is invalid!");
|
.expect("provided gateway id is invalid!");
|
||||||
|
|
||||||
#[cfg(feature = "coconut")]
|
self.runtime.block_on(async {
|
||||||
let bandwidth_controller = BandwidthController::new(
|
#[cfg(feature = "coconut")]
|
||||||
self.config.get_base().get_validator_api_endpoints(),
|
let coconut_credential = self.prepare_coconut_credential().await;
|
||||||
*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(
|
let mut gateway_client = GatewayClient::new(
|
||||||
gateway_address,
|
gateway_address,
|
||||||
self.key_manager.identity_keypair(),
|
self.key_manager.identity_keypair(),
|
||||||
gateway_identity,
|
gateway_identity,
|
||||||
gateway_owner,
|
Some(self.key_manager.gateway_shared_key()),
|
||||||
Some(self.key_manager.gateway_shared_key()),
|
mixnet_message_sender,
|
||||||
mixnet_message_sender,
|
ack_sender,
|
||||||
ack_sender,
|
self.config.get_base().get_gateway_response_timeout(),
|
||||||
self.config.get_base().get_gateway_response_timeout(),
|
);
|
||||||
Some(bandwidth_controller),
|
|
||||||
);
|
|
||||||
|
|
||||||
if self.config.get_base().get_testnet_mode() {
|
gateway_client
|
||||||
gateway_client.set_testnet_mode(true)
|
.authenticate_and_start(
|
||||||
}
|
#[cfg(feature = "coconut")]
|
||||||
gateway_client
|
Some(coconut_credential),
|
||||||
.authenticate_and_start()
|
)
|
||||||
.await
|
.await
|
||||||
.expect("could not authenticate and start up the gateway connection");
|
.expect("could not authenticate and start up the gateway connection");
|
||||||
|
|
||||||
gateway_client
|
gateway_client
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// future responsible for periodically polling directory server and updating
|
// future responsible for periodically polling directory server and updating
|
||||||
// the current global view of topology
|
// the current global view of topology
|
||||||
async fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
||||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||||
self.config.get_base().get_validator_api_endpoints(),
|
self.config.get_base().get_validator_api_endpoints(),
|
||||||
self.config.get_base().get_topology_refresh_rate(),
|
self.config.get_base().get_topology_refresh_rate(),
|
||||||
@@ -209,10 +237,13 @@ impl NymClient {
|
|||||||
// before returning, block entire runtime to refresh the current network view so that any
|
// before returning, block entire runtime to refresh the current network view so that any
|
||||||
// components depending on topology would see a non-empty view
|
// components depending on topology would see a non-empty view
|
||||||
info!("Obtaining initial network topology");
|
info!("Obtaining initial network topology");
|
||||||
topology_refresher.refresh().await;
|
self.runtime.block_on(topology_refresher.refresh());
|
||||||
|
|
||||||
// TODO: a slightly more graceful termination here
|
// 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!(
|
panic!(
|
||||||
"The current network topology seem to be insufficient to route any packets through\
|
"The current network topology seem to be insufficient to route any packets through\
|
||||||
- check if enough nodes and a gateway are online"
|
- check if enough nodes and a gateway are online"
|
||||||
@@ -220,7 +251,7 @@ impl NymClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
info!("Starting topology refresher...");
|
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)
|
// controller for sending sphinx packets to mixnet (either real traffic or cover traffic)
|
||||||
@@ -233,7 +264,7 @@ impl NymClient {
|
|||||||
gateway_client: GatewayClient,
|
gateway_client: GatewayClient,
|
||||||
) {
|
) {
|
||||||
info!("Starting mix traffic controller...");
|
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(
|
fn start_socks5_listener(
|
||||||
@@ -252,13 +283,14 @@ impl NymClient {
|
|||||||
self.config.get_provider_mix_address(),
|
self.config.get_provider_mix_address(),
|
||||||
self.as_mix_recipient(),
|
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)
|
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
|
||||||
pub async fn run_forever(&mut self) {
|
pub fn run_forever(&mut self) {
|
||||||
self.start().await;
|
self.start();
|
||||||
if let Err(e) = tokio::signal::ctrl_c().await {
|
if let Err(e) = self.runtime.block_on(tokio::signal::ctrl_c()) {
|
||||||
error!(
|
error!(
|
||||||
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
|
"There was an error while capturing SIGINT - {:?}. We will terminate regardless",
|
||||||
e
|
e
|
||||||
@@ -270,7 +302,7 @@ impl NymClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
info!("Starting nym client");
|
info!("Starting nym client");
|
||||||
// channels for inter-component communication
|
// channels for inter-component communication
|
||||||
// TODO: make the channels be internally created by the relevant components
|
// TODO: make the channels be internally created by the relevant components
|
||||||
@@ -302,17 +334,14 @@ impl NymClient {
|
|||||||
|
|
||||||
// the components are started in very specific order. Unless you know what you are doing,
|
// the components are started in very specific order. Unless you know what you are doing,
|
||||||
// do not change that.
|
// do not change that.
|
||||||
self.start_topology_refresher(shared_topology_accessor.clone())
|
self.start_topology_refresher(shared_topology_accessor.clone());
|
||||||
.await;
|
|
||||||
self.start_received_messages_buffer_controller(
|
self.start_received_messages_buffer_controller(
|
||||||
received_buffer_request_receiver,
|
received_buffer_request_receiver,
|
||||||
mixnet_messages_receiver,
|
mixnet_messages_receiver,
|
||||||
reply_key_storage.clone(),
|
reply_key_storage.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let gateway_client = self
|
let gateway_client = self.start_gateway_client(mixnet_messages_sender, ack_sender);
|
||||||
.start_gateway_client(mixnet_messages_sender, ack_sender)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
|
self.start_mix_traffic_controller(sphinx_message_receiver, gateway_client);
|
||||||
self.start_real_traffic_controller(
|
self.start_real_traffic_controller(
|
||||||
|
|||||||
@@ -1,23 +1,15 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::client::config::Config;
|
||||||
|
use crate::commands::override_config;
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use client_core::client::key_manager::KeyManager;
|
use client_core::client::key_manager::KeyManager;
|
||||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use coconut_interface::{hash_to_scalar, Credential, Parameters};
|
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use credentials::coconut::bandwidth::{
|
|
||||||
obtain_signature, prepare_for_spending, BandwidthVoucherAttributes, TOTAL_ATTRIBUTES,
|
|
||||||
};
|
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use credentials::obtain_aggregate_verification_key;
|
|
||||||
use crypto::asymmetric::{encryption, identity};
|
use crypto::asymmetric::{encryption, identity};
|
||||||
use gateway_client::GatewayClient;
|
use gateway_client::GatewayClient;
|
||||||
use gateway_requests::registration::handshake::SharedKeys;
|
use gateway_requests::registration::handshake::SharedKeys;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use network_defaults::BANDWIDTH_VALUE;
|
|
||||||
use nymsphinx::addressing::clients::Recipient;
|
use nymsphinx::addressing::clients::Recipient;
|
||||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||||
use rand::{prelude::SliceRandom, rngs::OsRng, thread_rng};
|
use rand::{prelude::SliceRandom, rngs::OsRng, thread_rng};
|
||||||
@@ -27,17 +19,8 @@ use std::time::Duration;
|
|||||||
use topology::{filter::VersionFilterable, gateway};
|
use topology::{filter::VersionFilterable, gateway};
|
||||||
use url::Url;
|
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> {
|
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||||
let app = App::new("init")
|
App::new("init")
|
||||||
.about("Initialise a Nym client. Do this first!")
|
.about("Initialise a Nym client. Do this first!")
|
||||||
.arg(Arg::with_name("id")
|
.arg(Arg::with_name("id")
|
||||||
.long("id")
|
.long("id")
|
||||||
@@ -57,9 +40,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("validators")
|
.arg(Arg::with_name("validators")
|
||||||
.long("validators")
|
.long("validators")
|
||||||
.help("Comma separated list of rest endpoints of the validators")
|
.help("Comma separated list of rest endpoints of the validators")
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("port")
|
.arg(Arg::with_name("port")
|
||||||
.short("p")
|
.short("p")
|
||||||
@@ -71,61 +54,7 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.long("fastmode")
|
.long("fastmode")
|
||||||
.hidden(true) // this will prevent this flag from being displayed in `--help`
|
.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")
|
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
|
||||||
);
|
|
||||||
#[cfg(feature = "eth")]
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
let app = app
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(TESTNET_MODE_ARG_NAME)
|
|
||||||
.long(TESTNET_MODE_ARG_NAME)
|
|
||||||
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
|
||||||
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
|
||||||
.long(ETH_ENDPOINT_ARG_NAME)
|
|
||||||
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT)
|
|
||||||
.required(true))
|
|
||||||
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
|
|
||||||
.long(ETH_PRIVATE_KEY_ARG_NAME)
|
|
||||||
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
|
|
||||||
.required(true)
|
|
||||||
);
|
|
||||||
|
|
||||||
app
|
|
||||||
}
|
|
||||||
|
|
||||||
// this behaviour should definitely be changed, we shouldn't
|
|
||||||
// need to get bandwidth credential for registration
|
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
async fn _prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential {
|
|
||||||
let verification_key = obtain_aggregate_verification_key(validators)
|
|
||||||
.await
|
|
||||||
.expect("could not obtain aggregate verification key of validators");
|
|
||||||
|
|
||||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
|
||||||
let bandwidth_credential_attributes = BandwidthVoucherAttributes {
|
|
||||||
serial_number: params.random_scalar(),
|
|
||||||
binding_number: params.random_scalar(),
|
|
||||||
voucher_value: hash_to_scalar(BANDWIDTH_VALUE.to_be_bytes()),
|
|
||||||
voucher_info: hash_to_scalar("BandwidthVoucher"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let bandwidth_credential =
|
|
||||||
obtain_signature(¶ms, &bandwidth_credential_attributes, validators)
|
|
||||||
.await
|
|
||||||
.expect("could not obtain bandwidth credential");
|
|
||||||
|
|
||||||
prepare_for_spending(
|
|
||||||
raw_identity,
|
|
||||||
&bandwidth_credential,
|
|
||||||
&bandwidth_credential_attributes,
|
|
||||||
&verification_key,
|
|
||||||
)
|
|
||||||
.expect("could not prepare out bandwidth credential for spending")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn register_with_gateway(
|
async fn register_with_gateway(
|
||||||
@@ -136,7 +65,6 @@ async fn register_with_gateway(
|
|||||||
let mut gateway_client = GatewayClient::new_init(
|
let mut gateway_client = GatewayClient::new_init(
|
||||||
gateway.clients_address(),
|
gateway.clients_address(),
|
||||||
gateway.identity_key,
|
gateway.identity_key,
|
||||||
gateway.owner.clone(),
|
|
||||||
our_identity.clone(),
|
our_identity.clone(),
|
||||||
timeout,
|
timeout,
|
||||||
);
|
);
|
||||||
@@ -220,7 +148,7 @@ fn show_address(config: &Config) {
|
|||||||
println!("\nThe address of this client is: {}", client_recipient);
|
println!("\nThe address of this client is: {}", client_recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn execute(matches: ArgMatches<'static>) {
|
pub fn execute(matches: &ArgMatches) {
|
||||||
println!("Initialising client...");
|
println!("Initialising client...");
|
||||||
|
|
||||||
let id = matches.value_of("id").unwrap(); // required for now
|
let id = matches.value_of("id").unwrap(); // required for now
|
||||||
@@ -239,7 +167,7 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
|||||||
|
|
||||||
// TODO: ideally that should be the last thing that's being done to config.
|
// TODO: ideally that should be the last thing that's being done to config.
|
||||||
// However, we are later further overriding it with gateway id
|
// 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") {
|
if matches.is_present("fastmode") {
|
||||||
config.get_base_mut().set_high_default_traffic_volume();
|
config.get_base_mut().set_high_default_traffic_volume();
|
||||||
}
|
}
|
||||||
@@ -252,19 +180,26 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
|||||||
|
|
||||||
let chosen_gateway_id = matches.value_of("gateway");
|
let chosen_gateway_id = matches.value_of("gateway");
|
||||||
|
|
||||||
let gateway_details = gateway_details(
|
let registration_fut = async {
|
||||||
config.get_base().get_validator_api_endpoints(),
|
let gate_details = gateway_details(
|
||||||
chosen_gateway_id,
|
config.get_base().get_validator_api_endpoints(),
|
||||||
)
|
chosen_gateway_id,
|
||||||
.await;
|
)
|
||||||
let shared_keys =
|
.await;
|
||||||
register_with_gateway(&gateway_details, key_manager.identity_keypair()).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(
|
// TODO: is there perhaps a way to make it work without having to spawn entire runtime?
|
||||||
gateway_details.identity_key.to_base58_string(),
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
gateway_details.owner.clone(),
|
let (shared_keys, gateway_listener) = rt.block_on(registration_fut);
|
||||||
gateway_details.clients_address(),
|
config
|
||||||
);
|
.get_base_mut()
|
||||||
|
.with_gateway_listener(gateway_listener);
|
||||||
key_manager.insert_gateway_shared_key(shared_keys);
|
key_manager.insert_gateway_shared_key(shared_keys);
|
||||||
|
|
||||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||||
|
|||||||
@@ -9,18 +9,6 @@ pub(crate) mod init;
|
|||||||
pub(crate) mod run;
|
pub(crate) mod run;
|
||||||
pub(crate) mod upgrade;
|
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> {
|
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||||
raw.split(',')
|
raw.split(',')
|
||||||
.map(|raw_validator| {
|
.map(|raw_validator| {
|
||||||
@@ -32,7 +20,7 @@ fn parse_validators(raw: &str) -> Vec<Url> {
|
|||||||
.collect()
|
.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") {
|
if let Some(raw_validators) = matches.value_of("validators") {
|
||||||
config
|
config
|
||||||
.get_base_mut()
|
.get_base_mut()
|
||||||
@@ -51,26 +39,5 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
|
|||||||
config = config.with_port(port.unwrap());
|
config = config.with_port(port.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
if let Some(eth_endpoint) = matches.value_of(ETH_ENDPOINT_ARG_NAME) {
|
|
||||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
|
||||||
} else if !cfg!(feature = "eth") {
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT);
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
if let Some(eth_private_key) = matches.value_of(ETH_PRIVATE_KEY_ARG_NAME) {
|
|
||||||
config.get_base_mut().with_eth_private_key(eth_private_key);
|
|
||||||
} else if !cfg!(feature = "eth") {
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cfg!(feature = "eth") || matches.is_present(TESTNET_MODE_ARG_NAME) {
|
|
||||||
config.get_base_mut().with_testnet_mode(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,13 @@
|
|||||||
use crate::client::config::Config;
|
use crate::client::config::Config;
|
||||||
use crate::client::NymClient;
|
use crate::client::NymClient;
|
||||||
use crate::commands::override_config;
|
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 clap::{App, Arg, ArgMatches};
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
use log::*;
|
use log::*;
|
||||||
use version_checker::is_minor_version_compatible;
|
use version_checker::is_minor_version_compatible;
|
||||||
|
|
||||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||||
let app = App::new("run")
|
App::new("run")
|
||||||
.about("Run the Nym client with provided configuration client optionally overriding set parameters")
|
.about("Run the Nym client with provided configuration client optionally overriding set parameters")
|
||||||
.arg(Arg::with_name("id")
|
.arg(Arg::with_name("id")
|
||||||
.long("id")
|
.long("id")
|
||||||
@@ -47,26 +44,7 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.long("port")
|
.long("port")
|
||||||
.help("Port for the socket to listen on")
|
.help("Port for the socket to listen on")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
);
|
|
||||||
#[cfg(feature = "eth")]
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
let app = app
|
|
||||||
.arg(
|
|
||||||
Arg::with_name(TESTNET_MODE_ARG_NAME)
|
|
||||||
.long(TESTNET_MODE_ARG_NAME)
|
|
||||||
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
|
||||||
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
|
||||||
.long(ETH_ENDPOINT_ARG_NAME)
|
|
||||||
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
|
||||||
.takes_value(true))
|
|
||||||
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
|
|
||||||
.long(ETH_PRIVATE_KEY_ARG_NAME)
|
|
||||||
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
|
||||||
.takes_value(true));
|
|
||||||
|
|
||||||
app
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this only checks compatibility between config the binary. It does not take into consideration
|
// this only checks compatibility between config the binary. It does not take into consideration
|
||||||
@@ -88,7 +66,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 id = matches.value_of("id").unwrap();
|
||||||
|
|
||||||
let mut config = match Config::load_from_file(Some(id)) {
|
let mut config = match Config::load_from_file(Some(id)) {
|
||||||
@@ -99,12 +77,12 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
config = override_config(config, &matches);
|
config = override_config(config, matches);
|
||||||
|
|
||||||
if !version_check(&config) {
|
if !version_check(&config) {
|
||||||
error!("failed the local version check");
|
error!("failed the local version check");
|
||||||
return;
|
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(
|
fn minor_0_12_upgrade(
|
||||||
mut config: Config,
|
mut config: Config,
|
||||||
_matches: &ArgMatches<'_>,
|
_matches: &ArgMatches,
|
||||||
config_version: &Version,
|
config_version: &Version,
|
||||||
package_version: &Version,
|
package_version: &Version,
|
||||||
) -> Config {
|
) -> Config {
|
||||||
@@ -131,7 +131,7 @@ fn minor_0_12_upgrade(
|
|||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Version) {
|
fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version) {
|
||||||
loop {
|
loop {
|
||||||
let config_version = parse_config_version(&config);
|
let config_version = parse_config_version(&config);
|
||||||
|
|
||||||
@@ -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 package_version = parse_package_version();
|
||||||
|
|
||||||
let id = matches.value_of("id").unwrap();
|
let id = matches.value_of("id").unwrap();
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use clap::{crate_version, App, ArgMatches};
|
use clap::{App, ArgMatches};
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
mod commands;
|
mod commands;
|
||||||
pub mod socks;
|
pub mod socks;
|
||||||
|
|
||||||
#[tokio::main]
|
fn main() {
|
||||||
async fn main() {
|
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
setup_logging();
|
setup_logging();
|
||||||
println!("{}", banner());
|
println!("{}", banner());
|
||||||
@@ -16,20 +15,19 @@ async fn main() {
|
|||||||
let arg_matches = App::new("Nym Socks5 Proxy")
|
let arg_matches = App::new("Nym Socks5 Proxy")
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
.version(env!("CARGO_PKG_VERSION"))
|
||||||
.author("Nymtech")
|
.author("Nymtech")
|
||||||
.long_version(&*long_version())
|
|
||||||
.about("A Socks5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address")
|
.about("A Socks5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address")
|
||||||
.subcommand(commands::init::command_args())
|
.subcommand(commands::init::command_args())
|
||||||
.subcommand(commands::run::command_args())
|
.subcommand(commands::run::command_args())
|
||||||
.subcommand(commands::upgrade::command_args())
|
.subcommand(commands::upgrade::command_args())
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
execute(arg_matches).await;
|
execute(arg_matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute(matches: ArgMatches<'static>) {
|
fn execute(matches: ArgMatches) {
|
||||||
match matches.subcommand() {
|
match matches.subcommand() {
|
||||||
("init", Some(m)) => commands::init::execute(m.clone()).await,
|
("init", Some(m)) => commands::init::execute(m),
|
||||||
("run", Some(m)) => commands::run::execute(m.clone()).await,
|
("run", Some(m)) => commands::run::execute(m),
|
||||||
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
||||||
_ => println!("{}", usage()),
|
_ => println!("{}", usage()),
|
||||||
}
|
}
|
||||||
@@ -52,38 +50,7 @@ fn banner() -> String {
|
|||||||
(socks5 proxy - version {:})
|
(socks5 proxy - version {:})
|
||||||
|
|
||||||
"#,
|
"#,
|
||||||
crate_version!()
|
env!("CARGO_PKG_VERSION")
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn long_version() -> String {
|
|
||||||
format!(
|
|
||||||
r#"
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
{:<20}{}
|
|
||||||
"#,
|
|
||||||
"Build Timestamp:",
|
|
||||||
env!("VERGEN_BUILD_TIMESTAMP"),
|
|
||||||
"Build Version:",
|
|
||||||
env!("VERGEN_BUILD_SEMVER"),
|
|
||||||
"Commit SHA:",
|
|
||||||
env!("VERGEN_GIT_SHA"),
|
|
||||||
"Commit Date:",
|
|
||||||
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
|
|
||||||
"Commit Branch:",
|
|
||||||
env!("VERGEN_GIT_BRANCH"),
|
|
||||||
"rustc Version:",
|
|
||||||
env!("VERGEN_RUSTC_SEMVER"),
|
|
||||||
"rustc Channel:",
|
|
||||||
env!("VERGEN_RUSTC_CHANNEL"),
|
|
||||||
"cargo Profile:",
|
|
||||||
env!("VERGEN_CARGO_PROFILE"),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+7
-17
@@ -11,7 +11,6 @@
|
|||||||
"@tauri-apps/api": "^1.0.0-beta.4",
|
"@tauri-apps/api": "^1.0.0-beta.4",
|
||||||
"compression": "^1.7.1",
|
"compression": "^1.7.1",
|
||||||
"polka": "next",
|
"polka": "next",
|
||||||
"qrious": "^4.0.2",
|
|
||||||
"sirv": "^1.0.0"
|
"sirv": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -6759,11 +6758,6 @@
|
|||||||
"node": ">=8"
|
"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": {
|
"node_modules/query-string": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
||||||
@@ -7585,10 +7579,11 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/simple-get": {
|
"node_modules/simple-get": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||||
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
|
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"decompress-response": "^4.2.0",
|
"decompress-response": "^4.2.0",
|
||||||
"once": "^1.3.1",
|
"once": "^1.3.1",
|
||||||
@@ -13479,11 +13474,6 @@
|
|||||||
"escape-goat": "^2.0.0"
|
"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": {
|
"query-string": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
||||||
@@ -14046,9 +14036,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"simple-get": {
|
"simple-get": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||||
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
|
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"decompress-response": "^4.2.0",
|
"decompress-response": "^4.2.0",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ authors = ["you"]
|
|||||||
license = ""
|
license = ""
|
||||||
repository = ""
|
repository = ""
|
||||||
default-run = "app"
|
default-run = "app"
|
||||||
edition = "2021"
|
edition = "2018"
|
||||||
build = "src/build.rs"
|
build = "src/build.rs"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
@@ -3,24 +3,21 @@
|
|||||||
windows_subsystem = "windows"
|
windows_subsystem = "windows"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use tokio::sync::RwLock;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use coconut_interface::{
|
use coconut_interface::{
|
||||||
self, hash_to_scalar, Attribute, Credential, Parameters, Signature, Theta, VerificationKey,
|
self, hash_to_scalar, Attribute, Credential, Parameters, Signature, Theta, VerificationKey,
|
||||||
};
|
};
|
||||||
use credentials::{obtain_aggregate_signature, obtain_aggregate_verification_key};
|
use credentials::{obtain_aggregate_signature, obtain_aggregate_verification_key};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
signatures: Vec<Signature>,
|
signatures: Vec<Signature>,
|
||||||
n_attributes: u32,
|
n_attributes: u32,
|
||||||
params: Parameters,
|
params: Parameters,
|
||||||
serial_number: Attribute,
|
public_attributes_bytes: Vec<Vec<u8>>,
|
||||||
binding_number: Attribute,
|
public_attributes: Vec<Attribute>,
|
||||||
voucher_value: Attribute,
|
private_attributes: Vec<Attribute>,
|
||||||
voucher_info: Attribute,
|
|
||||||
aggregated_verification_key: Option<VerificationKey>,
|
aggregated_verification_key: Option<VerificationKey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,10 +37,9 @@ impl State {
|
|||||||
signatures: Vec::new(),
|
signatures: Vec::new(),
|
||||||
n_attributes,
|
n_attributes,
|
||||||
params,
|
params,
|
||||||
serial_number: private_attributes[0],
|
public_attributes_bytes,
|
||||||
binding_number: private_attributes[1],
|
public_attributes,
|
||||||
voucher_value: public_attributes[0],
|
private_attributes,
|
||||||
voucher_info: public_attributes[1],
|
|
||||||
aggregated_verification_key: None,
|
aggregated_verification_key: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,8 +63,8 @@ async fn randomise_credential(
|
|||||||
) -> Result<Vec<Signature>, String> {
|
) -> Result<Vec<Signature>, String> {
|
||||||
let mut state = state.write().await;
|
let mut state = state.write().await;
|
||||||
let signature = state.signatures.remove(idx);
|
let signature = state.signatures.remove(idx);
|
||||||
let (new_signature, _) = signature.randomise(&state.params);
|
let new = signature.randomise(&state.params);
|
||||||
state.signatures.insert(idx, new_signature);
|
state.signatures.insert(idx, new);
|
||||||
Ok(state.signatures.clone())
|
Ok(state.signatures.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,15 +117,14 @@ async fn prove_credential(
|
|||||||
let state = state.read().await;
|
let state = state.read().await;
|
||||||
|
|
||||||
if let Some(signature) = state.signatures.get(idx) {
|
if let Some(signature) = state.signatures.get(idx) {
|
||||||
match coconut_interface::prove_bandwidth_credential(
|
match coconut_interface::prove_credential(
|
||||||
&state.params,
|
&state.params,
|
||||||
&verification_key,
|
&verification_key,
|
||||||
signature,
|
signature,
|
||||||
state.serial_number,
|
&state.private_attributes,
|
||||||
state.binding_number,
|
|
||||||
) {
|
) {
|
||||||
Ok(theta) => Ok(theta),
|
Ok(theta) => Ok(theta),
|
||||||
Err(e) => Err(format!("{:?}", e)),
|
Err(e) => Err(format!("{}", e)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("Got invalid Signature idx".to_string())
|
Err("Got invalid Signature idx".to_string())
|
||||||
@@ -149,15 +144,10 @@ async fn verify_credential(
|
|||||||
|
|
||||||
let state = state.read().await;
|
let state = state.read().await;
|
||||||
|
|
||||||
let public_attributes_bytes = vec![
|
|
||||||
state.voucher_value.to_bytes().to_vec(),
|
|
||||||
state.voucher_info.to_bytes().to_vec(),
|
|
||||||
];
|
|
||||||
|
|
||||||
let credential = Credential::new(
|
let credential = Credential::new(
|
||||||
state.n_attributes,
|
state.n_attributes,
|
||||||
theta,
|
theta,
|
||||||
public_attributes_bytes,
|
state.public_attributes_bytes.clone(),
|
||||||
state
|
state
|
||||||
.signatures
|
.signatures
|
||||||
.get(idx)
|
.get(idx)
|
||||||
@@ -174,13 +164,11 @@ async fn get_credential(
|
|||||||
) -> Result<Vec<Signature>, String> {
|
) -> Result<Vec<Signature>, String> {
|
||||||
let guard = state.read().await;
|
let guard = state.read().await;
|
||||||
let parsed_urls = parse_url_validators(&validator_urls)?;
|
let parsed_urls = parse_url_validators(&validator_urls)?;
|
||||||
let public_attributes = vec![guard.voucher_value, guard.voucher_info];
|
|
||||||
let private_attributes = vec![guard.serial_number, guard.binding_number];
|
|
||||||
|
|
||||||
let signature = obtain_aggregate_signature(
|
let signature = obtain_aggregate_signature(
|
||||||
&guard.params,
|
&guard.params,
|
||||||
&public_attributes,
|
&guard.public_attributes,
|
||||||
&private_attributes,
|
&guard.private_attributes,
|
||||||
&parsed_urls,
|
&parsed_urls,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -1813,7 +1813,7 @@ decompress-response@^3.2.0, decompress-response@^3.3.0:
|
|||||||
|
|
||||||
decompress-response@^4.2.0:
|
decompress-response@^4.2.0:
|
||||||
version "4.2.1"
|
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==
|
integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
|
||||||
dependencies:
|
dependencies:
|
||||||
mimic-response "^2.0.0"
|
mimic-response "^2.0.0"
|
||||||
@@ -3130,7 +3130,7 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
|
|||||||
|
|
||||||
mimic-response@^2.0.0:
|
mimic-response@^2.0.0:
|
||||||
version "2.1.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==
|
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
|
||||||
|
|
||||||
mimic-response@^3.1.0:
|
mimic-response@^3.1.0:
|
||||||
@@ -4056,13 +4056,13 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
|
|||||||
|
|
||||||
simple-concat@^1.0.0:
|
simple-concat@^1.0.0:
|
||||||
version "1.0.1"
|
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==
|
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
|
||||||
|
|
||||||
simple-get@^3.0.3, simple-get@^3.1.0:
|
simple-get@^3.0.3, simple-get@^3.1.0:
|
||||||
version "3.1.1"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55"
|
resolved "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz"
|
||||||
integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==
|
integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==
|
||||||
dependencies:
|
dependencies:
|
||||||
decompress-response "^4.2.0"
|
decompress-response "^4.2.0"
|
||||||
once "^1.3.1"
|
once "^1.3.1"
|
||||||
@@ -4673,7 +4673,7 @@ wrap-ansi@^7.0.0:
|
|||||||
|
|
||||||
wrappy@1:
|
wrappy@1:
|
||||||
version "1.0.2"
|
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=
|
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||||
|
|
||||||
write-file-atomic@^3.0.0:
|
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"
|
|
||||||
@@ -1,83 +1,41 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"node": true
|
"node": true,
|
||||||
|
"mocha": true
|
||||||
},
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2019,
|
"ecmaVersion": 2018,
|
||||||
"sourceType": "module"
|
"sourceType": "module"
|
||||||
},
|
},
|
||||||
"globals": {
|
|
||||||
"Atomics": "readonly",
|
|
||||||
"SharedArrayBuffer": "readonly"
|
|
||||||
},
|
|
||||||
"plugins": ["prettier", "mocha"],
|
|
||||||
"extends": [
|
|
||||||
"airbnb-base",
|
|
||||||
"airbnb-typescript/base",
|
|
||||||
"prettier"],
|
|
||||||
"rules": {
|
"rules": {
|
||||||
"prettier/prettier": "error",
|
"no-console": "off",
|
||||||
"import/prefer-default-export": "off",
|
"linebreak-style": "off",
|
||||||
"import/no-extraneous-dependencies": [
|
"quotes": [
|
||||||
"error",
|
"error",
|
||||||
|
"double",
|
||||||
{
|
{
|
||||||
"devDependencies": [
|
"allowTemplateLiterals": true
|
||||||
"**/*.test.[jt]s",
|
|
||||||
"**/*.spec.[jt]s"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"import/extensions": [
|
"keyword-spacing": [
|
||||||
"error",
|
"error",
|
||||||
"ignorePackages",
|
|
||||||
{
|
{
|
||||||
"ts": "never",
|
"before": true
|
||||||
"js": "never"
|
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"space-before-blocks": [
|
||||||
|
"error"
|
||||||
]
|
]
|
||||||
},
|
}
|
||||||
"overrides": [
|
}
|
||||||
{
|
|
||||||
"files": "**/*.ts",
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"project": "./tsconfig.json"
|
|
||||||
},
|
|
||||||
"plugins": ["@typescript-eslint/eslint-plugin"],
|
|
||||||
"extends": [
|
|
||||||
"plugin:@typescript-eslint/eslint-recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/explicit-function-return-type": "off",
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"no-use-before-define": [0],
|
|
||||||
"@typescript-eslint/no-use-before-define": [1],
|
|
||||||
"import/no-unresolved": 0,
|
|
||||||
"import/no-extraneous-dependencies": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"devDependencies": [
|
|
||||||
"**/*.test.ts",
|
|
||||||
"**/*.spec.ts"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"quotes": "off",
|
|
||||||
"@typescript-eslint/quotes": [
|
|
||||||
2,
|
|
||||||
"single",
|
|
||||||
{
|
|
||||||
"avoidEscape": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"@typescript-eslint/no-unused-vars": [2, { "argsIgnorePattern": "^_" }]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"trailingComma": "all",
|
|
||||||
"singleQuote": true,
|
|
||||||
"printWidth": 120,
|
|
||||||
"tabWidth": 2
|
|
||||||
}
|
|
||||||
@@ -3,20 +3,26 @@ Nym Validator Client
|
|||||||
|
|
||||||
A TypeScript client for interacting with CosmWasm smart contracts in Nym validators.
|
A TypeScript client for interacting with CosmWasm smart contracts in Nym validators.
|
||||||
|
|
||||||
|
Running examples
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
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
|
Running tests
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
The tests will be separated into three categories: unit, integration and mock.
|
|
||||||
|
|
||||||
Currently the command to run all tests:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
npm test
|
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
|
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
|
|
||||||
};
|
|
||||||
Generated
+3848
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "@nymproject/nym-validator-client",
|
"name": "@nymproject/nym-validator-client",
|
||||||
"version": "0.19.0",
|
"version": "0.18.0",
|
||||||
"description": "A TypeScript client for interacting with smart contracts in Nym validators",
|
"description": "A TypeScript client for interacting with smart contracts in Nym validators",
|
||||||
"repository": "https://github.com/nymtech/nym",
|
"repository": "https://github.com/nymtech/nym",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest --verbose false",
|
"build": "tsc",
|
||||||
"lint": "eslint src",
|
"test": "ts-mocha tests/**/*.test.ts",
|
||||||
"lint:fix": "eslint src --fix",
|
"coverage": "nyc npm test",
|
||||||
|
"lint": "eslint \"**/*.ts\"",
|
||||||
"docs": "typedoc --out docs src/index.ts"
|
"docs": "typedoc --out docs src/index.ts"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
@@ -19,32 +20,25 @@
|
|||||||
],
|
],
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "27.4.0",
|
"@types/chai": "^4.2.15",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.7.0",
|
"@types/expect": "^24.3.0",
|
||||||
"@typescript-eslint/parser": "^5.7.0",
|
"@types/mocha": "^8.2.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^4.14.0",
|
||||||
|
"@typescript-eslint/parser": "^4.14.0",
|
||||||
|
"chai": "^4.2.0",
|
||||||
"eslint": "^7.18.0",
|
"eslint": "^7.18.0",
|
||||||
"eslint-config-airbnb": "^19.0.2",
|
"mocha": "^8.2.1",
|
||||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
"moq.ts": "^7.2.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"nyc": "^15.1.0",
|
||||||
"eslint-import-resolver-root-import": "^1.0.4",
|
"ts-mocha": "^8.0.0",
|
||||||
"eslint-plugin-import": "^2.25.3",
|
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
|
||||||
"jest": "^27.4.5",
|
|
||||||
"prettier": "^2.5.1",
|
|
||||||
"ts-jest": "^27.1.2",
|
|
||||||
"typedoc": "^0.20.27",
|
"typedoc": "^0.20.27",
|
||||||
"typescript": "^4.5.4"
|
"typescript": "^4.1.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cosmjs/cosmwasm-stargate": "^0.27.0-rc2",
|
|
||||||
"@cosmjs/crypto": "^0.27.0-rc2",
|
|
||||||
"@cosmjs/math": "^0.27.0-rc2",
|
|
||||||
"@cosmjs/proto-signing": "^0.27.0-rc2",
|
|
||||||
"@cosmjs/stargate": "^0.27.0-rc2",
|
|
||||||
"@cosmjs/tendermint-rpc": "^0.27.0-rc2",
|
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"cosmjs-types": "^0.4.0",
|
"@cosmjs/cosmwasm-stargate": "^0.25.5",
|
||||||
"dotenv": "^10.0.0",
|
"@cosmjs/stargate": "^0.25.5",
|
||||||
"moq.ts": "^7.3.4"
|
"@cosmjs/math": "^0.25.5",
|
||||||
|
"@cosmjs/proto-signing": "^0.25.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"es6": true,
|
||||||
|
"node": true,
|
||||||
|
"mocha": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2018,
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-console": "off",
|
||||||
|
"linebreak-style": "off",
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"double",
|
||||||
|
{
|
||||||
|
"allowTemplateLiterals": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keyword-spacing": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"before": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"space-before-blocks": [
|
||||||
|
"error"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
15.0.1
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import {GatewayBond, PagedGatewayResponse} from "../types";
|
||||||
|
import {INetClient} from "../net-client"
|
||||||
|
import {IQueryClient} from "../query-client";
|
||||||
|
import {VALIDATOR_API_GATEWAYS, VALIDATOR_API_PORT} from "../index";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There are serious limits in smart contract systems, but we need to keep track of
|
||||||
|
* potentially thousands of nodes. GatewaysCache instances repeatedly make requests for
|
||||||
|
* paged data about what gateways exist, and keep them locally in memory so that they're
|
||||||
|
* available for querying.
|
||||||
|
**/
|
||||||
|
export default class GatewaysCache {
|
||||||
|
gateways: GatewayBond[]
|
||||||
|
client: INetClient | IQueryClient
|
||||||
|
perPage: number
|
||||||
|
|
||||||
|
constructor(client: INetClient | IQueryClient, perPage: number) {
|
||||||
|
this.client = client;
|
||||||
|
this.gateways = [];
|
||||||
|
this.perPage = perPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes repeated requests to assemble a full list of gateways.
|
||||||
|
/// Requests continue to be make as long as `shouldMakeAnotherRequest()`
|
||||||
|
/// returns true.
|
||||||
|
async refreshGateways(contractAddress: string): Promise<GatewayBond[]> {
|
||||||
|
let newGateways: GatewayBond[] = [];
|
||||||
|
let response: PagedGatewayResponse;
|
||||||
|
let next: string | undefined = undefined;
|
||||||
|
for (;;) {
|
||||||
|
response = await this.client.getGateways(contractAddress, this.perPage, next);
|
||||||
|
newGateways = newGateways.concat(response.nodes)
|
||||||
|
next = response.start_next_after;
|
||||||
|
// if `start_next_after` is not set, we're done
|
||||||
|
if (!next) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.gateways = newGateways
|
||||||
|
return newGateways;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes requests to assemble a full list of gateways from validator-api
|
||||||
|
async refreshValidatorAPIGateways(urls: string[]): Promise<GatewayBond[]> {
|
||||||
|
for (const url of urls) {
|
||||||
|
const validator_api_url = new URL(url);
|
||||||
|
validator_api_url.port = VALIDATOR_API_PORT;
|
||||||
|
validator_api_url.pathname += VALIDATOR_API_GATEWAYS;
|
||||||
|
const response = await axios.get(validator_api_url.toString());
|
||||||
|
if (response.status == 200) {
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("None of the provided validators seem to be alive")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import {MixNodeBond, PagedMixnodeResponse} from "../types";
|
||||||
|
import { INetClient } from "../net-client"
|
||||||
|
import {IQueryClient} from "../query-client";
|
||||||
|
import {VALIDATOR_API_MIXNODES, VALIDATOR_API_PORT} from "../index";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export { MixnodesCache };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There are serious limits in smart contract systems, but we need to keep track of
|
||||||
|
* potentially thousands of nodes. MixnodeCache instances repeatedly make requests for
|
||||||
|
* paged data about what mixnodes exist, and keep them locally in memory so that they're
|
||||||
|
* available for querying.
|
||||||
|
* */
|
||||||
|
export default class MixnodesCache {
|
||||||
|
mixNodes: MixNodeBond[]
|
||||||
|
client: INetClient | IQueryClient
|
||||||
|
perPage: number
|
||||||
|
|
||||||
|
constructor(client: INetClient | IQueryClient, perPage: number) {
|
||||||
|
this.client = client;
|
||||||
|
this.mixNodes = [];
|
||||||
|
this.perPage = perPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes repeated requests to assemble a full list of nodes.
|
||||||
|
/// Requests continue to be make as long as `shouldMakeAnotherRequest()`
|
||||||
|
// returns true.
|
||||||
|
async refreshMixNodes(contractAddress: string): Promise<MixNodeBond[]> {
|
||||||
|
let newMixnodes: MixNodeBond[] = [];
|
||||||
|
let response: PagedMixnodeResponse;
|
||||||
|
let next: string | undefined = undefined;
|
||||||
|
for (;;) {
|
||||||
|
response = await this.client.getMixNodes(contractAddress, this.perPage, next);
|
||||||
|
newMixnodes = newMixnodes.concat(response.nodes)
|
||||||
|
next = response.start_next_after;
|
||||||
|
// if `start_next_after` is not set, we're done
|
||||||
|
if (!next) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mixNodes = newMixnodes
|
||||||
|
return this.mixNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes requests to assemble a full list of mixnodes from validator-api
|
||||||
|
async refreshValidatorAPIMixNodes(urls: string[]): Promise<MixNodeBond[]> {
|
||||||
|
for (const url of urls) {
|
||||||
|
const validator_api_url = new URL(url);
|
||||||
|
validator_api_url.port = VALIDATOR_API_PORT;
|
||||||
|
validator_api_url.pathname += VALIDATOR_API_MIXNODES;
|
||||||
|
const response = await axios.get(validator_api_url.toString());
|
||||||
|
if (response.status == 200) {
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("None of the provided validators seem to be alive")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,68 +1,73 @@
|
|||||||
import { Decimal } from '@cosmjs/math';
|
import { Decimal } from "@cosmjs/math";
|
||||||
import { Coin } from '@cosmjs/stargate';
|
import { Coin } from ".";
|
||||||
|
|
||||||
// NARROW NO-BREAK SPACE (U+202F)
|
// NARROW NO-BREAK SPACE (U+202F)
|
||||||
const thinSpace = '\u202F';
|
const thinSpace = "\u202F";
|
||||||
|
|
||||||
export function printableCoin(coin?: Coin): string {
|
export function printableCoin(coin?: Coin): string {
|
||||||
if (!coin) {
|
if (!coin) {
|
||||||
return '0';
|
return "0";
|
||||||
}
|
}
|
||||||
if (coin.denom.startsWith('u')) {
|
if (coin.denom.startsWith("u")) {
|
||||||
const ticker = coin.denom.slice(1).toUpperCase();
|
const ticker = coin.denom.slice(1).toUpperCase();
|
||||||
return Decimal.fromAtomics(coin.amount, 6).toString() + thinSpace + ticker;
|
return Decimal.fromAtomics(coin.amount, 6).toString() + thinSpace + ticker;
|
||||||
}
|
} else {
|
||||||
return coin.amount + thinSpace + coin.denom;
|
return coin.amount + thinSpace + coin.denom;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function printableBalance(balance?: readonly Coin[]): string {
|
export function printableBalance(balance?: readonly Coin[]): string {
|
||||||
if (!balance || balance.length === 0) return '–';
|
if (!balance || balance.length === 0) return "–";
|
||||||
return balance.map(printableCoin).join(', ');
|
return balance.map(printableCoin).join(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// converts display amount, such as "12.0346" to its native token representation,
|
// converts display amount, such as "12.0346" to its native token representation,
|
||||||
// with 6 fractional digits. So in that case it would result in "12034600"
|
// with 6 fractional digits. So in that case it would result in "12034600"
|
||||||
// Basically does the same job as `displayAmountToNative` but without the requirement
|
// Basically does the same job as `displayAmountToNative` but without the requirement
|
||||||
// of having the coinMap
|
// of having the coinMap
|
||||||
export function printableBalanceToNative(amountToDisplay: string): string {
|
export function printableBalanceToNative(amountToDisplay: string): string {
|
||||||
const decimalAmount = Decimal.fromUserInput(amountToDisplay, 6);
|
const decimalAmount = Decimal.fromUserInput(amountToDisplay, 6);
|
||||||
return decimalAmount.atomics;
|
return decimalAmount.atomics;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reciprocal of `printableBalanceToNative`, takes, for example 10000000 and returns 10
|
// reciprocal of `printableBalanceToNative`, takes, for example 10000000 and returns 10
|
||||||
export function nativeToPrintable(nativeValue: string): string {
|
export function nativeToPrintable(nativeValue: string): string {
|
||||||
return Decimal.fromAtomics(nativeValue, 6).toString();
|
return Decimal.fromAtomics(nativeValue, 6).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MappedCoin {
|
export interface MappedCoin {
|
||||||
readonly denom: string;
|
readonly denom: string;
|
||||||
readonly fractionalDigits: number;
|
readonly fractionalDigits: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CoinMap {
|
export interface CoinMap {
|
||||||
readonly [key: string]: MappedCoin;
|
readonly [key: string]: MappedCoin;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function nativeCoinToDisplay(coin: Coin, coinMap: CoinMap): Coin {
|
export function nativeCoinToDisplay(coin: Coin, coinMap: CoinMap): Coin {
|
||||||
if (!coinMap) return coin;
|
if (!coinMap) return coin;
|
||||||
|
|
||||||
const coinToDisplay = coinMap[coin.denom];
|
const coinToDisplay = coinMap[coin.denom];
|
||||||
if (!coinToDisplay) return coin;
|
if (!coinToDisplay) return coin;
|
||||||
|
|
||||||
const amountToDisplay = Decimal.fromAtomics(coin.amount, coinToDisplay.fractionalDigits).toString();
|
const amountToDisplay = Decimal.fromAtomics(coin.amount, coinToDisplay.fractionalDigits).toString();
|
||||||
|
|
||||||
return { denom: coinToDisplay.denom, amount: amountToDisplay };
|
return { denom: coinToDisplay.denom, amount: amountToDisplay };
|
||||||
}
|
}
|
||||||
|
|
||||||
// display amount is eg "12.0346", return is in native tokens
|
// display amount is eg "12.0346", return is in native tokens
|
||||||
// with 6 fractional digits, this would be eg. "12034600"
|
// with 6 fractional digits, this would be eg. "12034600"
|
||||||
export function displayAmountToNative(amountToDisplay: string, coinMap: CoinMap, nativeDenom: string): string {
|
export function displayAmountToNative(
|
||||||
const fractionalDigits = coinMap[nativeDenom]?.fractionalDigits;
|
amountToDisplay: string,
|
||||||
if (fractionalDigits) {
|
coinMap: CoinMap,
|
||||||
// use https://github.com/CosmWasm/cosmjs/blob/v0.22.2/packages/math/src/decimal.ts
|
nativeDenom: string,
|
||||||
const decimalAmount = Decimal.fromUserInput(amountToDisplay, fractionalDigits);
|
): string {
|
||||||
return decimalAmount.atomics;
|
const fractionalDigits = coinMap[nativeDenom]?.fractionalDigits;
|
||||||
}
|
if (fractionalDigits) {
|
||||||
|
// use https://github.com/CosmWasm/cosmjs/blob/v0.22.2/packages/math/src/decimal.ts
|
||||||
|
const decimalAmount = Decimal.fromUserInput(amountToDisplay, fractionalDigits);
|
||||||
|
return decimalAmount.atomics;
|
||||||
|
}
|
||||||
|
|
||||||
return amountToDisplay;
|
return amountToDisplay;
|
||||||
}
|
}
|
||||||
|
|||||||
+623
-444
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,207 @@
|
|||||||
|
import { SigningCosmWasmClient, SigningCosmWasmClientOptions } from "@cosmjs/cosmwasm-stargate";
|
||||||
|
import {
|
||||||
|
Delegation,
|
||||||
|
GatewayOwnershipResponse,
|
||||||
|
MixOwnershipResponse, PagedGatewayDelegationsResponse,
|
||||||
|
PagedGatewayResponse, PagedMixDelegationsResponse,
|
||||||
|
PagedMixnodeResponse,
|
||||||
|
StateParams
|
||||||
|
} from "./types";
|
||||||
|
import { DirectSecp256k1HdWallet, EncodeObject } from "@cosmjs/proto-signing";
|
||||||
|
import { Coin, StdFee } from "@cosmjs/stargate";
|
||||||
|
import { BroadcastTxResponse } from "@cosmjs/stargate"
|
||||||
|
import { nymGasLimits, nymGasPrice } from "./stargate-helper"
|
||||||
|
import {
|
||||||
|
ExecuteResult,
|
||||||
|
InstantiateOptions,
|
||||||
|
InstantiateResult,
|
||||||
|
MigrateResult,
|
||||||
|
UploadMeta,
|
||||||
|
UploadResult
|
||||||
|
} from "@cosmjs/cosmwasm-stargate";
|
||||||
|
|
||||||
|
export interface INetClient {
|
||||||
|
clientAddress: string;
|
||||||
|
|
||||||
|
getBalance(address: string, denom: string): Promise<Coin | null>;
|
||||||
|
|
||||||
|
getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedMixnodeResponse>;
|
||||||
|
|
||||||
|
getGateways(contractAddress: string, limit: number, start_after?: string): Promise<PagedGatewayResponse>;
|
||||||
|
|
||||||
|
getMixDelegations(contractAddress: string, mixIdentity: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse>
|
||||||
|
|
||||||
|
getMixDelegation(contractAddress: string, mixIdentity: string, delegatorAddress: string): Promise<Delegation>
|
||||||
|
|
||||||
|
getGatewayDelegations(contractAddress: string, gatewayIdentity: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse>
|
||||||
|
|
||||||
|
getGatewayDelegation(contractAddress: string, gatewayIdentity: string, delegatorAddress: string): Promise<Delegation>
|
||||||
|
|
||||||
|
ownsMixNode(contractAddress: string, address: string): Promise<MixOwnershipResponse>;
|
||||||
|
|
||||||
|
ownsGateway(contractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
|
||||||
|
|
||||||
|
getStateParams(contractAddress: string): Promise<StateParams>;
|
||||||
|
|
||||||
|
signAndBroadcast(signerAddress: string, messages: readonly EncodeObject[], fee: StdFee, memo?: string): Promise<BroadcastTxResponse>;
|
||||||
|
|
||||||
|
executeContract(senderAddress: string, contractAddress: string, handleMsg: Record<string, unknown>, memo?: string, transferAmount?: readonly Coin[]): Promise<ExecuteResult>;
|
||||||
|
|
||||||
|
instantiate(senderAddress: string, codeId: number, initMsg: Record<string, unknown>, label: string, options?: InstantiateOptions): Promise<InstantiateResult>;
|
||||||
|
|
||||||
|
sendTokens(senderAddress: string, recipientAddress: string, transferAmount: readonly Coin[], memo?: string): Promise<BroadcastTxResponse>;
|
||||||
|
|
||||||
|
upload(senderAddress: string, wasmCode: Uint8Array, meta?: UploadMeta, memo?: string): Promise<UploadResult>;
|
||||||
|
|
||||||
|
changeValidator(newUrl: string): Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes care of network communication between this code and the validator.
|
||||||
|
* Depends on `SigningCosmWasClient`, which signs all requests using keypairs
|
||||||
|
* derived from on bech32 mnemonics.
|
||||||
|
*
|
||||||
|
* Wraps several methods from CosmWasmSigningClient so we can mock them for
|
||||||
|
* unit testing.
|
||||||
|
*/
|
||||||
|
export default class NetClient implements INetClient {
|
||||||
|
clientAddress: string;
|
||||||
|
private cosmClient: SigningCosmWasmClient;
|
||||||
|
|
||||||
|
// helpers for changing validators without having to remake the wallet
|
||||||
|
private readonly wallet: DirectSecp256k1HdWallet;
|
||||||
|
private readonly signerOptions: SigningCosmWasmClientOptions;
|
||||||
|
|
||||||
|
private constructor(clientAddress: string, cosmClient: SigningCosmWasmClient, wallet: DirectSecp256k1HdWallet, signerOptions: SigningCosmWasmClientOptions) {
|
||||||
|
this.clientAddress = clientAddress;
|
||||||
|
this.cosmClient = cosmClient;
|
||||||
|
this.wallet = wallet;
|
||||||
|
this.signerOptions = signerOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async connect(wallet: DirectSecp256k1HdWallet, url: string, prefix: string): Promise<INetClient> {
|
||||||
|
const [{address}] = await wallet.getAccounts();
|
||||||
|
const signerOptions: SigningCosmWasmClientOptions = {
|
||||||
|
gasPrice: nymGasPrice(prefix),
|
||||||
|
gasLimits: nymGasLimits,
|
||||||
|
};
|
||||||
|
const client = await SigningCosmWasmClient.connectWithSigner(url, wallet, signerOptions);
|
||||||
|
return new NetClient(address, client, wallet, signerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
async changeValidator(url: string): Promise<void> {
|
||||||
|
this.cosmClient = await SigningCosmWasmClient.connectWithSigner(url, this.wallet, this.signerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedMixnodeResponse> {
|
||||||
|
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {get_mix_nodes: {limit}});
|
||||||
|
} else {
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {get_mix_nodes: {limit, start_after}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getGateways(contractAddress: string, limit: number, start_after?: string): Promise<PagedGatewayResponse> {
|
||||||
|
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {get_gateways: {limit}});
|
||||||
|
} else {
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {get_gateways: {limit, start_after}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMixDelegations(contractAddress: string, mixIdentity: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse> {
|
||||||
|
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
|
get_mix_delegations: {
|
||||||
|
mix_identity: mixIdentity,
|
||||||
|
limit
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
|
get_mix_delegations: {
|
||||||
|
mix_identity: mixIdentity,
|
||||||
|
limit,
|
||||||
|
start_after
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMixDelegation(contractAddress: string, mixIdentity: string, delegatorAddress: string): Promise<Delegation> {
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
|
get_mix_delegation: {
|
||||||
|
mix_identity: mixIdentity,
|
||||||
|
address: delegatorAddress
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getGatewayDelegations(contractAddress: string, gatewayIdentity: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse> {
|
||||||
|
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
|
get_gateway_delegations: {
|
||||||
|
gateway_identity: gatewayIdentity,
|
||||||
|
limit
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
|
get_gateway_delegations: {
|
||||||
|
gateway_identity: gatewayIdentity,
|
||||||
|
limit,
|
||||||
|
start_after
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getGatewayDelegation(contractAddress: string, gatewayIdentity: string, delegatorAddress: string): Promise<Delegation> {
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
|
get_gateway_delegation: {
|
||||||
|
gateway_identity: gatewayIdentity,
|
||||||
|
address: delegatorAddress
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ownsMixNode(contractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {owns_mixnode: {address}});
|
||||||
|
}
|
||||||
|
|
||||||
|
public ownsGateway(contractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {owns_gateway: {address}});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getBalance(address: string, denom: string): Promise<Coin | null> {
|
||||||
|
return this.cosmClient.getBalance(address, denom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getStateParams(contractAddress: string): Promise<StateParams> {
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {state_params: {}});
|
||||||
|
}
|
||||||
|
|
||||||
|
public executeContract(senderAddress: string, contractAddress: string, handleMsg: Record<string, unknown>, memo?: string, transferAmount?: readonly Coin[]): Promise<ExecuteResult> {
|
||||||
|
return this.cosmClient.execute(senderAddress, contractAddress, handleMsg, memo, transferAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public signAndBroadcast(signerAddress: string, messages: readonly EncodeObject[], fee: StdFee, memo?: string): Promise<BroadcastTxResponse> {
|
||||||
|
return this.cosmClient.signAndBroadcast(signerAddress, messages, fee, memo)
|
||||||
|
}
|
||||||
|
|
||||||
|
public sendTokens(senderAddress: string, recipientAddress: string, transferAmount: readonly Coin[], memo?: string): Promise<BroadcastTxResponse> {
|
||||||
|
return this.cosmClient.sendTokens(senderAddress, recipientAddress, transferAmount, memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public upload(senderAddress: string, wasmCode: Uint8Array, meta?: UploadMeta, memo?: string): Promise<UploadResult> {
|
||||||
|
return this.cosmClient.upload(senderAddress, wasmCode, meta, memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public instantiate(senderAddress: string, codeId: number, initMsg: Record<string, unknown>, label: string, options?: InstantiateOptions): Promise<InstantiateResult> {
|
||||||
|
return this.cosmClient.instantiate(senderAddress, codeId, initMsg, label, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public migrate(senderAddress: string, contractAddress: string, codeId: number, migrateMsg: Record<string, unknown>, memo?: string): Promise<MigrateResult> {
|
||||||
|
return this.cosmClient.migrate(senderAddress, contractAddress, codeId, migrateMsg, memo)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { JsonObject } from '@cosmjs/cosmwasm-stargate/build/queries';
|
|
||||||
// eslint-disable-next-line import/no-cycle
|
|
||||||
import { INymdQuery } from './query-client';
|
|
||||||
import {
|
|
||||||
ContractStateParams,
|
|
||||||
Delegation,
|
|
||||||
GatewayOwnershipResponse,
|
|
||||||
LayerDistribution,
|
|
||||||
MixnetContractVersion,
|
|
||||||
MixOwnershipResponse,
|
|
||||||
PagedAllDelegationsResponse,
|
|
||||||
PagedDelegatorDelegationsResponse,
|
|
||||||
PagedGatewayResponse,
|
|
||||||
PagedMixDelegationsResponse,
|
|
||||||
PagedMixnodeResponse,
|
|
||||||
RewardingStatus,
|
|
||||||
} from './types';
|
|
||||||
|
|
||||||
interface SmartContractQuery {
|
|
||||||
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class NymdQuerier implements INymdQuery {
|
|
||||||
client: SmartContractQuery;
|
|
||||||
|
|
||||||
constructor(client: SmartContractQuery) {
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_contract_version: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_mix_nodes: {
|
|
||||||
limit,
|
|
||||||
start_after: startAfter,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_gateways: {
|
|
||||||
limit,
|
|
||||||
start_after: startAfter,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
owns_mixnode: {
|
|
||||||
address,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
owns_gateway: {
|
|
||||||
address,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
state_params: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getAllNetworkDelegationsPaged(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
limit?: number,
|
|
||||||
startAfter?: [string, string],
|
|
||||||
): Promise<PagedAllDelegationsResponse> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_all_network_delegations: {
|
|
||||||
start_after: startAfter,
|
|
||||||
limit,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getMixNodeDelegationsPaged(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
limit?: number,
|
|
||||||
startAfter?: string,
|
|
||||||
): Promise<PagedMixDelegationsResponse> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_mixnode_delegations: {
|
|
||||||
mix_identity: mixIdentity,
|
|
||||||
start_after: startAfter,
|
|
||||||
limit,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getDelegatorDelegationsPaged(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
delegator: string,
|
|
||||||
limit?: number,
|
|
||||||
startAfter?: string,
|
|
||||||
): Promise<PagedDelegatorDelegationsResponse> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_delegator_delegations: {
|
|
||||||
delegator,
|
|
||||||
start_after: startAfter,
|
|
||||||
limit,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_delegation_details: {
|
|
||||||
mix_identity: mixIdentity,
|
|
||||||
delegator,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
layer_distribution: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getRewardPool(mixnetContractAddress: string): Promise<string> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_reward_pool: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_circulating_supply: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_interval_reward_percent: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_sybil_resistance_percent: {},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getRewardingStatus(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
rewardingIntervalNonce: number,
|
|
||||||
): Promise<RewardingStatus> {
|
|
||||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
|
||||||
get_rewarding_status: {
|
|
||||||
mix_identity: mixIdentity,
|
|
||||||
rewarding_interval_nonce: rewardingIntervalNonce,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,212 +1,147 @@
|
|||||||
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate';
|
import { Coin } from "@cosmjs/stargate";
|
||||||
import { Tendermint34Client } from '@cosmjs/tendermint-rpc';
|
import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";
|
||||||
import {
|
import {
|
||||||
Account,
|
Delegation,
|
||||||
Block,
|
GatewayOwnershipResponse,
|
||||||
Coin,
|
MixOwnershipResponse, PagedGatewayDelegationsResponse,
|
||||||
DeliverTxResponse,
|
PagedGatewayResponse, PagedMixDelegationsResponse,
|
||||||
IndexedTx,
|
PagedMixnodeResponse,
|
||||||
SearchTxFilter,
|
StateParams
|
||||||
SearchTxQuery,
|
} from "./types";
|
||||||
SequenceResponse,
|
|
||||||
} from '@cosmjs/stargate';
|
|
||||||
import { JsonObject } from '@cosmjs/cosmwasm-stargate/build/queries';
|
|
||||||
import { Code, CodeDetails, Contract, ContractCodeHistoryEntry } from '@cosmjs/cosmwasm-stargate/build/cosmwasmclient';
|
|
||||||
// eslint-disable-next-line import/no-cycle
|
|
||||||
import NymdQuerier from './nymd-querier';
|
|
||||||
import {
|
|
||||||
ContractStateParams,
|
|
||||||
Delegation,
|
|
||||||
GatewayBond,
|
|
||||||
GatewayOwnershipResponse,
|
|
||||||
LayerDistribution,
|
|
||||||
MixnetContractVersion,
|
|
||||||
MixNodeBond,
|
|
||||||
MixOwnershipResponse,
|
|
||||||
PagedAllDelegationsResponse,
|
|
||||||
PagedDelegatorDelegationsResponse,
|
|
||||||
PagedGatewayResponse,
|
|
||||||
PagedMixDelegationsResponse,
|
|
||||||
PagedMixnodeResponse,
|
|
||||||
RewardingStatus,
|
|
||||||
} from './types';
|
|
||||||
import ValidatorApiQuerier, { IValidatorApiQuery } from './validator-api-querier';
|
|
||||||
|
|
||||||
export interface ICosmWasmQuery {
|
export interface IQueryClient {
|
||||||
// methods exposed by `CosmWasmClient`
|
getBalance(address: string, stakeDenom: string): Promise<Coin | null>;
|
||||||
getChainId(): Promise<string>;
|
|
||||||
getHeight(): Promise<number>;
|
getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedMixnodeResponse>;
|
||||||
getAccount(searchAddress: string): Promise<Account | null>;
|
|
||||||
getSequence(address: string): Promise<SequenceResponse>;
|
getGateways(contractAddress: string, limit: number, start_after?: string): Promise<PagedGatewayResponse>;
|
||||||
getBlock(height?: number): Promise<Block>;
|
|
||||||
getBalance(address: string, searchDenom: string): Promise<Coin>;
|
getMixDelegations(contractAddress: string, mixIdentity: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse>
|
||||||
getTx(id: string): Promise<IndexedTx | null>;
|
|
||||||
searchTx(query: SearchTxQuery, filter?: SearchTxFilter): Promise<readonly IndexedTx[]>;
|
getMixDelegation(contractAddress: string, mixIdentity: string, delegatorAddress: string): Promise<Delegation>
|
||||||
disconnect(): void;
|
|
||||||
broadcastTx(tx: Uint8Array, timeoutMs?: number, pollIntervalMs?: number): Promise<DeliverTxResponse>;
|
getGatewayDelegations(contractAddress: string, gatewayIdentity: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse>
|
||||||
getCodes(): Promise<readonly Code[]>;
|
|
||||||
getCodeDetails(codeId: number): Promise<CodeDetails>;
|
getGatewayDelegation(contractAddress: string, gatewayIdentity: string, delegatorAddress: string): Promise<Delegation>
|
||||||
getContracts(codeId: number): Promise<readonly string[]>;
|
|
||||||
getContract(address: string): Promise<Contract>;
|
ownsMixNode(contractAddress: string, address: string): Promise<MixOwnershipResponse>;
|
||||||
getContractCodeHistory(address: string): Promise<readonly ContractCodeHistoryEntry[]>;
|
|
||||||
queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null>;
|
ownsGateway(contractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
|
||||||
queryContractSmart(address: string, queryMsg: Record<string, unknown>): Promise<JsonObject>;
|
|
||||||
|
getStateParams(contractAddress: string): Promise<StateParams>;
|
||||||
|
|
||||||
|
changeValidator(newUrl: string): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INymdQuery {
|
/**
|
||||||
// nym-specific implemented inside NymQuerier
|
* Takes care of network communication between this code and the validator.
|
||||||
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion>;
|
* Depends on `SigningCosmWasClient`, which signs all requests using keypairs
|
||||||
|
* derived from on bech32 mnemonics.
|
||||||
|
*
|
||||||
|
* Wraps several methods from CosmWasmSigningClient so we can mock them for
|
||||||
|
* unit testing.
|
||||||
|
*/
|
||||||
|
export default class QueryClient implements IQueryClient {
|
||||||
|
private cosmClient: CosmWasmClient;
|
||||||
|
|
||||||
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse>;
|
private constructor(cosmClient: CosmWasmClient) {
|
||||||
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse>;
|
this.cosmClient = cosmClient;
|
||||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
|
}
|
||||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
|
|
||||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams>;
|
|
||||||
|
|
||||||
getAllNetworkDelegationsPaged(
|
public static async connect(url: string): Promise<IQueryClient> {
|
||||||
mixnetContractAddress: string,
|
const client = await CosmWasmClient.connect(url)
|
||||||
limit?: number,
|
return new QueryClient(client)
|
||||||
startAfter?: [string, string],
|
}
|
||||||
): Promise<PagedAllDelegationsResponse>;
|
|
||||||
getMixNodeDelegationsPaged(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
limit?: number,
|
|
||||||
startAfter?: string,
|
|
||||||
): Promise<PagedMixDelegationsResponse>;
|
|
||||||
getDelegatorDelegationsPaged(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
delegator: string,
|
|
||||||
limit?: number,
|
|
||||||
startAfter?: string,
|
|
||||||
): Promise<PagedDelegatorDelegationsResponse>;
|
|
||||||
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation>;
|
|
||||||
|
|
||||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution>;
|
async changeValidator(url: string): Promise<void> {
|
||||||
getRewardPool(mixnetContractAddress: string): Promise<string>;
|
this.cosmClient = await CosmWasmClient.connect(url)
|
||||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string>;
|
}
|
||||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number>;
|
|
||||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number>;
|
public getMixNodes(contractAddress: string, limit: number, start_after?: string): Promise<PagedMixnodeResponse> {
|
||||||
getRewardingStatus(
|
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
|
||||||
mixnetContractAddress: string,
|
return this.cosmClient.queryContractSmart(contractAddress, {get_mix_nodes: {limit}});
|
||||||
mixIdentity: string,
|
} else {
|
||||||
rewardingIntervalNonce: number,
|
return this.cosmClient.queryContractSmart(contractAddress, {get_mix_nodes: {limit, start_after}});
|
||||||
): Promise<RewardingStatus>;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IQueryClient extends ICosmWasmQuery, INymdQuery, IValidatorApiQuery {}
|
public getGateways(contractAddress: string, limit: number, start_after?: string): Promise<PagedGatewayResponse> {
|
||||||
|
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
|
||||||
export default class QueryClient extends CosmWasmClient implements IQueryClient {
|
return this.cosmClient.queryContractSmart(contractAddress, {get_gateways: {limit}});
|
||||||
private nymdQuerier: NymdQuerier;
|
} else {
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {get_gateways: {limit, start_after}});
|
||||||
private validatorApiQuerier: ValidatorApiQuerier;
|
}
|
||||||
|
}
|
||||||
private constructor(tmClient: Tendermint34Client, validatorApiUrl: string) {
|
|
||||||
super(tmClient);
|
public getMixDelegations(contractAddress: string, mixIdentity: string, limit: number, start_after?: string): Promise<PagedMixDelegationsResponse> {
|
||||||
this.nymdQuerier = new NymdQuerier(this);
|
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
|
||||||
this.validatorApiQuerier = new ValidatorApiQuerier(validatorApiUrl);
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
}
|
get_mix_delegations: {
|
||||||
|
mix_identity: mixIdentity,
|
||||||
public static async connectWithNym(nymdUrl: string, validatorApiUrl: string): Promise<QueryClient> {
|
limit
|
||||||
const tmClient = await Tendermint34Client.connect(nymdUrl);
|
}
|
||||||
return new QueryClient(tmClient, validatorApiUrl);
|
});
|
||||||
}
|
} else {
|
||||||
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
|
get_mix_delegations: {
|
||||||
return this.nymdQuerier.getContractVersion(mixnetContractAddress);
|
mix_identity: mixIdentity,
|
||||||
}
|
limit,
|
||||||
|
start_after
|
||||||
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
|
}
|
||||||
return this.nymdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
|
|
||||||
return this.nymdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
|
public getMixDelegation(contractAddress: string, mixIdentity: string, delegatorAddress: string): Promise<Delegation> {
|
||||||
}
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
|
get_mix_delegation: {
|
||||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
mix_identity: mixIdentity,
|
||||||
return this.nymdQuerier.ownsMixNode(mixnetContractAddress, address);
|
address: delegatorAddress
|
||||||
}
|
}
|
||||||
|
});
|
||||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
}
|
||||||
return this.nymdQuerier.ownsGateway(mixnetContractAddress, address);
|
|
||||||
}
|
public getGatewayDelegations(contractAddress: string, gatewayIdentity: string, limit: number, start_after?: string): Promise<PagedGatewayDelegationsResponse> {
|
||||||
|
if (start_after == undefined) { // TODO: check if we can take this out, I'm not sure what will happen if we send an "undefined" so I'm playing it safe here.
|
||||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
return this.nymdQuerier.getStateParams(mixnetContractAddress);
|
get_gateway_delegations: {
|
||||||
}
|
gateway_identity: gatewayIdentity,
|
||||||
|
limit
|
||||||
getAllNetworkDelegationsPaged(
|
}
|
||||||
mixnetContractAddress: string,
|
});
|
||||||
limit?: number,
|
} else {
|
||||||
startAfter?: [string, string],
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
): Promise<PagedAllDelegationsResponse> {
|
get_gateway_delegations: {
|
||||||
return this.nymdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
|
gateway_identity: gatewayIdentity,
|
||||||
}
|
limit,
|
||||||
|
start_after
|
||||||
getMixNodeDelegationsPaged(
|
}
|
||||||
mixnetContractAddress: string,
|
});
|
||||||
mixIdentity: string,
|
}
|
||||||
limit?: number,
|
}
|
||||||
startAfter?: string,
|
|
||||||
): Promise<PagedMixDelegationsResponse> {
|
public getGatewayDelegation(contractAddress: string, gatewayIdentity: string, delegatorAddress: string): Promise<Delegation> {
|
||||||
return this.nymdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
|
return this.cosmClient.queryContractSmart(contractAddress, {
|
||||||
}
|
get_gateway_delegation: {
|
||||||
|
gateway_identity: gatewayIdentity,
|
||||||
getDelegatorDelegationsPaged(
|
address: delegatorAddress
|
||||||
mixnetContractAddress: string,
|
}
|
||||||
delegator: string,
|
});
|
||||||
limit?: number,
|
}
|
||||||
startAfter?: string,
|
|
||||||
): Promise<PagedDelegatorDelegationsResponse> {
|
public ownsMixNode(contractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
||||||
return this.nymdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
|
return this.cosmClient.queryContractSmart(contractAddress, {owns_mixnode: {address}});
|
||||||
}
|
}
|
||||||
|
|
||||||
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
|
public ownsGateway(contractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
||||||
return this.nymdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
|
return this.cosmClient.queryContractSmart(contractAddress, {owns_gateway: {address}});
|
||||||
}
|
}
|
||||||
|
|
||||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
|
public getBalance(address: string, stakeDenom: string): Promise<Coin | null> {
|
||||||
return this.nymdQuerier.getLayerDistribution(mixnetContractAddress);
|
return this.cosmClient.getBalance(address, stakeDenom);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRewardPool(mixnetContractAddress: string): Promise<string> {
|
public getStateParams(contractAddress: string): Promise<StateParams> {
|
||||||
return this.nymdQuerier.getRewardPool(mixnetContractAddress);
|
return this.cosmClient.queryContractSmart(contractAddress, {state_params: {}});
|
||||||
}
|
}
|
||||||
|
|
||||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
|
|
||||||
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.nymdQuerier.getSybilResistancePercent(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getRewardingStatus(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
rewardingIntervalNonce: number,
|
|
||||||
): Promise<RewardingStatus> {
|
|
||||||
return this.nymdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCachedGateways(): Promise<GatewayBond[]> {
|
|
||||||
return this.validatorApiQuerier.getCachedGateways();
|
|
||||||
}
|
|
||||||
|
|
||||||
getCachedMixnodes(): Promise<MixNodeBond[]> {
|
|
||||||
return this.validatorApiQuerier.getCachedMixnodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
getActiveMixnodes(): Promise<MixNodeBond[]> {
|
|
||||||
return this.validatorApiQuerier.getActiveMixnodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
getRewardedMixnodes(): Promise<MixNodeBond[]> {
|
|
||||||
return this.validatorApiQuerier.getRewardedMixnodes();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,483 +0,0 @@
|
|||||||
import {
|
|
||||||
ExecuteResult,
|
|
||||||
InstantiateOptions,
|
|
||||||
InstantiateResult,
|
|
||||||
MigrateResult,
|
|
||||||
SigningCosmWasmClient,
|
|
||||||
SigningCosmWasmClientOptions,
|
|
||||||
UploadResult,
|
|
||||||
} from '@cosmjs/cosmwasm-stargate';
|
|
||||||
import { DirectSecp256k1HdWallet, EncodeObject } from '@cosmjs/proto-signing';
|
|
||||||
import { Coin, DeliverTxResponse, SignerData, StdFee } from '@cosmjs/stargate';
|
|
||||||
import { Tendermint34Client } from '@cosmjs/tendermint-rpc';
|
|
||||||
import { ChangeAdminResult } from '@cosmjs/cosmwasm-stargate/build/signingcosmwasmclient';
|
|
||||||
import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx';
|
|
||||||
import { nymGasPrice } from './stargate-helper';
|
|
||||||
import { IQueryClient } from './query-client';
|
|
||||||
import NymdQuerier from './nymd-querier';
|
|
||||||
import {
|
|
||||||
ContractStateParams,
|
|
||||||
Delegation,
|
|
||||||
Gateway,
|
|
||||||
GatewayBond,
|
|
||||||
GatewayOwnershipResponse,
|
|
||||||
LayerDistribution,
|
|
||||||
MixnetContractVersion,
|
|
||||||
MixNode,
|
|
||||||
MixNodeBond,
|
|
||||||
MixOwnershipResponse,
|
|
||||||
PagedAllDelegationsResponse,
|
|
||||||
PagedDelegatorDelegationsResponse,
|
|
||||||
PagedGatewayResponse,
|
|
||||||
PagedMixDelegationsResponse,
|
|
||||||
PagedMixnodeResponse,
|
|
||||||
RewardingStatus,
|
|
||||||
} from './types';
|
|
||||||
import ValidatorApiQuerier from './validator-api-querier';
|
|
||||||
|
|
||||||
// methods exposed by `SigningCosmWasmClient`
|
|
||||||
export interface ICosmWasmSigning {
|
|
||||||
simulate(signerAddress: string, messages: readonly EncodeObject[], memo: string | undefined): Promise<number>;
|
|
||||||
|
|
||||||
upload(
|
|
||||||
senderAddress: string,
|
|
||||||
wasmCode: Uint8Array,
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<UploadResult>;
|
|
||||||
|
|
||||||
instantiate(
|
|
||||||
senderAddress: string,
|
|
||||||
codeId: number,
|
|
||||||
msg: Record<string, unknown>,
|
|
||||||
label: string,
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
options?: InstantiateOptions,
|
|
||||||
): Promise<InstantiateResult>;
|
|
||||||
|
|
||||||
updateAdmin(
|
|
||||||
senderAddress: string,
|
|
||||||
contractAddress: string,
|
|
||||||
newAdmin: string,
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<ChangeAdminResult>;
|
|
||||||
|
|
||||||
clearAdmin(
|
|
||||||
senderAddress: string,
|
|
||||||
contractAddress: string,
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<ChangeAdminResult>;
|
|
||||||
|
|
||||||
migrate(
|
|
||||||
senderAddress: string,
|
|
||||||
contractAddress: string,
|
|
||||||
codeId: number,
|
|
||||||
migrateMsg: Record<string, unknown>,
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<MigrateResult>;
|
|
||||||
|
|
||||||
execute(
|
|
||||||
senderAddress: string,
|
|
||||||
contractAddress: string,
|
|
||||||
msg: Record<string, unknown>,
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
funds?: readonly Coin[],
|
|
||||||
): Promise<ExecuteResult>;
|
|
||||||
|
|
||||||
sendTokens(
|
|
||||||
senderAddress: string,
|
|
||||||
recipientAddress: string,
|
|
||||||
amount: readonly Coin[],
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<DeliverTxResponse>;
|
|
||||||
|
|
||||||
delegateTokens(
|
|
||||||
delegatorAddress: string,
|
|
||||||
validatorAddress: string,
|
|
||||||
amount: Coin,
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<DeliverTxResponse>;
|
|
||||||
|
|
||||||
undelegateTokens(
|
|
||||||
delegatorAddress: string,
|
|
||||||
validatorAddress: string,
|
|
||||||
amount: Coin,
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<DeliverTxResponse>;
|
|
||||||
|
|
||||||
withdrawRewards(
|
|
||||||
delegatorAddress: string,
|
|
||||||
validatorAddress: string,
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<DeliverTxResponse>;
|
|
||||||
|
|
||||||
signAndBroadcast(
|
|
||||||
signerAddress: string,
|
|
||||||
messages: readonly EncodeObject[],
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<DeliverTxResponse>;
|
|
||||||
|
|
||||||
sign(
|
|
||||||
signerAddress: string,
|
|
||||||
messages: readonly EncodeObject[],
|
|
||||||
fee: StdFee,
|
|
||||||
memo: string,
|
|
||||||
explicitSignerData?: SignerData,
|
|
||||||
): Promise<TxRaw>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface INymSigning {
|
|
||||||
clientAddress: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSigning {
|
|
||||||
bondMixNode(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixNode: MixNode,
|
|
||||||
ownerSignature: string,
|
|
||||||
pledge: Coin,
|
|
||||||
fee?: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<ExecuteResult>;
|
|
||||||
|
|
||||||
unbondMixNode(mixnetContractAddress: string, fee?: StdFee | 'auto' | number, memo?: string): Promise<ExecuteResult>;
|
|
||||||
|
|
||||||
bondGateway(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
gateway: Gateway,
|
|
||||||
ownerSignature: string,
|
|
||||||
pledge: Coin,
|
|
||||||
fee?: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<ExecuteResult>;
|
|
||||||
|
|
||||||
unbondGateway(mixnetContractAddress: string, fee?: StdFee | 'auto' | number, memo?: string): Promise<ExecuteResult>;
|
|
||||||
|
|
||||||
delegateToMixNode(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
amount: Coin,
|
|
||||||
fee?: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<ExecuteResult>;
|
|
||||||
|
|
||||||
undelegateFromMixNode(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
fee?: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<ExecuteResult>;
|
|
||||||
|
|
||||||
updateMixnodeConfig(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
profitMarginPercent: number,
|
|
||||||
fee: StdFee | 'auto' | number,
|
|
||||||
): Promise<ExecuteResult>;
|
|
||||||
|
|
||||||
updateContractStateParams(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
newParams: ContractStateParams,
|
|
||||||
fee?: StdFee | 'auto' | number,
|
|
||||||
memo?: string,
|
|
||||||
): Promise<ExecuteResult>;
|
|
||||||
|
|
||||||
// I don't see any point in exposing rewarding / vesting-related (INSIDE mixnet contract, like "BondMixnodeOnBehalf")
|
|
||||||
// functionalities in our typescript client. However, if for some reason, we find we need them
|
|
||||||
// they're rather trivial to add.
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class SigningClient extends SigningCosmWasmClient implements ISigningClient {
|
|
||||||
private nymdQuerier: NymdQuerier;
|
|
||||||
|
|
||||||
private validatorApiQuerier: ValidatorApiQuerier;
|
|
||||||
|
|
||||||
clientAddress: string;
|
|
||||||
|
|
||||||
private constructor(
|
|
||||||
clientAddress: string,
|
|
||||||
validatorApiUrl: string,
|
|
||||||
tmClient: Tendermint34Client,
|
|
||||||
wallet: DirectSecp256k1HdWallet,
|
|
||||||
signerOptions: SigningCosmWasmClientOptions,
|
|
||||||
) {
|
|
||||||
super(tmClient, wallet, signerOptions);
|
|
||||||
this.clientAddress = clientAddress;
|
|
||||||
this.nymdQuerier = new NymdQuerier(this);
|
|
||||||
this.validatorApiQuerier = new ValidatorApiQuerier(validatorApiUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async connectWithNymSigner(
|
|
||||||
wallet: DirectSecp256k1HdWallet,
|
|
||||||
nymdUrl: string,
|
|
||||||
validatorApiUrl: string,
|
|
||||||
prefix: string,
|
|
||||||
): Promise<SigningClient> {
|
|
||||||
const [{ address }] = await wallet.getAccounts();
|
|
||||||
const signerOptions: SigningCosmWasmClientOptions = {
|
|
||||||
gasPrice: nymGasPrice(prefix),
|
|
||||||
};
|
|
||||||
const tmClient = await Tendermint34Client.connect(nymdUrl);
|
|
||||||
return new SigningClient(address, validatorApiUrl, tmClient, wallet, signerOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
// query related:
|
|
||||||
|
|
||||||
getContractVersion(mixnetContractAddress: string): Promise<MixnetContractVersion> {
|
|
||||||
return this.nymdQuerier.getContractVersion(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getMixNodesPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedMixnodeResponse> {
|
|
||||||
return this.nymdQuerier.getMixNodesPaged(mixnetContractAddress, limit, startAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
getGatewaysPaged(mixnetContractAddress: string, limit?: number, startAfter?: string): Promise<PagedGatewayResponse> {
|
|
||||||
return this.nymdQuerier.getGatewaysPaged(mixnetContractAddress, limit, startAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse> {
|
|
||||||
return this.nymdQuerier.ownsMixNode(mixnetContractAddress, address);
|
|
||||||
}
|
|
||||||
|
|
||||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse> {
|
|
||||||
return this.nymdQuerier.ownsGateway(mixnetContractAddress, address);
|
|
||||||
}
|
|
||||||
|
|
||||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams> {
|
|
||||||
return this.nymdQuerier.getStateParams(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAllNetworkDelegationsPaged(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
limit?: number,
|
|
||||||
startAfter?: [string, string],
|
|
||||||
): Promise<PagedAllDelegationsResponse> {
|
|
||||||
return this.nymdQuerier.getAllNetworkDelegationsPaged(mixnetContractAddress, limit, startAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
getMixNodeDelegationsPaged(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
limit?: number,
|
|
||||||
startAfter?: string,
|
|
||||||
): Promise<PagedMixDelegationsResponse> {
|
|
||||||
return this.nymdQuerier.getMixNodeDelegationsPaged(mixnetContractAddress, mixIdentity, limit, startAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDelegatorDelegationsPaged(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
delegator: string,
|
|
||||||
limit?: number,
|
|
||||||
startAfter?: string,
|
|
||||||
): Promise<PagedDelegatorDelegationsResponse> {
|
|
||||||
return this.nymdQuerier.getDelegatorDelegationsPaged(mixnetContractAddress, delegator, limit, startAfter);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDelegationDetails(mixnetContractAddress: string, mixIdentity: string, delegator: string): Promise<Delegation> {
|
|
||||||
return this.nymdQuerier.getDelegationDetails(mixnetContractAddress, mixIdentity, delegator);
|
|
||||||
}
|
|
||||||
|
|
||||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution> {
|
|
||||||
return this.nymdQuerier.getLayerDistribution(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getRewardPool(mixnetContractAddress: string): Promise<string> {
|
|
||||||
return this.nymdQuerier.getRewardPool(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string> {
|
|
||||||
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
|
||||||
return this.nymdQuerier.getSybilResistancePercent(mixnetContractAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
getRewardingStatus(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
rewardingIntervalNonce: number,
|
|
||||||
): Promise<RewardingStatus> {
|
|
||||||
return this.nymdQuerier.getRewardingStatus(mixnetContractAddress, mixIdentity, rewardingIntervalNonce);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCachedGateways(): Promise<GatewayBond[]> {
|
|
||||||
return this.validatorApiQuerier.getCachedGateways();
|
|
||||||
}
|
|
||||||
|
|
||||||
getCachedMixnodes(): Promise<MixNodeBond[]> {
|
|
||||||
return this.validatorApiQuerier.getCachedMixnodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
getActiveMixnodes(): Promise<MixNodeBond[]> {
|
|
||||||
return this.validatorApiQuerier.getActiveMixnodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
getRewardedMixnodes(): Promise<MixNodeBond[]> {
|
|
||||||
return this.validatorApiQuerier.getRewardedMixnodes();
|
|
||||||
}
|
|
||||||
|
|
||||||
// signing related:
|
|
||||||
|
|
||||||
bondMixNode(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixNode: MixNode,
|
|
||||||
ownerSignature: string,
|
|
||||||
pledge: Coin,
|
|
||||||
fee: StdFee | 'auto' | number = 'auto',
|
|
||||||
memo = 'Default MixNode Bonding from Typescript',
|
|
||||||
): Promise<ExecuteResult> {
|
|
||||||
return this.execute(
|
|
||||||
this.clientAddress,
|
|
||||||
mixnetContractAddress,
|
|
||||||
{
|
|
||||||
bond_mixnode: {
|
|
||||||
mix_node: mixNode,
|
|
||||||
owner_signature: ownerSignature,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fee,
|
|
||||||
memo,
|
|
||||||
[pledge],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
unbondMixNode(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
fee: StdFee | 'auto' | number = 'auto',
|
|
||||||
memo = 'Default MixNode Unbonding from Typescript',
|
|
||||||
): Promise<ExecuteResult> {
|
|
||||||
return this.execute(
|
|
||||||
this.clientAddress,
|
|
||||||
mixnetContractAddress,
|
|
||||||
{
|
|
||||||
unbond_mixnode: {},
|
|
||||||
},
|
|
||||||
fee,
|
|
||||||
memo,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bondGateway(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
gateway: Gateway,
|
|
||||||
ownerSignature: string,
|
|
||||||
pledge: Coin,
|
|
||||||
fee: StdFee | 'auto' | number = 'auto',
|
|
||||||
memo = 'Default Gateway Bonding from Typescript',
|
|
||||||
): Promise<ExecuteResult> {
|
|
||||||
return this.execute(
|
|
||||||
this.clientAddress,
|
|
||||||
mixnetContractAddress,
|
|
||||||
{
|
|
||||||
bond_gateway: {
|
|
||||||
gateway,
|
|
||||||
owner_signature: ownerSignature,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fee,
|
|
||||||
memo,
|
|
||||||
[pledge],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
unbondGateway(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
fee: StdFee | 'auto' | number = 'auto',
|
|
||||||
memo = 'Default Gateway Unbonding from Typescript',
|
|
||||||
): Promise<ExecuteResult> {
|
|
||||||
return this.execute(
|
|
||||||
this.clientAddress,
|
|
||||||
mixnetContractAddress,
|
|
||||||
{
|
|
||||||
unbond_gateway: {},
|
|
||||||
},
|
|
||||||
fee,
|
|
||||||
memo,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
delegateToMixNode(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
amount: Coin,
|
|
||||||
fee: StdFee | 'auto' | number = 'auto',
|
|
||||||
memo = 'Default MixNode Delegation from Typescript',
|
|
||||||
): Promise<ExecuteResult> {
|
|
||||||
return this.execute(
|
|
||||||
this.clientAddress,
|
|
||||||
mixnetContractAddress,
|
|
||||||
{
|
|
||||||
delegate_to_mixnode: {
|
|
||||||
mix_identity: mixIdentity,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fee,
|
|
||||||
memo,
|
|
||||||
[amount],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
undelegateFromMixNode(
|
|
||||||
mixnetContractAddress: string,
|
|
||||||
mixIdentity: string,
|
|
||||||
fee: StdFee | 'auto' | number = 'auto',
|
|
||||||
memo = 'Default MixNode Undelegation from Typescript',
|
|
||||||
): Promise<ExecuteResult> {
|
|
||||||
return this.execute(
|
|
||||||
this.clientAddress,
|
|
||||||
mixnetContractAddress,
|
|
||||||
{
|
|
||||||
undelegate_from_mixnode: {
|
|
||||||
mix_identity: mixIdentity,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fee,
|
|
||||||
memo,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
fee: StdFee | 'auto' | number = 'auto',
|
|
||||||
memo = 'Default Contract State Params Update from Typescript',
|
|
||||||
): Promise<ExecuteResult> {
|
|
||||||
return this.execute(
|
|
||||||
this.clientAddress,
|
|
||||||
mixnetContractAddress,
|
|
||||||
{
|
|
||||||
update_contract_state_params: newParams,
|
|
||||||
},
|
|
||||||
fee,
|
|
||||||
memo,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +1,26 @@
|
|||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
import { GasPrice } from '@cosmjs/stargate';
|
import { GasLimits, GasPrice } from "@cosmjs/stargate";
|
||||||
|
import { CosmWasmFeeTable, defaultGasLimits } from "@cosmjs/cosmwasm-stargate";
|
||||||
|
|
||||||
const mainnetPrefix = 'n';
|
export const nymGasLimits: GasLimits<CosmWasmFeeTable> = {
|
||||||
const mainnetDenom = 'nym';
|
...defaultGasLimits,
|
||||||
|
upload: 2_500_000,
|
||||||
|
init: 500_000,
|
||||||
|
migrate: 200_000,
|
||||||
|
exec: 250_000,
|
||||||
|
send: 80_000,
|
||||||
|
changeAdmin: 80_000,
|
||||||
|
};
|
||||||
|
|
||||||
export function nymGasPrice(prefix: string): GasPrice {
|
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.
|
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`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const downloadWasm = async (url: string): Promise<Uint8Array> => {
|
export const downloadWasm = async (url: string): Promise<Uint8Array> => {
|
||||||
const r = await axios.get(url, { responseType: 'arraybuffer' });
|
const r = await axios.get(url, {responseType: "arraybuffer"});
|
||||||
if (r.status !== 200) {
|
if (r.status !== 200) {
|
||||||
throw new Error(`Download error: ${r.status}`);
|
throw new Error(`Download error: ${r.status}`);
|
||||||
}
|
}
|
||||||
return r.data;
|
return r.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user