Compare commits
294 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0881d4cbc3 | |||
| 3429e0a8e9 | |||
| 9c19ae322d | |||
| 07893828d8 | |||
| 1167f50543 | |||
| ba1818a903 | |||
| e631219a73 | |||
| 207c6cf2c7 | |||
| c5ece97872 | |||
| 8a2c95d044 | |||
| ba5e3d4efa | |||
| c81623a61a | |||
| 8bb42c2b1b | |||
| 33e161bd59 | |||
| 0233499036 | |||
| a059a29173 | |||
| 83c3398570 | |||
| 93f931459a | |||
| 5a7b19aeb6 | |||
| b901655591 | |||
| a9fdbccb82 | |||
| 9ca3f69aa8 | |||
| c485934b06 | |||
| d62e13c932 | |||
| 47f7a5f795 | |||
| f29200431f | |||
| 0912627e1f | |||
| 70fdcc9be0 | |||
| 90da6f152b | |||
| ad547e516a | |||
| 61c0092f27 | |||
| 851b80aaab | |||
| 1397662fc9 | |||
| 1847c8fe73 | |||
| 8ffe3adf0d | |||
| 3e517b461c | |||
| e42e4ddb9b | |||
| a31f51b7ca | |||
| c41a600bcb | |||
| 7aa34391fb | |||
| f72b56d07e | |||
| 44ddbf9ac3 | |||
| 27c377860a | |||
| 9a3f60f224 | |||
| 5776600db3 | |||
| e851ad8b27 | |||
| 2e0e319511 | |||
| 6dd3e36acb | |||
| 40cbfccb73 | |||
| b49681ef1d | |||
| 7e9409bbef | |||
| a11e674967 | |||
| 2f4e505aac | |||
| 90d8e5305f | |||
| 81a678f9e2 | |||
| 0a4bbf2573 | |||
| 2ae5ebb8cb | |||
| 11be1f8e3e | |||
| 513baba3fd | |||
| 21b87f6af5 | |||
| fe59697aa9 | |||
| 566ceae325 | |||
| 6bc4d32573 | |||
| 1fc3c9f31c | |||
| 3cb08a410e | |||
| 1648aef1d3 | |||
| d98b6b107c | |||
| d3db0fc2cd | |||
| 06b823f4c1 | |||
| 1c0ce9a420 | |||
| dede8899cf | |||
| 70f6059ceb | |||
| fefd2dd267 | |||
| 0fbe77d934 | |||
| 8a267cfe3d | |||
| 5ebb2a3efe | |||
| de605dc3b9 | |||
| 6a01edf5fe | |||
| 806b37bd83 | |||
| ffff596d45 | |||
| ad826da782 | |||
| 3414eeea7a | |||
| 3a32d34fb4 | |||
| 248052aec7 | |||
| 90c00fc343 | |||
| 7bdfbfdcc5 | |||
| 44e22b74a5 | |||
| 7920c27648 | |||
| be733fab5a | |||
| 1fc3c2b792 | |||
| f09b984b20 | |||
| f2afb42daf | |||
| 03a78c04ef | |||
| ea22a6f80f | |||
| bb0c3d251e | |||
| 3e2c54c283 | |||
| 6c9c961152 | |||
| 64c3009aa9 | |||
| a2409c0a84 | |||
| a646f84221 | |||
| 33a6cb1f3e | |||
| f8ceb0881f | |||
| a05830304f | |||
| 8c25bc47c4 | |||
| c39046c4aa | |||
| c2e4309212 | |||
| 11d1397906 | |||
| 479cc20083 | |||
| 97f77c4549 | |||
| 1de8b2abe9 | |||
| 70b01783bf | |||
| c2375850f9 | |||
| 0df801ab4e | |||
| fe9cb8a4e6 | |||
| 6f5878b6a7 | |||
| f4b15a8976 | |||
| bae495249c | |||
| aa3310fb9c | |||
| bfcc49ab78 | |||
| 3bd21300e0 | |||
| 63692eb30d | |||
| 6172f03ada | |||
| 5bb631fe8f | |||
| 03aec96592 | |||
| 8c3d6fa54b | |||
| ae88e25300 | |||
| 6b07f31a87 | |||
| 736fcafa9b | |||
| 64687e9656 | |||
| 49718e724e | |||
| 8bce52f9a9 | |||
| babc18d491 | |||
| a5da6ccdab | |||
| 8dd10a5e10 | |||
| f9d3a60c32 | |||
| 1178902634 | |||
| c39527c841 | |||
| 2fecde8f19 | |||
| 03b484dbdf | |||
| f3a926375a | |||
| 66b5f50ad0 | |||
| f9cc21dce9 | |||
| a693195b57 | |||
| c89c66b174 | |||
| 42ef2eb98c | |||
| b2df4ca4fd | |||
| 608ef779d2 | |||
| c0e178fdf7 | |||
| 60a58b30a1 | |||
| 135c818fee | |||
| b4f3a48550 | |||
| f02f914ae2 | |||
| 3e5aeefbb8 | |||
| 8cc244cf9c | |||
| 56b1dba66a | |||
| 6bd0ff796a | |||
| 287c45d6b5 | |||
| 3aff419a76 | |||
| 4f46e36aa8 | |||
| c59e8086a6 | |||
| f8c8b1a85e | |||
| c926e0e652 | |||
| 2689de2334 | |||
| 92f154fde5 | |||
| eaf207b667 | |||
| 13d74200e2 | |||
| 3816c94ee5 | |||
| b42472486f | |||
| 661a1420c1 | |||
| 784f6d0939 | |||
| 5b715acc4e | |||
| 8eae2e3136 | |||
| e1a1b70832 | |||
| dc0cb3f68b | |||
| 216b5535b3 | |||
| 8819e81393 | |||
| ce26c3cf76 | |||
| b826b5d957 | |||
| a4ec7e4912 | |||
| a67a80d28d | |||
| 1ad458b2be | |||
| 937ae22e6b | |||
| e6ffbc468b | |||
| 8a3f7a869b | |||
| 4240a88be3 | |||
| cc293dc166 | |||
| 01f9871d1f | |||
| 244410f69d | |||
| b0517705ba | |||
| e6a00ad8d4 | |||
| 815416bb41 | |||
| c7b480f488 | |||
| 088b7ab16d | |||
| f6f0e68fac | |||
| 70b23063c8 | |||
| 25a9fc85d2 | |||
| 3079a9ee7e | |||
| 06603ec585 | |||
| fd940c4c7c | |||
| a90a8926fe | |||
| e40cb960ce | |||
| 0f2d01c91a | |||
| 660c813c0a | |||
| ba8a8cbfa4 | |||
| 0702968f3a | |||
| ce7d02220f | |||
| 81bbb61dac | |||
| e768d99e3d | |||
| 987cb5d424 | |||
| 7f1d5e0691 | |||
| 6ed28b5a1d | |||
| b48184ba13 | |||
| d1499f3361 | |||
| 98e0e1fc4e | |||
| 70011d0592 | |||
| bdc8fb7ae7 | |||
| 0b0cbe3181 | |||
| 076a651199 | |||
| b4e9ebd9cc | |||
| 0a632599cd | |||
| a9ff98418c | |||
| 7d4f6c0bbd | |||
| b731aa0bcf | |||
| 89d4910e6f | |||
| 4ea9bb7dc6 | |||
| cfc7e6df77 | |||
| a7a39526b4 | |||
| 70a9bd0f6d | |||
| 4cbbead359 | |||
| 77d16d34af | |||
| 9e8868f357 | |||
| 64fdf5a270 | |||
| c17d65380c | |||
| 00e234a754 | |||
| 6ef50b93bc | |||
| 437b3b6885 | |||
| 6976be3e3a | |||
| dca7a9da94 | |||
| 56b1005226 | |||
| caa32299a9 | |||
| eff732aa2c | |||
| 68889bd362 | |||
| 1b4853b5ba | |||
| 45cc531056 | |||
| 8df24b8ce2 | |||
| e1b5407613 | |||
| 34d3d520d9 | |||
| 9a103d5e20 | |||
| 352111d443 | |||
| e9b72d1c37 | |||
| 6e355dc75e | |||
| d07279bde0 | |||
| 26f0001d9b | |||
| f3ab6a6a4c | |||
| 818d2585da | |||
| 5f2b9378fe | |||
| 5a374fe8ff | |||
| 62baada93d | |||
| fbdf31b879 | |||
| e9aa75ccac | |||
| be938cf4f0 | |||
| 12412d32e6 | |||
| fb5193163e | |||
| c13d3ab15d | |||
| f655a9d8c6 | |||
| 73a2fd68c6 | |||
| ca16dc6d66 | |||
| d5c456a370 | |||
| 24d3089458 | |||
| b020251b62 | |||
| f15ecdda06 | |||
| 7a74bb9ad5 | |||
| 67625fe768 | |||
| 858ef7b3f9 | |||
| c7dd48edbb | |||
| 7c6d8daba2 | |||
| c65c1ef7cb | |||
| e7a8221005 | |||
| 1ee1d4ebf7 | |||
| 780c6041ef | |||
| e9280f2c17 | |||
| ddd84295c4 | |||
| db09870352 | |||
| d703e160e3 | |||
| 1d6dabd0b5 | |||
| 4ce68fd6f2 | |||
| 83f6fbec5d | |||
| d87479f28c | |||
| 59db03a55d | |||
| 5db7f7e0bc | |||
| 2422f090cc | |||
| 1362d8a3eb | |||
| 40274689e3 | |||
| 3f9d6b2f1c |
+4
-4
@@ -19,10 +19,10 @@
|
||||
Cargo.* @durch @futurechimp @jstuczyn @neacsu @octol
|
||||
|
||||
# JS rules:
|
||||
*.js @mmsinclair @fmtabbara @Aid19801
|
||||
*.ts @mmsinclair @fmtabbara @Aid19801
|
||||
*.tsx @mmsinclair @fmtabbara @Aid19801
|
||||
*.jsx @mmsinclair @fmtabbara @Aid19801
|
||||
*.js @mmsinclair @fmtabbara
|
||||
*.ts @mmsinclair @fmtabbara
|
||||
*.tsx @mmsinclair @fmtabbara
|
||||
*.jsx @mmsinclair @fmtabbara
|
||||
|
||||
# Something looking like possible documentation rules:
|
||||
*.md @mfahampshire
|
||||
|
||||
@@ -13,9 +13,9 @@ jobs:
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
node-version: 16
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
- name: Build
|
||||
|
||||
@@ -41,6 +41,13 @@ jobs:
|
||||
command: test
|
||||
args: --workspace --all-features
|
||||
|
||||
- name: Run expensive tests
|
||||
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace --all-features -- --ignored
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -51,7 +58,7 @@ jobs:
|
||||
name: Clippy checks
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
args: --workspace --all-features
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
name: Nym Connect (rust)
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'explorer/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
env:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path nym-connect/Cargo.toml --workspace
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --manifest-path nym-connect/Cargo.toml --workspace
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path nym-connect/Cargo.toml --all -- --check
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --manifest-path nym-connect/Cargo.toml --workspace --all-features
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path nym-connect/Cargo.toml --workspace --all-features -- -D warnings
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Build release of Nym smart contracts
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: contracts
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Build release contracts
|
||||
run: RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown
|
||||
|
||||
- name: Upload Mixnet Contract Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mixnet_contract.wasm
|
||||
path: contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm
|
||||
retention-days: 5
|
||||
|
||||
- name: Upload Vesting Contract Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: vesting_contract.wasm
|
||||
path: contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm
|
||||
retention-days: 5
|
||||
@@ -14,9 +14,9 @@ jobs:
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
node-version: 16
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
- name: Run ESLint
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
name: CI for Network Explorer
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'explorer/**'
|
||||
@@ -17,9 +18,9 @@ jobs:
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
node-version: 16
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
continue-on-error: true
|
||||
@@ -75,3 +76,14 @@ jobs:
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
- name: Deploy
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.CD_PROD_NE_SSH_PRIVATE_KEY }}
|
||||
ARGS: "-rltgoDzvO --delete"
|
||||
SOURCE: "explorer/dist/"
|
||||
REMOTE_HOST: ${{ secrets.CD_PROD_NE_REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ secrets.CD_PROD_NE_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CD_PROD_NE_REMOTE_TARGET }}
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
|
||||
@@ -2,7 +2,7 @@ name: Nightly builds
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '14 4 * * *'
|
||||
- cron: '14 1 * * *'
|
||||
jobs:
|
||||
matrix_prep:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -50,6 +50,19 @@ jobs:
|
||||
command: test
|
||||
args: --workspace
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run expensive tests
|
||||
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace --all-features -- --ignored
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -75,9 +88,9 @@ jobs:
|
||||
command: clippy
|
||||
args: --workspace --all-targets -- -D warnings
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
- name: Reclaim some disk space
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
[
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"stable",
|
||||
"runOnEvent":"workflow_dispatch"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"stable",
|
||||
"runOnEvent":"workflow_dispatch"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"stable",
|
||||
"runOnEvent":"workflow_dispatch"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"workflow_dispatch"
|
||||
},
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"workflow_dispatch"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"workflow_dispatch"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"workflow_dispatch"
|
||||
},
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"workflow_dispatch"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"workflow_dispatch"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,174 @@
|
||||
name: Nightly builds on dispatch
|
||||
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
matrix_prep:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
# creates the matrix strategy from nightly_build_matrix_includes.json
|
||||
- uses: actions/checkout@v2
|
||||
- id: set-matrix
|
||||
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||
with:
|
||||
inputFile: '.github/workflows/nightly_build_matrix_on_dispatch.json'
|
||||
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
|
||||
build:
|
||||
needs: matrix_prep
|
||||
strategy:
|
||||
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run expensive tests
|
||||
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace --all-features -- --ignored
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets -- -D warnings
|
||||
|
||||
- name: Reclaim some disk space
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
# COCONUT stuff
|
||||
- name: Build all binaries with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --features=coconut
|
||||
|
||||
- name: Run all tests with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace --features=coconut
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run clippy with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets --features=coconut -- -D warnings
|
||||
|
||||
# nym-wallet (the rust part)
|
||||
- name: Build nym-wallet rust code
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace
|
||||
|
||||
- name: Run nym-wallet tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace
|
||||
|
||||
- name: Check nym-wallet formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
|
||||
|
||||
- name: Run clippy for nym-wallet
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
|
||||
|
||||
notification:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
- name: Keybase - Node Install
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Keybase - Send Notification
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: nightly
|
||||
NYM_PROJECT_NAME: "Nym nightly build"
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
||||
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_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
|
||||
@@ -0,0 +1,96 @@
|
||||
name: Publish Nym Connect (MacOS)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nym-connect
|
||||
|
||||
jobs:
|
||||
publish-tauri:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [macos-latest]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Check the release tag starts with `nym-connect-`
|
||||
if: startsWith(github.ref, 'refs/tags/nym-connect-') == false && github.event_name != 'workflow_dispatch'
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Release tag did not start with nym-connect-...')
|
||||
|
||||
- name: Node v16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install the Apple developer certificate for code signing
|
||||
env:
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
run: |
|
||||
# create variables
|
||||
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
|
||||
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||
|
||||
# import certificate and provisioning profile from secrets
|
||||
echo -n "$APPLE_CERTIFICATE" | base64 --decode --output $CERTIFICATE_PATH
|
||||
|
||||
# create temporary keychain
|
||||
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
|
||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
||||
|
||||
# import certificate to keychain
|
||||
security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Install app dependencies and build it
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
run: yarn && yarn build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-connect_1.0.0_x64.dmg
|
||||
path: nym-connect/target/release/bundle/dmg/nym-connect_1.0.0_x64.dmg
|
||||
retention-days: 30
|
||||
|
||||
- name: Clean up keychain
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
nym-connect/target/release/bundle/dmg/*.dmg
|
||||
nym-connect/target/release/bundle/macos/*.app.tar.gz*
|
||||
@@ -0,0 +1,68 @@
|
||||
name: Publish Nym Connect (Ubuntu)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nym-connect
|
||||
|
||||
jobs:
|
||||
publish-tauri:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-latest]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Tauri dependencies
|
||||
run: >
|
||||
sudo apt-get update &&
|
||||
sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev
|
||||
- name: Check the release tag starts with `nym-connect-`
|
||||
if: startsWith(github.ref, 'refs/tags/nym-connect-') == false && github.event_name != 'workflow_dispatch'
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Release tag did not start with nym-connect-...')
|
||||
|
||||
- name: Node v16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install app dependencies
|
||||
run: yarn
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Build app
|
||||
run: yarn build
|
||||
env:
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-connect.AppImage.tar.gz
|
||||
path: nym-connect/target/release/bundle/appimage/nym-connect_1.0.0_amd64.AppImage
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
nym-connect/target/release/bundle/appimage/*.AppImage
|
||||
nym-connect/target/release/bundle/appimage/*.AppImage.tar.gz*
|
||||
@@ -0,0 +1,90 @@
|
||||
name: Publish Nym Connect (Windows 10)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nym-connect
|
||||
|
||||
jobs:
|
||||
publish-tauri:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [windows10]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Clean up first
|
||||
continue-on-error: true
|
||||
working-directory: .
|
||||
run: |
|
||||
cd ..
|
||||
del /s /q /A:H nym
|
||||
rmdir /s /q nym
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Check the release tag starts with `nym-connect-`
|
||||
if: startsWith(github.ref, 'refs/tags/nym-connect-') == false && github.event_name != 'workflow_dispatch'
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Release tag did not start with nym-connect-...')
|
||||
|
||||
- name: Import signing certificate
|
||||
env:
|
||||
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
run: |
|
||||
New-Item -ItemType directory -Path certificate
|
||||
Set-Content -Path certificate/tempCert.txt -Value $env:WINDOWS_CERTIFICATE
|
||||
certutil -decode certificate/tempCert.txt certificate/certificate.pfx
|
||||
Remove-Item -path certificate -include tempCert.txt
|
||||
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
|
||||
|
||||
- name: Node v16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Install app dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Build and sign it
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
run: yarn build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nym-connect_1.0.0_x64_en-US.msi
|
||||
path: nym-connect/target/release/bundle/msi/nym-connect_1.0.0_x64_en-US.msi
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
nym-connect/target/release/bundle/msi/*.msi
|
||||
nym-connect/target/release/bundle/msi/*.msi.zip*
|
||||
@@ -0,0 +1,59 @@
|
||||
name: CI for nym-connect
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'nym-connect/**'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: nym-connect
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install Yarn
|
||||
run: npm install -g yarn
|
||||
- run: yarn
|
||||
continue-on-error: true
|
||||
- name: Set environment from the example
|
||||
run: cp .env.sample .env
|
||||
- run: yarn storybook:build
|
||||
- name: Deploy branch to CI www
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
|
||||
ARGS: "-rltgoDzvO --delete"
|
||||
SOURCE: "nym-connect/storybook-static/"
|
||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/nym-connect-${{ env.GITHUB_REF_SLUG }}
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
- name: Keybase - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
# - name: Keybase - Send Notification
|
||||
# env:
|
||||
# NYM_NOTIFICATION_KIND: nym-connect
|
||||
# NYM_PROJECT_NAME: "nym-connect"
|
||||
# NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
||||
# NYM_CI_WWW_LOCATION: "nym-connect-${{ env.GITHUB_REF_SLUG }}"
|
||||
# GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||
# GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||
# KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
||||
# KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||
# KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
# KEYBASE_NYM_CHANNEL: "ci-nym-connect"
|
||||
# IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
# uses: docker://keybaseio/client:stable-node
|
||||
# with:
|
||||
# args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
@@ -1,5 +1,6 @@
|
||||
name: Publish Nym Wallet (MacOS)
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
@@ -19,16 +20,16 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Check the release tag starts with `nym-wallet-`
|
||||
if: startsWith(github.ref, 'refs/tags/nym-wallet-') == false
|
||||
if: startsWith(github.ref, 'refs/tags/nym-wallet-') == false && github.event_name != 'workflow_dispatch'
|
||||
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
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 16
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -55,6 +56,12 @@ jobs:
|
||||
security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Install app dependencies and build it
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -68,14 +75,22 @@ jobs:
|
||||
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
run: yarn && yarn build
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
files: |
|
||||
nym-wallet/target/release/bundle/dmg/*.dmg
|
||||
nym-wallet/target/release/bundle/macos/*.app.tar.gz*
|
||||
name: nym-wallet.app.tar.gz
|
||||
path: nym-wallet/target/release/bundle/macos/nym-wallet.app.tar.gz
|
||||
retention-days: 5
|
||||
|
||||
- name: Clean up keychain
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db
|
||||
|
||||
- name: Upload to release based on tag name
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
files: |
|
||||
nym-wallet/target/release/bundle/dmg/*.dmg
|
||||
nym-wallet/target/release/bundle/macos/*.app.tar.gz*
|
||||
|
||||
@@ -30,15 +30,21 @@ jobs:
|
||||
core.setFailed('Release tag did not start with nym-wallet-...')
|
||||
|
||||
- name: Node v16
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 16
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Install app dependencies
|
||||
run: yarn
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Build app
|
||||
run: yarn build
|
||||
env:
|
||||
|
||||
@@ -45,15 +45,21 @@ jobs:
|
||||
Import-PfxCertificate -FilePath certificate/certificate.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String $env:WINDOWS_CERTIFICATE_PASSWORD -Force -AsPlainText)
|
||||
|
||||
- name: Node v16
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 16
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Create env file
|
||||
uses: timheuer/base64-to-file@v1.1
|
||||
with:
|
||||
fileName: '.env'
|
||||
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
|
||||
|
||||
- name: Install app dependencies
|
||||
run: yarn
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
name: Release Nym Wallet
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
nym_wallet_version:
|
||||
description: 'The version of the Nym Wallet to release'
|
||||
default: '1.0.x'
|
||||
required: true
|
||||
type: string
|
||||
jobs:
|
||||
create-release:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body: >-
|
||||
This is a pre-release
|
||||
|
||||
Download the wallet for your platform:
|
||||
|
||||
- [Linux](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_amd64_ubuntu20.04.AppImage)
|
||||
- [MacOS](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_x64_macos_11.dmg)
|
||||
- [Windows](https://github.com/nymtech/nym/releases/download/nym-wallet-v${{ inputs.nym_wallet_version}}/nym-wallet_v${{ inputs.nym_wallet_version}}_x64_windows.msi)
|
||||
prerelease: true
|
||||
name: Nym Wallet v${{ inputs.nym_wallet_version}}
|
||||
tag_name: nym-wallet-v${{ inputs.nym_wallet_version}}
|
||||
@@ -13,9 +13,9 @@ jobs:
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
node-version: 16
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
- name: Build dependencies
|
||||
|
||||
@@ -34,9 +34,9 @@ jobs:
|
||||
toolchain: stable
|
||||
|
||||
- name: Node v16
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 16
|
||||
|
||||
- name: Install yarn for building application
|
||||
run: yarn install
|
||||
|
||||
@@ -3,7 +3,7 @@ require('dotenv').config();
|
||||
const Bot = require('keybase-bot');
|
||||
|
||||
let context = {
|
||||
kinds: ['nym-wallet', 'ts-packages', 'network-explorer', 'nightly'],
|
||||
kinds: ['nym-wallet', 'ts-packages', 'network-explorer', 'nightly', 'nym-connect'],
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
|
||||
> :rocket: {{ env.NYM_PROJECT_NAME }}
|
||||
> 🔴 **FAILURE** :cry:
|
||||
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
|
||||
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
|
||||
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
|
||||
|
||||
Commit message:
|
||||
```
|
||||
{{ env.GIT_COMMIT_MESSAGE }}
|
||||
```
|
||||
@@ -0,0 +1,11 @@
|
||||
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
|
||||
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View storybook:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
|
||||
> ✅ **SUCCESS**
|
||||
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
|
||||
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
|
||||
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
|
||||
|
||||
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
|
||||
```
|
||||
{{ env.GIT_COMMIT_MESSAGE }}
|
||||
```
|
||||
+127
-21
@@ -1,25 +1,116 @@
|
||||
# Changelog
|
||||
|
||||
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- socks5 client/websocket client: add `--force-register-gateway` flag, useful when rerunning init ([#1353])
|
||||
- all: added network compilation target to `--help` (or `--version`) commands ([#1256]).
|
||||
- explorer-api: learned how to sum the delegations by owner in a new endpoint.
|
||||
- explorer-api: add apy values to `mix_nodes` endpoint
|
||||
- gateway: Added gateway coconut verifications and validator-api communication for double spending protection ([#1261])
|
||||
- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
|
||||
- network-requester: send traffic statistics from all network requesters and receive it in a special network-requester that aggregates the data and exposes it via a rest API ([#1267], [#1278]).
|
||||
- network-explorer-ui: Upgrade to React Router 6
|
||||
- rewarding: replace circulating supply with staking supply in reward calculations ([#1324])
|
||||
- validator-api: add `estimated_node_profit` and `estimated_operator_cost` to `reward-estimate` endpoint ([#1284])
|
||||
- validator-api: add detailed mixnode bond endpoints, and explorer-api makes use of that data to append stake saturation.
|
||||
- validator-api: add detailed mixnode bond endpoints, and explorer-api makes use of that data to append stake saturation
|
||||
- validator-api: add Swagger to document the REST API ([#1249]).
|
||||
- validator-api: Added new endpoints for coconut spending flow and communications with coconut & multisig contracts ([#1261])
|
||||
- validator-api: add `uptime`, `estimated_operator_apy`, `estimated_delegators_apy` to `/mixnodes/detailed` endpoint ([#1393])
|
||||
- network-statistics: a new mixnet service that aggregates and exposes anonymized data about mixnet services ([#1328])
|
||||
- mixnode: Added basic mixnode hardware reporting to the HTTP API ([#1308]).
|
||||
- validator-api: endpoint, in coconut mode, for returning the validator-api cosmos address ([#1404]).
|
||||
- validator-client: add `denom` argument and add simple test for querying an account balance
|
||||
- gateway, validator-api: Checks for coconut credential double spending attempts, taking the coconut bandwidth contract as source of truth ([#1457])
|
||||
- coconut-bandwidth-contract: Record the state of a coconut credential; create specific proposal for releasing funds ([#1457])
|
||||
|
||||
### Fixed
|
||||
|
||||
- mixnode, gateway: attempting to determine reconnection backoff to persistently failing mixnode could result in a crash ([#1260])
|
||||
- mixnode: the mixnode learned how to shutdown gracefully
|
||||
- mixnode: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service.
|
||||
- native & socks5 clients: fail early when clients try to re-init with a different gateway, which is not supported yet ([#1322])
|
||||
- native & socks5 clients: rerun init will now reuse previous gateway configuration instead of failing ([#1353])
|
||||
- native & socks5 clients: deduplicate big chunks of init logic
|
||||
- validator: fixed local docker-compose setup to work on Apple M1 ([#1329])
|
||||
- explorer-api: listen out for SIGTERM and SIGQUIT too, making it play nicely as a system service ([#1482]).
|
||||
- network-requester: fix filter for suffix-only domains ([#1487])
|
||||
|
||||
### Changed
|
||||
|
||||
- validator-client: created internal `Coin` type that replaces coins from `cosmrs` and `cosmwasm` for API entrypoints [[#1295]]
|
||||
- all: updated all `cosmwasm`-related dependencies to `1.0.0` and `cw-storage-plus` to `0.13.4` [[#1318]]
|
||||
- all: updated `rocket` to `0.5.0-rc.2`.
|
||||
- network-requester: allow to voluntarily store and send statistical data about the number of bytes the proxied server serves ([#1328])
|
||||
- gateway: allow to voluntarily send statistical data about the number of active inboxes served by a gateway ([#1376])
|
||||
- gateway & mixnode: move detailed build info back to `--version` from `--help`.
|
||||
- socks5 client/websocket client: upgrade to latest clap and switched to declarative commandline parsing.
|
||||
- validator-api: fee payment for multisig operations comes from the gateway account instead of the validator APIs' accounts ([#1419])
|
||||
- multisig-contract: Limit the proposal creating functionality to one address (coconut-bandwidth-contract address) ([#1457])
|
||||
- All binaries and cosmwasm blobs are configured at runtime now; binaries are configured using environment variables or .env files and contracts keep the configuration parameters in storage ([#1463])
|
||||
- gateway, network-statistics: include gateway id in the sent statistical data ([#1478])
|
||||
|
||||
|
||||
[#1249]: https://github.com/nymtech/nym/pull/1249
|
||||
[#1256]: https://github.com/nymtech/nym/pull/1256
|
||||
[#1260]: https://github.com/nymtech/nym/pull/1260
|
||||
[#1261]: https://github.com/nymtech/nym/pull/1261
|
||||
[#1267]: https://github.com/nymtech/nym/pull/1267
|
||||
[#1278]: https://github.com/nymtech/nym/pull/1278
|
||||
[#1295]: https://github.com/nymtech/nym/pull/1295
|
||||
[#1302]: https://github.com/nymtech/nym/pull/1302
|
||||
[#1308]: https://github.com/nymtech/nym/pull/1308
|
||||
[#1318]: https://github.com/nymtech/nym/pull/1318
|
||||
[#1322]: https://github.com/nymtech/nym/pull/1322
|
||||
[#1324]: https://github.com/nymtech/nym/pull/1324
|
||||
[#1328]: https://github.com/nymtech/nym/pull/1328
|
||||
[#1329]: https://github.com/nymtech/nym/pull/1329
|
||||
[#1353]: https://github.com/nymtech/nym/pull/1353
|
||||
[#1376]: https://github.com/nymtech/nym/pull/1376
|
||||
[#1393]: https://github.com/nymtech/nym/pull/1393
|
||||
[#1404]: https://github.com/nymtech/nym/pull/1404
|
||||
[#1419]: https://github.com/nymtech/nym/pull/1419
|
||||
[#1457]: https://github.com/nymtech/nym/pull/1457
|
||||
[#1463]: https://github.com/nymtech/nym/pull/1463
|
||||
[#1478]: https://github.com/nymtech/nym/pull/1478
|
||||
[#1482]: https://github.com/nymtech/nym/pull/1482
|
||||
[#1487]: https://github.com/nymtech/nym/pull/1487
|
||||
|
||||
## [nym-connect-v1.0.1](https://github.com/nymtech/nym/tree/nym-connect-v1.0.1) (2022-07-22)
|
||||
|
||||
### Added
|
||||
|
||||
- nym-connect: initial proof-of-concept of a UI around the socks5 client was added
|
||||
- nym-connect: add ability to select network requester and gateway ([#1427])
|
||||
- nym-connect: add ability to export gateway keys as JSON
|
||||
- nym-connect: add auto updater
|
||||
|
||||
### Changed
|
||||
|
||||
- nym-connect: reuse config id instead of creating a new id on each connection
|
||||
|
||||
[#1427]: https://github.com/nymtech/nym/pull/1427
|
||||
|
||||
## [nym-wallet-v1.0.7](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.7) (2022-07-11)
|
||||
|
||||
- wallet: dark mode
|
||||
- wallet: when simulating gas costs, an automatic adjustment is being used ([#1388]).
|
||||
|
||||
[#1388]: https://github.com/nymtech/nym/pull/1388
|
||||
|
||||
## [nym-contracts-v1.0.1](https://github.com/nymtech/nym/tree/nym-contracts-v1.0.1) (2022-06-22)
|
||||
|
||||
### Added
|
||||
|
||||
- mixnet-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
|
||||
- mixnet-contract: Added staking_supply field to ContractStateParams.
|
||||
- mixnet-contract: Added a query to get MixnodeBond by identity key ([#1369]).
|
||||
- mixnet-contract: Added a query to get GatewayBond by identity key ([#1369]).
|
||||
- vesting-contract: Added ClaimOperatorReward and ClaimDelegatorReward messages ([#1292])
|
||||
- wallet: add simple CLI tool for decrypting and recovering the wallet file.
|
||||
- wallet: added support for multiple accounts ([#1265])
|
||||
- wallet: compound and claim reward endpoints for operators and delegators ([#1302])
|
||||
- wallet: require password to switch accounts
|
||||
- wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint.
|
||||
- vesting-contract: Added limit to the amount of tokens one can pledge ([#1331])
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -28,26 +119,41 @@
|
||||
- mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257])
|
||||
- mixnet-contract: replaced integer division with fixed for performance calculations ([#1284])
|
||||
- mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258])
|
||||
- mixnode, gateway: attempting to determine reconnection backoff to persistently failing mixnode could result in a crash ([#1260])
|
||||
- mixnet-contract: Using correct staking supply when distributing rewards. ([#1373])
|
||||
- vesting-contract: replaced `checked_sub` with `saturating_sub` to fix the underflow in `get_vesting_tokens` ([#1275])
|
||||
|
||||
### Changed
|
||||
|
||||
- validator-client: created internal `Coin` type that replaces coins from `cosmrs` and `cosmwasm` for API entrypoints [[#1295]]
|
||||
|
||||
[#1249]: https://github.com/nymtech/nym/pull/1249
|
||||
[#1256]: https://github.com/nymtech/nym/pull/1256
|
||||
[#1255]: https://github.com/nymtech/nym/pull/1255
|
||||
[#1257]: https://github.com/nymtech/nym/pull/1257
|
||||
[#1258]: https://github.com/nymtech/nym/pull/1258
|
||||
[#1260]: https://github.com/nymtech/nym/pull/1260
|
||||
[#1261]: https://github.com/nymtech/nym/pull/1261
|
||||
[#1265]: https://github.com/nymtech/nym/pull/1265
|
||||
[#1267]: https://github.com/nymtech/nym/pull/1267
|
||||
[#1275]: https://github.com/nymtech/nym/pull/1275
|
||||
[#1278]: https://github.com/nymtech/nym/pull/1278
|
||||
[#1284]: https://github.com/nymtech/nym/pull/1284
|
||||
[#1292]: https://github.com/nymtech/nym/pull/1292
|
||||
[#1295]: https://github.com/nymtech/nym/pull/1295
|
||||
[#1331]: https://github.com/nymtech/nym/pull/1331
|
||||
[#1369]: https://github.com/nymtech/nym/pull/1369
|
||||
[#1373]: https://github.com/nymtech/nym/pull/1373
|
||||
|
||||
## [nym-wallet-v1.0.6](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.6) (2022-06-21)
|
||||
|
||||
- wallet: undelegating now uses either the mixnet or vesting contract, or both, depending on how delegations were made
|
||||
- wallet: redeeming and compounding now uses both the mixnet and vesting contract
|
||||
- wallet: the wallet backend learned how to archive wallet files
|
||||
- wallet: add ENABLE_QA_MODE environment variable to enable QA mode on built wallet
|
||||
|
||||
## [nym-wallet-v1.0.5](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.5) (2022-06-14)
|
||||
|
||||
- wallet: add simple CLI tool for decrypting and recovering the wallet file.
|
||||
- wallet: added support for multiple accounts ([#1265])
|
||||
- wallet: compound and claim reward endpoints for operators and delegators ([#1302])
|
||||
- wallet: require password to switch accounts
|
||||
- wallet: the wallet backend learned how to keep track of validator name, either hardcoded or by querying the status endpoint.
|
||||
- wallet: new delegation and rewards UI
|
||||
- wallet: show version in nav bar
|
||||
- wallet: contract admin route put back
|
||||
- wallet: staking_supply field to StateParams
|
||||
- wallet: show transaction hash for redeeming or compounding rewards
|
||||
|
||||
[#1265]: https://github.com/nymtech/nym/pull/1265
|
||||
[#1302]: https://github.com/nymtech/nym/pull/1302
|
||||
|
||||
## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04)
|
||||
|
||||
Generated
+342
-346
File diff suppressed because it is too large
Load Diff
+6
-1
@@ -53,17 +53,22 @@ members = [
|
||||
"common/nymsphinx/params",
|
||||
"common/nymsphinx/types",
|
||||
"common/pemstore",
|
||||
"common/statistics",
|
||||
"common/socks5/proxy-helpers",
|
||||
"common/socks5/requests",
|
||||
"common/task",
|
||||
"common/topology",
|
||||
"common/types",
|
||||
"common/wasm-utils",
|
||||
"explorer-api",
|
||||
"gateway",
|
||||
"gateway/gateway-requests",
|
||||
"mixnode",
|
||||
"service-providers/network-requester",
|
||||
"service-providers/network-statistics",
|
||||
"validator-api",
|
||||
"validator-api/validator-api-requests",
|
||||
"tools/ts-rs-cli"
|
||||
]
|
||||
|
||||
default-members = [
|
||||
@@ -76,4 +81,4 @@ default-members = [
|
||||
"explorer-api",
|
||||
]
|
||||
|
||||
exclude = ["explorer", "contracts", "tokenomics-py", "clients/webassembly"]
|
||||
exclude = ["explorer", "contracts", "tokenomics-py", "clients/webassembly", "nym-wallet", "nym-connect"]
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
test: build clippy-all cargo-test wasm fmt
|
||||
test: clippy-all cargo-test wasm fmt
|
||||
test-all: test cargo-test-expensive
|
||||
no-clippy: build 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-contracts build-wallet build-main
|
||||
fmt: fmt-main fmt-contracts fmt-wallet
|
||||
clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet clippy-all-connect
|
||||
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect
|
||||
cargo-test: test-main test-contracts test-wallet test-connect
|
||||
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive
|
||||
build: build-contracts build-wallet build-main build-connect
|
||||
fmt: fmt-main fmt-contracts fmt-wallet fmt-connect
|
||||
|
||||
clippy-happy-main:
|
||||
cargo clippy
|
||||
@@ -16,6 +18,9 @@ clippy-happy-contracts:
|
||||
clippy-happy-wallet:
|
||||
cargo clippy --manifest-path nym-wallet/Cargo.toml
|
||||
|
||||
clippy-happy-connect:
|
||||
cargo clippy --manifest-path nym-connect/Cargo.toml
|
||||
|
||||
clippy-all-main:
|
||||
cargo clippy --workspace --all-features -- -D warnings
|
||||
|
||||
@@ -25,15 +30,33 @@ clippy-all-contracts:
|
||||
clippy-all-wallet:
|
||||
cargo clippy --workspace --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings
|
||||
|
||||
clippy-all-connect:
|
||||
cargo clippy --workspace --manifest-path nym-connect/Cargo.toml --all-features -- -D warnings
|
||||
|
||||
test-main:
|
||||
cargo test --all-features --workspace
|
||||
|
||||
test-main-expensive:
|
||||
cargo test --all-features --workspace -- --ignored
|
||||
|
||||
test-contracts:
|
||||
cargo test --manifest-path contracts/Cargo.toml --all-features
|
||||
|
||||
test-contracts-expensive:
|
||||
cargo test --manifest-path contracts/Cargo.toml --all-features -- --ignored
|
||||
|
||||
test-wallet:
|
||||
cargo test --manifest-path nym-wallet/Cargo.toml --all-features
|
||||
|
||||
test-wallet-expensive:
|
||||
cargo test --manifest-path nym-wallet/Cargo.toml --all-features -- --ignored
|
||||
|
||||
test-connect:
|
||||
cargo test --manifest-path nym-connect/Cargo.toml --all-features
|
||||
|
||||
test-connect-expensive:
|
||||
cargo test --manifest-path nym-connect/Cargo.toml --all-features -- --ignored
|
||||
|
||||
build-main:
|
||||
cargo build --workspace
|
||||
|
||||
@@ -43,6 +66,9 @@ build-contracts:
|
||||
build-wallet:
|
||||
cargo build --manifest-path nym-wallet/Cargo.toml --workspace
|
||||
|
||||
build-connect:
|
||||
cargo build --manifest-path nym-connect/Cargo.toml --workspace
|
||||
|
||||
fmt-main:
|
||||
cargo fmt --all
|
||||
|
||||
@@ -52,5 +78,12 @@ fmt-contracts:
|
||||
fmt-wallet:
|
||||
cargo fmt --manifest-path nym-wallet/Cargo.toml --all
|
||||
|
||||
fmt-connect:
|
||||
cargo fmt --manifest-path nym-connect/Cargo.toml --all
|
||||
|
||||
wasm:
|
||||
RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
|
||||
|
||||
generate-typescript:
|
||||
cd tools/ts-rs-cli && cargo run && cd ../..
|
||||
yarn types:lint:fix
|
||||
|
||||
@@ -9,8 +9,8 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
||||
|
||||
* nym-mixnode - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers.
|
||||
* nym-client - an executable which you can build into your own applications. Use it for interacting with Nym nodes.
|
||||
* nym-socks5-client - a Socks5 proxy you can run on your machine, and use with existing applications
|
||||
* nym-gateway - acts sort of like a mailbox for mixnet messages, removing the need for directly delivery to potentially offline or firewalled devices.
|
||||
* nym-socks5-client - a Socks5 proxy you can run on your machine and use with existing applications.
|
||||
* nym-gateway - acts sort of like a mailbox for mixnet messages, which removes the need for direct delivery to potentially offline or firewalled devices.
|
||||
* nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
|
||||
* nym-explorer - a (projected) block explorer and (existing) mixnet viewer.
|
||||
* nym-wallet - a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
Update fonts by doing the following:
|
||||
|
||||
1. Go to https://fonts.google.com/specimen/Open+Sans
|
||||
2. Add all the styles you want and select `@import`
|
||||
3. Copy the url (e.g. curl https://fonts.googleapis.com/css2\?family\=Open+Sans:ital,wght@0,300\;0,400\;0,500\;0,600\;0,700\;0,800\;1,300\;1,400\;1,500\;1,600\;1,700\;1,800\&display\=swap)
|
||||
4. Run `curl curl https://fonts.googleapis.com/css2\?family\=Open+Sans:ital,wght@0,300\;0,400\;0,500\;0,600\;0,700\;0,800\;1,300\;1,400\;1,500\;1,600\;1,700\;1,800\&display\=swap`
|
||||
5. Use the response as the CSS import directives and download the font files for each font weight
|
||||
6. Remember to delete any old font files
|
||||
@@ -0,0 +1,96 @@
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk5hkaVc.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkaVc.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 500;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk_RkaVc.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkxhjaVc.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0RkyFjaVc.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: italic;
|
||||
font-weight: 800;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk0ZjaVc.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0C4n.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0C4n.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjr0C4n.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsgH1y4n.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsg-1y4n.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
font-stretch: normal;
|
||||
font-display: swap;
|
||||
src: url(./memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgshZ1y4n.ttf) format('truetype');
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M171.7,30.3001 C132.7,-8.7999 69.3001,-8.7999 30.3001,30.3001 C-8.7999,69.4001 -8.7999,132.7 30.3001,171.7 C69.4001,210.8 132.7,210.8 171.7,171.7 C210.8,132.7 210.8,69.3001 171.7,30.3001 Z M163.1,163.1 C128.8,197.4 73.1001,197.4 38.8001,163.1 C4.5001,128.8 4.5001,73.1001 38.8001,38.8001 C73.1001,4.5001 128.8,4.5001 163.1,38.8001 C197.5,73.2001 197.5,128.8 163.1,163.1 Z" id="Shape" fill="#fff"></path>
|
||||
<path d="M163.1,38.9 C128.8,4.60005 73.1002,4.60005 38.8002,38.9 C4.50019,73.2 4.50019,128.9 38.8002,163.2 C73.1002,197.5 128.8,197.5 163.1,163.2 C197.5,128.8 197.5,73.2 163.1,38.9 Z" id="Shape" fill="#000"></path>
|
||||
<g id="T" transform="translate(25, 25) scale(5,5)">
|
||||
<path d="M18.4804688,24 C19.203125,24 19.7182617,23.8608398 20.0258789,23.5825195 C20.3334961,23.3041992 20.4873047,22.9453125 20.4873047,22.5058594 C20.4873047,22.0566406 20.3334961,21.6928711 20.0258789,21.4145508 C19.7182617,21.1362305 19.203125,20.9970703 18.4804688,20.9970703 L18.4804688,20.9970703 L16.4589844,20.9970703 L16.4589844,9.24902344 L19.7548828,9.24902344 L19.7548828,12.0908203 C19.7548828,12.8134766 19.894043,13.3286133 20.1723633,13.6362305 C20.4506836,13.9438477 20.8095703,14.0976562 21.2490234,14.0976562 C21.6982422,14.0976562 22.0620117,13.9438477 22.340332,13.6362305 C22.6186523,13.3286133 22.7578125,12.8134766 22.7578125,12.0908203 L22.7578125,12.0908203 L22.7578125,6.24609375 L7.20117188,6.23144531 L7.20117188,12.0908203 C7.20117188,12.8134766 7.34033203,13.3286133 7.61865234,13.6362305 C7.89697266,13.9438477 8.25585938,14.0976562 8.6953125,14.0976562 C9.14453125,14.0976562 9.50830078,13.9438477 9.78662109,13.6362305 C10.0649414,13.3286133 10.2041016,12.8134766 10.2041016,12.0908203 L10.2041016,12.0908203 L10.2041016,9.24902344 L13.4560547,9.24902344 L13.4560547,20.9970703 L11.4492188,20.9970703 C10.7265625,20.9970703 10.2114258,21.1362305 9.90380859,21.4145508 C9.59619141,21.6928711 9.44238281,22.0517578 9.44238281,22.4912109 C9.44238281,22.9404297 9.59619141,23.3041992 9.90380859,23.5825195 C10.2114258,23.8608398 10.7265625,24 11.4492188,24 L11.4492188,24 L18.4804688,24 Z" id="T" fill="#fff"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M170.7 29.3001C131.7 -9.7999 68.3001 -9.7999 29.3001 29.3001C-9.7999 68.4001 -9.7999 131.7 29.3001 170.7C68.4001 209.8 131.7 209.8 170.7 170.7C209.8 131.7 209.8 68.3001 170.7 29.3001ZM162.1 162.1C127.8 196.4 72.1001 196.4 37.8001 162.1C3.5001 127.8 3.5001 72.1001 37.8001 37.8001C72.1001 3.5001 127.8 3.5001 162.1 37.8001C196.5 72.2001 196.5 127.8 162.1 162.1Z" fill="white"/>
|
||||
<path d="M162.1 37.9C127.8 3.60005 72.1002 3.60005 37.8002 37.9C3.50019 72.2 3.50019 127.9 37.8002 162.2C72.1002 196.5 127.8 196.5 162.1 162.2C196.5 127.8 196.5 72.2 162.1 37.9ZM63.0002 170.7C56.8002 167.4 51.1002 163.2 46.1002 158.4V41.7C51.3002 36.7 57.2002 32.5 63.6002 29.1L137 140.9V29.3C143.2 32.6 148.9 36.8 153.9 41.6V158.3C148.7 163.3 142.8 167.5 136.4 170.9L63.0002 59.1V170.7Z" fill="#070B15"/>
|
||||
<path d="M154 158.3V41.7C148.9 36.9 143.2 32.7 137.1 29.4V140.9L63.5 29C57.1 32.4 51.2 36.6 46 41.6V158.3C51.1 163.1 56.8 167.3 62.9 170.6V59.1L136.5 171C142.9 167.6 148.8 163.3 154 158.3Z" fill="white"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M171.7,30.3001 C132.7,-8.7999 69.3001,-8.7999 30.3001,30.3001 C-8.7999,69.4001 -8.7999,132.7 30.3001,171.7 C69.4001,210.8 132.7,210.8 171.7,171.7 C210.8,132.7 210.8,69.3001 171.7,30.3001 Z M163.1,163.1 C128.8,197.4 73.1001,197.4 38.8001,163.1 C4.5001,128.8 4.5001,73.1001 38.8001,38.8001 C73.1001,4.5001 128.8,4.5001 163.1,38.8001 C197.5,73.2001 197.5,128.8 163.1,163.1 Z" id="Shape" fill="#141521"></path>
|
||||
<path d="M163.1,38.9 C128.8,4.60005 73.1002,4.60005 38.8002,38.9 C4.50019,73.2 4.50019,128.9 38.8002,163.2 C73.1002,197.5 128.8,197.5 163.1,163.2 C197.5,128.8 197.5,73.2 163.1,38.9 Z" id="Shape" fill="#FFFFFF"></path>
|
||||
<g id="T" transform="translate(25, 25) scale(5,5)">
|
||||
<path d="M18.4804688,24 C19.203125,24 19.7182617,23.8608398 20.0258789,23.5825195 C20.3334961,23.3041992 20.4873047,22.9453125 20.4873047,22.5058594 C20.4873047,22.0566406 20.3334961,21.6928711 20.0258789,21.4145508 C19.7182617,21.1362305 19.203125,20.9970703 18.4804688,20.9970703 L18.4804688,20.9970703 L16.4589844,20.9970703 L16.4589844,9.24902344 L19.7548828,9.24902344 L19.7548828,12.0908203 C19.7548828,12.8134766 19.894043,13.3286133 20.1723633,13.6362305 C20.4506836,13.9438477 20.8095703,14.0976562 21.2490234,14.0976562 C21.6982422,14.0976562 22.0620117,13.9438477 22.340332,13.6362305 C22.6186523,13.3286133 22.7578125,12.8134766 22.7578125,12.0908203 L22.7578125,12.0908203 L22.7578125,6.24609375 L7.20117188,6.23144531 L7.20117188,12.0908203 C7.20117188,12.8134766 7.34033203,13.3286133 7.61865234,13.6362305 C7.89697266,13.9438477 8.25585938,14.0976562 8.6953125,14.0976562 C9.14453125,14.0976562 9.50830078,13.9438477 9.78662109,13.6362305 C10.0649414,13.3286133 10.2041016,12.8134766 10.2041016,12.0908203 L10.2041016,12.0908203 L10.2041016,9.24902344 L13.4560547,9.24902344 L13.4560547,20.9970703 L11.4492188,20.9970703 C10.7265625,20.9970703 10.2114258,21.1362305 9.90380859,21.4145508 C9.59619141,21.6928711 9.44238281,22.0517578 9.44238281,22.4912109 C9.44238281,22.9404297 9.59619141,23.3041992 9.90380859,23.5825195 C10.2114258,23.8608398 10.7265625,24 11.4492188,24 L11.4492188,24 L18.4804688,24 Z" id="T" fill="#000" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M170.7 29.3001C131.7 -9.7999 68.3001 -9.7999 29.3001 29.3001C-9.7999 68.4001 -9.7999 131.7 29.3001 170.7C68.4001 209.8 131.7 209.8 170.7 170.7C209.8 131.7 209.8 68.3001 170.7 29.3001ZM162.1 162.1C127.8 196.4 72.1001 196.4 37.8001 162.1C3.5001 127.8 3.5001 72.1001 37.8001 37.8001C72.1001 3.5001 127.8 3.5001 162.1 37.8001C196.5 72.2001 196.5 127.8 162.1 162.1Z" fill="#141521"/>
|
||||
<path d="M162.1 37.9C127.8 3.60005 72.1002 3.60005 37.8002 37.9C3.50019 72.2 3.50019 127.9 37.8002 162.2C72.1002 196.5 127.8 196.5 162.1 162.2C196.5 127.8 196.5 72.2 162.1 37.9ZM63.0002 170.7C56.8002 167.4 51.1002 163.2 46.1002 158.4V41.7C51.3002 36.7 57.2002 32.5 63.6002 29.1L137 140.9V29.3C143.2 32.6 148.9 36.8 153.9 41.6V158.3C148.7 163.3 142.8 167.5 136.4 170.9L63.0002 59.1V170.7Z" fill="white"/>
|
||||
<path d="M154 158.3V41.7C148.9 36.9 143.2 32.7 137.1 29.4V140.9L63.5 29C57.1 32.4 51.2 36.6 46 41.6V158.3C51.1 163.1 56.8 167.3 62.9 170.6V59.1L136.5 171C142.9 167.6 148.8 163.3 154 158.3Z" fill="#141521"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -7,7 +7,7 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dirs = "3.0"
|
||||
dirs = "4.0"
|
||||
futures = "0.3"
|
||||
humantime-serde = "1.0"
|
||||
log = "0.4"
|
||||
@@ -32,4 +32,4 @@ validator-client = { path = "../../common/client-libs/validator-client" }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[features]
|
||||
coconut = ["gateway-client/coconut", "gateway-requests/coconut"]
|
||||
coconut = ["gateway-client/coconut", "gateway-requests/coconut"]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use config::defaults::*;
|
||||
use config::NymConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::marker::PhantomData;
|
||||
@@ -114,12 +113,8 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.disabled_credentials_mode = disabled_credentials_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_endpoint(&mut self, gateway_endpoint: GatewayEndpoint) {
|
||||
self.client.gateway_endpoint = gateway_endpoint;
|
||||
}
|
||||
|
||||
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
|
||||
@@ -142,7 +137,7 @@ impl<T: NymConfig> Config<T> {
|
||||
|
||||
pub fn set_high_default_traffic_volume(&mut self) {
|
||||
self.debug.average_packet_delay = Duration::from_millis(10);
|
||||
self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2000000); // basically don't really send cover messages
|
||||
self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2_000_000); // basically don't really send cover messages
|
||||
self.debug.message_sending_average_delay = Duration::from_millis(4); // 250 "real" messages / s
|
||||
}
|
||||
|
||||
@@ -206,6 +201,10 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.gateway_endpoint.gateway_listener.clone()
|
||||
}
|
||||
|
||||
pub fn get_gateway_endpoint(&self) -> &GatewayEndpoint {
|
||||
&self.client.gateway_endpoint
|
||||
}
|
||||
|
||||
pub fn get_database_path(&self) -> PathBuf {
|
||||
self.client.database_path.clone()
|
||||
}
|
||||
@@ -272,20 +271,31 @@ impl<T: NymConfig> Default for Config<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
struct GatewayEndpoint {
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub 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,
|
||||
pub gateway_id: String,
|
||||
|
||||
/// Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner: String,
|
||||
pub gateway_owner: String,
|
||||
|
||||
/// Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener: String,
|
||||
pub gateway_listener: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
impl From<topology::gateway::Node> for GatewayEndpoint {
|
||||
fn from(node: topology::gateway::Node) -> GatewayEndpoint {
|
||||
let gateway_listener = node.clients_address();
|
||||
GatewayEndpoint {
|
||||
gateway_id: node.identity_key.to_base58_string(),
|
||||
gateway_owner: node.owner,
|
||||
gateway_listener,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
pub struct Client<T> {
|
||||
/// Version of the client for which this configuration was created.
|
||||
#[serde(default = "missing_string_value")]
|
||||
@@ -345,7 +355,7 @@ pub struct Client<T> {
|
||||
nym_root_directory: PathBuf,
|
||||
|
||||
#[serde(skip)]
|
||||
super_struct: PhantomData<*const T>,
|
||||
super_struct: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: NymConfig> Default for Client<T> {
|
||||
@@ -355,7 +365,7 @@ impl<T: NymConfig> Default for Client<T> {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
id: "".to_string(),
|
||||
disabled_credentials_mode: true,
|
||||
validator_api_urls: default_api_endpoints(),
|
||||
validator_api_urls: vec![],
|
||||
private_identity_key_file: Default::default(),
|
||||
public_identity_key_file: Default::default(),
|
||||
private_encryption_key_file: Default::default(),
|
||||
@@ -408,7 +418,7 @@ impl<T: NymConfig> Client<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Logging {}
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Collection of initialization steps used by client implementations
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use config::NymConfig;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use gateway_client::GatewayClient;
|
||||
use gateway_requests::registration::handshake::SharedKeys;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use topology::{filter::VersionFilterable, gateway};
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
client::key_manager::KeyManager,
|
||||
config::{persistence::key_pathfinder::ClientKeyPathfinder, Config},
|
||||
};
|
||||
|
||||
pub async fn query_gateway_details(
|
||||
validator_servers: Vec<Url>,
|
||||
chosen_gateway_id: Option<&str>,
|
||||
) -> gateway::Node {
|
||||
let validator_api = validator_servers
|
||||
.choose(&mut thread_rng())
|
||||
.expect("The list of validator apis is empty");
|
||||
let validator_client = validator_client::ApiClient::new(validator_api.clone());
|
||||
|
||||
log::trace!("Fetching list of gateways from: {}", validator_api);
|
||||
let gateways = validator_client.get_cached_gateways().await.unwrap();
|
||||
let valid_gateways = gateways
|
||||
.into_iter()
|
||||
.filter_map(|gateway| gateway.try_into().ok())
|
||||
.collect::<Vec<gateway::Node>>();
|
||||
|
||||
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
|
||||
|
||||
// if we have chosen particular gateway - use it, otherwise choose a random one.
|
||||
// (remember that in active topology all gateways have at least 100 reputation so should
|
||||
// be working correctly)
|
||||
if let Some(gateway_id) = chosen_gateway_id {
|
||||
filtered_gateways
|
||||
.iter()
|
||||
.find(|gateway| gateway.identity_key.to_base58_string() == gateway_id)
|
||||
.expect(&*format!("no gateway with id {} exists!", gateway_id))
|
||||
.clone()
|
||||
} else {
|
||||
filtered_gateways
|
||||
.choose(&mut rand::thread_rng())
|
||||
.expect("there are no gateways on the network!")
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn register_with_gateway_and_store_keys<T>(
|
||||
gateway_details: gateway::Node,
|
||||
config: &Config<T>,
|
||||
) where
|
||||
T: NymConfig,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let mut key_manager = KeyManager::new(&mut rng);
|
||||
|
||||
let shared_keys = register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config);
|
||||
key_manager
|
||||
.store_keys(&pathfinder)
|
||||
.expect("Failed to generated keys");
|
||||
}
|
||||
|
||||
async fn register_with_gateway(
|
||||
gateway: &gateway::Node,
|
||||
our_identity: Arc<identity::KeyPair>,
|
||||
) -> Arc<SharedKeys> {
|
||||
let timeout = Duration::from_millis(1500);
|
||||
let mut gateway_client = GatewayClient::new_init(
|
||||
gateway.clients_address(),
|
||||
gateway.identity_key,
|
||||
gateway.owner.clone(),
|
||||
our_identity.clone(),
|
||||
timeout,
|
||||
);
|
||||
gateway_client
|
||||
.establish_connection()
|
||||
.await
|
||||
.expect("failed to establish connection with the gateway!");
|
||||
gateway_client
|
||||
.perform_initial_authentication()
|
||||
.await
|
||||
.expect("failed to register with the gateway!")
|
||||
}
|
||||
|
||||
pub fn show_address<T>(config: &Config<T>)
|
||||
where
|
||||
T: config::NymConfig,
|
||||
{
|
||||
fn load_identity_keys(pathfinder: &ClientKeyPathfinder) -> identity::KeyPair {
|
||||
let identity_keypair: identity::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
pathfinder.private_identity_key().to_owned(),
|
||||
pathfinder.public_identity_key().to_owned(),
|
||||
))
|
||||
.expect("Failed to read stored identity key files");
|
||||
identity_keypair
|
||||
}
|
||||
|
||||
fn load_sphinx_keys(pathfinder: &ClientKeyPathfinder) -> encryption::KeyPair {
|
||||
let sphinx_keypair: encryption::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
pathfinder.private_encryption_key().to_owned(),
|
||||
pathfinder.public_encryption_key().to_owned(),
|
||||
))
|
||||
.expect("Failed to read stored sphinx key files");
|
||||
sphinx_keypair
|
||||
}
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config);
|
||||
let identity_keypair = load_identity_keys(&pathfinder);
|
||||
let sphinx_keypair = load_sphinx_keys(&pathfinder);
|
||||
|
||||
let client_recipient = Recipient::new(
|
||||
*identity_keypair.public_key(),
|
||||
*sphinx_keypair.public_key(),
|
||||
// TODO: below only works under assumption that gateway address == gateway id
|
||||
// (which currently is true)
|
||||
NodeIdentity::from_base58_string(config.get_gateway_id()).unwrap(),
|
||||
);
|
||||
|
||||
println!("\nThe address of this client is: {}", client_recipient);
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod client;
|
||||
pub mod config;
|
||||
pub mod init;
|
||||
|
||||
@@ -26,4 +26,4 @@ pemstore = { path = "../../common/pemstore" }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
|
||||
|
||||
[features]
|
||||
coconut = ["credentials/coconut"]
|
||||
coconut = ["credentials/coconut"]
|
||||
|
||||
@@ -1,30 +1,35 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use bip39::Mnemonic;
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::{MNEMONIC, NYMD_URL};
|
||||
|
||||
use network_defaults::{DEFAULT_NETWORK, DENOM, VOUCHER_INFO};
|
||||
use bip39::Mnemonic;
|
||||
use network_defaults::{NymNetworkDetails, VOUCHER_INFO};
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
use validator_client::nymd;
|
||||
use validator_client::nymd::traits::CoconutBandwidthSigningClient;
|
||||
use validator_client::nymd::{Coin, Fee, NymdClient, SigningNymdClient};
|
||||
|
||||
pub(crate) struct Client {
|
||||
nymd_client: NymdClient<SigningNymdClient>,
|
||||
mix_denom_base: String,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new() -> Self {
|
||||
let nymd_url = Url::from_str(NYMD_URL).unwrap();
|
||||
let mnemonic = Mnemonic::from_str(MNEMONIC).unwrap();
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let config = nymd::Config::try_from_nym_network_details(&network_details)
|
||||
.expect("failed to construct valid validator client config with the provided network");
|
||||
let nymd_client =
|
||||
NymdClient::connect_with_mnemonic(DEFAULT_NETWORK, nymd_url.as_ref(), mnemonic, None)
|
||||
.unwrap();
|
||||
NymdClient::connect_with_mnemonic(config, nymd_url.as_ref(), mnemonic, None).unwrap();
|
||||
|
||||
Client { nymd_client }
|
||||
Client {
|
||||
nymd_client,
|
||||
mix_denom_base: network_details.chain_details.mix_denom.base,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn deposit(
|
||||
@@ -34,7 +39,7 @@ impl Client {
|
||||
encryption_key: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<String> {
|
||||
let amount = Coin::new(amount as u128, DENOM.to_string());
|
||||
let amount = Coin::new(amount as u128, self.mix_denom_base.clone());
|
||||
Ok(self
|
||||
.nymd_client
|
||||
.deposit(
|
||||
|
||||
Generated
-2402
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
||||
name = "nym-client"
|
||||
version = "1.0.1"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
|
||||
@@ -19,9 +20,8 @@ futures = "0.3" # bunch of futures stuff, however, now that I think about it, it
|
||||
# and the single instance of abortable we have should really be refactored anyway
|
||||
url = "2.2"
|
||||
|
||||
clap = "2.33.0" # for the command line arguments
|
||||
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)
|
||||
clap = { version = "3.2.8", features = ["cargo", "derive"] }
|
||||
dirs = "4.0"
|
||||
log = "0.4" # self explanatory
|
||||
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
|
||||
@@ -55,4 +55,4 @@ eth = []
|
||||
serde_json = "1.0" # for the "textsend" example
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::path::PathBuf;
|
||||
|
||||
mod template;
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum SocketType {
|
||||
WebSocket,
|
||||
@@ -105,7 +105,7 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Socket {
|
||||
socket_type: SocketType,
|
||||
|
||||
+167
-216
@@ -1,245 +1,196 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use client_core::client::key_manager::KeyManager;
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
use clap::Args;
|
||||
use client_core::config::GatewayEndpoint;
|
||||
use config::NymConfig;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use gateway_client::GatewayClient;
|
||||
use gateway_requests::registration::handshake::SharedKeys;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use topology::{filter::VersionFilterable, gateway};
|
||||
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, ENABLED_CREDENTIALS_MODE_ARG_NAME,
|
||||
ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
|
||||
use crate::{
|
||||
client::config::Config,
|
||||
commands::{override_config, OverrideConfig},
|
||||
};
|
||||
|
||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
let app = App::new("init")
|
||||
.about("Initialise a Nym client. Do this first!")
|
||||
.arg(Arg::with_name("id")
|
||||
.long("id")
|
||||
.help("Id of the nym-mixnet-client we want to create config for.")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
)
|
||||
.arg(Arg::with_name("gateway")
|
||||
.long("gateway")
|
||||
.help("Id of the gateway we are going to connect to.")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("validators")
|
||||
.long("validators")
|
||||
.help("Comma separated list of rest endpoints of the validators")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(Arg::with_name("disable-socket")
|
||||
.long("disable-socket")
|
||||
.help("Whether to not start the websocket")
|
||||
)
|
||||
.arg(Arg::with_name("port")
|
||||
.short("p")
|
||||
.long("port")
|
||||
.help("Port for the socket (if applicable) to listen on in all subsequent runs")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("fastmode")
|
||||
.long("fastmode")
|
||||
.hidden(true) // this will prevent this flag from being displayed in `--help`
|
||||
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
|
||||
);
|
||||
#[cfg(feature = "eth")]
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let app = app
|
||||
.arg(
|
||||
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||
.help("Set this client to work in a disabled credentials 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(ENABLED_CREDENTIALS_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(ENABLED_CREDENTIALS_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
|
||||
.required(true)
|
||||
);
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
use crate::commands::{DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY};
|
||||
|
||||
app
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Init {
|
||||
/// Id of the nym-mixnet-client we want to create config for.
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
|
||||
/// Id of the gateway we are going to connect to.
|
||||
#[clap(long)]
|
||||
gateway: Option<String>,
|
||||
|
||||
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
|
||||
/// potentially causing loss of access.
|
||||
#[clap(long)]
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the validators
|
||||
#[clap(long)]
|
||||
validators: Option<String>,
|
||||
|
||||
/// Whether to not start the websocket
|
||||
#[clap(long)]
|
||||
disable_socket: bool,
|
||||
|
||||
/// Port for the socket (if applicable) to listen on in all subsequent runs
|
||||
#[clap(short, long)]
|
||||
port: Option<u16>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hidden = true)]
|
||||
fastmode: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
|
||||
/// --eth-private_key don't need to be set.
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// 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 --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_ENDPOINT))
|
||||
)]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead")
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_PRIVATE_KEY))
|
||||
)]
|
||||
eth_private_key: String,
|
||||
}
|
||||
|
||||
async fn register_with_gateway(
|
||||
gateway: &gateway::Node,
|
||||
our_identity: Arc<identity::KeyPair>,
|
||||
) -> Arc<SharedKeys> {
|
||||
let timeout = Duration::from_millis(1500);
|
||||
let mut gateway_client = GatewayClient::new_init(
|
||||
gateway.clients_address(),
|
||||
gateway.identity_key,
|
||||
gateway.owner.clone(),
|
||||
our_identity.clone(),
|
||||
timeout,
|
||||
);
|
||||
gateway_client
|
||||
.establish_connection()
|
||||
.await
|
||||
.expect("failed to establish connection with the gateway!");
|
||||
gateway_client
|
||||
.perform_initial_authentication()
|
||||
.await
|
||||
.expect("failed to register with the gateway!")
|
||||
}
|
||||
impl From<Init> for OverrideConfig {
|
||||
fn from(init_config: Init) -> Self {
|
||||
OverrideConfig {
|
||||
validators: init_config.validators,
|
||||
disable_socket: init_config.disable_socket,
|
||||
port: init_config.port,
|
||||
fastmode: init_config.fastmode,
|
||||
|
||||
async fn gateway_details(
|
||||
validator_servers: Vec<Url>,
|
||||
chosen_gateway_id: Option<&str>,
|
||||
) -> gateway::Node {
|
||||
let validator_api = validator_servers
|
||||
.choose(&mut thread_rng())
|
||||
.expect("The list of validator apis is empty");
|
||||
let validator_client = validator_client::ApiClient::new(validator_api.clone());
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
|
||||
let gateways = validator_client.get_cached_gateways().await.unwrap();
|
||||
let valid_gateways = gateways
|
||||
.into_iter()
|
||||
.filter_map(|gateway| gateway.try_into().ok())
|
||||
.collect::<Vec<gateway::Node>>();
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Some(init_config.eth_private_key),
|
||||
|
||||
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
|
||||
|
||||
// if we have chosen particular gateway - use it, otherwise choose a random one.
|
||||
// (remember that in active topology all gateways have at least 100 reputation so should
|
||||
// be working correctly)
|
||||
if let Some(gateway_id) = chosen_gateway_id {
|
||||
filtered_gateways
|
||||
.iter()
|
||||
.find(|gateway| gateway.identity_key.to_base58_string() == gateway_id)
|
||||
.expect(&*format!("no gateway with id {} exists!", gateway_id))
|
||||
.clone()
|
||||
} else {
|
||||
filtered_gateways
|
||||
.choose(&mut rand::thread_rng())
|
||||
.expect("there are no gateways on the network!")
|
||||
.clone()
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Some(init_config.eth_endpoint),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn show_address(config: &Config) {
|
||||
fn load_identity_keys(pathfinder: &ClientKeyPathfinder) -> identity::KeyPair {
|
||||
let identity_keypair: identity::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
pathfinder.private_identity_key().to_owned(),
|
||||
pathfinder.public_identity_key().to_owned(),
|
||||
))
|
||||
.expect("Failed to read stored identity key files");
|
||||
identity_keypair
|
||||
}
|
||||
|
||||
fn load_sphinx_keys(pathfinder: &ClientKeyPathfinder) -> encryption::KeyPair {
|
||||
let sphinx_keypair: encryption::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
pathfinder.private_encryption_key().to_owned(),
|
||||
pathfinder.public_encryption_key().to_owned(),
|
||||
))
|
||||
.expect("Failed to read stored sphinx key files");
|
||||
sphinx_keypair
|
||||
}
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
let identity_keypair = load_identity_keys(&pathfinder);
|
||||
let sphinx_keypair = load_sphinx_keys(&pathfinder);
|
||||
|
||||
let client_recipient = Recipient::new(
|
||||
*identity_keypair.public_key(),
|
||||
*sphinx_keypair.public_key(),
|
||||
// TODO: below only works under assumption that gateway address == gateway id
|
||||
// (which currently is true)
|
||||
NodeIdentity::from_base58_string(config.get_base().get_gateway_id()).unwrap(),
|
||||
);
|
||||
|
||||
println!("\nThe address of this client is: {}", client_recipient);
|
||||
}
|
||||
|
||||
pub async fn execute(matches: ArgMatches<'static>) {
|
||||
pub(crate) async fn execute(args: &Init) {
|
||||
println!("Initialising client...");
|
||||
|
||||
let id = matches.value_of("id").unwrap(); // required for now
|
||||
let id = &args.id;
|
||||
|
||||
let already_init = if Config::default_config_file_path(Some(id)).exists() {
|
||||
println!("Client \"{}\" was already initialised before! Config information will be overwritten (but keys will be kept)!", id);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let already_init = Config::default_config_file_path(Some(id)).exists();
|
||||
if already_init {
|
||||
println!(
|
||||
"Client \"{}\" was already initialised before! \
|
||||
Config information will be overwritten (but keys will be kept)!",
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
// re-registering if wanted.
|
||||
let user_wants_force_register = args.force_register_gateway;
|
||||
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
// Unless the user really wants to.
|
||||
let register_gateway = !already_init || user_wants_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let user_chosen_gateway_id = args.gateway.as_deref();
|
||||
|
||||
let mut config = Config::new(id);
|
||||
let override_config_fields = OverrideConfig::from(args.clone());
|
||||
config = override_config(config, override_config_fields);
|
||||
|
||||
let mut rng = OsRng;
|
||||
|
||||
// TODO: ideally that should be the last thing that's being done to config.
|
||||
// However, we are later further overriding it with gateway id
|
||||
config = override_config(config, &matches);
|
||||
if matches.is_present("fastmode") {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
|
||||
// if client was already initialised, don't generate new keys, not re-register with gateway
|
||||
// (because this would create new shared key)
|
||||
if !already_init {
|
||||
// create identity, encryption and ack keys.
|
||||
let mut key_manager = KeyManager::new(&mut rng);
|
||||
|
||||
let chosen_gateway_id = matches.value_of("gateway");
|
||||
|
||||
let gateway_details = gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
let shared_keys =
|
||||
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
|
||||
|
||||
config.get_base_mut().with_gateway_endpoint(
|
||||
gateway_details.identity_key.to_base58_string(),
|
||||
gateway_details.owner.clone(),
|
||||
gateway_details.clients_address(),
|
||||
);
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
key_manager
|
||||
.store_keys(&pathfinder)
|
||||
.expect("Failed to generated keys");
|
||||
println!("Saved all generated keys");
|
||||
}
|
||||
let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config).await;
|
||||
config.get_base_mut().with_gateway_endpoint(gateway);
|
||||
|
||||
let config_save_location = config.get_config_file_save_location();
|
||||
config
|
||||
.save_to_file(None)
|
||||
.expect("Failed to save the config file");
|
||||
println!("Saved configuration file to {:?}", config_save_location);
|
||||
println!("Using gateway: {}", config.get_base().get_gateway_id(),);
|
||||
println!("Client configuration completed.\n\n\n");
|
||||
|
||||
show_address(&config);
|
||||
println!("Saved configuration file to {:?}", config_save_location);
|
||||
println!("Using gateway: {}", config.get_base().get_gateway_id());
|
||||
log::debug!("Gateway id: {}", config.get_base().get_gateway_id());
|
||||
log::debug!("Gateway owner: {}", config.get_base().get_gateway_owner());
|
||||
log::debug!(
|
||||
"Gateway listener: {}",
|
||||
config.get_base().get_gateway_listener()
|
||||
);
|
||||
println!("Client configuration completed.");
|
||||
|
||||
client_core::init::show_address(config.get_base());
|
||||
}
|
||||
|
||||
async fn setup_gateway(
|
||||
id: &str,
|
||||
register: bool,
|
||||
user_chosen_gateway_id: Option<&str>,
|
||||
config: &Config,
|
||||
) -> GatewayEndpoint {
|
||||
if register {
|
||||
// Get the gateway details by querying the validator-api. Either pick one at random or use
|
||||
// the chosen one if it's among the available ones.
|
||||
println!("Configuring gateway");
|
||||
let gateway = client_core::init::query_gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
user_chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
|
||||
// Registering with gateway by setting up and writing shared keys to disk
|
||||
log::trace!("Registering gateway");
|
||||
client_core::init::register_with_gateway_and_store_keys(gateway.clone(), config.get_base())
|
||||
.await;
|
||||
println!("Saved all generated keys");
|
||||
|
||||
gateway.into()
|
||||
} else if user_chosen_gateway_id.is_some() {
|
||||
// Just set the config, don't register or create any keys
|
||||
// This assumes that the user knows what they are doing, and that the existing keys are
|
||||
// valid for the gateway being used
|
||||
println!("Using gateway provided by user, keeping existing keys");
|
||||
let gateway = client_core::init::query_gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
user_chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
gateway.into()
|
||||
} else {
|
||||
println!("Not registering gateway, will reuse existing config and keys");
|
||||
match Config::load_from_file(Some(id)) {
|
||||
Ok(existing_config) => existing_config.get_base().get_gateway_endpoint().clone(),
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"Unable to configure gateway: {err}. \n
|
||||
Seems like the client was already initialized but it was not possible to read \
|
||||
the existing configuration file. \n
|
||||
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
|
||||
removing the existing configuration and starting over."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::{Config, SocketType};
|
||||
use clap::ArgMatches;
|
||||
use clap::{Parser, Subcommand};
|
||||
use url::Url;
|
||||
|
||||
pub(crate) const ENABLED_CREDENTIALS_MODE_ARG_NAME: &str = "enabled-credentials-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";
|
||||
@@ -21,6 +16,87 @@ pub(crate) mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
fn long_version() -> String {
|
||||
format!(
|
||||
r#"
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
"#,
|
||||
"Build Timestamp:",
|
||||
env!("VERGEN_BUILD_TIMESTAMP"),
|
||||
"Build Version:",
|
||||
env!("VERGEN_BUILD_SEMVER"),
|
||||
"Commit SHA:",
|
||||
env!("VERGEN_GIT_SHA"),
|
||||
"Commit Date:",
|
||||
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
|
||||
"Commit Branch:",
|
||||
env!("VERGEN_GIT_BRANCH"),
|
||||
"rustc Version:",
|
||||
env!("VERGEN_RUSTC_SEMVER"),
|
||||
"rustc Channel:",
|
||||
env!("VERGEN_RUSTC_CHANNEL"),
|
||||
"cargo Profile:",
|
||||
env!("VERGEN_CARGO_PROFILE"),
|
||||
)
|
||||
}
|
||||
|
||||
fn long_version_static() -> &'static str {
|
||||
Box::leak(long_version().into_boxed_str())
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author = "Nymtech", version, long_version = long_version_static(), about)]
|
||||
pub(crate) struct Cli {
|
||||
/// Path pointing to an env file that configures the client.
|
||||
#[clap(long)]
|
||||
pub(crate) config_env_file: Option<std::path::PathBuf>,
|
||||
|
||||
#[clap(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum Commands {
|
||||
/// Initialise a Nym client. Do this first!
|
||||
Init(init::Init),
|
||||
/// Run the Nym client with provided configuration client optionally overriding set parameters
|
||||
Run(run::Run),
|
||||
/// Try to upgrade the client
|
||||
Upgrade(upgrade::Upgrade),
|
||||
}
|
||||
|
||||
// Configuration that can be overridden.
|
||||
pub(crate) struct OverrideConfig {
|
||||
validators: Option<String>,
|
||||
disable_socket: bool,
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Option<String>,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: &Cli) {
|
||||
match &args.command {
|
||||
Commands::Init(m) => init::execute(m).await,
|
||||
Commands::Run(m) => run::execute(m).await,
|
||||
Commands::Upgrade(m) => upgrade::execute(m),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
@@ -32,49 +108,64 @@ fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config {
|
||||
if let Some(raw_validators) = matches.value_of("validators") {
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
if let Some(raw_validators) = args.validators {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(parse_validators(raw_validators));
|
||||
.set_custom_validator_apis(parse_validators(&raw_validators));
|
||||
} else if std::env::var(network_defaults::var_names::CONFIGURED).is_ok() {
|
||||
let raw_validators = std::env::var(network_defaults::var_names::API_VALIDATOR)
|
||||
.expect("api validator not set");
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(parse_validators(&raw_validators));
|
||||
}
|
||||
|
||||
if let Some(gateway_id) = matches.value_of("gateway") {
|
||||
config.get_base_mut().with_gateway_id(gateway_id);
|
||||
}
|
||||
|
||||
if matches.is_present("disable-socket") {
|
||||
if args.disable_socket {
|
||||
config = config.with_socket(SocketType::None);
|
||||
}
|
||||
|
||||
if let Some(port) = matches.value_of("port").map(|port| port.parse::<u16>()) {
|
||||
if let Err(err) = port {
|
||||
// if port was overridden, it must be parsable
|
||||
panic!("Invalid port value provided - {:?}", err);
|
||||
if let Some(port) = args.port {
|
||||
config = config.with_port(port);
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "eth"), not(feature = "coconut")))]
|
||||
{
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT.to_string());
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
{
|
||||
if args.enabled_credentials_mode {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
}
|
||||
if let Some(eth_endpoint) = args.eth_endpoint {
|
||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||
}
|
||||
if let Some(eth_private_key) = args.eth_private_key {
|
||||
config.get_base_mut().with_eth_private_key(eth_private_key);
|
||||
}
|
||||
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 matches.is_present(ENABLED_CREDENTIALS_MODE_ARG_NAME) {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
if args.fastmode {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use clap::CommandFactory;
|
||||
|
||||
#[test]
|
||||
fn verify_cli() {
|
||||
Cli::command().debug_assert();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,77 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::Config;
|
||||
use crate::client::NymClient;
|
||||
use crate::commands::override_config;
|
||||
#[cfg(feature = "eth")]
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use crate::commands::{
|
||||
ENABLED_CREDENTIALS_MODE_ARG_NAME, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
|
||||
use crate::{
|
||||
client::{config::Config, NymClient},
|
||||
commands::{override_config, OverrideConfig},
|
||||
};
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
|
||||
use clap::Args;
|
||||
use config::NymConfig;
|
||||
use log::*;
|
||||
use version_checker::is_minor_version_compatible;
|
||||
|
||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
let app = App::new("run")
|
||||
.about("Run the Nym client with provided configuration client optionally overriding set parameters")
|
||||
.arg(Arg::with_name("id")
|
||||
.long("id")
|
||||
.help("Id of the nym-mixnet-client we want to run.")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
)
|
||||
// the rest of arguments are optional, they are used to override settings in config file
|
||||
.arg(Arg::with_name("validators")
|
||||
.long("validators")
|
||||
.help("Comma separated list rest rest endpoints of the validators")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(Arg::with_name("gateway")
|
||||
.long("gateway")
|
||||
.help("Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("disable-socket")
|
||||
.long("disable-socket")
|
||||
.help("Whether to not start the websocket")
|
||||
)
|
||||
.arg(Arg::with_name("port")
|
||||
.short("p")
|
||||
.long("port")
|
||||
.help("Port for the socket (if applicable) to listen on")
|
||||
.takes_value(true)
|
||||
);
|
||||
#[cfg(feature = "eth")]
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let app = app
|
||||
.arg(
|
||||
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||
.help("Set this client to work in a enabled credentials mode that would attempt to use gateway with 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));
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Run {
|
||||
/// Id of the nym-mixnet-client we want to run.
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
|
||||
app
|
||||
/// Comma separated list of rest endpoints of the validators
|
||||
#[clap(long)]
|
||||
validators: Option<String>,
|
||||
|
||||
/// Id of the gateway we want to connect to. If overridden, it is user's responsibility to
|
||||
/// ensure prior registration happened
|
||||
#[clap(long)]
|
||||
gateway: Option<String>,
|
||||
|
||||
/// Whether to not start the websocket
|
||||
#[clap(long)]
|
||||
disable_socket: bool,
|
||||
|
||||
/// Port for the socket to listen on
|
||||
#[clap(short, long)]
|
||||
port: Option<u16>,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
|
||||
/// --eth-private-key don't need to be set.
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// 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 --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_endpoint: Option<String>,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead")
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_private_key: Option<String>,
|
||||
}
|
||||
|
||||
impl From<Run> for OverrideConfig {
|
||||
fn from(run_config: Run) -> Self {
|
||||
OverrideConfig {
|
||||
validators: run_config.validators,
|
||||
disable_socket: run_config.disable_socket,
|
||||
port: run_config.port,
|
||||
fastmode: false,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: run_config.eth_private_key,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: run_config.eth_endpoint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this only checks compatibility between config the binary. It does not take into consideration
|
||||
@@ -70,7 +79,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
fn version_check(cfg: &Config) -> bool {
|
||||
let binary_version = env!("CARGO_PKG_VERSION");
|
||||
let config_version = cfg.get_base().get_version();
|
||||
if binary_version != config_version {
|
||||
if binary_version == config_version {
|
||||
true
|
||||
} else {
|
||||
warn!("The mixnode binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
|
||||
if is_minor_version_compatible(binary_version, config_version) {
|
||||
info!("but they are still semver compatible. However, consider running the `upgrade` command");
|
||||
@@ -79,13 +90,11 @@ fn version_check(cfg: &Config) -> bool {
|
||||
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
|
||||
false
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(matches: ArgMatches<'static>) {
|
||||
let id = matches.value_of("id").unwrap();
|
||||
pub(crate) async fn execute(args: &Run) {
|
||||
let id = &args.id;
|
||||
|
||||
let mut config = match Config::load_from_file(Some(id)) {
|
||||
Ok(cfg) => cfg,
|
||||
@@ -95,7 +104,8 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
}
|
||||
};
|
||||
|
||||
config = override_config(config, &matches);
|
||||
let override_config_fields = OverrideConfig::from(args.clone());
|
||||
config = override_config(config, override_config_fields);
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("failed the local version check");
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::{Config, MISSING_VALUE};
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use config::defaults::default_api_endpoints;
|
||||
|
||||
use config::NymConfig;
|
||||
use version_checker::Version;
|
||||
|
||||
use clap::Args;
|
||||
use std::fmt::Display;
|
||||
use std::process;
|
||||
use version_checker::Version;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
|
||||
@@ -49,14 +50,11 @@ fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> !
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
pub fn command_args<'a, 'b>() -> App<'a, 'b> {
|
||||
App::new("upgrade").about("Try to upgrade the client").arg(
|
||||
Arg::with_name("id")
|
||||
.long("id")
|
||||
.help("Id of the nym-client we want to upgrade")
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Upgrade {
|
||||
/// Id of the nym-client we want to upgrade
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
}
|
||||
|
||||
fn parse_config_version(config: &Config) -> Version {
|
||||
@@ -95,7 +93,7 @@ fn parse_package_version() -> Version {
|
||||
|
||||
fn minor_0_12_upgrade(
|
||||
mut config: Config,
|
||||
_matches: &ArgMatches<'_>,
|
||||
_matches: &Upgrade,
|
||||
config_version: &Version,
|
||||
package_version: &Version,
|
||||
) -> Config {
|
||||
@@ -107,15 +105,6 @@ fn minor_0_12_upgrade(
|
||||
|
||||
print_start_upgrade(&config_version, &to_version);
|
||||
|
||||
println!(
|
||||
"Setting validator API endpoints to {:?}",
|
||||
default_api_endpoints()
|
||||
);
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(default_api_endpoints());
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_version(to_version.to_string().as_ref());
|
||||
@@ -131,30 +120,30 @@ fn minor_0_12_upgrade(
|
||||
config
|
||||
}
|
||||
|
||||
fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Version) {
|
||||
fn do_upgrade(mut config: Config, args: &Upgrade, package_version: &Version) {
|
||||
loop {
|
||||
let config_version = parse_config_version(&config);
|
||||
|
||||
if config_version == package_version {
|
||||
if &config_version == package_version {
|
||||
println!("You're using the most recent version!");
|
||||
return;
|
||||
}
|
||||
|
||||
config = match config_version.major {
|
||||
0 => match config_version.minor {
|
||||
9 | 10 => outdated_upgrade(&config_version, &package_version),
|
||||
11 => minor_0_12_upgrade(config, matches, &config_version, &package_version),
|
||||
_ => unsupported_upgrade(&config_version, &package_version),
|
||||
9 | 10 => outdated_upgrade(&config_version, package_version),
|
||||
11 => minor_0_12_upgrade(config, args, &config_version, package_version),
|
||||
_ => unsupported_upgrade(&config_version, package_version),
|
||||
},
|
||||
_ => unsupported_upgrade(&config_version, &package_version),
|
||||
_ => unsupported_upgrade(&config_version, package_version),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(matches: &ArgMatches<'_>) {
|
||||
pub(crate) fn execute(args: &Upgrade) {
|
||||
let package_version = parse_package_version();
|
||||
|
||||
let id = matches.value_of("id").unwrap();
|
||||
let id = &args.id;
|
||||
|
||||
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
|
||||
eprintln!("failed to load existing config file! - {:?}", err);
|
||||
@@ -167,5 +156,5 @@ pub fn execute(matches: &ArgMatches<'_>) {
|
||||
}
|
||||
|
||||
// here be upgrade path to 0.9.X and beyond based on version number from config
|
||||
do_upgrade(existing_config, matches, package_version)
|
||||
do_upgrade(existing_config, args, &package_version)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{crate_version, App, ArgMatches};
|
||||
use network_defaults::DEFAULT_NETWORK;
|
||||
use clap::{crate_version, Parser};
|
||||
use network_defaults::setup_env;
|
||||
|
||||
pub mod client;
|
||||
pub mod commands;
|
||||
@@ -10,34 +10,12 @@ pub mod websocket;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
dotenv::dotenv().ok();
|
||||
setup_logging();
|
||||
println!("{}", banner());
|
||||
|
||||
let arg_matches = App::new("Nym Client")
|
||||
.version(crate_version!())
|
||||
.long_version(&*long_version())
|
||||
.author("Nymtech")
|
||||
.about("Implementation of the Nym Client")
|
||||
.subcommand(commands::init::command_args())
|
||||
.subcommand(commands::run::command_args())
|
||||
.subcommand(commands::upgrade::command_args())
|
||||
.get_matches();
|
||||
|
||||
execute(arg_matches).await;
|
||||
}
|
||||
|
||||
async fn execute(matches: ArgMatches<'static>) {
|
||||
match matches.subcommand() {
|
||||
("init", Some(m)) => commands::init::execute(m.clone()).await,
|
||||
("run", Some(m)) => commands::run::execute(m.clone()).await,
|
||||
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
||||
_ => println!("{}", usage()),
|
||||
}
|
||||
}
|
||||
|
||||
fn usage() -> &'static str {
|
||||
"usage: --help to see available options.\n\n"
|
||||
let args = commands::Cli::parse();
|
||||
setup_env(args.config_env_file.clone());
|
||||
commands::execute(&args).await;
|
||||
}
|
||||
|
||||
fn banner() -> String {
|
||||
@@ -57,40 +35,6 @@ fn banner() -> String {
|
||||
)
|
||||
}
|
||||
|
||||
fn long_version() -> String {
|
||||
format!(
|
||||
r#"
|
||||
{:<20}{}
|
||||
{:<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"),
|
||||
"Network:",
|
||||
DEFAULT_NETWORK
|
||||
)
|
||||
}
|
||||
|
||||
fn setup_logging() {
|
||||
let mut log_builder = pretty_env_logger::formatted_timed_builder();
|
||||
if let Ok(s) = ::std::env::var("RUST_LOG") {
|
||||
@@ -108,5 +52,7 @@ fn setup_logging() {
|
||||
.filter_module("want", log::LevelFilter::Warn)
|
||||
.filter_module("tungstenite", log::LevelFilter::Warn)
|
||||
.filter_module("tokio_tungstenite", log::LevelFilter::Warn)
|
||||
.filter_module("handlebars", log::LevelFilter::Warn)
|
||||
.filter_module("sled", log::LevelFilter::Warn)
|
||||
.init();
|
||||
}
|
||||
|
||||
@@ -10,4 +10,4 @@ edition = "2021"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
nymsphinx = { path = "../../../common/nymsphinx" }
|
||||
nymsphinx = { path = "../../../common/nymsphinx" }
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
// no need to go fancy here like we've done in other places.
|
||||
#[derive(PartialEq, Clone, Serialize, Deserialize)]
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub struct Error {
|
||||
pub kind: ErrorKind,
|
||||
pub message: String,
|
||||
@@ -30,7 +30,7 @@ impl Error {
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(PartialEq, Clone, Serialize, Deserialize)]
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
pub enum ErrorKind {
|
||||
/// The received request contained no data.
|
||||
EmptyRequest = 0x01,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-socks5-client"
|
||||
version = "1.0.1"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
|
||||
@@ -10,9 +11,8 @@ name = "nym_socks5"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.0"
|
||||
dirs = "3.0" # for determining default store directories in config
|
||||
dotenv = "0.15.0"
|
||||
clap = { version = "3.2.8", features = ["cargo", "derive"] }
|
||||
dirs = "4.0"
|
||||
futures = "0.3"
|
||||
log = "0.4"
|
||||
pin-project = "1.0"
|
||||
@@ -47,4 +47,4 @@ coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gate
|
||||
eth = []
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
|
||||
@@ -89,7 +89,7 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Socks5 {
|
||||
/// The port on which the client will be listening for incoming requests
|
||||
|
||||
@@ -20,6 +20,7 @@ use client_core::client::topology_control::{
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
use crypto::asymmetric::identity;
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
use gateway_client::{
|
||||
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
|
||||
@@ -35,7 +36,17 @@ use crate::socks::{
|
||||
server::SphinxSocksServer,
|
||||
};
|
||||
|
||||
pub(crate) mod config;
|
||||
pub mod config;
|
||||
|
||||
// Channels used to control the main task from outside
|
||||
pub type Socks5ControlMessageSender = mpsc::UnboundedSender<Socks5ControlMessage>;
|
||||
pub type Socks5ControlMessageReceiver = mpsc::UnboundedReceiver<Socks5ControlMessage>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Socks5ControlMessage {
|
||||
/// Tell the main task to stop
|
||||
Stop,
|
||||
}
|
||||
|
||||
pub struct NymClient {
|
||||
/// Client configuration options, including, among other things, packet sending rates,
|
||||
@@ -272,6 +283,23 @@ impl NymClient {
|
||||
);
|
||||
}
|
||||
|
||||
// Variant of `run_forever` that listends for remote control messages
|
||||
pub async fn run_and_listen(&mut self, mut receiver: Socks5ControlMessageReceiver) {
|
||||
self.start().await;
|
||||
tokio::select! {
|
||||
message = receiver.next() => {
|
||||
log::debug!("Received message: {:?}", message);
|
||||
match message {
|
||||
Some(Socks5ControlMessage::Stop) => {
|
||||
log::info!("Shutting down");
|
||||
log::info!("Graceful shutdown of tasks not yet implemented, you might see (harmless) panics until then");
|
||||
}
|
||||
None => log::debug!("None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start(&mut self) {
|
||||
info!("Starting nym client");
|
||||
// channels for inter-component communication
|
||||
|
||||
+167
-217
@@ -1,246 +1,196 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use client_core::client::key_manager::KeyManager;
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
use clap::Args;
|
||||
use client_core::config::GatewayEndpoint;
|
||||
use config::NymConfig;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use gateway_client::GatewayClient;
|
||||
use gateway_requests::registration::handshake::SharedKeys;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||
use rand::{prelude::SliceRandom, rngs::OsRng, thread_rng};
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use topology::{filter::VersionFilterable, gateway};
|
||||
use url::Url;
|
||||
|
||||
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, ENABLED_CREDENTIALS_MODE_ARG_NAME,
|
||||
ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
|
||||
use crate::{
|
||||
client::config::Config,
|
||||
commands::{override_config, OverrideConfig},
|
||||
};
|
||||
|
||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
let app = App::new("init")
|
||||
.about("Initialise a Nym client. Do this first!")
|
||||
.arg(Arg::with_name("id")
|
||||
.long("id")
|
||||
.help("Id of the nym-mixnet-client we want to create config for.")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
)
|
||||
.arg(Arg::with_name("provider")
|
||||
.long("provider")
|
||||
.help("Address of the socks5 provider to send messages to.")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
)
|
||||
.arg(Arg::with_name("gateway")
|
||||
.long("gateway")
|
||||
.help("Id of the gateway we are going to connect to.")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("validators")
|
||||
.long("validators")
|
||||
.help("Comma separated list of rest endpoints of the validators")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(Arg::with_name("port")
|
||||
.short("p")
|
||||
.long("port")
|
||||
.help("Port for the socket to listen on in all subsequent runs")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("fastmode")
|
||||
.long("fastmode")
|
||||
.hidden(true) // this will prevent this flag from being displayed in `--help`
|
||||
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
|
||||
);
|
||||
#[cfg(feature = "eth")]
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let app = app
|
||||
.arg(
|
||||
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||
.help("Set this client to work in a enabled credentials mode that would attempt to use gateway with 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(ENABLED_CREDENTIALS_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(ENABLED_CREDENTIALS_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
|
||||
.required(true)
|
||||
);
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
use crate::commands::{DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY};
|
||||
|
||||
app
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Init {
|
||||
/// Id of the nym-mixnet-client we want to create config for.
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
|
||||
/// Address of the socks5 provider to send messages to.
|
||||
#[clap(long)]
|
||||
provider: String,
|
||||
|
||||
/// Id of the gateway we are going to connect to.
|
||||
#[clap(long)]
|
||||
gateway: Option<String>,
|
||||
|
||||
/// Force register gateway. WARNING: this will overwrite any existing keys for the given id,
|
||||
/// potentially causing loss of access.
|
||||
#[clap(long)]
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the validators
|
||||
#[clap(long)]
|
||||
validators: Option<String>,
|
||||
|
||||
/// Port for the socket to listen on in all subsequent runs
|
||||
#[clap(short, long)]
|
||||
port: Option<u16>,
|
||||
|
||||
/// Mostly debug-related option to increase default traffic rate so that you would not need to
|
||||
/// modify config post init
|
||||
#[clap(long, hidden = true)]
|
||||
fastmode: bool,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
|
||||
/// --eth-private_key don't need to be set.
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// 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 --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_ENDPOINT))
|
||||
)]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead")
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_PRIVATE_KEY))
|
||||
)]
|
||||
eth_private_key: String,
|
||||
}
|
||||
|
||||
async fn register_with_gateway(
|
||||
gateway: &gateway::Node,
|
||||
our_identity: Arc<identity::KeyPair>,
|
||||
) -> Arc<SharedKeys> {
|
||||
let timeout = Duration::from_millis(1500);
|
||||
let mut gateway_client = GatewayClient::new_init(
|
||||
gateway.clients_address(),
|
||||
gateway.identity_key,
|
||||
gateway.owner.clone(),
|
||||
our_identity.clone(),
|
||||
timeout,
|
||||
);
|
||||
gateway_client
|
||||
.establish_connection()
|
||||
.await
|
||||
.expect("failed to establish connection with the gateway!");
|
||||
gateway_client
|
||||
.perform_initial_authentication()
|
||||
.await
|
||||
.expect("failed to register with the gateway!")
|
||||
}
|
||||
impl From<Init> for OverrideConfig {
|
||||
fn from(init_config: Init) -> Self {
|
||||
OverrideConfig {
|
||||
validators: init_config.validators,
|
||||
port: init_config.port,
|
||||
fastmode: init_config.fastmode,
|
||||
|
||||
async fn gateway_details(
|
||||
validator_servers: Vec<Url>,
|
||||
chosen_gateway_id: Option<&str>,
|
||||
) -> gateway::Node {
|
||||
let validator_api = validator_servers
|
||||
.choose(&mut thread_rng())
|
||||
.expect("The list of validator apis is empty");
|
||||
let validator_client = validator_client::ApiClient::new(validator_api.clone());
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
|
||||
let gateways = validator_client.get_cached_gateways().await.unwrap();
|
||||
let valid_gateways = gateways
|
||||
.into_iter()
|
||||
.filter_map(|gateway| gateway.try_into().ok())
|
||||
.collect::<Vec<gateway::Node>>();
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Some(init_config.eth_private_key),
|
||||
|
||||
let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION"));
|
||||
|
||||
// if we have chosen particular gateway - use it, otherwise choose a random one.
|
||||
// (remember that in active topology all gateways have at least 100 reputation so should
|
||||
// be working correctly)
|
||||
if let Some(gateway_id) = chosen_gateway_id {
|
||||
filtered_gateways
|
||||
.iter()
|
||||
.find(|gateway| gateway.identity_key.to_base58_string() == gateway_id)
|
||||
.expect(&*format!("no gateway with id {} exists!", gateway_id))
|
||||
.clone()
|
||||
} else {
|
||||
filtered_gateways
|
||||
.choose(&mut rand::thread_rng())
|
||||
.expect("there are no gateways on the network!")
|
||||
.clone()
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Some(init_config.eth_endpoint),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn show_address(config: &Config) {
|
||||
fn load_identity_keys(pathfinder: &ClientKeyPathfinder) -> identity::KeyPair {
|
||||
let identity_keypair: identity::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
pathfinder.private_identity_key().to_owned(),
|
||||
pathfinder.public_identity_key().to_owned(),
|
||||
))
|
||||
.expect("Failed to read stored identity key files");
|
||||
identity_keypair
|
||||
}
|
||||
|
||||
fn load_sphinx_keys(pathfinder: &ClientKeyPathfinder) -> encryption::KeyPair {
|
||||
let sphinx_keypair: encryption::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
pathfinder.private_encryption_key().to_owned(),
|
||||
pathfinder.public_encryption_key().to_owned(),
|
||||
))
|
||||
.expect("Failed to read stored sphinx key files");
|
||||
sphinx_keypair
|
||||
}
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
let identity_keypair = load_identity_keys(&pathfinder);
|
||||
let sphinx_keypair = load_sphinx_keys(&pathfinder);
|
||||
|
||||
let client_recipient = Recipient::new(
|
||||
*identity_keypair.public_key(),
|
||||
*sphinx_keypair.public_key(),
|
||||
// TODO: below only works under assumption that gateway address == gateway id
|
||||
// (which currently is true)
|
||||
NodeIdentity::from_base58_string(config.get_base().get_gateway_id()).unwrap(),
|
||||
);
|
||||
|
||||
println!("\nThe address of this client is: {}", client_recipient);
|
||||
}
|
||||
|
||||
pub async fn execute(matches: ArgMatches<'static>) {
|
||||
pub(crate) async fn execute(args: &Init) {
|
||||
println!("Initialising client...");
|
||||
|
||||
let id = matches.value_of("id").unwrap(); // required for now
|
||||
let provider_address = matches.value_of("provider").unwrap();
|
||||
let id = &args.id;
|
||||
let provider_address = &args.provider;
|
||||
|
||||
let already_init = if Config::default_config_file_path(Some(id)).exists() {
|
||||
println!("Socks5 client \"{}\" was already initialised before! Config information will be overwritten (but keys will be kept)!", id);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let already_init = Config::default_config_file_path(Some(id)).exists();
|
||||
if already_init {
|
||||
println!(
|
||||
"SOCKS5 client \"{}\" was already initialised before! \
|
||||
Config information will be overwritten (but keys will be kept)!",
|
||||
id
|
||||
);
|
||||
}
|
||||
|
||||
// Usually you only register with the gateway on the first init, however you can force
|
||||
// re-registering if wanted.
|
||||
let user_wants_force_register = args.force_register_gateway;
|
||||
|
||||
// If the client was already initialized, don't generate new keys and don't re-register with
|
||||
// the gateway (because this would create a new shared key).
|
||||
// Unless the user really wants to.
|
||||
let register_gateway = !already_init || user_wants_force_register;
|
||||
|
||||
// Attempt to use a user-provided gateway, if possible
|
||||
let user_chosen_gateway_id = args.gateway.as_deref();
|
||||
|
||||
let mut config = Config::new(id, provider_address);
|
||||
let override_config_fields = OverrideConfig::from(args.clone());
|
||||
config = override_config(config, override_config_fields);
|
||||
|
||||
let mut rng = OsRng;
|
||||
|
||||
// TODO: ideally that should be the last thing that's being done to config.
|
||||
// However, we are later further overriding it with gateway id
|
||||
config = override_config(config, &matches);
|
||||
if matches.is_present("fastmode") {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
|
||||
// if client was already initialised, don't generate new keys, not re-register with gateway
|
||||
// (because this would create new shared key)
|
||||
if !already_init {
|
||||
// create identity, encryption and ack keys.
|
||||
let mut key_manager = KeyManager::new(&mut rng);
|
||||
|
||||
let chosen_gateway_id = matches.value_of("gateway");
|
||||
|
||||
let gateway_details = gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
let shared_keys =
|
||||
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
|
||||
|
||||
config.get_base_mut().with_gateway_endpoint(
|
||||
gateway_details.identity_key.to_base58_string(),
|
||||
gateway_details.owner.clone(),
|
||||
gateway_details.clients_address(),
|
||||
);
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
key_manager
|
||||
.store_keys(&pathfinder)
|
||||
.expect("Failed to generated keys");
|
||||
println!("Saved all generated keys");
|
||||
}
|
||||
let gateway = setup_gateway(id, register_gateway, user_chosen_gateway_id, &config).await;
|
||||
config.get_base_mut().with_gateway_endpoint(gateway);
|
||||
|
||||
let config_save_location = config.get_config_file_save_location();
|
||||
config
|
||||
.save_to_file(None)
|
||||
.expect("Failed to save the config file");
|
||||
println!("Saved configuration file to {:?}", config_save_location);
|
||||
println!("Using gateway: {}", config.get_base().get_gateway_id(),);
|
||||
println!("Client configuration completed.\n\n\n");
|
||||
|
||||
show_address(&config);
|
||||
println!("Saved configuration file to {:?}", config_save_location);
|
||||
println!("Using gateway: {}", config.get_base().get_gateway_id());
|
||||
log::debug!("Gateway id: {}", config.get_base().get_gateway_id());
|
||||
log::debug!("Gateway owner: {}", config.get_base().get_gateway_owner());
|
||||
log::debug!(
|
||||
"Gateway listener: {}",
|
||||
config.get_base().get_gateway_listener()
|
||||
);
|
||||
println!("Client configuration completed.");
|
||||
|
||||
client_core::init::show_address(config.get_base());
|
||||
}
|
||||
|
||||
async fn setup_gateway(
|
||||
id: &str,
|
||||
register: bool,
|
||||
user_chosen_gateway_id: Option<&str>,
|
||||
config: &Config,
|
||||
) -> GatewayEndpoint {
|
||||
if register {
|
||||
// Get the gateway details by querying the validator-api. Either pick one at random or use
|
||||
// the chosen one if it's among the available ones.
|
||||
println!("Configuring gateway");
|
||||
let gateway = client_core::init::query_gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
user_chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
|
||||
// Registering with gateway by setting up and writing shared keys to disk
|
||||
log::trace!("Registering gateway");
|
||||
client_core::init::register_with_gateway_and_store_keys(gateway.clone(), config.get_base())
|
||||
.await;
|
||||
println!("Saved all generated keys");
|
||||
|
||||
gateway.into()
|
||||
} else if user_chosen_gateway_id.is_some() {
|
||||
// Just set the config, don't register or create any keys
|
||||
// This assumes that the user knows what they are doing, and that the existing keys are
|
||||
// valid for the gateway being used
|
||||
println!("Using gateway provided by user, keeping existing keys");
|
||||
let gateway = client_core::init::query_gateway_details(
|
||||
config.get_base().get_validator_api_endpoints(),
|
||||
user_chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
gateway.into()
|
||||
} else {
|
||||
println!("Not registering gateway, will reuse existing config and keys");
|
||||
match Config::load_from_file(Some(id)) {
|
||||
Ok(existing_config) => existing_config.get_base().get_gateway_endpoint().clone(),
|
||||
Err(err) => {
|
||||
panic!(
|
||||
"Unable to configure gateway: {err}. \n
|
||||
Seems like the client was already initialized but it was not possible to read \
|
||||
the existing configuration file. \n
|
||||
CAUTION: Consider backing up your gateway keys and try force gateway registration, or \
|
||||
removing the existing configuration and starting over."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::Config;
|
||||
use clap::ArgMatches;
|
||||
use clap::{Parser, Subcommand};
|
||||
use url::Url;
|
||||
|
||||
pub(crate) mod init;
|
||||
pub mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
pub(crate) const ENABLED_CREDENTIALS_MODE_ARG_NAME: &str = "enabled-credentials-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";
|
||||
@@ -21,7 +16,87 @@ pub(crate) const DEFAULT_ETH_ENDPOINT: &str =
|
||||
pub(crate) const DEFAULT_ETH_PRIVATE_KEY: &str =
|
||||
"0000000000000000000000000000000000000000000000000000000000000001";
|
||||
|
||||
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
fn long_version() -> String {
|
||||
format!(
|
||||
r#"
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
{:<20}{}
|
||||
"#,
|
||||
"Build Timestamp:",
|
||||
env!("VERGEN_BUILD_TIMESTAMP"),
|
||||
"Build Version:",
|
||||
env!("VERGEN_BUILD_SEMVER"),
|
||||
"Commit SHA:",
|
||||
env!("VERGEN_GIT_SHA"),
|
||||
"Commit Date:",
|
||||
env!("VERGEN_GIT_COMMIT_TIMESTAMP"),
|
||||
"Commit Branch:",
|
||||
env!("VERGEN_GIT_BRANCH"),
|
||||
"rustc Version:",
|
||||
env!("VERGEN_RUSTC_SEMVER"),
|
||||
"rustc Channel:",
|
||||
env!("VERGEN_RUSTC_CHANNEL"),
|
||||
"cargo Profile:",
|
||||
env!("VERGEN_CARGO_PROFILE"),
|
||||
)
|
||||
}
|
||||
|
||||
fn long_version_static() -> &'static str {
|
||||
Box::leak(long_version().into_boxed_str())
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author = "Nymtech", version, long_version = long_version_static(), about)]
|
||||
pub(crate) struct Cli {
|
||||
/// Path pointing to an env file that configures the client.
|
||||
#[clap(long)]
|
||||
pub(crate) config_env_file: Option<std::path::PathBuf>,
|
||||
|
||||
#[clap(subcommand)]
|
||||
command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum Commands {
|
||||
/// Initialise a Nym client. Do this first!
|
||||
Init(init::Init),
|
||||
/// Run the Nym client with provided configuration client optionally overriding set parameters
|
||||
Run(run::Run),
|
||||
/// Try to upgrade the client
|
||||
Upgrade(upgrade::Upgrade),
|
||||
}
|
||||
|
||||
// Configuration that can be overridden.
|
||||
pub(crate) struct OverrideConfig {
|
||||
validators: Option<String>,
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Option<String>,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: &Cli) {
|
||||
match &args.command {
|
||||
Commands::Init(m) => init::execute(m).await,
|
||||
Commands::Run(m) => run::execute(m).await,
|
||||
Commands::Upgrade(m) => upgrade::execute(m),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
raw.split(',')
|
||||
.map(|raw_validator| {
|
||||
raw_validator
|
||||
@@ -32,45 +107,58 @@ fn parse_validators(raw: &str) -> Vec<Url> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> Config {
|
||||
if let Some(raw_validators) = matches.value_of("validators") {
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
if let Some(raw_validators) = args.validators {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(parse_validators(raw_validators));
|
||||
.set_custom_validator_apis(parse_validators(&raw_validators));
|
||||
} else if let Ok(raw_validators) = std::env::var(network_defaults::var_names::API_VALIDATOR) {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(parse_validators(&raw_validators));
|
||||
}
|
||||
|
||||
if let Some(gateway_id) = matches.value_of("gateway") {
|
||||
config.get_base_mut().with_gateway_id(gateway_id);
|
||||
if let Some(port) = args.port {
|
||||
config = config.with_port(port);
|
||||
}
|
||||
|
||||
if let Some(port) = matches.value_of("port").map(|port| port.parse::<u16>()) {
|
||||
if let Err(err) = port {
|
||||
// if port was overridden, it must be parsable
|
||||
panic!("Invalid port value provided - {:?}", err);
|
||||
#[cfg(all(not(feature = "eth"), not(feature = "coconut")))]
|
||||
{
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT.to_string());
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
{
|
||||
if args.enabled_credentials_mode {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
}
|
||||
if let Some(eth_endpoint) = args.eth_endpoint {
|
||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||
}
|
||||
if let Some(eth_private_key) = args.eth_private_key {
|
||||
config.get_base_mut().with_eth_private_key(eth_private_key);
|
||||
}
|
||||
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 matches.is_present(ENABLED_CREDENTIALS_MODE_ARG_NAME) {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
if args.fastmode {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use clap::CommandFactory;
|
||||
|
||||
#[test]
|
||||
fn verify_cli() {
|
||||
Cli::command().debug_assert();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +1,80 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::Config;
|
||||
use crate::client::NymClient;
|
||||
use crate::commands::override_config;
|
||||
#[cfg(feature = "eth")]
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use crate::commands::{
|
||||
ENABLED_CREDENTIALS_MODE_ARG_NAME, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
|
||||
use crate::{
|
||||
client::{config::Config, NymClient},
|
||||
commands::{override_config, OverrideConfig},
|
||||
};
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
|
||||
use clap::Args;
|
||||
use config::NymConfig;
|
||||
use log::*;
|
||||
use version_checker::is_minor_version_compatible;
|
||||
|
||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
let app = App::new("run")
|
||||
.about("Run the Nym client with provided configuration client optionally overriding set parameters")
|
||||
.arg(Arg::with_name("id")
|
||||
.long("id")
|
||||
.help("Id of the nym-mixnet-client we want to run.")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
)
|
||||
// the rest of arguments are optional, they are used to override settings in config file
|
||||
.arg(Arg::with_name("config")
|
||||
.long("config")
|
||||
.help("Custom path to the nym-mixnet-client configuration file")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("provider")
|
||||
.long("provider")
|
||||
.help("Address of the socks5 provider to send messages to.")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("validators")
|
||||
.long("validators")
|
||||
.help("Comma separated list of rest endpoints of the validators")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(Arg::with_name("gateway")
|
||||
.long("gateway")
|
||||
.help("Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(Arg::with_name("port")
|
||||
.short("p")
|
||||
.long("port")
|
||||
.help("Port for the socket to listen on")
|
||||
.takes_value(true)
|
||||
);
|
||||
#[cfg(feature = "eth")]
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let app = app
|
||||
.arg(
|
||||
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||
.help("Set this client to work in a disabled credentials 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));
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Run {
|
||||
/// Id of the nym-mixnet-client we want to run.
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
|
||||
app
|
||||
/// Custom path to the nym-mixnet-client configuration file
|
||||
#[clap(long)]
|
||||
config: Option<String>,
|
||||
|
||||
/// Address of the socks5 provider to send messages to.
|
||||
#[clap(long)]
|
||||
provider: Option<String>,
|
||||
|
||||
/// Id of the gateway we want to connect to. If overridden, it is user's responsibility to
|
||||
/// ensure prior registration happened
|
||||
#[clap(long)]
|
||||
gateway: Option<String>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the validators
|
||||
#[clap(long)]
|
||||
validators: Option<String>,
|
||||
|
||||
/// Port for the socket to listen on
|
||||
#[clap(short, long)]
|
||||
port: Option<u16>,
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement. If this value is set, --eth-endpoint and
|
||||
/// --eth-private-key don't need to be set.
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long, conflicts_with_all = &["eth-endpoint", "eth-private-key"])]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// 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 --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_endpoint: Option<String>,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_private_key: Option<String>,
|
||||
}
|
||||
|
||||
impl From<Run> for OverrideConfig {
|
||||
fn from(run_config: Run) -> Self {
|
||||
OverrideConfig {
|
||||
validators: run_config.validators,
|
||||
port: run_config.port,
|
||||
fastmode: false,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: run_config.eth_private_key,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: run_config.eth_endpoint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this only checks compatibility between config the binary. It does not take into consideration
|
||||
@@ -76,8 +82,13 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||
fn version_check(cfg: &Config) -> bool {
|
||||
let binary_version = env!("CARGO_PKG_VERSION");
|
||||
let config_version = cfg.get_base().get_version();
|
||||
if binary_version != config_version {
|
||||
warn!("The mixnode binary has different version than what is specified in config file! {} and {}", binary_version, config_version);
|
||||
if binary_version == config_version {
|
||||
true
|
||||
} else {
|
||||
warn!(
|
||||
"The mixnode binary has different version than what is specified in config file! {} and {}",
|
||||
binary_version, config_version
|
||||
);
|
||||
if is_minor_version_compatible(binary_version, config_version) {
|
||||
info!("but they are still semver compatible. However, consider running the `upgrade` command");
|
||||
true
|
||||
@@ -85,13 +96,11 @@ fn version_check(cfg: &Config) -> bool {
|
||||
error!("and they are semver incompatible! - please run the `upgrade` command before attempting `run` again");
|
||||
false
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(matches: ArgMatches<'static>) {
|
||||
let id = matches.value_of("id").unwrap();
|
||||
pub(crate) async fn execute(args: &Run) {
|
||||
let id = &args.id;
|
||||
|
||||
let mut config = match Config::load_from_file(Some(id)) {
|
||||
Ok(cfg) => cfg,
|
||||
@@ -101,7 +110,8 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
}
|
||||
};
|
||||
|
||||
config = override_config(config, &matches);
|
||||
let override_config_fields = OverrideConfig::from(args.clone());
|
||||
config = override_config(config, override_config_fields);
|
||||
|
||||
if !version_check(&config) {
|
||||
error!("failed the local version check");
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::{Config, MISSING_VALUE};
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use config::defaults::default_api_endpoints;
|
||||
|
||||
use config::NymConfig;
|
||||
use std::fmt::Display;
|
||||
use std::process;
|
||||
use version_checker::Version;
|
||||
|
||||
use clap::Args;
|
||||
use std::{fmt::Display, process};
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
|
||||
print_failed_upgrade(from_version, to_version);
|
||||
@@ -49,14 +49,11 @@ fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> !
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
pub fn command_args<'a, 'b>() -> App<'a, 'b> {
|
||||
App::new("upgrade").about("Try to upgrade the client").arg(
|
||||
Arg::with_name("id")
|
||||
.long("id")
|
||||
.help("Id of the nym-client we want to upgrade")
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Upgrade {
|
||||
/// Id of the nym-client we want to upgrade
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
}
|
||||
|
||||
fn parse_config_version(config: &Config) -> Version {
|
||||
@@ -95,7 +92,7 @@ fn parse_package_version() -> Version {
|
||||
|
||||
fn minor_0_12_upgrade(
|
||||
mut config: Config,
|
||||
_matches: &ArgMatches<'_>,
|
||||
_args: &Upgrade,
|
||||
config_version: &Version,
|
||||
package_version: &Version,
|
||||
) -> Config {
|
||||
@@ -107,15 +104,6 @@ fn minor_0_12_upgrade(
|
||||
|
||||
print_start_upgrade(&config_version, &to_version);
|
||||
|
||||
println!(
|
||||
"Setting validator API endpoints to {:?}",
|
||||
default_api_endpoints()
|
||||
);
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(default_api_endpoints());
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_version(to_version.to_string().as_ref());
|
||||
@@ -131,30 +119,30 @@ fn minor_0_12_upgrade(
|
||||
config
|
||||
}
|
||||
|
||||
fn do_upgrade(mut config: Config, matches: &ArgMatches<'_>, package_version: Version) {
|
||||
fn do_upgrade(mut config: Config, args: &Upgrade, package_version: &Version) {
|
||||
loop {
|
||||
let config_version = parse_config_version(&config);
|
||||
|
||||
if config_version == package_version {
|
||||
if &config_version == package_version {
|
||||
println!("You're using the most recent version!");
|
||||
return;
|
||||
}
|
||||
|
||||
config = match config_version.major {
|
||||
0 => match config_version.minor {
|
||||
9 | 10 => outdated_upgrade(&config_version, &package_version),
|
||||
11 => minor_0_12_upgrade(config, matches, &config_version, &package_version),
|
||||
_ => unsupported_upgrade(&config_version, &package_version),
|
||||
9 | 10 => outdated_upgrade(&config_version, package_version),
|
||||
11 => minor_0_12_upgrade(config, args, &config_version, package_version),
|
||||
_ => unsupported_upgrade(&config_version, package_version),
|
||||
},
|
||||
_ => unsupported_upgrade(&config_version, &package_version),
|
||||
_ => unsupported_upgrade(&config_version, package_version),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(matches: &ArgMatches<'_>) {
|
||||
pub(crate) fn execute(args: &Upgrade) {
|
||||
let package_version = parse_package_version();
|
||||
|
||||
let id = matches.value_of("id").unwrap();
|
||||
let id = &args.id;
|
||||
|
||||
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
|
||||
eprintln!("failed to load existing config file! - {:?}", err);
|
||||
@@ -167,5 +155,5 @@ pub fn execute(matches: &ArgMatches<'_>) {
|
||||
}
|
||||
|
||||
// here be upgrade path to 0.9.X and beyond based on version number from config
|
||||
do_upgrade(existing_config, matches, package_version)
|
||||
do_upgrade(existing_config, args, &package_version)
|
||||
}
|
||||
|
||||
@@ -2,4 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod client;
|
||||
// This is only used as we reach into the init functions in nym-connect. We need to refactor the
|
||||
// init functions so that nym-connect can just call the same init function as the regular socks5
|
||||
// client.
|
||||
#[allow(unused)]
|
||||
pub mod commands;
|
||||
pub mod socks;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{crate_version, App, ArgMatches};
|
||||
use network_defaults::DEFAULT_NETWORK;
|
||||
use clap::{crate_version, Parser};
|
||||
use network_defaults::setup_env;
|
||||
|
||||
pub mod client;
|
||||
mod commands;
|
||||
@@ -10,34 +10,12 @@ pub mod socks;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
dotenv::dotenv().ok();
|
||||
setup_logging();
|
||||
println!("{}", banner());
|
||||
|
||||
let arg_matches = App::new("Nym Socks5 Proxy")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.author("Nymtech")
|
||||
.long_version(&*long_version())
|
||||
.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::run::command_args())
|
||||
.subcommand(commands::upgrade::command_args())
|
||||
.get_matches();
|
||||
|
||||
execute(arg_matches).await;
|
||||
}
|
||||
|
||||
async fn execute(matches: ArgMatches<'static>) {
|
||||
match matches.subcommand() {
|
||||
("init", Some(m)) => commands::init::execute(m.clone()).await,
|
||||
("run", Some(m)) => commands::run::execute(m.clone()).await,
|
||||
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
||||
_ => println!("{}", usage()),
|
||||
}
|
||||
}
|
||||
|
||||
fn usage() -> &'static str {
|
||||
"usage: --help to see available options.\n\n"
|
||||
let args = commands::Cli::parse();
|
||||
setup_env(args.config_env_file.clone());
|
||||
commands::execute(&args).await;
|
||||
}
|
||||
|
||||
fn banner() -> String {
|
||||
@@ -57,40 +35,6 @@ fn banner() -> String {
|
||||
)
|
||||
}
|
||||
|
||||
fn long_version() -> String {
|
||||
format!(
|
||||
r#"
|
||||
{:<20}{}
|
||||
{:<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"),
|
||||
"Network:",
|
||||
DEFAULT_NETWORK
|
||||
)
|
||||
}
|
||||
|
||||
fn setup_logging() {
|
||||
let mut log_builder = pretty_env_logger::formatted_timed_builder();
|
||||
if let Ok(s) = ::std::env::var("RUST_LOG") {
|
||||
|
||||
@@ -9,7 +9,7 @@ pub(crate) enum AuthenticationMethods {
|
||||
NoMethods = 0xFF,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
/// A socks5 user with a matching password.
|
||||
pub struct User {
|
||||
pub username: String,
|
||||
|
||||
@@ -5,7 +5,7 @@ use futures::StreamExt;
|
||||
use log::*;
|
||||
use nymsphinx::receiver::ReconstructedMessage;
|
||||
use proxy_helpers::connection_controller::{ControllerCommand, ControllerSender};
|
||||
use socks5_requests::Response;
|
||||
use socks5_requests::Message;
|
||||
|
||||
pub(crate) struct MixnetResponseListener {
|
||||
buffer_requester: ReceivedBufferRequestSender,
|
||||
@@ -44,12 +44,16 @@ impl MixnetResponseListener {
|
||||
warn!("this message had a surb - we didn't do anything with it");
|
||||
}
|
||||
|
||||
let response = match Response::try_from_bytes(&raw_message) {
|
||||
let response = match Message::try_from_bytes(&raw_message) {
|
||||
Err(err) => {
|
||||
warn!("failed to parse received response - {:?}", err);
|
||||
return;
|
||||
}
|
||||
Ok(data) => data,
|
||||
Ok(Message::Request(_)) => {
|
||||
warn!("unexpected request");
|
||||
return;
|
||||
}
|
||||
Ok(Message::Response(data)) => data,
|
||||
};
|
||||
|
||||
self.controller_sender
|
||||
|
||||
@@ -1 +1 @@
|
||||
15.0.1
|
||||
16
|
||||
@@ -31,9 +31,12 @@
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-mocha": "^10.0.3",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"expect": "^28.1.3",
|
||||
"mocha": "^10.0.0",
|
||||
"prettier": "^2.5.1",
|
||||
"typedoc": "^0.22.13",
|
||||
"typescript": "^4.1.3"
|
||||
"ts-mocha": "^10.0.0",
|
||||
"typescript": "^4.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cosmjs/cosmwasm-stargate": "^0.28.0",
|
||||
|
||||
@@ -64,23 +64,20 @@ export default class ValidatorClient implements INymClient {
|
||||
|
||||
readonly vestingContract: string;
|
||||
|
||||
readonly mainnetDenom = "unym";
|
||||
readonly mainnetDenom = 'unym';
|
||||
|
||||
readonly mainnetPrefix = "n";
|
||||
readonly mainnetPrefix = 'n';
|
||||
|
||||
private constructor(
|
||||
client: SigningClient | QueryClient,
|
||||
prefix: string,
|
||||
mixnetContract: string,
|
||||
vestingContract: string
|
||||
vestingContract: string,
|
||||
denom: string,
|
||||
) {
|
||||
this.client = client;
|
||||
this.prefix = prefix;
|
||||
if (prefix == this.mainnetPrefix) {
|
||||
this.denom = this.mainnetDenom;
|
||||
} else {
|
||||
this.denom = `u${prefix}`;
|
||||
}
|
||||
this.denom = `u${denom}`;
|
||||
|
||||
this.mixnetContract = mixnetContract;
|
||||
this.vestingContract = vestingContract;
|
||||
@@ -93,11 +90,12 @@ export default class ValidatorClient implements INymClient {
|
||||
prefix: string,
|
||||
mixnetContract: string,
|
||||
vestingContract: string,
|
||||
denom: string,
|
||||
): Promise<ValidatorClient> {
|
||||
const wallet = await ValidatorClient.buildWallet(mnemonic, prefix);
|
||||
|
||||
const signingClient = await SigningClient.connectWithNymSigner(wallet, nymdUrl, validatorApiUrl, prefix);
|
||||
return new ValidatorClient(signingClient, prefix, mixnetContract, vestingContract);
|
||||
const signingClient = await SigningClient.connectWithNymSigner(wallet, nymdUrl, validatorApiUrl, prefix, denom);
|
||||
return new ValidatorClient(signingClient, prefix, mixnetContract, vestingContract, denom);
|
||||
}
|
||||
|
||||
static async connectForQuery(
|
||||
@@ -106,9 +104,10 @@ export default class ValidatorClient implements INymClient {
|
||||
prefix: string,
|
||||
mixnetContract: string,
|
||||
vestingContract: string,
|
||||
denom: string,
|
||||
): Promise<ValidatorClient> {
|
||||
const queryClient = await QueryClient.connectWithNym(nymdUrl, validatorApiUrl);
|
||||
return new ValidatorClient(queryClient, prefix, mixnetContract, vestingContract);
|
||||
return new ValidatorClient(queryClient, prefix, mixnetContract, vestingContract, denom);
|
||||
}
|
||||
|
||||
public get address(): string {
|
||||
@@ -457,4 +456,4 @@ export default class ValidatorClient implements INymClient {
|
||||
this.assertSigning();
|
||||
return (this.client as ISigningClient).updateContractStateParams(this.mixnetContract, newParams, fee, memo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,10 +221,12 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
nymdUrl: string,
|
||||
validatorApiUrl: string,
|
||||
prefix: string,
|
||||
denom: string,
|
||||
): Promise<SigningClient> {
|
||||
const [{ address }] = await wallet.getAccounts();
|
||||
const signerOptions: SigningCosmWasmClientOptions = {
|
||||
gasPrice: nymGasPrice(prefix),
|
||||
prefix,
|
||||
gasPrice: nymGasPrice(denom),
|
||||
};
|
||||
const tmClient = await Tendermint34Client.connect(nymdUrl);
|
||||
return new SigningClient(address, validatorApiUrl, tmClient, wallet, signerOptions);
|
||||
|
||||
@@ -7,13 +7,12 @@ const mainnetDenom = 'nym';
|
||||
export function nymGasPrice(prefix: string): GasPrice {
|
||||
if (typeof prefix === 'string') {
|
||||
if (prefix === mainnetPrefix) {
|
||||
prefix = mainnetDenom;
|
||||
return GasPrice.fromString(`0.025u${mainnetDenom}`);
|
||||
}
|
||||
return GasPrice.fromString(`0.025u${prefix}`); // TODO: ideally this ugly conversion shouldn't be hardcoded here.
|
||||
}
|
||||
else {
|
||||
throw new Error(`${prefix} is not of type string`);
|
||||
}
|
||||
|
||||
throw new Error(`${prefix} is not of type string`);
|
||||
}
|
||||
|
||||
export const downloadWasm = async (url: string): Promise<Uint8Array> => {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import ValidatorClient from '../../dist';
|
||||
import expect from 'expect';
|
||||
|
||||
describe('Query: balances', () => {
|
||||
it('can query for an account balance', async () => {
|
||||
const client = await ValidatorClient.connectForQuery(
|
||||
'https://rpc.nyx.nodes.guru/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
|
||||
const balance = await client.getBalance('n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy');
|
||||
expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
|
||||
}).timeout(5000);
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
import ValidatorClient from '../../dist';
|
||||
import expect from 'expect';
|
||||
|
||||
// TODO: implement for QA with .env for mnemonics
|
||||
// describe('Sign: send', () => {
|
||||
// it('can send tokens', async () => {
|
||||
// const client = await ValidatorClient.connect(
|
||||
// '<ADD MNEMONIC HERE>',
|
||||
// 'https://rpc.nyx.nodes.guru/', 'https://validator.nymtech.net/api/', 'n', 'n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g', 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw', 'nym');
|
||||
// await client.send('<ADD ADDRESS HERE>')
|
||||
// const balance = await client.getBalance('n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy');
|
||||
// expect(Number.parseFloat(balance.amount)).toBeGreaterThan(0);
|
||||
// }).timeout(5000);
|
||||
// })
|
||||
@@ -5,7 +5,8 @@
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist"
|
||||
"outDir": "./dist",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"typedocOptions": {
|
||||
"entryPoints": [
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -24,7 +24,8 @@
|
||||
},
|
||||
"../pkg": {
|
||||
"name": "@nymproject/nym-client-wasm",
|
||||
"version": "0.0.1"
|
||||
"version": "1.0.1",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@discoveryjs/json-ext": {
|
||||
"version": "0.5.7",
|
||||
|
||||
@@ -5,7 +5,7 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// Serializable structures for what we find in common/crypto
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct PublicKey([u8; 32]);
|
||||
|
||||
impl PublicKey {
|
||||
@@ -24,7 +24,7 @@ impl AsRef<[u8]> for PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct Signature([u8; 32], [u8; 32]);
|
||||
|
||||
impl Signature {
|
||||
|
||||
@@ -7,16 +7,16 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::keys::PublicKey;
|
||||
use crate::payment::LinkPaymentData;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct InstantiateMsg {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
LinkPayment { data: LinkPaymentData },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum QueryMsg {
|
||||
GetPayments {
|
||||
@@ -25,6 +25,6 @@ pub enum QueryMsg {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {}
|
||||
|
||||
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::keys::{PublicKey, Signature};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct Payment {
|
||||
verification_key: PublicKey,
|
||||
gateway_identity: PublicKey,
|
||||
@@ -27,7 +27,7 @@ impl Payment {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct LinkPaymentData {
|
||||
pub verification_key: PublicKey,
|
||||
pub gateway_identity: PublicKey,
|
||||
@@ -51,7 +51,7 @@ impl LinkPaymentData {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PagedPaymentResponse {
|
||||
pub payments: Vec<Payment>,
|
||||
pub per_page: usize,
|
||||
|
||||
@@ -74,4 +74,4 @@ features = ["js"]
|
||||
[features]
|
||||
coconut = ["gateway-requests/coconut", "coconut-interface", "validator-client", "credentials/coconut"]
|
||||
wasm = ["web3/wasm", "web3/http", "web3/signing"]
|
||||
default = ["web3/default"]
|
||||
default = ["web3/default"]
|
||||
|
||||
@@ -1,47 +1,49 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::wasm_storage::{Storage, StorageError};
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_interface::Base58;
|
||||
#[cfg(feature = "coconut")]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use credential_storage::error::StorageError;
|
||||
use crate::wasm_storage::Storage;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use credential_storage::storage::Storage;
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", feature = "coconut"))]
|
||||
use crate::wasm_storage::StorageError;
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "coconut"))]
|
||||
use credential_storage::error::StorageError;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
use credentials::coconut::{
|
||||
bandwidth::prepare_for_spending, utils::obtain_aggregate_verification_key,
|
||||
};
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use credentials::token::bandwidth::TokenCredential;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use crypto::asymmetric::identity;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use network_defaults::{
|
||||
eth_contract::ETH_ERC20_JSON_ABI, eth_contract::ETH_JSON_ABI, BANDWIDTH_VALUE,
|
||||
ETH_BURN_FUNCTION_NAME, ETH_CONTRACT_ADDRESS, ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
ETH_ERC20_CONTRACT_ADDRESS, ETH_MIN_BLOCK_DEPTH, TOKENS_TO_BURN, UTOKENS_TO_BURN,
|
||||
};
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use pemstore::traits::PemStorableKeyPair;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use rand::rngs::OsRng;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use secp256k1::SecretKey;
|
||||
use std::str::FromStr;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use web3::{
|
||||
contract::{Contract, Options},
|
||||
ethabi::Token,
|
||||
signing::{Key, SecretKeyRef},
|
||||
transports::Http,
|
||||
types::{Address, U256, U64},
|
||||
Web3,
|
||||
use {
|
||||
coconut_interface::Base58,
|
||||
credentials::coconut::{
|
||||
bandwidth::prepare_for_spending, utils::obtain_aggregate_verification_key,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use {
|
||||
credentials::token::bandwidth::TokenCredential,
|
||||
crypto::asymmetric::identity,
|
||||
network_defaults::{
|
||||
eth_contract::ETH_ERC20_JSON_ABI, eth_contract::ETH_JSON_ABI, BANDWIDTH_VALUE,
|
||||
ETH_BURN_FUNCTION_NAME, ETH_CONTRACT_ADDRESS, ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
ETH_ERC20_CONTRACT_ADDRESS, ETH_MIN_BLOCK_DEPTH, TOKENS_TO_BURN, UTOKENS_TO_BURN,
|
||||
},
|
||||
pemstore::traits::PemStorableKeyPair,
|
||||
rand::rngs::OsRng,
|
||||
secp256k1::SecretKey,
|
||||
web3::{
|
||||
contract::{Contract, Options},
|
||||
ethabi::Token,
|
||||
signing::{Key, SecretKeyRef},
|
||||
transports::Http,
|
||||
types::{Address, U256, U64},
|
||||
Web3,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
|
||||
@@ -83,7 +83,7 @@ impl GatewayClient {
|
||||
) -> Self {
|
||||
GatewayClient {
|
||||
authenticated: false,
|
||||
disabled_credentials_mode: false,
|
||||
disabled_credentials_mode: true,
|
||||
bandwidth_remaining: 0,
|
||||
gateway_address,
|
||||
gateway_identity,
|
||||
@@ -100,8 +100,8 @@ impl GatewayClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_disabled_credentials_mode(&mut self, _disabled_credentials_mode: bool) {
|
||||
self.disabled_credentials_mode = false;
|
||||
pub fn set_disabled_credentials_mode(&mut self, disabled_credentials_mode: bool) {
|
||||
self.disabled_credentials_mode = disabled_credentials_mode;
|
||||
}
|
||||
|
||||
// TODO: later convert into proper builder methods
|
||||
|
||||
@@ -27,7 +27,7 @@ futures = "0.3"
|
||||
|
||||
coconut-interface = { path = "../../coconut-interface" }
|
||||
network-defaults = { path = "../../network-defaults" }
|
||||
validator-api-requests = { path = "../../../validator-api/validator-api-requests" }
|
||||
validator-api-requests = { path = "../../../validator-api/validator-api-requests", features = ["coconut"] }
|
||||
|
||||
# required for nymd-client
|
||||
# at some point it might be possible to make it wasm-compatible
|
||||
@@ -35,12 +35,12 @@ validator-api-requests = { path = "../../../validator-api/validator-api-requests
|
||||
async-trait = { version = "0.1.51", optional = true }
|
||||
bip39 = { version = "1", features = ["rand"], optional = true }
|
||||
config = { path = "../../config", optional = true }
|
||||
cosmrs = { version = "0.7.0", features = ["rpc", "bip32", "cosmwasm"], optional = true}
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true}
|
||||
prost = { version = "0.10", default-features = false, optional = true }
|
||||
flate2 = { version = "1.0.20", optional = true }
|
||||
sha2 = { version = "0.9.5", optional = true }
|
||||
itertools = { version = "0.10", optional = true }
|
||||
cosmwasm-std = { version = "1.0.0-beta8", optional = true }
|
||||
cosmwasm-std = { version = "1.0.0", optional = true }
|
||||
execute = { path = "../../execute" }
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -58,3 +58,5 @@ nymd-client = [
|
||||
"itertools",
|
||||
"cosmwasm-std",
|
||||
]
|
||||
generate-ts = []
|
||||
|
||||
|
||||
@@ -2,27 +2,23 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{validator_api, ValidatorClientError};
|
||||
use coconut_interface::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, ExecuteReleaseFundsRequestBody,
|
||||
ProposeReleaseFundsRequestBody, ProposeReleaseFundsResponse, VerificationKeyResponse,
|
||||
VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||
use url::Url;
|
||||
|
||||
use validator_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
|
||||
VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use validator_api_requests::models::{
|
||||
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse,
|
||||
};
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use validator_api_requests::models::{MixNodeBondAnnotated, UptimeResponse};
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use network_defaults::DEFAULT_NETWORK;
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use crate::nymd::{
|
||||
error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient,
|
||||
self, error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient,
|
||||
};
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
@@ -32,18 +28,18 @@ use mixnet_contract_common::{
|
||||
RewardedSetUpdateDetails,
|
||||
};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use network_defaults::NymNetworkDetails;
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
#[must_use]
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
network: network_defaults::all::Network,
|
||||
api_url: Url,
|
||||
nymd_url: Url,
|
||||
mixnet_contract_address: cosmrs::AccountId,
|
||||
vesting_contract_address: cosmrs::AccountId,
|
||||
bandwidth_claim_contract_address: cosmrs::AccountId,
|
||||
|
||||
nymd_config: nymd::Config,
|
||||
|
||||
mixnode_page_limit: Option<u32>,
|
||||
gateway_page_limit: Option<u32>,
|
||||
@@ -53,42 +49,44 @@ pub struct Config {
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
impl Config {
|
||||
pub fn new(network: network_defaults::all::Network, nymd_url: Url, api_url: Url) -> Self {
|
||||
Config {
|
||||
network,
|
||||
nymd_url,
|
||||
mixnet_contract_address: DEFAULT_NETWORK
|
||||
.mixnet_contract_address()
|
||||
pub fn try_from_nym_network_details(
|
||||
details: &NymNetworkDetails,
|
||||
) -> Result<Self, ValidatorClientError> {
|
||||
let mut api_url = details
|
||||
.endpoints
|
||||
.iter()
|
||||
.filter_map(|d| d.api_url.as_ref())
|
||||
.map(|url| Url::parse(url))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
if api_url.is_empty() {
|
||||
return Err(ValidatorClientError::NoAPIUrlAvailable);
|
||||
}
|
||||
|
||||
Ok(Config {
|
||||
api_url: api_url.pop().unwrap(),
|
||||
nymd_url: details.endpoints[0]
|
||||
.nymd_url
|
||||
.parse()
|
||||
.expect("Error parsing mixnet contract address"),
|
||||
vesting_contract_address: DEFAULT_NETWORK
|
||||
.vesting_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing vesting contract address"),
|
||||
bandwidth_claim_contract_address: DEFAULT_NETWORK
|
||||
.bandwidth_claim_contract_address()
|
||||
.parse()
|
||||
.expect("Error parsing bandwidth claim contract address"),
|
||||
api_url,
|
||||
.map_err(ValidatorClientError::MalformedUrlProvided)?,
|
||||
nymd_config: nymd::Config::try_from_nym_network_details(details)?,
|
||||
mixnode_page_limit: None,
|
||||
gateway_page_limit: None,
|
||||
mixnode_delegations_page_limit: None,
|
||||
rewarded_set_page_limit: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_mixnode_contract_address(mut self, address: cosmrs::AccountId) -> Self {
|
||||
self.mixnet_contract_address = address;
|
||||
// TODO: this method shouldn't really exist as all information should be included immediately
|
||||
// via `from_nym_network_details`, but it's here for, you guessed it, legacy compatibility
|
||||
pub fn with_urls(mut self, nymd_url: Url, api_url: Url) -> Self {
|
||||
self.nymd_url = nymd_url;
|
||||
self.api_url = api_url;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_vesting_contract_address(mut self, address: cosmrs::AccountId) -> Self {
|
||||
self.vesting_contract_address = address;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_bandwidth_claim_contract_address(mut self, address: cosmrs::AccountId) -> Self {
|
||||
self.bandwidth_claim_contract_address = address;
|
||||
pub fn with_nymd_url(mut self, nymd_url: Url) -> Self {
|
||||
self.nymd_url = nymd_url;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -115,10 +113,8 @@ impl Config {
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
pub struct Client<C> {
|
||||
pub network: network_defaults::all::Network,
|
||||
mixnet_contract_address: cosmrs::AccountId,
|
||||
vesting_contract_address: cosmrs::AccountId,
|
||||
bandwidth_claim_contract_address: cosmrs::AccountId,
|
||||
// TODO: we really shouldn't be storing a mnemonic here, but removing it would be
|
||||
// non-trivial amount of work and it's out of scope of the current branch
|
||||
mnemonic: Option<bip39::Mnemonic>,
|
||||
|
||||
mixnode_page_limit: Option<u32>,
|
||||
@@ -139,25 +135,18 @@ impl Client<SigningNymdClient> {
|
||||
) -> Result<Client<SigningNymdClient>, ValidatorClientError> {
|
||||
let validator_api_client = validator_api::Client::new(config.api_url.clone());
|
||||
let nymd_client = NymdClient::connect_with_mnemonic(
|
||||
config.network,
|
||||
config.nymd_config.clone(),
|
||||
config.nymd_url.as_str(),
|
||||
mnemonic.clone(),
|
||||
None,
|
||||
)?
|
||||
.with_mixnet_contract_address(config.mixnet_contract_address.clone())
|
||||
.with_vesting_contract_address(config.vesting_contract_address.clone())
|
||||
.with_bandwidth_claim_contract_address(config.bandwidth_claim_contract_address.clone());
|
||||
)?;
|
||||
|
||||
Ok(Client {
|
||||
network: config.network,
|
||||
mixnet_contract_address: config.mixnet_contract_address,
|
||||
vesting_contract_address: config.vesting_contract_address,
|
||||
bandwidth_claim_contract_address: config.bandwidth_claim_contract_address,
|
||||
mnemonic: Some(mnemonic),
|
||||
mixnode_page_limit: config.mixnode_page_limit,
|
||||
gateway_page_limit: config.gateway_page_limit,
|
||||
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
|
||||
rewarded_set_page_limit: None,
|
||||
rewarded_set_page_limit: config.rewarded_set_page_limit,
|
||||
validator_api: validator_api_client,
|
||||
nymd: nymd_client,
|
||||
})
|
||||
@@ -165,14 +154,11 @@ impl Client<SigningNymdClient> {
|
||||
|
||||
pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
|
||||
self.nymd = NymdClient::connect_with_mnemonic(
|
||||
self.network,
|
||||
self.nymd.current_config().clone(),
|
||||
new_endpoint.as_ref(),
|
||||
self.mnemonic.clone().unwrap(),
|
||||
None,
|
||||
)?
|
||||
.with_mixnet_contract_address(self.mixnet_contract_address.clone())
|
||||
.with_vesting_contract_address(self.vesting_contract_address.clone())
|
||||
.with_bandwidth_claim_contract_address(self.bandwidth_claim_contract_address.clone());
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -185,15 +171,10 @@ impl Client<SigningNymdClient> {
|
||||
impl Client<QueryNymdClient> {
|
||||
pub fn new_query(config: Config) -> Result<Client<QueryNymdClient>, ValidatorClientError> {
|
||||
let validator_api_client = validator_api::Client::new(config.api_url.clone());
|
||||
let nymd_client = NymdClient::connect(config.nymd_url.as_str())?
|
||||
.with_mixnet_contract_address(config.mixnet_contract_address.clone())
|
||||
.with_vesting_contract_address(config.vesting_contract_address.clone());
|
||||
let nymd_client =
|
||||
NymdClient::connect(config.nymd_config.clone(), config.nymd_url.as_str())?;
|
||||
|
||||
Ok(Client {
|
||||
network: config.network,
|
||||
mixnet_contract_address: config.mixnet_contract_address,
|
||||
vesting_contract_address: config.vesting_contract_address,
|
||||
bandwidth_claim_contract_address: config.bandwidth_claim_contract_address,
|
||||
mnemonic: None,
|
||||
mixnode_page_limit: config.mixnode_page_limit,
|
||||
gateway_page_limit: config.gateway_page_limit,
|
||||
@@ -205,10 +186,7 @@ impl Client<QueryNymdClient> {
|
||||
}
|
||||
|
||||
pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
|
||||
self.nymd = NymdClient::connect(new_endpoint.as_ref())?
|
||||
.with_mixnet_contract_address(self.mixnet_contract_address.clone())
|
||||
.with_vesting_contract_address(self.vesting_contract_address.clone())
|
||||
.with_bandwidth_claim_contract_address(self.bandwidth_claim_contract_address.clone());
|
||||
self.nymd = NymdClient::connect(self.nymd.current_config().clone(), new_endpoint.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -222,11 +200,12 @@ impl<C> Client<C> {
|
||||
// use case: somebody initialised client without a contract in order to upload and initialise one
|
||||
// and now they want to actually use it without making new client
|
||||
pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) {
|
||||
self.mixnet_contract_address = mixnet_contract_address
|
||||
self.nymd
|
||||
.set_mixnet_contract_address(mixnet_contract_address)
|
||||
}
|
||||
|
||||
pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId {
|
||||
self.mixnet_contract_address.clone()
|
||||
self.nymd.mixnet_contract_address().clone()
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
@@ -318,6 +297,13 @@ impl<C> Client<C> {
|
||||
Ok(self.nymd.get_current_epoch().await?)
|
||||
}
|
||||
|
||||
pub async fn get_current_operator_cost(&self) -> Result<u64, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_current_operator_cost().await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnet_contract_version(&self) -> Result<MixnetContractVersion, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
@@ -734,6 +720,10 @@ impl ApiClient {
|
||||
Ok(self.validator_api.get_coconut_verification_key().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cosmos_address(&self) -> Result<CosmosAddressResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_cosmos_address().await?)
|
||||
}
|
||||
|
||||
pub async fn verify_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
@@ -743,24 +733,4 @@ impl ApiClient {
|
||||
.verify_bandwidth_credential(request_body)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn propose_release_funds(
|
||||
&self,
|
||||
request_body: &ProposeReleaseFundsRequestBody,
|
||||
) -> Result<ProposeReleaseFundsResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.propose_release_funds(request_body)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn execute_release_funds(
|
||||
&self,
|
||||
request_body: &ExecuteReleaseFundsRequestBody,
|
||||
) -> Result<(), ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.execute_release_funds(request_body)
|
||||
.await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::{NymdClient, QueryNymdClient};
|
||||
use crate::nymd::{Config as ClientConfig, NymdClient, QueryNymdClient};
|
||||
use crate::ApiClient;
|
||||
use network_defaults::all::Network;
|
||||
|
||||
@@ -54,10 +54,20 @@ fn setup_connection_tests<H: BuildHasher + 'static>(
|
||||
.get(&network)
|
||||
.expect("No configured contract address")
|
||||
.clone();
|
||||
NymdClient::<QueryNymdClient>::connect(url.as_str())
|
||||
.map(|client| client.with_mixnet_contract_address(address))
|
||||
.map(move |client| ClientForConnectionTest::Nymd(network, url, Box::new(client)))
|
||||
.ok()
|
||||
let config = ClientConfig::try_from_nym_network_details(&network.details())
|
||||
.expect("failed to create valid nymd client config");
|
||||
|
||||
if let Ok(mut client) = NymdClient::<QueryNymdClient>::connect(config, url.as_str()) {
|
||||
// possibly redundant, but lets just leave it here
|
||||
client.set_mixnet_contract_address(address);
|
||||
Some(ClientForConnectionTest::Nymd(
|
||||
network,
|
||||
url,
|
||||
Box::new(client),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let api_connection_test_clients = api_urls.map(|(network, url)| {
|
||||
@@ -76,7 +86,7 @@ fn extract_and_collect_results_into_map(
|
||||
.filter(|c| &c.url_type() == url_type)
|
||||
.map(|c| {
|
||||
let (network, url, result) = c.result();
|
||||
(*network, (url.clone(), *result))
|
||||
(network.clone(), (url.clone(), *result))
|
||||
})
|
||||
.into_group_map()
|
||||
}
|
||||
|
||||
@@ -18,4 +18,7 @@ pub enum ValidatorClientError {
|
||||
#[cfg(feature = "nymd-client")]
|
||||
#[error("There was an issue with the Nymd client - {0}")]
|
||||
NymdError(#[from] crate::nymd::error::NymdError),
|
||||
|
||||
#[error("No validator API url has been provided")]
|
||||
NoAPIUrlAvailable,
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ use std::fmt;
|
||||
pub use cosmrs::Coin as CosmosCoin;
|
||||
pub use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq, Eq)]
|
||||
pub struct MismatchedDenoms;
|
||||
|
||||
// the reason the coin is created here as opposed to different place in the codebase is that
|
||||
// eventually we want to either publish the cosmwasm client separately or commit it to
|
||||
// some other project, like cosmrs. Either way, in that case we can't really have
|
||||
// a dependency on an internal type
|
||||
#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq)]
|
||||
pub struct Coin {
|
||||
pub amount: u128,
|
||||
pub denom: String,
|
||||
|
||||
@@ -26,7 +26,7 @@ use cosmrs::rpc::{self, HttpClient, Order};
|
||||
use cosmrs::tendermint::abci::Code as AbciCode;
|
||||
use cosmrs::tendermint::abci::Transaction;
|
||||
use cosmrs::tendermint::{abci, block, chain};
|
||||
use cosmrs::{tx, AccountId, Coin as CosmosCoin, Denom, Tx};
|
||||
use cosmrs::{tx, AccountId, Coin as CosmosCoin, Tx};
|
||||
use prost::Message;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
@@ -121,7 +121,7 @@ pub trait CosmWasmClient: rpc::Client {
|
||||
async fn get_balance(
|
||||
&self,
|
||||
address: &AccountId,
|
||||
search_denom: Denom,
|
||||
search_denom: String,
|
||||
) -> Result<Option<Coin>, NymdError> {
|
||||
let path = Some("/cosmos.bank.v1beta1.Query/Balance".parse().unwrap());
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
use crate::nymd::error::NymdError;
|
||||
use cosmrs::tendermint::abci;
|
||||
use itertools::Itertools;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// it seems that currently validators just emit stringified events (which are also returned as part of deliverTx response)
|
||||
// as theirs logs
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Log {
|
||||
#[serde(default)]
|
||||
// weird thing is that the first msg_index seems to always be undefined on the raw logs
|
||||
|
||||
@@ -8,10 +8,13 @@ use crate::nymd::cosmwasm_client::types::*;
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER};
|
||||
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
||||
use crate::nymd::{Coin, GasPrice, TxResponse};
|
||||
use crate::nymd::{Coin, GasAdjustable, GasPrice, TxResponse};
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::bank::MsgSend;
|
||||
use cosmrs::distribution::MsgWithdrawDelegatorReward;
|
||||
use cosmrs::feegrant::{
|
||||
AllowedMsgAllowance, BasicAllowance, MsgGrantAllowance, MsgRevokeAllowance,
|
||||
};
|
||||
use cosmrs::proto::cosmos::tx::signing::v1beta1::SignMode;
|
||||
use cosmrs::rpc::endpoint::broadcast;
|
||||
use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest};
|
||||
@@ -23,7 +26,7 @@ use serde::Serialize;
|
||||
use sha2::Digest;
|
||||
use sha2::Sha256;
|
||||
use std::convert::TryInto;
|
||||
use std::time::Duration;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4);
|
||||
const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
@@ -420,6 +423,63 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
||||
.check_response()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn grant_allowance(
|
||||
&self,
|
||||
granter: &AccountId,
|
||||
grantee: &AccountId,
|
||||
spend_limit: Vec<Coin>,
|
||||
expiration: Option<SystemTime>,
|
||||
allowed_messages: Vec<String>,
|
||||
fee: Fee,
|
||||
memo: impl Into<String> + Send + 'static,
|
||||
) -> Result<TxResponse, NymdError> {
|
||||
let basic_allowance = BasicAllowance {
|
||||
spend_limit: spend_limit.into_iter().map(Into::into).collect(),
|
||||
expiration,
|
||||
}
|
||||
.to_any()
|
||||
.map_err(|_| NymdError::SerializationError("BasicAllowance".to_owned()))?;
|
||||
|
||||
let allowed_msg_allowance = AllowedMsgAllowance {
|
||||
allowance: Some(basic_allowance),
|
||||
allowed_messages,
|
||||
}
|
||||
.to_any()
|
||||
.map_err(|_| NymdError::SerializationError("AllowedMsgAllowance".to_owned()))?;
|
||||
|
||||
let grant_allowance_msg = MsgGrantAllowance {
|
||||
granter: granter.to_owned(),
|
||||
grantee: grantee.to_owned(),
|
||||
allowance: Some(allowed_msg_allowance),
|
||||
}
|
||||
.to_any()
|
||||
.map_err(|_| NymdError::SerializationError("MsgGrantAllowance".to_owned()))?;
|
||||
|
||||
self.sign_and_broadcast(granter, vec![grant_allowance_msg], fee, memo)
|
||||
.await?
|
||||
.check_response()
|
||||
}
|
||||
|
||||
async fn revoke_allowance(
|
||||
&self,
|
||||
granter: &AccountId,
|
||||
grantee: &AccountId,
|
||||
fee: Fee,
|
||||
memo: impl Into<String> + Send + 'static,
|
||||
) -> Result<TxResponse, NymdError> {
|
||||
let revoke_allowance_msg = MsgRevokeAllowance {
|
||||
granter: granter.to_owned(),
|
||||
grantee: grantee.to_owned(),
|
||||
}
|
||||
.to_any()
|
||||
.map_err(|_| NymdError::SerializationError("MsgRevokeAllowance".to_owned()))?;
|
||||
|
||||
self.sign_and_broadcast(granter, vec![revoke_allowance_msg], fee, memo)
|
||||
.await?
|
||||
.check_response()
|
||||
}
|
||||
|
||||
async fn delegate_tokens(
|
||||
&self,
|
||||
delegator_address: &AccountId,
|
||||
@@ -490,28 +550,35 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
||||
fee: Fee,
|
||||
memo: &String,
|
||||
) -> Result<tx::Fee, NymdError> {
|
||||
let auto_fee = |multiplier: Option<f32>| async move {
|
||||
debug!("Trying to simulate gas costs...");
|
||||
// from what I've seen in manual testing, gas estimation does not exist if transaction
|
||||
// fails to get executed (for example if you send 'BondMixnode" with invalid signature)
|
||||
let gas_estimation = self
|
||||
.simulate(signer_address, messages.to_vec(), memo.clone())
|
||||
.await?
|
||||
.gas_info
|
||||
.ok_or(NymdError::GasEstimationFailure)?
|
||||
.gas_used;
|
||||
|
||||
let multiplier = multiplier.unwrap_or(DEFAULT_SIMULATED_GAS_MULTIPLIER);
|
||||
let gas = gas_estimation.adjust_gas(multiplier);
|
||||
|
||||
debug!("Gas estimation: {}", gas_estimation);
|
||||
debug!("Multiplying the estimation by {}", multiplier);
|
||||
debug!("Final gas limit used: {}", gas);
|
||||
|
||||
let fee = self.gas_price() * gas;
|
||||
Ok::<tx::Fee, NymdError>(tx::Fee::from_amount_and_gas(fee, gas))
|
||||
};
|
||||
let fee = match fee {
|
||||
Fee::Manual(fee) => fee,
|
||||
Fee::Auto(multiplier) => {
|
||||
debug!("Trying to simulate gas costs...");
|
||||
// from what I've seen in manual testing, gas estimation does not exist if transaction
|
||||
// fails to get executed (for example if you send 'BondMixnode" with invalid signature)
|
||||
let gas_estimation = self
|
||||
.simulate(signer_address, messages.to_vec(), memo.clone())
|
||||
.await?
|
||||
.gas_info
|
||||
.ok_or(NymdError::GasEstimationFailure)?
|
||||
.gas_used;
|
||||
|
||||
let multiplier = multiplier.unwrap_or(DEFAULT_SIMULATED_GAS_MULTIPLIER);
|
||||
let gas = ((gas_estimation.value() as f32 * multiplier) as u64).into();
|
||||
|
||||
debug!("Gas estimation: {}", gas_estimation);
|
||||
debug!("Multiplying the estimation by {}", multiplier);
|
||||
debug!("Final gas limit used: {}", gas);
|
||||
|
||||
let fee = self.gas_price() * gas;
|
||||
tx::Fee::from_amount_and_gas(fee, gas)
|
||||
Fee::Auto(multiplier) => auto_fee(multiplier).await?,
|
||||
Fee::PayerGranterAuto(auto_feegrant) => {
|
||||
let mut fee = auto_fee(auto_feegrant.gas_adjustment).await?;
|
||||
fee.payer = auto_feegrant.payer;
|
||||
fee.granter = auto_feegrant.granter;
|
||||
fee
|
||||
}
|
||||
};
|
||||
debug!("Fee used for the transaction: {:?}", fee);
|
||||
|
||||
@@ -130,6 +130,9 @@ pub enum NymdError {
|
||||
|
||||
#[error("Coconut interface error: {0}")]
|
||||
CoconutInterfaceError(#[from] coconut_interface::error::CoconutInterfaceError),
|
||||
|
||||
#[error("Account had an unexpected bech32 prefix. Expected: {expected}, got: {got}")]
|
||||
UnexpectedBech32Prefix { got: String, expected: String },
|
||||
}
|
||||
|
||||
impl NymdError {
|
||||
|
||||
@@ -1,17 +1,86 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmrs::tx;
|
||||
use crate::nymd::Coin;
|
||||
use crate::nymd::Gas;
|
||||
use cosmrs::{tx, AccountId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
pub mod gas_price;
|
||||
|
||||
pub const DEFAULT_SIMULATED_GAS_MULTIPLIER: f32 = 1.3;
|
||||
pub type GasAdjustment = f32;
|
||||
|
||||
pub const DEFAULT_SIMULATED_GAS_MULTIPLIER: GasAdjustment = 1.3;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AutoFeeGrant {
|
||||
pub gas_adjustment: Option<GasAdjustment>,
|
||||
pub payer: Option<AccountId>,
|
||||
pub granter: Option<AccountId>,
|
||||
}
|
||||
|
||||
impl Display for AutoFeeGrant {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if let Some(gas_adjustment) = self.gas_adjustment {
|
||||
write!(f, "Feegrant in auto mode with {gas_adjustment} simulated multiplier with {:?} payer and {:?} granter", self.payer, self.granter)
|
||||
} else {
|
||||
write!(f, "Feegrant in auto mode with no custom simulated multiplier with {:?} payer and {:?} granter", self.payer, self.granter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Fee {
|
||||
Manual(#[serde(with = "sealed::TxFee")] tx::Fee),
|
||||
Auto(Option<f32>),
|
||||
Auto(Option<GasAdjustment>),
|
||||
PayerGranterAuto(AutoFeeGrant),
|
||||
}
|
||||
|
||||
impl Display for Fee {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Fee::Manual(fee) => {
|
||||
write!(f, "Fee in manual mode with ")?;
|
||||
for fee in &fee.amount {
|
||||
write!(f, "{}{} paid in fees, ", fee.amount, fee.denom)?;
|
||||
}
|
||||
write!(f, "{} set as gas limit, ", fee.gas_limit)?;
|
||||
if let Some(payer) = &fee.payer {
|
||||
write!(f, "{payer} set as payer, ")?;
|
||||
}
|
||||
if let Some(granter) = &fee.granter {
|
||||
write!(f, "{granter} set as granter")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Fee::Auto(Some(multiplier)) => {
|
||||
write!(f, "Fee in auto mode with {multiplier} simulated multiplier")
|
||||
}
|
||||
Fee::Auto(None) => write!(f, "Fee in auto mode with no custom simulated multiplier"),
|
||||
Fee::PayerGranterAuto(auto_feegrant) => write!(f, "{}", auto_feegrant),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Fee {
|
||||
pub fn new_payer_granter_auto(
|
||||
gas_adjustment: Option<GasAdjustment>,
|
||||
payer: Option<AccountId>,
|
||||
granter: Option<AccountId>,
|
||||
) -> Self {
|
||||
Fee::PayerGranterAuto(AutoFeeGrant {
|
||||
gas_adjustment,
|
||||
payer,
|
||||
granter,
|
||||
})
|
||||
}
|
||||
pub fn try_get_manual_amount(&self) -> Option<Vec<Coin>> {
|
||||
match self {
|
||||
Fee::Manual(tx_fee) => Some(tx_fee.amount.iter().cloned().map(Into::into).collect()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<tx::Fee> for Fee {
|
||||
@@ -20,8 +89,8 @@ impl From<tx::Fee> for Fee {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for Fee {
|
||||
fn from(multiplier: f32) -> Self {
|
||||
impl From<GasAdjustment> for Fee {
|
||||
fn from(multiplier: GasAdjustment) -> Self {
|
||||
Fee::Auto(Some(multiplier))
|
||||
}
|
||||
}
|
||||
@@ -32,6 +101,21 @@ impl Default for Fee {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GasAdjustable {
|
||||
fn adjust_gas(&self, adjustment: GasAdjustment) -> Self;
|
||||
}
|
||||
|
||||
impl GasAdjustable for Gas {
|
||||
fn adjust_gas(&self, adjustment: GasAdjustment) -> Self {
|
||||
if adjustment == 1.0 {
|
||||
*self
|
||||
} else {
|
||||
let adjusted = (self.value() as f32 * adjustment).ceil();
|
||||
(adjusted as u64).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a workaround to provide serde implementation for tx::Fee. We don't want to ever expose any of those
|
||||
// types to the public and ideally they will get replaced by proper implementation inside comrs
|
||||
mod sealed {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user