Compare commits
286 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b8494eb83a | |||
| cb549dfe25 | |||
| 17507bc8c5 | |||
| 12f8e453ea | |||
| 97ef024b8a | |||
| 3dc94223ae | |||
| 50cd18a926 | |||
| 897d51cba0 | |||
| 1f1d91bd1e | |||
| 9d63a30b07 | |||
| 5b22b1c298 | |||
| 0302d9ae5d | |||
| eb6949528a | |||
| 41b77c3ad5 | |||
| 2f78f5eb26 | |||
| cdbd731f10 | |||
| 5cb4949d46 | |||
| 9ec0b30812 | |||
| 7e1cf2f105 | |||
| 04636a569a | |||
| 52f5b980a4 | |||
| 0e4f833715 | |||
| d22914c4dc | |||
| 7d506e6e2a | |||
| c7007de1ea | |||
| 10bf70b22b | |||
| d952f3233a | |||
| af4ac1b7c9 | |||
| eae4276381 | |||
| d8fab74df6 | |||
| b8c184cc60 | |||
| 8bcc931d9b | |||
| 955ef7b871 | |||
| 9ee7ad4fa8 | |||
| 79aa1febbe | |||
| 4ed9d3a297 | |||
| 97dee2e1a0 | |||
| 32c4974c44 | |||
| 5dadd73dfd | |||
| 6ad5badef4 | |||
| 480ad18b2e | |||
| cdb21f418b | |||
| bd4c18c723 | |||
| e7716ae852 | |||
| b19528d47e | |||
| ef88ce9252 | |||
| 488f1c7742 | |||
| 63c698d903 | |||
| 7e1c3b4501 | |||
| e1d7772a78 | |||
| 9db589e0b3 | |||
| 57c8d69785 | |||
| 4a9cc5b757 | |||
| 06aac9393b | |||
| f4c5b1d67f | |||
| 2e0aba6100 | |||
| 8c6549c5dd | |||
| 7b431ebfa8 | |||
| 3152fd6887 | |||
| e03e7f186a | |||
| ec056a548f | |||
| 24aa075a07 | |||
| 8488c4cb0f | |||
| 20c96b940f | |||
| cfb43699d6 | |||
| 79e7b5b938 | |||
| 47156e693c | |||
| b61f6c8833 | |||
| cc3b710af9 | |||
| 4de3f16bb3 | |||
| 7aad7daa5e | |||
| 4107d670cf | |||
| 284e797fb3 | |||
| 8a63df3e82 | |||
| 7b8731ddcc | |||
| 1017c9c642 | |||
| 4e0c59a8f1 | |||
| f6d85a0784 | |||
| 6516811276 | |||
| ca0ed3594f | |||
| a28ad4f81a | |||
| 0ea3183288 | |||
| c6ff7c51f0 | |||
| 0240ab6144 | |||
| 2746ec49a3 | |||
| b3f1c943a7 | |||
| 7654ede904 | |||
| c882487d01 | |||
| 865e4839f3 | |||
| 5a270f6489 | |||
| 6776bb4bab | |||
| cbe9bad66e | |||
| 5761e1c1a4 | |||
| 062a5edf76 | |||
| a009e76568 | |||
| dcc9b856d7 | |||
| 592e52b333 | |||
| bc923be862 | |||
| e880631500 | |||
| d9b0834476 | |||
| 2fda22e168 | |||
| a5091cd124 | |||
| b2ec19ece4 | |||
| 72e64cfab7 | |||
| 7eacb6b57e | |||
| e0e1ca992f | |||
| f1a628dee4 | |||
| 81d49dc4b9 | |||
| 7e581183f4 | |||
| a9f3fa0c72 | |||
| e6c5a97f8c | |||
| efb9b70293 | |||
| 666c233d6c | |||
| 4307756719 | |||
| 622d0d3ff9 | |||
| ad763c2131 | |||
| 87a2ec9468 | |||
| dd590e8779 | |||
| 9555f3a2cd | |||
| 0b79fc33d7 | |||
| b7e14489da | |||
| 84b426042e | |||
| f683b9e770 | |||
| 5fb2c9d822 | |||
| 0f7280c227 | |||
| 4ebca86a66 | |||
| 1a0c6eed27 | |||
| 673e13ec57 | |||
| 7caac334f4 | |||
| 4e0e081b3e | |||
| 51dc8c81ed | |||
| 727eedf7d3 | |||
| e37fddf3ab | |||
| 885679e575 | |||
| 36554c3c34 | |||
| 2197062372 | |||
| 487b894f3d | |||
| a03603b274 | |||
| e6879dba63 | |||
| 68a43e33b9 | |||
| db2ab41636 | |||
| c624327a7a | |||
| 32ff5cbc1e | |||
| 9ba203c7b3 | |||
| 729625ef4f | |||
| fd3923a5f9 | |||
| c2fd07ea18 | |||
| d76a2d84b5 | |||
| 2a98f7482d | |||
| 12637d93ff | |||
| f63aba9058 | |||
| 668325a4ce | |||
| e891c68158 | |||
| b1fb032f43 | |||
| 23ea82952e | |||
| 9a65e44166 | |||
| 020cad897d | |||
| 5dfaff6296 | |||
| 5b03982043 | |||
| 8f6856d6fb | |||
| 976dd7aae2 | |||
| 0d21f4e937 | |||
| 1403449ad5 | |||
| e84af4f601 | |||
| 26b032c15c | |||
| cba3625394 | |||
| e1ddaff04d | |||
| 66ab5de442 | |||
| 0b9c03ca90 | |||
| c9dce0c1da | |||
| e00e77db15 | |||
| 1074449f91 | |||
| 08276e6e42 | |||
| 9a3d824a4a | |||
| 2789ee8f18 | |||
| a7ba643c35 | |||
| 28be53eefb | |||
| 219c45a352 | |||
| 1a3b83752e | |||
| c42f3c6844 | |||
| a5d3ba3900 | |||
| cdf0d44341 | |||
| 92f976a45d | |||
| 2bc858cde3 | |||
| 92e13a5d00 | |||
| 122f5d9f2e | |||
| 982ee0266c | |||
| 5f42a9bd05 | |||
| 1811df9ddb | |||
| 6bdfe7f895 | |||
| c6b286a1db | |||
| b3568a26f5 | |||
| 15ae0f521e | |||
| 2923d4b872 | |||
| d740e8b8a9 | |||
| a274edffba | |||
| eec211e038 | |||
| f5f888a0aa | |||
| 86ec1d6026 | |||
| bc3f5838f1 | |||
| 7e58b3273d | |||
| bed09bf8a4 | |||
| 28e55c6de6 | |||
| abb4537551 | |||
| b48f2f1660 | |||
| ccf8063a52 | |||
| 8694396942 | |||
| fb253e53a4 | |||
| fdd34863ba | |||
| 4ac95ad8cf | |||
| 132d550cb6 | |||
| efc5035144 | |||
| ebeac73f30 | |||
| 5303288b9c | |||
| bcac045c77 | |||
| d643df74b3 | |||
| 4c1c6d86c8 | |||
| 623448a220 | |||
| b403ce1e62 | |||
| 6faf8786ea | |||
| aaa4378b33 | |||
| 6512bfb4d9 | |||
| 1afabfd1b6 | |||
| f05e2880a9 | |||
| 81675008a6 | |||
| 8b4fc5719e | |||
| e4f7452241 | |||
| 9602379368 | |||
| 35ba39c196 | |||
| 6d09da40e0 | |||
| de44f21d64 | |||
| edc91e0e36 | |||
| 518cd21fd6 | |||
| b36ca2c4f1 | |||
| 2db79de722 | |||
| 3a9531b586 | |||
| f96d768341 | |||
| 26fb13fe2d | |||
| 67a9d80299 | |||
| 84249cab4b | |||
| f4305dfaa5 | |||
| 0c45c61c85 | |||
| 7d250b3384 | |||
| bb2029e21c | |||
| 0f0611f390 | |||
| 2904141c14 | |||
| c569a7a858 | |||
| 87db1fd8c1 | |||
| a1d2d09f28 | |||
| b949d7b221 | |||
| de468760d6 | |||
| 30a78df986 | |||
| e8622a190a | |||
| 75f098f002 | |||
| b034a07439 | |||
| 68956456dd | |||
| aa048cabe6 | |||
| 5dc67b995e | |||
| 721e4bda8b | |||
| 2673f83116 | |||
| bc68513a78 | |||
| 91250f199a | |||
| 3ec92a0784 | |||
| 70a9317ab3 | |||
| 71e36065e5 | |||
| 389d1ba3b9 | |||
| 18f5d4084d | |||
| 83744962f1 | |||
| df08a894cc | |||
| 2354828481 | |||
| 74693bda6f | |||
| 613ff5da71 | |||
| a11937699f | |||
| eb59ca77ef | |||
| 12d07fd870 | |||
| 1d18def642 | |||
| 70d4bce42f | |||
| c81454e00b | |||
| ac4ee2bcf2 | |||
| 9184c119d6 | |||
| 216b114346 | |||
| 793dbdef7e | |||
| 432e1e6065 | |||
| c8a02e1354 | |||
| 698345dbe9 | |||
| 5568073c17 |
@@ -1,3 +1,40 @@
|
|||||||
# Global owners. If you want to set more specific (per-directory, per-language) owners, see the write-up at
|
# Lines starting with '#' are comments.
|
||||||
# https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners
|
# Each line is a file pattern followed by one or more owners.
|
||||||
* @durch @futurechimp @jstuczyn @neacsu
|
|
||||||
|
# More details are here: https://help.github.com/articles/about-codeowners/
|
||||||
|
|
||||||
|
# The '*' pattern is global owners.
|
||||||
|
|
||||||
|
# Order is important. The last matching pattern has the most precedence.
|
||||||
|
# The folders are ordered as follows:
|
||||||
|
|
||||||
|
# In each subsection folders are ordered first by depth, then alphabetically.
|
||||||
|
# This should make it easy to add new rules without breaking existing ones.
|
||||||
|
|
||||||
|
# Something weird not covered by anything else
|
||||||
|
* @futurechimp @mmsinclair
|
||||||
|
|
||||||
|
# Rust rules:
|
||||||
|
*.rs @durch @futurechimp @jstuczyn @neacsu
|
||||||
|
Cargo.* @durch @futurechimp @jstuczyn @neacsu
|
||||||
|
|
||||||
|
# JS rules:
|
||||||
|
*.js @mmsinclair @fmtabbara @Aid19801
|
||||||
|
*.ts @mmsinclair @fmtabbara @Aid19801
|
||||||
|
*.tsx @mmsinclair @fmtabbara @Aid19801
|
||||||
|
*.jsx @mmsinclair @fmtabbara @Aid19801
|
||||||
|
|
||||||
|
# Something looking like possible documentation rules:
|
||||||
|
*.md @mfahampshire
|
||||||
|
|
||||||
|
# our docker scripts
|
||||||
|
/docker/ @neacsu
|
||||||
|
|
||||||
|
# if there are any changes in the core crypto, I feel like Ania should take a look:
|
||||||
|
/common/crypto/ @aniampio
|
||||||
|
/common/nymsphinx/ @aniampio
|
||||||
|
|
||||||
|
# Explorer and wallet should probably get looked by the product team
|
||||||
|
/explorer/ @nymtech/product
|
||||||
|
/nym-wallet/ @nymtech/product
|
||||||
|
/wallet-web/ @nymtech/product
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an enhancement to the product
|
||||||
|
title: "[Feature Request]"
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is...
|
||||||
|
|
||||||
|
**Is your request a feature not related to an existing problem? A new feature.**
|
||||||
|
For example.
|
||||||
|
- Given I am using the nym wallet
|
||||||
|
- When I transfer nym tokens across the network
|
||||||
|
- Then I want to have an url link in the wallet which navigates outside the application to the nym-explorer
|
||||||
|
|
||||||
|
**Where does the feature fit in the Nym real estate?**
|
||||||
|
- Application / UI
|
||||||
|
|
||||||
|
**What is this solving?**
|
||||||
|
How will this improve the product...
|
||||||
|
|
||||||
|
**Is this an update to packages or libraries?**
|
||||||
|
If so, please list them. If not, please ignore this section.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
name: Report
|
||||||
|
about: To help identify and reproduce issues
|
||||||
|
title: "[Issue]"
|
||||||
|
labels: bug, bug-needs-triage, qa
|
||||||
|
assignees: tommyv1987
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the issue**
|
||||||
|
A clear and concise description of what the issue is...
|
||||||
|
|
||||||
|
**Expected behaviour**
|
||||||
|
A clear and concise description of what you expected to happen...
|
||||||
|
|
||||||
|
**Stack Traces**
|
||||||
|
If there are stack traces or logs, please provide them here...
|
||||||
|
|
||||||
|
**Steps to Reproduce**
|
||||||
|
Steps to reproduce the behaviour, if you're familiar with BDD syntax, please write it in this style:
|
||||||
|
- Given I was doing X
|
||||||
|
- And I installed Y
|
||||||
|
- When I actioned Y
|
||||||
|
- Then I expect Z
|
||||||
|
|
||||||
|
*An example:*
|
||||||
|
- Given I was setting up a mix-node following the instructions in the docs
|
||||||
|
- And I successfully bonded my node via the the wallet
|
||||||
|
- When I went to start my mixnode
|
||||||
|
- Then I was presented with an error
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem...
|
||||||
|
|
||||||
|
**Which area of Nym were you using?**
|
||||||
|
- UI: [e.g. Websites - network-explorer, nym-website]
|
||||||
|
- Application: [e.g Gateway, Client, Wallet]
|
||||||
|
- OS: [e.g. Ubuntu 20.x, MacOs Big Sur, Windows 10]
|
||||||
|
- Browser: [e.g Chrome (if applicable)]
|
||||||
|
- Version: [e.g. nym binary(0.11.0), browser(94.0)]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Please provide any other information
|
||||||
@@ -1,72 +1,102 @@
|
|||||||
name: Continuous integration
|
name: Continuous integration
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- 'explorer/**'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- 'explorer/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
matrix_prep:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ubuntu-latest
|
||||||
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' }}
|
outputs:
|
||||||
strategy:
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
matrix:
|
|
||||||
rust: [stable, beta, nightly]
|
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
# creates the matrix strategy from build_matrix_includes.json
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
- id: set-matrix
|
||||||
|
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||||
|
with:
|
||||||
|
inputFile: '.github/workflows/build_matrix_includes.json'
|
||||||
|
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
|
||||||
|
build:
|
||||||
|
needs: matrix_prep
|
||||||
|
strategy:
|
||||||
|
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.os == 'windows-latest' }}
|
||||||
|
steps:
|
||||||
|
- name: Install Dependencies (Linux)
|
||||||
|
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install rust toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: ${{ matrix.rust }}
|
toolchain: ${{ matrix.rust }}
|
||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
# main repo
|
- name: Build all binaries
|
||||||
- uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --all
|
args: --all
|
||||||
|
|
||||||
# validator API
|
- name: Run all tests
|
||||||
- uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
|
||||||
command: build
|
|
||||||
args: --manifest-path validator-api/Cargo.toml
|
|
||||||
|
|
||||||
# main repo
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
args: --all
|
args: --all
|
||||||
|
|
||||||
# validator API
|
- name: Check formatting
|
||||||
- uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: --manifest-path validator-api/Cargo.toml
|
|
||||||
|
|
||||||
# main repo
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
with:
|
||||||
command: fmt
|
command: fmt
|
||||||
args: --all -- --check
|
args: --all -- --check
|
||||||
|
|
||||||
# validator API
|
- uses: actions-rs/clippy-check@v1
|
||||||
- uses: actions-rs/cargo@v1
|
name: Clippy checks
|
||||||
|
# if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable'
|
||||||
with:
|
with:
|
||||||
command: fmt
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
args: --manifest-path validator-api/Cargo.toml -- --check
|
args: --all-features
|
||||||
|
|
||||||
# main repo
|
- name: Run clippy
|
||||||
- uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
if: ${{ matrix.rust != 'nightly' }}
|
if: ${{ matrix.rust != 'nightly' }}
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: -- -D warnings
|
args: -- -D warnings
|
||||||
|
|
||||||
# validator API
|
# COCONUT stuff
|
||||||
- uses: actions-rs/cargo@v1
|
- name: Reclaim some disk space (because Windows is being annoying)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
if: ${{ matrix.os == 'windows-latest' }}
|
||||||
|
with:
|
||||||
|
command: clean
|
||||||
|
|
||||||
|
- name: Build all binaries with coconut enabled
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --all --features=coconut
|
||||||
|
|
||||||
|
- name: Run all tests with coconut enabled
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --all --features=coconut
|
||||||
|
|
||||||
|
- name: Run clippy with coconut enabled
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
if: ${{ matrix.rust != 'nightly' }}
|
if: ${{ matrix.rust != 'nightly' }}
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --manifest-path validator-api/Cargo.toml -- -D warnings
|
args: --features=coconut -- -D warnings
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"os":"ubuntu-latest",
|
||||||
|
"rust":"stable",
|
||||||
|
"runOnEvent":"always"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"os":"windows-latest",
|
||||||
|
"rust":"stable",
|
||||||
|
"runOnEvent":"pull_request"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"os":"macos-latest",
|
||||||
|
"rust":"stable",
|
||||||
|
"runOnEvent":"pull_request"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"os":"ubuntu-latest",
|
||||||
|
"rust":"beta",
|
||||||
|
"runOnEvent":"pull_request"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"os":"windows-latest",
|
||||||
|
"rust":"beta",
|
||||||
|
"runOnEvent":"pull_request"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"os":"macos-latest",
|
||||||
|
"rust":"beta",
|
||||||
|
"runOnEvent":"pull_request"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"os":"ubuntu-latest",
|
||||||
|
"rust":"nightly",
|
||||||
|
"runOnEvent":"pull_request"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"os":"windows-latest",
|
||||||
|
"rust":"nightly",
|
||||||
|
"runOnEvent":"pull_request"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"os":"macos-latest",
|
||||||
|
"rust":"nightly",
|
||||||
|
"runOnEvent":"pull_request"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
name: Clippy check
|
|
||||||
|
|
||||||
on: push
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
clippy_check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- run: rustup component add clippy
|
|
||||||
- uses: actions-rs/clippy-check@v1
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
args: --all-features
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"rust":"stable",
|
||||||
|
"runOnEvent":"always"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rust":"beta",
|
||||||
|
"runOnEvent":"pull_request"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"rust":"nightly",
|
||||||
|
"runOnEvent":"pull_request"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
name: ERC20 Bridge Contract
|
||||||
|
|
||||||
|
on: [ push, pull_request ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
matrix_prep:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
|
steps:
|
||||||
|
# creates the matrix strategy from build_matrix_includes.json
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- id: set-matrix
|
||||||
|
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||||
|
with:
|
||||||
|
inputFile: '.github/workflows/contract_matrix_includes.json'
|
||||||
|
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
|
||||||
|
erc20-bridge-contract:
|
||||||
|
needs: matrix_prep
|
||||||
|
strategy:
|
||||||
|
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
|
||||||
|
# since it's going to be compiled into wasm, there's absolutely
|
||||||
|
# no point in running CI on different OS-es
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: ${{ matrix.rust == 'nightly' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: ${{ matrix.rust }}
|
||||||
|
target: wasm32-unknown-unknown
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: '-C link-arg=-s'
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --manifest-path contracts/erc20-bridge/Cargo.toml --target wasm32-unknown-unknown
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --manifest-path contracts/erc20-bridge/Cargo.toml
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --manifest-path contracts/erc20-bridge/Cargo.toml -- --check
|
||||||
|
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
if: ${{ matrix.rust != 'nightly' }}
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --manifest-path contracts/erc20-bridge/Cargo.toml -- -D warnings
|
||||||
@@ -1,16 +1,34 @@
|
|||||||
name: Mixnet Contract
|
name: Mixnet Contract
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- 'explorer/**'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- 'explorer/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
matrix_prep:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
|
steps:
|
||||||
|
# creates the matrix strategy from build_matrix_includes.json
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- id: set-matrix
|
||||||
|
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||||
|
with:
|
||||||
|
inputFile: '.github/workflows/contract_matrix_includes.json'
|
||||||
|
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
|
||||||
|
mixnet-contract:
|
||||||
# since it's going to be compiled into wasm, there's absolutely
|
# since it's going to be compiled into wasm, there's absolutely
|
||||||
# no point in running CI on different OS-es
|
# no point in running CI on different OS-es
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
continue-on-error: ${{ matrix.rust == 'nightly' }}
|
continue-on-error: ${{ matrix.rust == 'nightly' }}
|
||||||
|
needs: matrix_prep
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
|
||||||
rust: [ stable, beta, nightly ]
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
@@ -23,6 +41,8 @@ jobs:
|
|||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: '-C link-arg=-s'
|
||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --manifest-path contracts/mixnet/Cargo.toml --target wasm32-unknown-unknown
|
args: --manifest-path contracts/mixnet/Cargo.toml --target wasm32-unknown-unknown
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name: Linting for Network Explorer (eslint/prettier)
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'explorer/**'
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: explorer
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: custom-runner-linux
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '14'
|
||||||
|
- run: npm install
|
||||||
|
- name: Run ESLint
|
||||||
|
# GitHub should automatically annotate the PR
|
||||||
|
run: npm run lint
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
name: CI for Network Explorer
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'explorer/**'
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: explorer
|
||||||
|
|
||||||
|
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@v2
|
||||||
|
with:
|
||||||
|
node-version: '14'
|
||||||
|
- run: npm install
|
||||||
|
continue-on-error: true
|
||||||
|
- run: npm run test
|
||||||
|
continue-on-error: true
|
||||||
|
- run: npm run build
|
||||||
|
continue-on-error: true
|
||||||
|
- 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: "explorer/dist/"
|
||||||
|
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
||||||
|
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||||
|
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
|
||||||
|
EXCLUDE: "/dist/, /node_modules/"
|
||||||
|
- name: Keybase - Node Install
|
||||||
|
run: npm install
|
||||||
|
working-directory: .github/workflows/support-files/messages
|
||||||
|
- name: Keybase - Send Notification
|
||||||
|
env:
|
||||||
|
NYM_PROJECT_NAME: "Network Explorer"
|
||||||
|
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
||||||
|
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
|
||||||
|
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||||
|
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||||
|
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
||||||
|
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||||
|
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||||
|
KEYBASE_NYM_CHANNEL: "ci-network-explorer"
|
||||||
|
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||||
|
uses: docker://keybaseio/client:stable-node
|
||||||
|
with:
|
||||||
|
args: .github/workflows/support-files/messages/entry_point_notifications.sh
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
name: Webdriverio tests for nym wallet
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- "nym-wallet/**"
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: nym-wallet
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: wallet tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Tauri dependencies
|
||||||
|
run: >
|
||||||
|
sudo apt-get update &&
|
||||||
|
sudo apt-get install -y
|
||||||
|
libgtk-3-dev
|
||||||
|
libgtksourceview-3.0-dev
|
||||||
|
webkit2gtk-4.0
|
||||||
|
libappindicator3-dev
|
||||||
|
webkit2gtk-driver
|
||||||
|
xvfb
|
||||||
|
|
||||||
|
- name: Install minimal stable
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
|
||||||
|
- name: Node v16
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 16.x
|
||||||
|
|
||||||
|
- name: Install yarn for building application
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
|
- name: Build application
|
||||||
|
run: yarn run webpack:build & yarn run tauri:build
|
||||||
|
|
||||||
|
- name: Check binary exists
|
||||||
|
run: |
|
||||||
|
cd target/release/
|
||||||
|
(test -f nym-wallet && echo nym binary exists) || echo wallet does not exist
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
working-directory: nym-wallet/webdriver
|
||||||
|
|
||||||
|
- name: Remove existing user datafile
|
||||||
|
uses: JesseTG/rm@v1.0.2
|
||||||
|
with:
|
||||||
|
path: nym-wallet/webdriver/common/data/user-data.json
|
||||||
|
|
||||||
|
- name: Create user data json file
|
||||||
|
id: create-json
|
||||||
|
uses: jsdaniell/create-json@1.1.2
|
||||||
|
with:
|
||||||
|
name: "user-data.json"
|
||||||
|
json: ${{ secrets.WALLET_USERDATA }}
|
||||||
|
dir: "nym-wallet/webdriver/common/data/"
|
||||||
|
|
||||||
|
- name: Install tauri-driver
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: install
|
||||||
|
args: tauri-driver
|
||||||
|
|
||||||
|
- name: Launch tests
|
||||||
|
run: xvfb-run yarn test:runall
|
||||||
|
working-directory: nym-wallet/webdriver
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
.idea
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "all",
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# pass exit codes out to GitHub Actions
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
# change to the directory that contains this script
|
||||||
|
cd "${0%/*}"
|
||||||
|
|
||||||
|
# run the node script
|
||||||
|
node send_message.js
|
||||||
@@ -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,16 @@
|
|||||||
|
{
|
||||||
|
"name": "send-keybase-message",
|
||||||
|
"description": "Sends a notification message with the keybase package that fails when piped into the keybase CLI",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"format": "prettier --write send_message.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"handlebars": "^4.7.7",
|
||||||
|
"keybase-bot": "^3.6.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "2.3.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
const Bot = require('keybase-bot');
|
||||||
|
const Handlebars = require('handlebars');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const data = { env: process.env };
|
||||||
|
// const data = { ...PASTE TEST DATA HERE ... }; // -- DEV: uncomment to set test data
|
||||||
|
|
||||||
|
// validation of environment
|
||||||
|
if(!(process.env.NYM_PROJECT_NAME || data.env.NYM_PROJECT_NAME)) {
|
||||||
|
throw new Error('Please set env var NYM_PROJECT_NAME with the project name for displaying in notification messages');
|
||||||
|
}
|
||||||
|
const keybaseChannel = process.env.KEYBASE_NYM_CHANNEL || data.env.KEYBASE_NYM_CHANNEL;
|
||||||
|
if(!keybaseChannel) {
|
||||||
|
throw new Error('Please set env var KEYBASE_NYM_CHANNEL with the channel name for the notification message');
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract the git branch name
|
||||||
|
const GIT_BRANCH_NAME = (process.env.GITHUB_REF || data.env.GITHUB_REF).split('/').slice(2).join('/');
|
||||||
|
|
||||||
|
data.env.GIT_BRANCH_NAME = GIT_BRANCH_NAME;
|
||||||
|
const source = fs
|
||||||
|
.readFileSync(process.env.IS_SUCCESS === 'true' ? 'success' : 'failure')
|
||||||
|
.toString();
|
||||||
|
const template = Handlebars.compile(source);
|
||||||
|
const result = template(data);
|
||||||
|
|
||||||
|
// -- DEV: uncomment to show what is available in the handlebars template / show the result
|
||||||
|
// console.dir({ data }, { depth: null });
|
||||||
|
// console.log(result);
|
||||||
|
|
||||||
|
const bot = new Bot();
|
||||||
|
try {
|
||||||
|
const username = process.env.KEYBASE_NYMBOT_USERNAME;
|
||||||
|
const paperkey = process.env.KEYBASE_NYMBOT_PAPERKEY;
|
||||||
|
|
||||||
|
if(!username) {
|
||||||
|
throw new Error('Username is not defined. Please set env var KEYBASE_NYMBOT_USERNAME');
|
||||||
|
}
|
||||||
|
if(!paperkey) {
|
||||||
|
throw new Error('Paperkey is not defined. Please set env var KEYBASE_NYMBOT_PAPERKEY');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Initialising keybase with user "${username}" and key: "${'*'.repeat(paperkey.length)}"...`);
|
||||||
|
await bot.init(username, paperkey, { verbose: false });
|
||||||
|
|
||||||
|
const channel = {
|
||||||
|
name: 'nymtech_bot',
|
||||||
|
membersType: 'team',
|
||||||
|
topicName: keybaseChannel,
|
||||||
|
topic_type: 'CHAT',
|
||||||
|
};
|
||||||
|
const message = {
|
||||||
|
body: result,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(`Sending to ${channel.name}#${channel.topicName}...`);
|
||||||
|
await bot.chat.send(channel, message);
|
||||||
|
|
||||||
|
console.log('Message sent!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
process.exitCode = -1;
|
||||||
|
} finally {
|
||||||
|
await bot.deinit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
|
||||||
|
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** 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:
|
||||||
|
```
|
||||||
|
{{ env.GIT_COMMIT_MESSAGE }}
|
||||||
|
```
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
name: Publish Tauri Wallet
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- nym-wallet-*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Run a one-line script
|
||||||
|
run: echo Hello, world!
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
name: Generate TS types
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- "explorer/**"
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- "explorer/**"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
nym-wallet-types:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
steps:
|
||||||
|
- name: Prepare
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y libpango1.0-dev libatk1.0-dev libgdk-pixbuf2.0-dev libsoup2.4-dev librust-gdk-dev libwebkit2gtk-4.0-dev
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
- name: Generate TS
|
||||||
|
run: cd nym-wallet/src-tauri && cargo test
|
||||||
|
- uses: EndBug/add-and-commit@v7.2.1 # https://github.com/marketplace/actions/add-commit
|
||||||
|
with:
|
||||||
|
add: '["nym-wallet"]'
|
||||||
|
message: "[ci skip] Generate TS types"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
name: Wasm Client
|
name: Wasm Client
|
||||||
|
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- 'explorer/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
wasm:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -16,10 +19,16 @@ jobs:
|
|||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
# token credentials (non-coconut) don't work for wasm right now
|
||||||
|
# - uses: actions-rs/cargo@v1
|
||||||
|
# with:
|
||||||
|
# command: build
|
||||||
|
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
|
||||||
|
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: build
|
command: build
|
||||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
|
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --features=coconut
|
||||||
|
|
||||||
# for some reason this does not seem to work correctly, leave it for later, building is good enough for now
|
# for some reason this does not seem to work correctly, leave it for later, building is good enough for now
|
||||||
# - uses: actions-rs/cargo@v1
|
# - uses: actions-rs/cargo@v1
|
||||||
@@ -36,4 +45,4 @@ jobs:
|
|||||||
# - uses: actions-rs/cargo@v1
|
# - uses: actions-rs/cargo@v1
|
||||||
# with:
|
# with:
|
||||||
# command: clippy
|
# command: clippy
|
||||||
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
|
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ target
|
|||||||
/.vscode/settings.json
|
/.vscode/settings.json
|
||||||
validator/.vscode
|
validator/.vscode
|
||||||
sample-configs/validator-config.toml
|
sample-configs/validator-config.toml
|
||||||
.vscode
|
|
||||||
scripts/deploy_qa.sh
|
scripts/deploy_qa.sh
|
||||||
scripts/run_gate.sh
|
scripts/run_gate.sh
|
||||||
scripts/run_mix.sh
|
scripts/run_mix.sh
|
||||||
@@ -29,3 +28,9 @@ v6-topology.json
|
|||||||
validator-api/v4.json
|
validator-api/v4.json
|
||||||
validator-api/v6.json
|
validator-api/v6.json
|
||||||
**/node_modules
|
**/node_modules
|
||||||
|
validator-api/keypair
|
||||||
|
contracts/mixnet/code_id
|
||||||
|
contracts/mixnet/Justfile
|
||||||
|
contracts/mixnet/Makefile
|
||||||
|
validator-config
|
||||||
|
*.patch
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
opt-level = "s"
|
opt-level = "s"
|
||||||
|
overflow-checks = true
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
@@ -11,18 +12,23 @@ panic = "abort"
|
|||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
|
"clients/client-core",
|
||||||
"clients/native",
|
"clients/native",
|
||||||
"clients/native/websocket-requests",
|
"clients/native/websocket-requests",
|
||||||
"clients/socks5",
|
"clients/socks5",
|
||||||
|
"clients/tauri-client/src-tauri",
|
||||||
"clients/webassembly",
|
"clients/webassembly",
|
||||||
"clients/client-core",
|
|
||||||
"common/client-libs/gateway-client",
|
"common/client-libs/gateway-client",
|
||||||
"common/client-libs/mixnet-client",
|
"common/client-libs/mixnet-client",
|
||||||
"common/client-libs/validator-client",
|
"common/client-libs/validator-client",
|
||||||
|
"common/coconut-interface",
|
||||||
"common/config",
|
"common/config",
|
||||||
|
"common/credentials",
|
||||||
"common/crypto",
|
"common/crypto",
|
||||||
|
"common/erc20-bridge-contract",
|
||||||
"common/mixnet-contract",
|
"common/mixnet-contract",
|
||||||
"common/mixnode-common",
|
"common/mixnode-common",
|
||||||
|
"common/network-defaults",
|
||||||
"common/nonexhaustive-delayqueue",
|
"common/nonexhaustive-delayqueue",
|
||||||
"common/nymsphinx",
|
"common/nymsphinx",
|
||||||
"common/nymsphinx/acknowledgements",
|
"common/nymsphinx/acknowledgements",
|
||||||
@@ -35,15 +41,16 @@ members = [
|
|||||||
"common/nymsphinx/params",
|
"common/nymsphinx/params",
|
||||||
"common/nymsphinx/types",
|
"common/nymsphinx/types",
|
||||||
"common/pemstore",
|
"common/pemstore",
|
||||||
"common/socks5/requests",
|
|
||||||
"common/socks5/proxy-helpers",
|
"common/socks5/proxy-helpers",
|
||||||
|
"common/socks5/requests",
|
||||||
"common/topology",
|
"common/topology",
|
||||||
"common/wasm-utils",
|
"common/wasm-utils",
|
||||||
|
"explorer-api",
|
||||||
"gateway",
|
"gateway",
|
||||||
"gateway/gateway-requests",
|
"gateway/gateway-requests",
|
||||||
"mixnode",
|
"mixnode",
|
||||||
# "validator-api",
|
|
||||||
"service-providers/network-requester",
|
"service-providers/network-requester",
|
||||||
|
"validator-api",
|
||||||
]
|
]
|
||||||
|
|
||||||
default-members = [
|
default-members = [
|
||||||
@@ -53,7 +60,7 @@ default-members = [
|
|||||||
"gateway",
|
"gateway",
|
||||||
"service-providers/network-requester",
|
"service-providers/network-requester",
|
||||||
"mixnode",
|
"mixnode",
|
||||||
# "validator-api",
|
"validator-api",
|
||||||
]
|
]
|
||||||
|
|
||||||
exclude = ["explorer", "contracts", "validator-api"]
|
exclude = ["explorer", "contracts", "tokenomics-py"]
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: Apache-2.0
|
|||||||
|
|
||||||
## The Nym Privacy Platform
|
## The Nym Privacy Platform
|
||||||
|
|
||||||
This repository contains the Nym mixnet.
|
|
||||||
|
|
||||||
The platform is composed of multiple Rust crates. Top-level executable binary crates include:
|
The platform is composed of multiple Rust crates. Top-level executable binary crates include:
|
||||||
|
|
||||||
* nym-mixnode - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers.
|
* nym-mixnode - shuffles [Sphinx](https://github.com/nymtech/sphinx) packets together to provide privacy against network-level attackers.
|
||||||
@@ -15,6 +13,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
|||||||
* nym-gateway - acts sort of like a mailbox for mixnet messages, removing the need for directly delivery to potentially offline or firewalled devices.
|
* nym-gateway - acts sort of like a mailbox for mixnet messages, removing the need for directly delivery to potentially offline or firewalled devices.
|
||||||
* nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
|
* nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing").
|
||||||
* nym-explorer - a (projected) block explorer and (existing) mixnet viewer.
|
* nym-explorer - a (projected) block explorer and (existing) mixnet viewer.
|
||||||
|
* nym-wallet (currently in development)- a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework.
|
||||||
|
|
||||||
[](https://opensource.org/licenses/Apache-2.0)
|
[](https://opensource.org/licenses/Apache-2.0)
|
||||||
[](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
|
[](https://github.com/nymtech/nym/actions?query=branch%3Adevelop)
|
||||||
@@ -22,7 +21,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
|||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
Platform build instructions are available on [our docs site](https://nymtech.net/docs).
|
Platform build instructions are available on [our docs site](https://nymtech.net/docs/0.11.0/overview/index/).
|
||||||
|
|
||||||
### Developing
|
### Developing
|
||||||
|
|
||||||
@@ -32,6 +31,45 @@ There's a `.env.sample-dev` file provided which you can rename to `.env` if you
|
|||||||
|
|
||||||
You can chat to us in [Keybase](https://keybase.io). Download their chat app, then click **Teams -> Join a team**. Type **nymtech.friends** into the team name and hit **continue**. For general chat, hang out in the **#general** channel. Our development takes places in the **#dev** channel. Node operators should be in the **#node-operators** channel.
|
You can chat to us in [Keybase](https://keybase.io). Download their chat app, then click **Teams -> Join a team**. Type **nymtech.friends** into the team name and hit **continue**. For general chat, hang out in the **#general** channel. Our development takes places in the **#dev** channel. Node operators should be in the **#node-operators** channel.
|
||||||
|
|
||||||
|
### Rewards
|
||||||
|
|
||||||
|
Node, node operator and delegator rewards are determined according to the principles laid out in the section 6 of [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf). Below is a TLDR of the variables and formulas involved in calculating the epoch rewards. Initial reward pool is set to 250 million Nym, making the circulating supply 750 million Nym.
|
||||||
|
|
||||||
|
|Symbol|Definition|
|
||||||
|
|---|---|
|
||||||
|
|<img src="https://render.githubusercontent.com/render/math?math=R">|global share of rewards available, starts at 2% of the reward pool.
|
||||||
|
|<img src="https://render.githubusercontent.com/render/math?math=R_{i}">|node reward for mixnode `i`.
|
||||||
|
|<img src="https://render.githubusercontent.com/render/math?math=\sigma_{i}">|ratio of total node stake (node bond + all delegations) to the token circulating supply.
|
||||||
|
|<img src="https://render.githubusercontent.com/render/math?math=\lambda_{i}">|ratio of stake operator has plaged to their node to the token circulating supply.
|
||||||
|
|<img src="https://render.githubusercontent.com/render/math?math=\omega_{i}">|fraction of total effort undertaken by node `i`, set to `1/k` in testnet Milhon.
|
||||||
|
|<img src="https://render.githubusercontent.com/render/math?math=k">|number of nodes stakeholders are incentivised to create, set by the validators, a matter of governance. Currently determined by the `active set` size, and set to 5000 in testnet Milhon.
|
||||||
|
|<img src="https://render.githubusercontent.com/render/math?math=\alpha">|Sybil attack resistance parameter - the higher this parameter is set the stronger the reduction in competitivness gets for a Sybil attacker.
|
||||||
|
|<img src="https://render.githubusercontent.com/render/math?math=PM_{i}">|declared profit margin of operator `i`, defaults to 10% in testnet Milhon.
|
||||||
|
|<img src="https://render.githubusercontent.com/render/math?math=PF_{i}">|uptime of node `i`, scaled to 0 - 1, for the rewarding epoch
|
||||||
|
|<img src="https://render.githubusercontent.com/render/math?math=PP_{i}">|cost of operating node `i` for the duration of the rewarding eopoch, set to 40 Nym for testnet Milhon.
|
||||||
|
|
||||||
|
Node reward for node `i` is determined as:
|
||||||
|
|
||||||
|
<img src="https://render.githubusercontent.com/render/math?math=R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)">
|
||||||
|
|
||||||
|
where:
|
||||||
|
|
||||||
|
<img src="https://render.githubusercontent.com/render/math?math=\sigma^'_{i} = min\{\sigma_{i}, 1/k\}">
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
<img src="https://render.githubusercontent.com/render/math?math=\lambda^'_{i} = min\{\lambda_{i}, 1/k\}">
|
||||||
|
|
||||||
|
Operator of node `i` is credited with the following amount:
|
||||||
|
|
||||||
|
<img src="https://render.githubusercontent.com/render/math?math=min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}">
|
||||||
|
|
||||||
|
Delegate with stake `s` recieves:
|
||||||
|
|
||||||
|
<img src="https://render.githubusercontent.com/render/math?math=max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}">
|
||||||
|
|
||||||
|
where `s'` is stake `s` scaled over total token circulating supply.
|
||||||
|
|
||||||
### Licensing and copyright information
|
### Licensing and copyright information
|
||||||
|
|
||||||
This program is available as open source under the terms of the Apache 2.0 license. However, some elements are being licensed under CC0-1.0 and MIT. For accurate information, please check individual files.
|
This program is available as open source under the terms of the Apache 2.0 license. However, some elements are being licensed under CC0-1.0 and MIT. For accurate information, please check individual files.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
sled = "0.34"
|
sled = "0.34"
|
||||||
tokio = { version = "1.4", features = ["macros"] }
|
tokio = { version = "1.4", features = ["macros"] }
|
||||||
|
url = { version ="2.2", features = ["serde"] }
|
||||||
|
|
||||||
# internal
|
# internal
|
||||||
config = { path = "../../common/config" }
|
config = { path = "../../common/config" }
|
||||||
@@ -29,3 +30,6 @@ validator-client = { path = "../../common/client-libs/validator-client" }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
coconut = []
|
||||||
@@ -61,8 +61,8 @@ impl KeyManager {
|
|||||||
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
|
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
/// After shared key with the gateway is derived, puts its ownership to this instance of a [`KeyManager`].
|
/// After shared key with the gateway is derived, puts its ownership to this instance of a [`KeyManager`].
|
||||||
pub fn insert_gateway_shared_key(&mut self, gateway_shared_key: SharedKeys) {
|
pub fn insert_gateway_shared_key(&mut self, gateway_shared_key: Arc<SharedKeys>) {
|
||||||
self.gateway_shared_key = Some(Arc::new(gateway_shared_key))
|
self.gateway_shared_key = Some(gateway_shared_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads previously stored keys from the disk.
|
/// Loads previously stored keys from the disk.
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ impl MixTrafficController {
|
|||||||
async fn on_messages(&mut self, mut mix_packets: Vec<MixPacket>) {
|
async fn on_messages(&mut self, mut mix_packets: Vec<MixPacket>) {
|
||||||
debug_assert!(!mix_packets.is_empty());
|
debug_assert!(!mix_packets.is_empty());
|
||||||
|
|
||||||
let success = if mix_packets.len() == 1 {
|
let result = if mix_packets.len() == 1 {
|
||||||
let mix_packet = mix_packets.pop().unwrap();
|
let mix_packet = mix_packets.pop().unwrap();
|
||||||
self.gateway_client.send_mix_packet(mix_packet).await
|
self.gateway_client.send_mix_packet(mix_packet).await
|
||||||
} else {
|
} else {
|
||||||
@@ -49,7 +49,7 @@ impl MixTrafficController {
|
|||||||
.await
|
.await
|
||||||
};
|
};
|
||||||
|
|
||||||
match success {
|
match result {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to send sphinx packet(s) to the gateway! - {:?}", e);
|
error!("Failed to send sphinx packet(s) to the gateway! - {:?}", e);
|
||||||
self.consecutive_gateway_failure_count += 1;
|
self.consecutive_gateway_failure_count += 1;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ use crate::client::{inbound_messages::InputMessageReceiver, topology_control::To
|
|||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use gateway_client::AcknowledgementReceiver;
|
use gateway_client::AcknowledgementReceiver;
|
||||||
use log::*;
|
use log::*;
|
||||||
use nymsphinx::params::PacketMode;
|
|
||||||
use nymsphinx::{
|
use nymsphinx::{
|
||||||
acknowledgements::AckKey,
|
acknowledgements::AckKey,
|
||||||
addressing::clients::Recipient,
|
addressing::clients::Recipient,
|
||||||
@@ -120,14 +119,6 @@ pub(super) struct Config {
|
|||||||
|
|
||||||
/// Average delay a data packet is going to get delayed at a single mixnode.
|
/// Average delay a data packet is going to get delayed at a single mixnode.
|
||||||
average_packet_delay: Duration,
|
average_packet_delay: Duration,
|
||||||
|
|
||||||
/// Mode of all mix packets created - VPN or Mix. They indicate whether packets should get delayed
|
|
||||||
/// and keys reused.
|
|
||||||
packet_mode: PacketMode,
|
|
||||||
|
|
||||||
/// If the mode of the client is set to VPN it specifies number of packets created with the
|
|
||||||
/// same initial secret until it gets rotated.
|
|
||||||
vpn_key_reuse_limit: Option<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -136,16 +127,12 @@ impl Config {
|
|||||||
ack_wait_multiplier: f64,
|
ack_wait_multiplier: f64,
|
||||||
average_ack_delay: Duration,
|
average_ack_delay: Duration,
|
||||||
average_packet_delay: Duration,
|
average_packet_delay: Duration,
|
||||||
packet_mode: PacketMode,
|
|
||||||
vpn_key_reuse_limit: Option<usize>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Config {
|
Config {
|
||||||
ack_wait_addition,
|
ack_wait_addition,
|
||||||
ack_wait_multiplier,
|
ack_wait_multiplier,
|
||||||
average_ack_delay,
|
average_ack_delay,
|
||||||
average_packet_delay,
|
average_packet_delay,
|
||||||
packet_mode,
|
|
||||||
vpn_key_reuse_limit,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,8 +173,6 @@ where
|
|||||||
ack_recipient,
|
ack_recipient,
|
||||||
config.average_packet_delay,
|
config.average_packet_delay,
|
||||||
config.average_ack_delay,
|
config.average_ack_delay,
|
||||||
config.packet_mode,
|
|
||||||
config.vpn_key_reuse_limit,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// will listen for any acks coming from the network
|
// will listen for any acks coming from the network
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ use gateway_client::AcknowledgementReceiver;
|
|||||||
use log::*;
|
use log::*;
|
||||||
use nymsphinx::acknowledgements::AckKey;
|
use nymsphinx::acknowledgements::AckKey;
|
||||||
use nymsphinx::addressing::clients::Recipient;
|
use nymsphinx::addressing::clients::Recipient;
|
||||||
use nymsphinx::params::PacketMode;
|
|
||||||
use rand::{rngs::OsRng, CryptoRng, Rng};
|
use rand::{rngs::OsRng, CryptoRng, Rng};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -51,20 +50,9 @@ pub struct Config {
|
|||||||
|
|
||||||
/// Average delay an acknowledgement packet is going to get delayed at a single mixnode.
|
/// Average delay an acknowledgement packet is going to get delayed at a single mixnode.
|
||||||
average_ack_delay_duration: Duration,
|
average_ack_delay_duration: Duration,
|
||||||
|
|
||||||
/// Mode of all mix packets created - VPN or Mix. They indicate whether packets should get delayed
|
|
||||||
/// and keys reused.
|
|
||||||
packet_mode: PacketMode,
|
|
||||||
|
|
||||||
/// If the mode of the client is set to VPN it specifies number of packets created with the
|
|
||||||
/// same initial secret until it gets rotated.
|
|
||||||
vpn_key_reuse_limit: Option<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
// at this point I'm not entirely sure how to deal with this warning without
|
|
||||||
// some considerable refactoring
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
ack_key: Arc<AckKey>,
|
ack_key: Arc<AckKey>,
|
||||||
ack_wait_multiplier: f64,
|
ack_wait_multiplier: f64,
|
||||||
@@ -73,8 +61,6 @@ impl Config {
|
|||||||
average_message_sending_delay: Duration,
|
average_message_sending_delay: Duration,
|
||||||
average_packet_delay_duration: Duration,
|
average_packet_delay_duration: Duration,
|
||||||
self_recipient: Recipient,
|
self_recipient: Recipient,
|
||||||
packet_mode: PacketMode,
|
|
||||||
vpn_key_reuse_limit: Option<usize>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Config {
|
Config {
|
||||||
ack_key,
|
ack_key,
|
||||||
@@ -84,8 +70,6 @@ impl Config {
|
|||||||
average_message_sending_delay,
|
average_message_sending_delay,
|
||||||
average_packet_delay_duration,
|
average_packet_delay_duration,
|
||||||
average_ack_delay_duration,
|
average_ack_delay_duration,
|
||||||
packet_mode,
|
|
||||||
vpn_key_reuse_limit,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,8 +110,6 @@ impl RealMessagesController<OsRng> {
|
|||||||
config.ack_wait_multiplier,
|
config.ack_wait_multiplier,
|
||||||
config.average_ack_delay_duration,
|
config.average_ack_delay_duration,
|
||||||
config.average_packet_delay_duration,
|
config.average_packet_delay_duration,
|
||||||
config.packet_mode,
|
|
||||||
config.vpn_key_reuse_limit,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let ack_control = AcknowledgementController::new(
|
let ack_control = AcknowledgementController::new(
|
||||||
@@ -163,7 +145,7 @@ impl RealMessagesController<OsRng> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn run(&mut self, vpn_mode: bool) {
|
pub(super) async fn run(&mut self) {
|
||||||
let mut out_queue_control = self.out_queue_control.take().unwrap();
|
let mut out_queue_control = self.out_queue_control.take().unwrap();
|
||||||
let mut ack_control = self.ack_control.take().unwrap();
|
let mut ack_control = self.ack_control.take().unwrap();
|
||||||
|
|
||||||
@@ -171,7 +153,7 @@ impl RealMessagesController<OsRng> {
|
|||||||
// the task to ever finish. This will of course change once we introduce
|
// the task to ever finish. This will of course change once we introduce
|
||||||
// graceful shutdowns.
|
// graceful shutdowns.
|
||||||
let out_queue_control_fut = tokio::spawn(async move {
|
let out_queue_control_fut = tokio::spawn(async move {
|
||||||
out_queue_control.run_out_queue_control(vpn_mode).await;
|
out_queue_control.run_out_queue_control().await;
|
||||||
error!("The out queue controller has finished execution!");
|
error!("The out queue controller has finished execution!");
|
||||||
out_queue_control
|
out_queue_control
|
||||||
});
|
});
|
||||||
@@ -190,9 +172,9 @@ impl RealMessagesController<OsRng> {
|
|||||||
|
|
||||||
// &Handle is only passed for consistency sake with other client modules, but I think
|
// &Handle is only passed for consistency sake with other client modules, but I think
|
||||||
// when we get to refactoring, we should apply gateway approach and make it implicit
|
// when we get to refactoring, we should apply gateway approach and make it implicit
|
||||||
pub fn start(mut self, handle: &Handle, vpn_mode: bool) -> JoinHandle<Self> {
|
pub fn start(mut self, handle: &Handle) -> JoinHandle<Self> {
|
||||||
handle.spawn(async move {
|
handle.spawn(async move {
|
||||||
self.run(vpn_mode).await;
|
self.run().await;
|
||||||
self
|
self
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,15 +249,6 @@ where
|
|||||||
tokio::task::yield_now().await;
|
tokio::task::yield_now().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_batch_received(&mut self, real_messages: Vec<RealMessage>) {
|
|
||||||
let mut mix_packets = Vec::with_capacity(real_messages.len());
|
|
||||||
for real_message in real_messages.into_iter() {
|
|
||||||
self.sent_notify(real_message.fragment_id);
|
|
||||||
mix_packets.push(real_message.mix_packet);
|
|
||||||
}
|
|
||||||
self.mix_tx.unbounded_send(mix_packets).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send messages at certain rate and if no real traffic is available, send cover message.
|
// Send messages at certain rate and if no real traffic is available, send cover message.
|
||||||
async fn run_normal_out_queue(&mut self) {
|
async fn run_normal_out_queue(&mut self) {
|
||||||
// we should set initial delay only when we actually start the stream
|
// we should set initial delay only when we actually start the stream
|
||||||
@@ -271,20 +262,8 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send real message as soon as it's available and don't inject ANY cover traffic.
|
pub(crate) async fn run_out_queue_control(&mut self) {
|
||||||
async fn run_vpn_out_queue(&mut self) {
|
debug!("Starting out queue controller...");
|
||||||
while let Some(next_messages) = self.real_receiver.next().await {
|
self.run_normal_out_queue().await
|
||||||
self.on_batch_received(next_messages).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn run_out_queue_control(&mut self, vpn_mode: bool) {
|
|
||||||
if vpn_mode {
|
|
||||||
debug!("Starting out queue controller in vpn mode...");
|
|
||||||
self.run_vpn_out_queue().await
|
|
||||||
} else {
|
|
||||||
debug!("Starting out queue controller...");
|
|
||||||
self.run_normal_out_queue().await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub enum ReplyKeyStorageError {
|
|||||||
/// Permanent storage for keys in all sent [`ReplySURB`]
|
/// Permanent storage for keys in all sent [`ReplySURB`]
|
||||||
///
|
///
|
||||||
/// Each sent out [`ReplySURB`] has a new key associated with it that is going to be used for
|
/// Each sent out [`ReplySURB`] has a new key associated with it that is going to be used for
|
||||||
/// payload encryption. In order to decrypt whatever reply we receive, we need to know which
|
/// payload encryption. In order to -decrypt whatever reply we receive, we need to know which
|
||||||
/// key to use for that purpose. We do it based on received `H(t)` which has to be included
|
/// key to use for that purpose. We do it based on received `H(t)` which has to be included
|
||||||
/// with each reply.
|
/// with each reply.
|
||||||
/// Moreover, there is no restriction when the [`ReplySURB`] might get used so we need to
|
/// Moreover, there is no restriction when the [`ReplySURB`] might get used so we need to
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
use log::*;
|
use log::*;
|
||||||
use nymsphinx::addressing::clients::Recipient;
|
use nymsphinx::addressing::clients::Recipient;
|
||||||
use nymsphinx::params::DEFAULT_NUM_MIX_HOPS;
|
use nymsphinx::params::DEFAULT_NUM_MIX_HOPS;
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
use rand::thread_rng;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time;
|
use std::time;
|
||||||
@@ -12,6 +14,7 @@ use tokio::runtime::Handle;
|
|||||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use topology::{nym_topology_from_bonds, NymTopology};
|
use topology::{nym_topology_from_bonds, NymTopology};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
// I'm extremely curious why compiler NEVER complained about lack of Debug here before
|
// I'm extremely curious why compiler NEVER complained about lack of Debug here before
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -104,7 +107,7 @@ impl TopologyAccessor {
|
|||||||
self.inner.read().await.into()
|
self.inner.read().await.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_global_topology(&mut self, new_topology: Option<NymTopology>) {
|
async fn update_global_topology(&self, new_topology: Option<NymTopology>) {
|
||||||
self.inner.write().await.update(new_topology);
|
self.inner.write().await.update(new_topology);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,54 +128,128 @@ impl Default for TopologyAccessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct TopologyRefresherConfig {
|
pub struct TopologyRefresherConfig {
|
||||||
available_validators: Vec<String>,
|
validator_api_urls: Vec<Url>,
|
||||||
mixnet_contract_address: String,
|
|
||||||
refresh_rate: time::Duration,
|
refresh_rate: time::Duration,
|
||||||
|
client_version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TopologyRefresherConfig {
|
impl TopologyRefresherConfig {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
available_validators: Vec<String>,
|
validator_api_urls: Vec<Url>,
|
||||||
mixnet_contract_address: String,
|
|
||||||
refresh_rate: time::Duration,
|
refresh_rate: time::Duration,
|
||||||
|
client_version: String,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
TopologyRefresherConfig {
|
TopologyRefresherConfig {
|
||||||
available_validators,
|
validator_api_urls,
|
||||||
mixnet_contract_address,
|
|
||||||
refresh_rate,
|
refresh_rate,
|
||||||
|
client_version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TopologyRefresher {
|
pub struct TopologyRefresher {
|
||||||
validator_client: validator_client::Client,
|
validator_client: validator_client::ApiClient,
|
||||||
|
client_version: String,
|
||||||
|
|
||||||
|
validator_api_urls: Vec<Url>,
|
||||||
topology_accessor: TopologyAccessor,
|
topology_accessor: TopologyAccessor,
|
||||||
refresh_rate: Duration,
|
refresh_rate: Duration,
|
||||||
|
|
||||||
|
currently_used_api: usize,
|
||||||
was_latest_valid: bool,
|
was_latest_valid: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TopologyRefresher {
|
impl TopologyRefresher {
|
||||||
pub fn new(cfg: TopologyRefresherConfig, topology_accessor: TopologyAccessor) -> Self {
|
pub fn new(mut cfg: TopologyRefresherConfig, topology_accessor: TopologyAccessor) -> Self {
|
||||||
let validator_client_config =
|
cfg.validator_api_urls.shuffle(&mut thread_rng());
|
||||||
validator_client::Config::new(cfg.available_validators, cfg.mixnet_contract_address);
|
|
||||||
let validator_client = validator_client::Client::new(validator_client_config);
|
|
||||||
|
|
||||||
TopologyRefresher {
|
TopologyRefresher {
|
||||||
validator_client,
|
validator_client: validator_client::ApiClient::new(cfg.validator_api_urls[0].clone()),
|
||||||
|
client_version: cfg.client_version,
|
||||||
|
validator_api_urls: cfg.validator_api_urls,
|
||||||
topology_accessor,
|
topology_accessor,
|
||||||
refresh_rate: cfg.refresh_rate,
|
refresh_rate: cfg.refresh_rate,
|
||||||
|
currently_used_api: 0,
|
||||||
was_latest_valid: true,
|
was_latest_valid: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_current_compatible_topology(&mut self) -> Option<NymTopology> {
|
fn use_next_validator_api(&mut self) {
|
||||||
|
if self.validator_api_urls.len() == 1 {
|
||||||
|
warn!("There's only a single validator API available - it won't be possible to use a different one");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currently_used_api = (self.currently_used_api + 1) % self.validator_api_urls.len();
|
||||||
|
self.validator_client
|
||||||
|
.change_validator_api(self.validator_api_urls[self.currently_used_api].clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies whether nodes a reasonably distributed among all mix layers.
|
||||||
|
///
|
||||||
|
/// In ideal world we would have 33% nodes on layer 1, 33% on layer 2 and 33% on layer 3.
|
||||||
|
/// However, this is a rather unrealistic expectation, instead we check whether there exists
|
||||||
|
/// a layer with more than 66% of nodes or with fewer than 15% and if so, we trigger a failure.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `topology`: active topology constructed from validator api data
|
||||||
|
/// * `mixnodes_count`: total number of active mixnodes
|
||||||
|
fn check_layer_distribution(
|
||||||
|
&self,
|
||||||
|
active_topology: &NymTopology,
|
||||||
|
mixnodes_count: usize,
|
||||||
|
) -> bool {
|
||||||
|
let mixes = active_topology.mixes();
|
||||||
|
if active_topology.gateways().is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// trivial check to see if have at least a single node on each layer (regardless of active set size)
|
||||||
|
if mixes.get(&1).is_none() || mixes.get(&2).is_none() || mixes.get(&3).is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let upper_bound = (mixnodes_count as f32 * 0.66) as usize;
|
||||||
|
let lower_bound = (mixnodes_count as f32 * 0.15) as usize;
|
||||||
|
|
||||||
|
let layer1 = mixes.get(&1).unwrap().len();
|
||||||
|
let layer2 = mixes.get(&2).unwrap().len();
|
||||||
|
let layer3 = mixes.get(&3).unwrap().len();
|
||||||
|
|
||||||
|
if layer1 < lower_bound || layer1 > upper_bound {
|
||||||
|
warn!(
|
||||||
|
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
|
||||||
|
mixnodes_count, layer1, layer2, layer3
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if layer2 < lower_bound || layer2 > upper_bound {
|
||||||
|
warn!(
|
||||||
|
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
|
||||||
|
mixnodes_count, layer1, layer2, layer3
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if layer3 < lower_bound || layer3 > upper_bound {
|
||||||
|
warn!(
|
||||||
|
"nodes: {}, layer1: {}, layer2: {}, layer3: {}",
|
||||||
|
mixnodes_count, layer1, layer2, layer3
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_current_compatible_topology(&self) -> Option<NymTopology> {
|
||||||
// TODO: optimization for the future:
|
// TODO: optimization for the future:
|
||||||
// only refresh mixnodes on timer and refresh gateways only when
|
// only refresh mixnodes on timer and refresh gateways only when
|
||||||
// we have to send to a new, unknown, gateway
|
// we have to send to a new, unknown, gateway
|
||||||
|
|
||||||
let mixnodes = match self.validator_client.get_cached_mix_nodes().await {
|
let mixnodes = match self.validator_client.get_cached_active_mixnodes().await {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("failed to get network mixnodes - {}", err);
|
error!("failed to get network mixnodes - {}", err);
|
||||||
return None;
|
return None;
|
||||||
@@ -188,17 +265,26 @@ impl TopologyRefresher {
|
|||||||
Ok(gateways) => gateways,
|
Ok(gateways) => gateways,
|
||||||
};
|
};
|
||||||
|
|
||||||
let topology = nym_topology_from_bonds(mixnodes, gateways);
|
let mixnodes_count = mixnodes.len();
|
||||||
|
let topology =
|
||||||
|
nym_topology_from_bonds(mixnodes, gateways).filter_system_version(&self.client_version);
|
||||||
|
|
||||||
// TODO: I didn't want to change it now, but the expected system version should rather be put in config
|
if !self.check_layer_distribution(&topology, mixnodes_count) {
|
||||||
// rather than pulled from package version of `client_core`
|
warn!("The current filtered active topology has extremely skewed layer distribution. It cannot be used.");
|
||||||
Some(topology.filter_system_version(env!("CARGO_PKG_VERSION")))
|
None
|
||||||
|
} else {
|
||||||
|
Some(topology)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn refresh(&mut self) {
|
pub async fn refresh(&mut self) {
|
||||||
trace!("Refreshing the topology");
|
trace!("Refreshing the topology");
|
||||||
let new_topology = self.get_current_compatible_topology().await;
|
let new_topology = self.get_current_compatible_topology().await;
|
||||||
|
|
||||||
|
if new_topology.is_none() {
|
||||||
|
self.use_next_validator_api();
|
||||||
|
}
|
||||||
|
|
||||||
if new_topology.is_none() && self.was_latest_valid {
|
if new_topology.is_none() && self.was_latest_valid {
|
||||||
// if we failed to grab this topology, but the one before it was alright, let's assume
|
// if we failed to grab this topology, but the one before it was alright, let's assume
|
||||||
// validator had a tiny hiccup and use the old data
|
// validator had a tiny hiccup and use the old data
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use config::{deserialize_duration, deserialize_validators, NymConfig};
|
use config::defaults::*;
|
||||||
|
use config::NymConfig;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
pub mod persistence;
|
pub mod persistence;
|
||||||
|
|
||||||
pub const MISSING_VALUE: &str = "MISSING VALUE";
|
pub const MISSING_VALUE: &str = "MISSING VALUE";
|
||||||
|
|
||||||
// 'CLIENT'
|
|
||||||
pub const DEFAULT_VALIDATOR_REST_ENDPOINTS: &[&str] = &[
|
|
||||||
"http://testnet-milhon-validator1.nymtech.net:1317",
|
|
||||||
"http://testnet-milhon-validator2.nymtech.net:1317",
|
|
||||||
];
|
|
||||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "punk10pyejy66429refv3g35g2t7am0was7yalwrzen";
|
|
||||||
|
|
||||||
// 'DEBUG'
|
// 'DEBUG'
|
||||||
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
|
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
|
||||||
|
|
||||||
@@ -27,27 +22,15 @@ const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20)
|
|||||||
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
|
const DEFAULT_AVERAGE_PACKET_DELAY: Duration = Duration::from_millis(50);
|
||||||
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
|
const DEFAULT_TOPOLOGY_REFRESH_RATE: Duration = Duration::from_secs(5 * 60); // every 5min
|
||||||
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
|
const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
|
||||||
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_millis(1_500);
|
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
|
||||||
const DEFAULT_VPN_KEY_REUSE_LIMIT: usize = 1000;
|
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
|
||||||
|
// bandwidth bridging protocol, we can come back to a smaller timeout value
|
||||||
const ZERO_DELAY: Duration = Duration::from_nanos(0);
|
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
|
||||||
|
|
||||||
// helper function to get default validators as a Vec<String>
|
|
||||||
pub fn default_validator_rest_endpoints() -> Vec<String> {
|
|
||||||
DEFAULT_VALIDATOR_REST_ENDPOINTS
|
|
||||||
.iter()
|
|
||||||
.map(|&endpoint| endpoint.to_string())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn missing_string_value() -> String {
|
pub fn missing_string_value() -> String {
|
||||||
MISSING_VALUE.to_string()
|
MISSING_VALUE.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn missing_vec_string_value() -> Vec<String> {
|
|
||||||
vec![missing_string_value()]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Config<T> {
|
pub struct Config<T> {
|
||||||
@@ -120,6 +103,17 @@ impl<T: NymConfig> Config<T> {
|
|||||||
self::Client::<T>::default_reply_encryption_key_store_path(&id);
|
self::Client::<T>::default_reply_encryption_key_store_path(&id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
if self
|
||||||
|
.client
|
||||||
|
.backup_bandwidth_token_keys_dir
|
||||||
|
.as_os_str()
|
||||||
|
.is_empty()
|
||||||
|
{
|
||||||
|
self.client.backup_bandwidth_token_keys_dir =
|
||||||
|
self::Client::<T>::default_backup_bandwidth_token_keys_dir(&id);
|
||||||
|
}
|
||||||
|
|
||||||
self.client.id = id;
|
self.client.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,12 +125,18 @@ impl<T: NymConfig> Config<T> {
|
|||||||
self.client.gateway_listener = gateway_listener.into();
|
self.client.gateway_listener = gateway_listener.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_custom_validators(&mut self, validators: Vec<String>) {
|
#[cfg(not(feature = "coconut"))]
|
||||||
self.client.validator_rest_urls = validators;
|
pub fn with_eth_private_key<S: Into<String>>(&mut self, eth_private_key: S) {
|
||||||
|
self.client.eth_private_key = eth_private_key.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_mixnet_contract<S: Into<String>>(&mut self, contract_address: S) {
|
#[cfg(not(feature = "coconut"))]
|
||||||
self.client.mixnet_contract_address = contract_address.into();
|
pub fn with_eth_endpoint<S: Into<String>>(&mut self, eth_endpoint: S) {
|
||||||
|
self.client.eth_endpoint = eth_endpoint.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
|
||||||
|
self.client.validator_api_urls = validator_api_urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_high_default_traffic_volume(&mut self) {
|
pub fn set_high_default_traffic_volume(&mut self) {
|
||||||
@@ -145,14 +145,6 @@ impl<T: NymConfig> Config<T> {
|
|||||||
self.debug.message_sending_average_delay = Duration::from_millis(4); // 250 "real" messages / s
|
self.debug.message_sending_average_delay = Duration::from_millis(4); // 250 "real" messages / s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_vpn_mode(&mut self, vpn_mode: bool) {
|
|
||||||
self.client.vpn_mode = vpn_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_vpn_key_reuse_limit(&mut self, reuse_limit: usize) {
|
|
||||||
self.debug.vpn_key_reuse_limit = Some(reuse_limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_custom_version(&mut self, version: &str) {
|
pub fn set_custom_version(&mut self, version: &str) {
|
||||||
self.client.version = version.to_string();
|
self.client.version = version.to_string();
|
||||||
}
|
}
|
||||||
@@ -193,12 +185,8 @@ impl<T: NymConfig> Config<T> {
|
|||||||
self.client.ack_key_file.clone()
|
self.client.ack_key_file.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_validator_rest_endpoints(&self) -> Vec<String> {
|
pub fn get_validator_api_endpoints(&self) -> Vec<Url> {
|
||||||
self.client.validator_rest_urls.clone()
|
self.client.validator_api_urls.clone()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_validator_mixnet_contract_address(&self) -> String {
|
|
||||||
self.client.mixnet_contract_address.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_gateway_id(&self) -> String {
|
pub fn get_gateway_id(&self) -> String {
|
||||||
@@ -209,21 +197,28 @@ impl<T: NymConfig> Config<T> {
|
|||||||
self.client.gateway_listener.clone()
|
self.client.gateway_listener.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
pub fn get_backup_bandwidth_token_keys_dir(&self) -> PathBuf {
|
||||||
|
self.client.backup_bandwidth_token_keys_dir.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
pub fn get_eth_endpoint(&self) -> String {
|
||||||
|
self.client.eth_endpoint.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
pub fn get_eth_private_key(&self) -> String {
|
||||||
|
self.client.eth_private_key.clone()
|
||||||
|
}
|
||||||
|
|
||||||
// Debug getters
|
// Debug getters
|
||||||
pub fn get_average_packet_delay(&self) -> Duration {
|
pub fn get_average_packet_delay(&self) -> Duration {
|
||||||
if self.client.vpn_mode {
|
self.debug.average_packet_delay
|
||||||
ZERO_DELAY
|
|
||||||
} else {
|
|
||||||
self.debug.average_packet_delay
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_average_ack_delay(&self) -> Duration {
|
pub fn get_average_ack_delay(&self) -> Duration {
|
||||||
if self.client.vpn_mode {
|
self.debug.average_ack_delay
|
||||||
ZERO_DELAY
|
|
||||||
} else {
|
|
||||||
self.debug.average_ack_delay
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ack_wait_multiplier(&self) -> f64 {
|
pub fn get_ack_wait_multiplier(&self) -> f64 {
|
||||||
@@ -239,11 +234,7 @@ impl<T: NymConfig> Config<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_message_sending_average_delay(&self) -> Duration {
|
pub fn get_message_sending_average_delay(&self) -> Duration {
|
||||||
if self.client.vpn_mode {
|
self.debug.message_sending_average_delay
|
||||||
ZERO_DELAY
|
|
||||||
} else {
|
|
||||||
self.debug.message_sending_average_delay
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_gateway_response_timeout(&self) -> Duration {
|
pub fn get_gateway_response_timeout(&self) -> Duration {
|
||||||
@@ -258,21 +249,6 @@ impl<T: NymConfig> Config<T> {
|
|||||||
self.debug.topology_resolution_timeout
|
self.debug.topology_resolution_timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_vpn_mode(&self) -> bool {
|
|
||||||
self.client.vpn_mode
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_vpn_key_reuse_limit(&self) -> Option<usize> {
|
|
||||||
match self.get_vpn_mode() {
|
|
||||||
false => None,
|
|
||||||
true => Some(
|
|
||||||
self.debug
|
|
||||||
.vpn_key_reuse_limit
|
|
||||||
.unwrap_or(DEFAULT_VPN_KEY_REUSE_LIMIT),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_version(&self) -> &str {
|
pub fn get_version(&self) -> &str {
|
||||||
&self.client.version
|
&self.client.version
|
||||||
}
|
}
|
||||||
@@ -297,23 +273,8 @@ pub struct Client<T> {
|
|||||||
/// ID specifies the human readable ID of this particular client.
|
/// ID specifies the human readable ID of this particular client.
|
||||||
id: String,
|
id: String,
|
||||||
|
|
||||||
/// URL to the validator server for obtaining network topology.
|
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
||||||
#[serde(
|
validator_api_urls: Vec<Url>,
|
||||||
deserialize_with = "deserialize_validators",
|
|
||||||
default = "missing_vec_string_value",
|
|
||||||
alias = "validator_rest_url"
|
|
||||||
)]
|
|
||||||
validator_rest_urls: Vec<String>,
|
|
||||||
|
|
||||||
/// Address of the validator contract managing the network.
|
|
||||||
#[serde(default = "missing_string_value")]
|
|
||||||
mixnet_contract_address: String,
|
|
||||||
|
|
||||||
/// Special mode of the system such that all messages are sent as soon as they are received
|
|
||||||
/// and no cover traffic is generated. If set all message delays are set to 0 and overwriting
|
|
||||||
/// 'Debug' values will have no effect.
|
|
||||||
#[serde(default)]
|
|
||||||
vpn_mode: bool,
|
|
||||||
|
|
||||||
/// Path to file containing private identity key.
|
/// Path to file containing private identity key.
|
||||||
private_identity_key_file: PathBuf,
|
private_identity_key_file: PathBuf,
|
||||||
@@ -346,6 +307,20 @@ pub struct Client<T> {
|
|||||||
/// Address of the gateway listener to which all client requests should be sent.
|
/// Address of the gateway listener to which all client requests should be sent.
|
||||||
gateway_listener: String,
|
gateway_listener: String,
|
||||||
|
|
||||||
|
/// Path to directory containing public/private keys used for bandwidth token purchase.
|
||||||
|
/// Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
|
||||||
|
/// The public key is the name of the file, while the private key is the content.
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
backup_bandwidth_token_keys_dir: PathBuf,
|
||||||
|
|
||||||
|
/// Ethereum private key.
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
eth_private_key: String,
|
||||||
|
|
||||||
|
/// Address to an Ethereum full node.
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
eth_endpoint: String,
|
||||||
|
|
||||||
/// nym_home_directory specifies absolute path to the home nym Clients directory.
|
/// nym_home_directory specifies absolute path to the home nym Clients directory.
|
||||||
/// It is expected to use default value and hence .toml file should not redefine this field.
|
/// It is expected to use default value and hence .toml file should not redefine this field.
|
||||||
nym_root_directory: PathBuf,
|
nym_root_directory: PathBuf,
|
||||||
@@ -360,9 +335,7 @@ impl<T: NymConfig> Default for Client<T> {
|
|||||||
Client {
|
Client {
|
||||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
id: "".to_string(),
|
id: "".to_string(),
|
||||||
validator_rest_urls: default_validator_rest_endpoints(),
|
validator_api_urls: default_api_endpoints(),
|
||||||
mixnet_contract_address: DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string(),
|
|
||||||
vpn_mode: false,
|
|
||||||
private_identity_key_file: Default::default(),
|
private_identity_key_file: Default::default(),
|
||||||
public_identity_key_file: Default::default(),
|
public_identity_key_file: Default::default(),
|
||||||
private_encryption_key_file: Default::default(),
|
private_encryption_key_file: Default::default(),
|
||||||
@@ -372,6 +345,12 @@ impl<T: NymConfig> Default for Client<T> {
|
|||||||
reply_encryption_key_store_path: Default::default(),
|
reply_encryption_key_store_path: Default::default(),
|
||||||
gateway_id: "".to_string(),
|
gateway_id: "".to_string(),
|
||||||
gateway_listener: "".to_string(),
|
gateway_listener: "".to_string(),
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
backup_bandwidth_token_keys_dir: Default::default(),
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
eth_private_key: "".to_string(),
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
eth_endpoint: "".to_string(),
|
||||||
nym_root_directory: T::default_root_directory(),
|
nym_root_directory: T::default_root_directory(),
|
||||||
super_struct: Default::default(),
|
super_struct: Default::default(),
|
||||||
}
|
}
|
||||||
@@ -406,18 +385,17 @@ impl<T: NymConfig> Client<T> {
|
|||||||
fn default_reply_encryption_key_store_path(id: &str) -> PathBuf {
|
fn default_reply_encryption_key_store_path(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(Some(id)).join("reply_key_store")
|
T::default_data_directory(Some(id)).join("reply_key_store")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
#[cfg(not(feature = "coconut"))]
|
||||||
#[serde(deny_unknown_fields)]
|
fn default_backup_bandwidth_token_keys_dir(id: &str) -> PathBuf {
|
||||||
pub struct Logging {}
|
T::default_data_directory(Some(id)).join("backup_bandwidth_token_keys")
|
||||||
|
|
||||||
impl Default for Logging {
|
|
||||||
fn default() -> Self {
|
|
||||||
Logging {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Logging {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||||
#[serde(default, deny_unknown_fields)]
|
#[serde(default, deny_unknown_fields)]
|
||||||
pub struct Debug {
|
pub struct Debug {
|
||||||
@@ -425,20 +403,14 @@ pub struct Debug {
|
|||||||
/// sent packet is going to be delayed at any given mix node.
|
/// sent packet is going to be delayed at any given mix node.
|
||||||
/// So for a packet going through three mix nodes, on average, it will take three times this value
|
/// So for a packet going through three mix nodes, on average, it will take three times this value
|
||||||
/// until the packet reaches its destination.
|
/// until the packet reaches its destination.
|
||||||
#[serde(
|
#[serde(with = "humantime_serde")]
|
||||||
deserialize_with = "deserialize_duration",
|
|
||||||
serialize_with = "humantime_serde::serialize"
|
|
||||||
)]
|
|
||||||
average_packet_delay: Duration,
|
average_packet_delay: Duration,
|
||||||
|
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
/// sent acknowledgement is going to be delayed at any given mix node.
|
/// sent acknowledgement is going to be delayed at any given mix node.
|
||||||
/// So for an ack going through three mix nodes, on average, it will take three times this value
|
/// So for an ack going through three mix nodes, on average, it will take three times this value
|
||||||
/// until the packet reaches its destination.
|
/// until the packet reaches its destination.
|
||||||
#[serde(
|
#[serde(with = "humantime_serde")]
|
||||||
deserialize_with = "deserialize_duration",
|
|
||||||
serialize_with = "humantime_serde::serialize"
|
|
||||||
)]
|
|
||||||
average_ack_delay: Duration,
|
average_ack_delay: Duration,
|
||||||
|
|
||||||
/// Value multiplied with the expected round trip time of an acknowledgement packet before
|
/// Value multiplied with the expected round trip time of an acknowledgement packet before
|
||||||
@@ -449,58 +421,36 @@ pub struct Debug {
|
|||||||
/// Value added to the expected round trip time of an acknowledgement packet before
|
/// Value added to the expected round trip time of an acknowledgement packet before
|
||||||
/// it is assumed it was lost and retransmission of the data packet happens.
|
/// it is assumed it was lost and retransmission of the data packet happens.
|
||||||
/// In an ideal network with 0 latency, this value would have been 0.
|
/// In an ideal network with 0 latency, this value would have been 0.
|
||||||
#[serde(
|
#[serde(with = "humantime_serde")]
|
||||||
deserialize_with = "deserialize_duration",
|
|
||||||
serialize_with = "humantime_serde::serialize"
|
|
||||||
)]
|
|
||||||
ack_wait_addition: Duration,
|
ack_wait_addition: Duration,
|
||||||
|
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
/// it is going to take for another loop cover traffic message to be sent.
|
/// it is going to take for another loop cover traffic message to be sent.
|
||||||
#[serde(
|
#[serde(with = "humantime_serde")]
|
||||||
deserialize_with = "deserialize_duration",
|
|
||||||
serialize_with = "humantime_serde::serialize"
|
|
||||||
)]
|
|
||||||
loop_cover_traffic_average_delay: Duration,
|
loop_cover_traffic_average_delay: Duration,
|
||||||
|
|
||||||
/// The parameter of Poisson distribution determining how long, on average,
|
/// The parameter of Poisson distribution determining how long, on average,
|
||||||
/// it is going to take another 'real traffic stream' message to be sent.
|
/// it is going to take another 'real traffic stream' message to be sent.
|
||||||
/// If no real packets are available and cover traffic is enabled,
|
/// If no real packets are available and cover traffic is enabled,
|
||||||
/// a loop cover message is sent instead in order to preserve the rate.
|
/// a loop cover message is sent instead in order to preserve the rate.
|
||||||
#[serde(
|
#[serde(with = "humantime_serde")]
|
||||||
deserialize_with = "deserialize_duration",
|
|
||||||
serialize_with = "humantime_serde::serialize"
|
|
||||||
)]
|
|
||||||
message_sending_average_delay: Duration,
|
message_sending_average_delay: Duration,
|
||||||
|
|
||||||
/// How long we're willing to wait for a response to a message sent to the gateway,
|
/// How long we're willing to wait for a response to a message sent to the gateway,
|
||||||
/// before giving up on it.
|
/// before giving up on it.
|
||||||
#[serde(
|
#[serde(with = "humantime_serde")]
|
||||||
deserialize_with = "deserialize_duration",
|
|
||||||
serialize_with = "humantime_serde::serialize"
|
|
||||||
)]
|
|
||||||
gateway_response_timeout: Duration,
|
gateway_response_timeout: Duration,
|
||||||
|
|
||||||
/// The uniform delay every which clients are querying the directory server
|
/// The uniform delay every which clients are querying the directory server
|
||||||
/// to try to obtain a compatible network topology to send sphinx packets through.
|
/// to try to obtain a compatible network topology to send sphinx packets through.
|
||||||
#[serde(
|
#[serde(with = "humantime_serde")]
|
||||||
deserialize_with = "deserialize_duration",
|
|
||||||
serialize_with = "humantime_serde::serialize"
|
|
||||||
)]
|
|
||||||
topology_refresh_rate: Duration,
|
topology_refresh_rate: Duration,
|
||||||
|
|
||||||
/// During topology refresh, test packets are sent through every single possible network
|
/// During topology refresh, test packets are sent through every single possible network
|
||||||
/// path. This timeout determines waiting period until it is decided that the packet
|
/// path. This timeout determines waiting period until it is decided that the packet
|
||||||
/// did not reach its destination.
|
/// did not reach its destination.
|
||||||
#[serde(
|
#[serde(with = "humantime_serde")]
|
||||||
deserialize_with = "deserialize_duration",
|
|
||||||
serialize_with = "humantime_serde::serialize"
|
|
||||||
)]
|
|
||||||
topology_resolution_timeout: Duration,
|
topology_resolution_timeout: Duration,
|
||||||
|
|
||||||
/// If the mode of the client is set to VPN it specifies number of packets created with the
|
|
||||||
/// same initial secret until it gets rotated.
|
|
||||||
vpn_key_reuse_limit: Option<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Debug {
|
impl Default for Debug {
|
||||||
@@ -515,7 +465,6 @@ impl Default for Debug {
|
|||||||
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||||
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
|
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
|
||||||
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
||||||
vpn_key_reuse_limit: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ name = "nym-client"
|
|||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
rust-version = "1.56"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ futures = "0.3" # bunch of futures stuff, however, now that I think about it, it
|
|||||||
# the AsyncRead, AsyncWrite, Stream, Sink, etc. traits could be used from tokio
|
# the AsyncRead, AsyncWrite, Stream, Sink, etc. traits could be used from tokio
|
||||||
# channels should really be replaced with crossbeam due to that implementation being more efficient
|
# channels should really be replaced with crossbeam due to that implementation being more efficient
|
||||||
# and the single instance of abortable we have should really be refactored anyway
|
# and the single instance of abortable we have should really be refactored anyway
|
||||||
url = "2.1" # I think we should just replace it with String
|
url = "2.2"
|
||||||
|
|
||||||
clap = "2.33.0" # for the command line arguments
|
clap = "2.33.0" # for the command line arguments
|
||||||
dirs = "3.0" # for determining default store directories in config
|
dirs = "3.0" # for determining default store directories in config
|
||||||
@@ -31,6 +32,8 @@ tokio-tungstenite = "0.14" # websocket
|
|||||||
|
|
||||||
## internal
|
## internal
|
||||||
client-core = { path = "../client-core" }
|
client-core = { path = "../client-core" }
|
||||||
|
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||||
|
credentials = { path = "../../common/credentials", optional = true }
|
||||||
config = { path = "../../common/config" }
|
config = { path = "../../common/config" }
|
||||||
crypto = { path = "../../common/crypto" }
|
crypto = { path = "../../common/crypto" }
|
||||||
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
||||||
@@ -42,5 +45,8 @@ websocket-requests = { path = "websocket-requests" }
|
|||||||
validator-client = { path = "../../common/client-libs/validator-client" }
|
validator-client = { path = "../../common/client-libs/validator-client" }
|
||||||
version-checker = { path = "../../common/version-checker" }
|
version-checker = { path = "../../common/version-checker" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json = "1.0" # for the "textsend" example
|
serde_json = "1.0" # for the "textsend" example
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
use crate::client::config::template::config_template;
|
use crate::client::config::template::config_template;
|
||||||
use client_core::config::Config as BaseConfig;
|
use client_core::config::Config as BaseConfig;
|
||||||
pub use client_core::config::MISSING_VALUE;
|
pub use client_core::config::MISSING_VALUE;
|
||||||
|
use config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
const DEFAULT_LISTENING_PORT: u16 = 1977;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)]
|
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub enum SocketType {
|
pub enum SocketType {
|
||||||
@@ -117,7 +116,7 @@ impl Default for Socket {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Socket {
|
Socket {
|
||||||
socket_type: SocketType::WebSocket,
|
socket_type: SocketType::WebSocket,
|
||||||
listening_port: DEFAULT_LISTENING_PORT,
|
listening_port: DEFAULT_WEBSOCKET_LISTENING_PORT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,21 +19,13 @@ version = '{{ client.version }}'
|
|||||||
# Human readable ID of this particular client.
|
# Human readable ID of this particular client.
|
||||||
id = '{{ client.id }}'
|
id = '{{ client.id }}'
|
||||||
|
|
||||||
# URL to the validator server for obtaining network topology.
|
# Addresses to APIs running on validator from which the client gets the view of the network.
|
||||||
validator_rest_urls = [
|
validator_api_urls = [
|
||||||
{{#each client.validator_rest_urls }}
|
{{#each client.validator_api_urls }}
|
||||||
'{{this}}',
|
'{{this}}',
|
||||||
{{/each}}
|
{{/each}}
|
||||||
]
|
]
|
||||||
|
|
||||||
# Address of the validator contract managing the network.
|
|
||||||
mixnet_contract_address = '{{ client.mixnet_contract_address }}'
|
|
||||||
|
|
||||||
# Special mode of the system such that all messages are sent as soon as they are received
|
|
||||||
# and no cover traffic is generated. If set all message delays are set to 0 and overwriting
|
|
||||||
# 'Debug' values will have no effect.
|
|
||||||
vpn_mode = {{ client.vpn_mode }}
|
|
||||||
|
|
||||||
# Path to file containing private identity key.
|
# Path to file containing private identity key.
|
||||||
private_identity_key_file = '{{ client.private_identity_key_file }}'
|
private_identity_key_file = '{{ client.private_identity_key_file }}'
|
||||||
|
|
||||||
@@ -50,6 +42,17 @@ public_encryption_key_file = '{{ client.public_encryption_key_file }}'
|
|||||||
# sent but not received back.
|
# sent but not received back.
|
||||||
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
||||||
|
|
||||||
|
# Path to directory containing public/private keys used for bandwidth token purchase.
|
||||||
|
# Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
|
||||||
|
# The public key is the name of the file, while the private key is the content.
|
||||||
|
backup_bandwidth_token_keys_dir = '{{ client.backup_bandwidth_token_keys_dir }}'
|
||||||
|
|
||||||
|
# Ethereum private key.
|
||||||
|
eth_private_key = '{{ client.eth_private_key }}'
|
||||||
|
|
||||||
|
# Addess to an Ethereum full node.
|
||||||
|
eth_endpoint = '{{ client.eth_endpoint }}'
|
||||||
|
|
||||||
##### additional client config options #####
|
##### additional client config options #####
|
||||||
|
|
||||||
# ID of the gateway from which the client should be fetching messages.
|
# ID of the gateway from which the client should be fetching messages.
|
||||||
|
|||||||
@@ -32,10 +32,11 @@ use log::*;
|
|||||||
use nymsphinx::addressing::clients::Recipient;
|
use nymsphinx::addressing::clients::Recipient;
|
||||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||||
use nymsphinx::anonymous_replies::ReplySurb;
|
use nymsphinx::anonymous_replies::ReplySurb;
|
||||||
use nymsphinx::params::PacketMode;
|
|
||||||
use nymsphinx::receiver::ReconstructedMessage;
|
use nymsphinx::receiver::ReconstructedMessage;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
use gateway_client::bandwidth::BandwidthController;
|
||||||
|
|
||||||
pub(crate) mod config;
|
pub(crate) mod config;
|
||||||
|
|
||||||
pub struct NymClient {
|
pub struct NymClient {
|
||||||
@@ -119,12 +120,6 @@ impl NymClient {
|
|||||||
input_receiver: InputMessageReceiver,
|
input_receiver: InputMessageReceiver,
|
||||||
mix_sender: BatchMixMessageSender,
|
mix_sender: BatchMixMessageSender,
|
||||||
) {
|
) {
|
||||||
let packet_mode = if self.config.get_base().get_vpn_mode() {
|
|
||||||
PacketMode::Vpn
|
|
||||||
} else {
|
|
||||||
PacketMode::Mix
|
|
||||||
};
|
|
||||||
|
|
||||||
let controller_config = real_messages_control::Config::new(
|
let controller_config = real_messages_control::Config::new(
|
||||||
self.key_manager.ack_key(),
|
self.key_manager.ack_key(),
|
||||||
self.config.get_base().get_ack_wait_multiplier(),
|
self.config.get_base().get_ack_wait_multiplier(),
|
||||||
@@ -133,8 +128,6 @@ impl NymClient {
|
|||||||
self.config.get_base().get_message_sending_average_delay(),
|
self.config.get_base().get_message_sending_average_delay(),
|
||||||
self.config.get_base().get_average_packet_delay(),
|
self.config.get_base().get_average_packet_delay(),
|
||||||
self.as_mix_recipient(),
|
self.as_mix_recipient(),
|
||||||
packet_mode,
|
|
||||||
self.config.get_base().get_vpn_key_reuse_limit(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
info!("Starting real traffic stream...");
|
info!("Starting real traffic stream...");
|
||||||
@@ -151,7 +144,7 @@ impl NymClient {
|
|||||||
topology_accessor,
|
topology_accessor,
|
||||||
reply_key_storage,
|
reply_key_storage,
|
||||||
)
|
)
|
||||||
.start(self.runtime.handle(), self.config.get_base().get_vpn_mode());
|
.start(self.runtime.handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
// buffer controlling all messages fetched from provider
|
// buffer controlling all messages fetched from provider
|
||||||
@@ -189,35 +182,47 @@ impl NymClient {
|
|||||||
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
||||||
.expect("provided gateway id is invalid!");
|
.expect("provided gateway id is invalid!");
|
||||||
|
|
||||||
let mut gateway_client = GatewayClient::new(
|
|
||||||
gateway_address,
|
|
||||||
self.key_manager.identity_keypair(),
|
|
||||||
gateway_identity,
|
|
||||||
Some(self.key_manager.gateway_shared_key()),
|
|
||||||
mixnet_message_sender,
|
|
||||||
ack_sender,
|
|
||||||
self.config.get_base().get_gateway_response_timeout(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.runtime.block_on(async {
|
self.runtime.block_on(async {
|
||||||
|
#[cfg(feature = "coconut")]
|
||||||
|
let bandwidth_controller = BandwidthController::new(
|
||||||
|
self.config.get_base().get_validator_api_endpoints(),
|
||||||
|
*self.key_manager.identity_keypair().public_key(),
|
||||||
|
);
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
let bandwidth_controller = BandwidthController::new(
|
||||||
|
self.config.get_base().get_eth_endpoint(),
|
||||||
|
self.config.get_base().get_eth_private_key(),
|
||||||
|
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
|
||||||
|
)
|
||||||
|
.expect("Could not create bandwidth controller");
|
||||||
|
|
||||||
|
let mut gateway_client = GatewayClient::new(
|
||||||
|
gateway_address,
|
||||||
|
self.key_manager.identity_keypair(),
|
||||||
|
gateway_identity,
|
||||||
|
Some(self.key_manager.gateway_shared_key()),
|
||||||
|
mixnet_message_sender,
|
||||||
|
ack_sender,
|
||||||
|
self.config.get_base().get_gateway_response_timeout(),
|
||||||
|
Some(bandwidth_controller),
|
||||||
|
);
|
||||||
|
|
||||||
gateway_client
|
gateway_client
|
||||||
.authenticate_and_start()
|
.authenticate_and_start()
|
||||||
.await
|
.await
|
||||||
.expect("could not authenticate and start up the gateway connection")
|
.expect("could not authenticate and start up the gateway connection");
|
||||||
});
|
|
||||||
|
|
||||||
gateway_client
|
gateway_client
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// future responsible for periodically polling directory server and updating
|
// future responsible for periodically polling directory server and updating
|
||||||
// the current global view of topology
|
// the current global view of topology
|
||||||
fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
||||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||||
self.config.get_base().get_validator_rest_endpoints(),
|
self.config.get_base().get_validator_api_endpoints(),
|
||||||
self.config
|
|
||||||
.get_base()
|
|
||||||
.get_validator_mixnet_contract_address(),
|
|
||||||
self.config.get_base().get_topology_refresh_rate(),
|
self.config.get_base().get_topology_refresh_rate(),
|
||||||
|
env!("CARGO_PKG_VERSION").to_string(),
|
||||||
);
|
);
|
||||||
let mut topology_refresher =
|
let mut topology_refresher =
|
||||||
TopologyRefresher::new(topology_refresher_config, topology_accessor);
|
TopologyRefresher::new(topology_refresher_config, topology_accessor);
|
||||||
@@ -376,9 +381,7 @@ impl NymClient {
|
|||||||
sphinx_message_sender.clone(),
|
sphinx_message_sender.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if !self.config.get_base().get_vpn_mode() {
|
self.start_cover_traffic_stream(shared_topology_accessor, sphinx_message_sender);
|
||||||
self.start_cover_traffic_stream(shared_topology_accessor, sphinx_message_sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.config.get_socket_type() {
|
match self.config.get_socket_type() {
|
||||||
SocketType::WebSocket => {
|
SocketType::WebSocket => {
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ use nymsphinx::addressing::clients::Recipient;
|
|||||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
|
use rand::thread_rng;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use topology::{filter::VersionFilterable, gateway};
|
use topology::{filter::VersionFilterable, gateway};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||||
App::new("init")
|
let app = App::new("init")
|
||||||
.about("Initialise a Nym client. Do this first!")
|
.about("Initialise a Nym client. Do this first!")
|
||||||
.arg(Arg::with_name("id")
|
.arg(Arg::with_name("id")
|
||||||
.long("id")
|
.long("id")
|
||||||
@@ -38,11 +40,6 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.help("Comma separated list of rest endpoints of the validators")
|
.help("Comma separated list of rest endpoints of the validators")
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("mixnet-contract")
|
|
||||||
.long("mixnet-contract")
|
|
||||||
.help("Address of the validator contract managing the network")
|
|
||||||
.takes_value(true),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name("disable-socket")
|
.arg(Arg::with_name("disable-socket")
|
||||||
.long("disable-socket")
|
.long("disable-socket")
|
||||||
.help("Whether to not start the websocket")
|
.help("Whether to not start the websocket")
|
||||||
@@ -53,28 +50,31 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.help("Port for the socket (if applicable) to listen on in all subsequent runs")
|
.help("Port for the socket (if applicable) to listen on in all subsequent runs")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("vpn-mode")
|
|
||||||
.long("vpn-mode")
|
|
||||||
.help("Set the vpn mode of the client")
|
|
||||||
.long_help(
|
|
||||||
r#"
|
|
||||||
Special mode of the system such that all messages are sent as soon as they are received
|
|
||||||
and no cover traffic is generated. If set all message delays are set to 0 and overwriting
|
|
||||||
'Debug' values will have no effect.
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name("fastmode")
|
.arg(Arg::with_name("fastmode")
|
||||||
.long("fastmode")
|
.long("fastmode")
|
||||||
.hidden(true) // this will prevent this flag from being displayed in `--help`
|
.hidden(true) // this will prevent this flag from being displayed in `--help`
|
||||||
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
|
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
|
||||||
)
|
);
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
let app = app
|
||||||
|
.arg(Arg::with_name("eth_endpoint")
|
||||||
|
.long("eth_endpoint")
|
||||||
|
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true))
|
||||||
|
.arg(Arg::with_name("eth_private_key")
|
||||||
|
.long("eth_private_key")
|
||||||
|
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true));
|
||||||
|
|
||||||
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn register_with_gateway(
|
async fn register_with_gateway(
|
||||||
gateway: &gateway::Node,
|
gateway: &gateway::Node,
|
||||||
our_identity: Arc<identity::KeyPair>,
|
our_identity: Arc<identity::KeyPair>,
|
||||||
) -> SharedKeys {
|
) -> Arc<SharedKeys> {
|
||||||
let timeout = Duration::from_millis(1500);
|
let timeout = Duration::from_millis(1500);
|
||||||
let mut gateway_client = GatewayClient::new_init(
|
let mut gateway_client = GatewayClient::new_init(
|
||||||
gateway.clients_address(),
|
gateway.clients_address(),
|
||||||
@@ -87,18 +87,19 @@ async fn register_with_gateway(
|
|||||||
.await
|
.await
|
||||||
.expect("failed to establish connection with the gateway!");
|
.expect("failed to establish connection with the gateway!");
|
||||||
gateway_client
|
gateway_client
|
||||||
.register()
|
.perform_initial_authentication()
|
||||||
.await
|
.await
|
||||||
.expect("failed to register with the gateway!")
|
.expect("failed to register with the gateway!")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn gateway_details(
|
async fn gateway_details(
|
||||||
validator_servers: Vec<String>,
|
validator_servers: Vec<Url>,
|
||||||
mixnet_contract: &str,
|
|
||||||
chosen_gateway_id: Option<&str>,
|
chosen_gateway_id: Option<&str>,
|
||||||
) -> gateway::Node {
|
) -> gateway::Node {
|
||||||
let validator_client_config = validator_client::Config::new(validator_servers, mixnet_contract);
|
let validator_api = validator_servers
|
||||||
let validator_client = validator_client::Client::new(validator_client_config);
|
.choose(&mut thread_rng())
|
||||||
|
.expect("The list of validator apis is empty");
|
||||||
|
let validator_client = validator_client::ApiClient::new(validator_api.clone());
|
||||||
|
|
||||||
let gateways = validator_client.get_cached_gateways().await.unwrap();
|
let gateways = validator_client.get_cached_gateways().await.unwrap();
|
||||||
let valid_gateways = gateways
|
let valid_gateways = gateways
|
||||||
@@ -194,8 +195,7 @@ pub fn execute(matches: &ArgMatches) {
|
|||||||
|
|
||||||
let registration_fut = async {
|
let registration_fut = async {
|
||||||
let gate_details = gateway_details(
|
let gate_details = gateway_details(
|
||||||
config.get_base().get_validator_rest_endpoints(),
|
config.get_base().get_validator_api_endpoints(),
|
||||||
&config.get_base().get_validator_mixnet_contract_address(),
|
|
||||||
chosen_gateway_id,
|
chosen_gateway_id,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@@ -3,14 +3,20 @@
|
|||||||
|
|
||||||
use crate::client::config::{Config, SocketType};
|
use crate::client::config::{Config, SocketType};
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
pub(crate) mod init;
|
pub(crate) mod init;
|
||||||
pub(crate) mod run;
|
pub(crate) mod run;
|
||||||
pub(crate) mod upgrade;
|
pub(crate) mod upgrade;
|
||||||
|
|
||||||
fn parse_validators(raw: &str) -> Vec<String> {
|
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||||
raw.split(',')
|
raw.split(',')
|
||||||
.map(|raw_validator| raw_validator.trim().into())
|
.map(|raw_validator| {
|
||||||
|
raw_validator
|
||||||
|
.trim()
|
||||||
|
.parse()
|
||||||
|
.expect("one of the provided validator api urls is invalid")
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,11 +24,7 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi
|
|||||||
if let Some(raw_validators) = matches.value_of("validators") {
|
if let Some(raw_validators) = matches.value_of("validators") {
|
||||||
config
|
config
|
||||||
.get_base_mut()
|
.get_base_mut()
|
||||||
.set_custom_validators(parse_validators(raw_validators));
|
.set_custom_validator_apis(parse_validators(raw_validators));
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(contract_address) = matches.value_of("mixnet-contract") {
|
|
||||||
config.get_base_mut().set_mixnet_contract(contract_address)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(gateway_id) = matches.value_of("gateway") {
|
if let Some(gateway_id) = matches.value_of("gateway") {
|
||||||
@@ -33,10 +35,6 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi
|
|||||||
config = config.with_socket(SocketType::None);
|
config = config.with_socket(SocketType::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches.is_present("vpn-mode") {
|
|
||||||
config.get_base_mut().set_vpn_mode(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(port) = matches.value_of("port").map(|port| port.parse::<u16>()) {
|
if let Some(port) = matches.value_of("port").map(|port| port.parse::<u16>()) {
|
||||||
if let Err(err) = port {
|
if let Err(err) = port {
|
||||||
// if port was overridden, it must be parsable
|
// if port was overridden, it must be parsable
|
||||||
@@ -45,5 +43,14 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi
|
|||||||
config = config.with_port(port.unwrap());
|
config = config.with_port(port.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
if let Some(eth_endpoint) = matches.value_of("eth_endpoint") {
|
||||||
|
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
if let Some(eth_private_key) = matches.value_of("eth_private_key") {
|
||||||
|
config.get_base_mut().with_eth_private_key(eth_private_key);
|
||||||
|
}
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use log::*;
|
|||||||
use version_checker::is_minor_version_compatible;
|
use version_checker::is_minor_version_compatible;
|
||||||
|
|
||||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||||
App::new("run")
|
let app = App::new("run")
|
||||||
.about("Run the Nym client with provided configuration client optionally overriding set parameters")
|
.about("Run the Nym client with provided configuration client optionally overriding set parameters")
|
||||||
.arg(Arg::with_name("id")
|
.arg(Arg::with_name("id")
|
||||||
.long("id")
|
.long("id")
|
||||||
@@ -24,11 +24,6 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.help("Comma separated list rest rest endpoints of the validators")
|
.help("Comma separated list rest rest endpoints of the validators")
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("mixnet-contract")
|
|
||||||
.long("mixnet-contract")
|
|
||||||
.help("Address of the validator contract managing the network")
|
|
||||||
.takes_value(true),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name("gateway")
|
.arg(Arg::with_name("gateway")
|
||||||
.long("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")
|
.help("Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened")
|
||||||
@@ -38,23 +33,24 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.long("disable-socket")
|
.long("disable-socket")
|
||||||
.help("Whether to not start the websocket")
|
.help("Whether to not start the websocket")
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("vpn-mode")
|
|
||||||
.long("vpn-mode")
|
|
||||||
.help("Set the vpn mode of the client")
|
|
||||||
.long_help(
|
|
||||||
r#"
|
|
||||||
Special mode of the system such that all messages are sent as soon as they are received
|
|
||||||
and no cover traffic is generated. If set all message delays are set to 0 and overwriting
|
|
||||||
'Debug' values will have no effect.
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name("port")
|
.arg(Arg::with_name("port")
|
||||||
.short("p")
|
.short("p")
|
||||||
.long("port")
|
.long("port")
|
||||||
.help("Port for the socket (if applicable) to listen on")
|
.help("Port for the socket (if applicable) to listen on")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
)
|
);
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
let app = app
|
||||||
|
.arg(Arg::with_name("eth_endpoint")
|
||||||
|
.long("eth_endpoint")
|
||||||
|
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
|
||||||
|
.takes_value(true))
|
||||||
|
.arg(Arg::with_name("eth_private_key")
|
||||||
|
.long("eth_private_key")
|
||||||
|
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
|
||||||
|
.takes_value(true));
|
||||||
|
|
||||||
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
// this only checks compatibility between config the binary. It does not take into consideration
|
// this only checks compatibility between config the binary. It does not take into consideration
|
||||||
|
|||||||
@@ -3,11 +3,17 @@
|
|||||||
|
|
||||||
use crate::client::config::{Config, MISSING_VALUE};
|
use crate::client::config::{Config, MISSING_VALUE};
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use client_core::config::{default_validator_rest_endpoints, DEFAULT_MIXNET_CONTRACT_ADDRESS};
|
use config::defaults::default_api_endpoints;
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::process;
|
use std::process;
|
||||||
use version_checker::{parse_version, Version};
|
use version_checker::Version;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
|
||||||
|
print_failed_upgrade(from_version, to_version);
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||||
println!(
|
println!(
|
||||||
@@ -30,28 +36,29 @@ fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn command_args<'a, 'b>() -> App<'a, 'b> {
|
fn outdated_upgrade(config_version: &Version, package_version: &Version) -> ! {
|
||||||
App::new("upgrade").about("Try to upgrade the client")
|
eprintln!(
|
||||||
.arg(
|
"Cannot perform upgrade from {} to {}. Your version is too old to perform the upgrade.!",
|
||||||
Arg::with_name("id")
|
config_version, package_version
|
||||||
.long("id")
|
);
|
||||||
.help("Id of the nym-client we want to upgrade")
|
process::exit(1)
|
||||||
.takes_value(true)
|
|
||||||
.required(true),
|
|
||||||
)
|
|
||||||
// the rest of arguments depend on the upgrade path
|
|
||||||
.arg(Arg::with_name("current version")
|
|
||||||
.long("current-version")
|
|
||||||
.help("REQUIRED FOR PRE-0.9.0 UPGRADES. Specifies current version of the configuration file to help to determine a valid upgrade path. Valid formats include '0.8.1', 'v0.8.1' or 'V0.8.1'")
|
|
||||||
.takes_value(true)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unsupported_upgrade(config_version: Version, package_version: Version) -> ! {
|
fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> ! {
|
||||||
eprintln!("Cannot perform upgrade from {} to {}. Please let the developers know about this issue if you expected it to work!", config_version, package_version);
|
eprintln!("Cannot perform upgrade from {} to {}. Please let the developers know about this issue if you expected it to work!", config_version, current_version);
|
||||||
process::exit(1)
|
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),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_config_version(config: &Config) -> Version {
|
fn parse_config_version(config: &Config) -> Version {
|
||||||
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
|
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to parse client version! - {:?}", err);
|
eprintln!("failed to parse client version! - {:?}", err);
|
||||||
@@ -86,190 +93,28 @@ fn parse_package_version() -> Version {
|
|||||||
version
|
version
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_090_upgrade(from: &str, mut config: Config) -> Config {
|
fn minor_0_12_upgrade(
|
||||||
// this is not extracted to separate function as you only have to manually pass version
|
|
||||||
// if upgrading from pre090 version
|
|
||||||
let from = match from.strip_prefix('v') {
|
|
||||||
Some(stripped) => stripped,
|
|
||||||
None => from,
|
|
||||||
};
|
|
||||||
|
|
||||||
let from = match from.strip_prefix('V') {
|
|
||||||
Some(stripped) => stripped,
|
|
||||||
None => from,
|
|
||||||
};
|
|
||||||
|
|
||||||
let from_version = parse_version(from).expect("invalid version provided!");
|
|
||||||
if from_version.major == 0 && from_version.minor < 8 {
|
|
||||||
// technically this could be implemented, but is there any point in that?
|
|
||||||
eprintln!("upgrading client from before v0.8.0 is not supported. Please run `init` with new binary instead");
|
|
||||||
process::exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (from_version.major == 0 && from_version.minor >= 9) || from_version.major >= 1 {
|
|
||||||
eprintln!("self reported version is higher than 0.9.0. Those releases should have already contained version numbers in config! Make sure you provided correct version");
|
|
||||||
process::exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: current is guaranteed to not have any `build` information suffix (nor pre-release
|
|
||||||
// information), as this was asserted at the beginning of this command)
|
|
||||||
//
|
|
||||||
// upgrade to current (if it's a 0.9.X) or try to upgrade to 0.9.0 as an intermediate
|
|
||||||
// step in future upgrades (so, for example, we might go 0.8.0 -> 0.9.0 -> 0.10.0)
|
|
||||||
// this way we don't need to have all the crazy paths on how to upgrade from any version to any
|
|
||||||
// other version. We just upgrade one minor version at a time.
|
|
||||||
let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
|
||||||
let to_version = if current.major == 0 && current.minor == 9 {
|
|
||||||
current
|
|
||||||
} else {
|
|
||||||
Version::new(0, 9, 0)
|
|
||||||
};
|
|
||||||
|
|
||||||
if config.get_base().get_validator_rest_endpoints()[0] != MISSING_VALUE {
|
|
||||||
eprintln!("existing config seems to have specified new validator rest endpoint which was only introduced in 0.9.0! Can't perform upgrade.");
|
|
||||||
print_failed_upgrade(&from_version, &to_version);
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
print_start_upgrade(&from_version, &to_version);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_version(to_version.to_string().as_ref());
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Setting validator REST endpoint to to {:?}",
|
|
||||||
default_validator_rest_endpoints()
|
|
||||||
);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_validators(default_validator_rest_endpoints());
|
|
||||||
|
|
||||||
config.save_to_file(None).unwrap_or_else(|err| {
|
|
||||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
|
||||||
print_failed_upgrade(&from_version, &to_version);
|
|
||||||
process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
print_successful_upgrade(from_version, to_version);
|
|
||||||
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
changes:
|
|
||||||
- introduction of mixnet contract address field
|
|
||||||
- change to default validator rest endpoint
|
|
||||||
*/
|
|
||||||
fn minor_010_upgrade(
|
|
||||||
mut config: Config,
|
mut config: Config,
|
||||||
_matches: &ArgMatches,
|
_matches: &ArgMatches,
|
||||||
config_version: &Version,
|
config_version: &Version,
|
||||||
package_version: &Version,
|
package_version: &Version,
|
||||||
) -> Config {
|
) -> Config {
|
||||||
let to_version = if package_version.major == 0 && package_version.minor == 10 {
|
let to_version = if package_version.major == 0 && package_version.minor == 12 {
|
||||||
package_version.clone()
|
package_version.clone()
|
||||||
} else {
|
} else {
|
||||||
Version::new(0, 10, 0)
|
Version::new(0, 12, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
print_start_upgrade(&config_version, &to_version);
|
print_start_upgrade(&config_version, &to_version);
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_version(to_version.to_string().as_ref());
|
|
||||||
|
|
||||||
if config.get_base().get_validator_mixnet_contract_address() != MISSING_VALUE {
|
|
||||||
eprintln!("existing config seems to have specified mixnet contract address which was only introduced in 0.10.0! Can't perform upgrade.");
|
|
||||||
print_failed_upgrade(&config_version, &to_version);
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Setting mixnet contract address to {}",
|
"Setting validator API endpoints to {:?}",
|
||||||
DEFAULT_MIXNET_CONTRACT_ADDRESS
|
default_api_endpoints()
|
||||||
);
|
);
|
||||||
|
|
||||||
config
|
config
|
||||||
.get_base_mut()
|
.get_base_mut()
|
||||||
.set_mixnet_contract(DEFAULT_MIXNET_CONTRACT_ADDRESS);
|
.set_custom_validator_apis(default_api_endpoints());
|
||||||
|
|
||||||
// The default validator endpoint changed
|
|
||||||
println!(
|
|
||||||
"Setting validator REST endpoint to to {:?}",
|
|
||||||
default_validator_rest_endpoints()
|
|
||||||
);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_validators(default_validator_rest_endpoints());
|
|
||||||
|
|
||||||
config.save_to_file(None).unwrap_or_else(|err| {
|
|
||||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
|
||||||
print_failed_upgrade(&config_version, &to_version);
|
|
||||||
process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
print_successful_upgrade(config_version, to_version);
|
|
||||||
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
// no changes but version number
|
|
||||||
fn patch_010_upgrade(
|
|
||||||
mut config: Config,
|
|
||||||
_matches: &ArgMatches,
|
|
||||||
config_version: &Version,
|
|
||||||
package_version: &Version,
|
|
||||||
) -> Config {
|
|
||||||
let to_version = package_version;
|
|
||||||
|
|
||||||
print_start_upgrade(&config_version, &to_version);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_version(to_version.to_string().as_ref());
|
|
||||||
|
|
||||||
config.save_to_file(None).unwrap_or_else(|err| {
|
|
||||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
|
||||||
print_failed_upgrade(&config_version, &to_version);
|
|
||||||
process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
print_successful_upgrade(config_version, to_version);
|
|
||||||
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
fn minor_011_upgrade(
|
|
||||||
mut config: Config,
|
|
||||||
_matches: &ArgMatches,
|
|
||||||
config_version: &Version,
|
|
||||||
package_version: &Version,
|
|
||||||
) -> Config {
|
|
||||||
let to_version = package_version;
|
|
||||||
|
|
||||||
print_start_upgrade(&config_version, &to_version);
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Setting mixnet contract address to {}",
|
|
||||||
DEFAULT_MIXNET_CONTRACT_ADDRESS
|
|
||||||
);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_mixnet_contract(DEFAULT_MIXNET_CONTRACT_ADDRESS);
|
|
||||||
|
|
||||||
// The default validator endpoint changed
|
|
||||||
println!(
|
|
||||||
"Setting validator REST endpoint to {:?}",
|
|
||||||
default_validator_rest_endpoints()
|
|
||||||
);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_validators(default_validator_rest_endpoints());
|
|
||||||
|
|
||||||
config
|
config
|
||||||
.get_base_mut()
|
.get_base_mut()
|
||||||
@@ -297,18 +142,11 @@ fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version
|
|||||||
|
|
||||||
config = match config_version.major {
|
config = match config_version.major {
|
||||||
0 => match config_version.minor {
|
0 => match config_version.minor {
|
||||||
9 => minor_010_upgrade(config, matches, &config_version, &Version::new(0, 10, 0)),
|
9 | 10 => outdated_upgrade(&config_version, &package_version),
|
||||||
10 => match config_version.patch {
|
11 => minor_0_12_upgrade(config, matches, &config_version, &package_version),
|
||||||
0 => {
|
_ => unsupported_upgrade(&config_version, &package_version),
|
||||||
patch_010_upgrade(config, matches, &config_version, &Version::new(0, 10, 1))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
minor_011_upgrade(config, matches, &config_version, &Version::new(0, 11, 0))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => unsupported_upgrade(config_version, package_version),
|
|
||||||
},
|
},
|
||||||
_ => unsupported_upgrade(config_version, package_version),
|
_ => unsupported_upgrade(&config_version, &package_version),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,22 +156,14 @@ pub fn execute(matches: &ArgMatches) {
|
|||||||
|
|
||||||
let id = matches.value_of("id").unwrap();
|
let id = matches.value_of("id").unwrap();
|
||||||
|
|
||||||
let mut existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
|
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to load existing config file! - {:?}", err);
|
eprintln!("failed to load existing config file! - {:?}", err);
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
});
|
});
|
||||||
|
|
||||||
// versions fields were added in 0.9.0
|
|
||||||
if existing_config.get_base().get_version() == MISSING_VALUE {
|
if existing_config.get_base().get_version() == MISSING_VALUE {
|
||||||
let self_reported_version = matches.value_of("current version").unwrap_or_else(|| {
|
eprintln!("the existing configuration file does not seem to contain version number.");
|
||||||
eprintln!(
|
process::exit(1);
|
||||||
"trying to upgrade from pre v0.9.0 without providing current system version!"
|
|
||||||
);
|
|
||||||
process::exit(1)
|
|
||||||
});
|
|
||||||
|
|
||||||
// upgrades up to 0.9.0
|
|
||||||
existing_config = pre_090_upgrade(self_reported_version, existing_config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// here be upgrade path to 0.9.X and beyond based on version number from config
|
// here be upgrade path to 0.9.X and beyond based on version number from config
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ name = "nym-socks5-client"
|
|||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
rust-version = "1.56"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "nym_socks5"
|
name = "nym_socks5"
|
||||||
@@ -20,9 +21,12 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
|||||||
serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization
|
serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization
|
||||||
snafu = "0.6"
|
snafu = "0.6"
|
||||||
tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal"] }
|
tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal"] }
|
||||||
|
url = "2.2"
|
||||||
|
|
||||||
# internal
|
# internal
|
||||||
client-core = { path = "../client-core" }
|
client-core = { path = "../client-core" }
|
||||||
|
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||||
|
credentials = { path = "../../common/credentials", optional = true }
|
||||||
config = { path = "../../common/config" }
|
config = { path = "../../common/config" }
|
||||||
crypto = { path = "../../common/crypto" }
|
crypto = { path = "../../common/crypto" }
|
||||||
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
||||||
@@ -35,3 +39,6 @@ pemstore = { path = "../../common/pemstore" }
|
|||||||
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
||||||
validator-client = { path = "../../common/client-libs/validator-client" }
|
validator-client = { path = "../../common/client-libs/validator-client" }
|
||||||
version-checker = { path = "../../common/version-checker" }
|
version-checker = { path = "../../common/version-checker" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
use crate::client::config::template::config_template;
|
use crate::client::config::template::config_template;
|
||||||
use client_core::config::Config as BaseConfig;
|
use client_core::config::Config as BaseConfig;
|
||||||
pub use client_core::config::MISSING_VALUE;
|
pub use client_core::config::MISSING_VALUE;
|
||||||
|
use config::defaults::DEFAULT_SOCKS5_LISTENING_PORT;
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
use nymsphinx::addressing::clients::Recipient;
|
use nymsphinx::addressing::clients::Recipient;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -11,8 +12,6 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
const DEFAULT_LISTENING_PORT: u16 = 1080;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@@ -103,7 +102,7 @@ pub struct Socks5 {
|
|||||||
impl Socks5 {
|
impl Socks5 {
|
||||||
pub fn new<S: Into<String>>(provider_mix_address: S) -> Self {
|
pub fn new<S: Into<String>>(provider_mix_address: S) -> Self {
|
||||||
Socks5 {
|
Socks5 {
|
||||||
listening_port: DEFAULT_LISTENING_PORT,
|
listening_port: DEFAULT_SOCKS5_LISTENING_PORT,
|
||||||
provider_mix_address: provider_mix_address.into(),
|
provider_mix_address: provider_mix_address.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +111,7 @@ impl Socks5 {
|
|||||||
impl Default for Socks5 {
|
impl Default for Socks5 {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Socks5 {
|
Socks5 {
|
||||||
listening_port: DEFAULT_LISTENING_PORT,
|
listening_port: DEFAULT_SOCKS5_LISTENING_PORT,
|
||||||
provider_mix_address: "".into(),
|
provider_mix_address: "".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,21 +19,13 @@ version = '{{ client.version }}'
|
|||||||
# Human readable ID of this particular client.
|
# Human readable ID of this particular client.
|
||||||
id = '{{ client.id }}'
|
id = '{{ client.id }}'
|
||||||
|
|
||||||
# URL to the validator server for obtaining network topology.
|
# Addresses to APIs running on validator from which the client gets the view of the network.
|
||||||
validator_rest_urls = [
|
validator_api_urls = [
|
||||||
{{#each client.validator_rest_urls }}
|
{{#each client.validator_api_urls }}
|
||||||
'{{this}}',
|
'{{this}}',
|
||||||
{{/each}}
|
{{/each}}
|
||||||
]
|
]
|
||||||
|
|
||||||
# Address of the validator contract managing the network.
|
|
||||||
mixnet_contract_address = '{{ client.mixnet_contract_address }}'
|
|
||||||
|
|
||||||
# Special mode of the system such that all messages are sent as soon as they are received
|
|
||||||
# and no cover traffic is generated. If set all message delays are set to 0 and overwriting
|
|
||||||
# 'Debug' values will have no effect.
|
|
||||||
vpn_mode = {{ client.vpn_mode }}
|
|
||||||
|
|
||||||
# Path to file containing private identity key.
|
# Path to file containing private identity key.
|
||||||
private_identity_key_file = '{{ client.private_identity_key_file }}'
|
private_identity_key_file = '{{ client.private_identity_key_file }}'
|
||||||
|
|
||||||
@@ -50,6 +42,17 @@ public_encryption_key_file = '{{ client.public_encryption_key_file }}'
|
|||||||
# sent but not received back.
|
# sent but not received back.
|
||||||
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
||||||
|
|
||||||
|
# Path to directory containing public/private keys used for bandwidth token purchase.
|
||||||
|
# Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
|
||||||
|
# The public key is the name of the file, while the private key is the content.
|
||||||
|
backup_bandwidth_token_keys_dir = '{{ client.backup_bandwidth_token_keys_dir }}'
|
||||||
|
|
||||||
|
# Ethereum private key.
|
||||||
|
eth_private_key = '{{ client.eth_private_key }}'
|
||||||
|
|
||||||
|
# Addess to an Ethereum full node.
|
||||||
|
eth_endpoint = '{{ client.eth_endpoint }}'
|
||||||
|
|
||||||
##### additional client config options #####
|
##### additional client config options #####
|
||||||
|
|
||||||
# ID of the gateway from which the client should be fetching messages.
|
# ID of the gateway from which the client should be fetching messages.
|
||||||
|
|||||||
@@ -32,9 +32,10 @@ use gateway_client::{
|
|||||||
use log::*;
|
use log::*;
|
||||||
use nymsphinx::addressing::clients::Recipient;
|
use nymsphinx::addressing::clients::Recipient;
|
||||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||||
use nymsphinx::params::PacketMode;
|
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
use gateway_client::bandwidth::BandwidthController;
|
||||||
|
|
||||||
pub(crate) mod config;
|
pub(crate) mod config;
|
||||||
|
|
||||||
pub struct NymClient {
|
pub struct NymClient {
|
||||||
@@ -107,12 +108,6 @@ impl NymClient {
|
|||||||
input_receiver: InputMessageReceiver,
|
input_receiver: InputMessageReceiver,
|
||||||
mix_sender: BatchMixMessageSender,
|
mix_sender: BatchMixMessageSender,
|
||||||
) {
|
) {
|
||||||
let packet_mode = if self.config.get_base().get_vpn_mode() {
|
|
||||||
PacketMode::Vpn
|
|
||||||
} else {
|
|
||||||
PacketMode::Mix
|
|
||||||
};
|
|
||||||
|
|
||||||
let controller_config = client_core::client::real_messages_control::Config::new(
|
let controller_config = client_core::client::real_messages_control::Config::new(
|
||||||
self.key_manager.ack_key(),
|
self.key_manager.ack_key(),
|
||||||
self.config.get_base().get_ack_wait_multiplier(),
|
self.config.get_base().get_ack_wait_multiplier(),
|
||||||
@@ -121,8 +116,6 @@ impl NymClient {
|
|||||||
self.config.get_base().get_message_sending_average_delay(),
|
self.config.get_base().get_message_sending_average_delay(),
|
||||||
self.config.get_base().get_average_packet_delay(),
|
self.config.get_base().get_average_packet_delay(),
|
||||||
self.as_mix_recipient(),
|
self.as_mix_recipient(),
|
||||||
packet_mode,
|
|
||||||
self.config.get_base().get_vpn_key_reuse_limit(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
info!("Starting real traffic stream...");
|
info!("Starting real traffic stream...");
|
||||||
@@ -139,7 +132,7 @@ impl NymClient {
|
|||||||
topology_accessor,
|
topology_accessor,
|
||||||
reply_key_storage,
|
reply_key_storage,
|
||||||
)
|
)
|
||||||
.start(self.runtime.handle(), self.config.get_base().get_vpn_mode());
|
.start(self.runtime.handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
// buffer controlling all messages fetched from provider
|
// buffer controlling all messages fetched from provider
|
||||||
@@ -177,44 +170,53 @@ impl NymClient {
|
|||||||
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
||||||
.expect("provided gateway id is invalid!");
|
.expect("provided gateway id is invalid!");
|
||||||
|
|
||||||
let mut gateway_client = GatewayClient::new(
|
|
||||||
gateway_address,
|
|
||||||
self.key_manager.identity_keypair(),
|
|
||||||
gateway_identity,
|
|
||||||
Some(self.key_manager.gateway_shared_key()),
|
|
||||||
mixnet_message_sender,
|
|
||||||
ack_sender,
|
|
||||||
self.config.get_base().get_gateway_response_timeout(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.runtime.block_on(async {
|
self.runtime.block_on(async {
|
||||||
|
#[cfg(feature = "coconut")]
|
||||||
|
let bandwidth_controller = BandwidthController::new(
|
||||||
|
self.config.get_base().get_validator_api_endpoints(),
|
||||||
|
*self.key_manager.identity_keypair().public_key(),
|
||||||
|
);
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
let bandwidth_controller = BandwidthController::new(
|
||||||
|
self.config.get_base().get_eth_endpoint(),
|
||||||
|
self.config.get_base().get_eth_private_key(),
|
||||||
|
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
|
||||||
|
)
|
||||||
|
.expect("Could not create bandwidth controller");
|
||||||
|
|
||||||
|
let mut gateway_client = GatewayClient::new(
|
||||||
|
gateway_address,
|
||||||
|
self.key_manager.identity_keypair(),
|
||||||
|
gateway_identity,
|
||||||
|
Some(self.key_manager.gateway_shared_key()),
|
||||||
|
mixnet_message_sender,
|
||||||
|
ack_sender,
|
||||||
|
self.config.get_base().get_gateway_response_timeout(),
|
||||||
|
Some(bandwidth_controller),
|
||||||
|
);
|
||||||
|
|
||||||
gateway_client
|
gateway_client
|
||||||
.authenticate_and_start()
|
.authenticate_and_start()
|
||||||
.await
|
.await
|
||||||
.expect("could not authenticate and start up the gateway connection")
|
.expect("could not authenticate and start up the gateway connection");
|
||||||
});
|
|
||||||
|
|
||||||
gateway_client
|
gateway_client
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// future responsible for periodically polling directory server and updating
|
// future responsible for periodically polling directory server and updating
|
||||||
// the current global view of topology
|
// the current global view of topology
|
||||||
fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
|
||||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||||
self.config.get_base().get_validator_rest_endpoints(),
|
self.config.get_base().get_validator_api_endpoints(),
|
||||||
self.config
|
|
||||||
.get_base()
|
|
||||||
.get_validator_mixnet_contract_address(),
|
|
||||||
self.config.get_base().get_topology_refresh_rate(),
|
self.config.get_base().get_topology_refresh_rate(),
|
||||||
|
env!("CARGO_PKG_VERSION").to_string(),
|
||||||
);
|
);
|
||||||
let mut topology_refresher =
|
let mut topology_refresher =
|
||||||
TopologyRefresher::new(topology_refresher_config, topology_accessor);
|
TopologyRefresher::new(topology_refresher_config, topology_accessor);
|
||||||
// before returning, block entire runtime to refresh the current network view so that any
|
// before returning, block entire runtime to refresh the current network view so that any
|
||||||
// components depending on topology would see a non-empty view
|
// components depending on topology would see a non-empty view
|
||||||
info!(
|
info!("Obtaining initial network topology");
|
||||||
"Obtaining initial network topology from {}",
|
|
||||||
self.config.get_base().get_validator_rest_endpoints()[0]
|
|
||||||
);
|
|
||||||
self.runtime.block_on(topology_refresher.refresh());
|
self.runtime.block_on(topology_refresher.refresh());
|
||||||
|
|
||||||
// TODO: a slightly more graceful termination here
|
// TODO: a slightly more graceful termination here
|
||||||
@@ -329,9 +331,8 @@ impl NymClient {
|
|||||||
input_receiver,
|
input_receiver,
|
||||||
sphinx_message_sender.clone(),
|
sphinx_message_sender.clone(),
|
||||||
);
|
);
|
||||||
if !self.config.get_base().get_vpn_mode() {
|
|
||||||
self.start_cover_traffic_stream(shared_topology_accessor, sphinx_message_sender);
|
self.start_cover_traffic_stream(shared_topology_accessor, sphinx_message_sender);
|
||||||
}
|
|
||||||
self.start_socks5_listener(received_buffer_request_sender, input_sender);
|
self.start_socks5_listener(received_buffer_request_sender, input_sender);
|
||||||
|
|
||||||
info!("Client startup finished!");
|
info!("Client startup finished!");
|
||||||
|
|||||||
@@ -12,14 +12,15 @@ use gateway_client::GatewayClient;
|
|||||||
use gateway_requests::registration::handshake::SharedKeys;
|
use gateway_requests::registration::handshake::SharedKeys;
|
||||||
use nymsphinx::addressing::clients::Recipient;
|
use nymsphinx::addressing::clients::Recipient;
|
||||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||||
use rand::{prelude::SliceRandom, rngs::OsRng};
|
use rand::{prelude::SliceRandom, rngs::OsRng, thread_rng};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use topology::{filter::VersionFilterable, gateway};
|
use topology::{filter::VersionFilterable, gateway};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||||
App::new("init")
|
let app = App::new("init")
|
||||||
.about("Initialise a Nym client. Do this first!")
|
.about("Initialise a Nym client. Do this first!")
|
||||||
.arg(Arg::with_name("id")
|
.arg(Arg::with_name("id")
|
||||||
.long("id")
|
.long("id")
|
||||||
@@ -43,39 +44,37 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.help("Comma separated list of rest endpoints of the validators")
|
.help("Comma separated list of rest endpoints of the validators")
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("mixnet-contract")
|
|
||||||
.long("mixnet-contract")
|
|
||||||
.help("Address of the validator contract managing the network")
|
|
||||||
.takes_value(true),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name("port")
|
.arg(Arg::with_name("port")
|
||||||
.short("p")
|
.short("p")
|
||||||
.long("port")
|
.long("port")
|
||||||
.help("Port for the socket to listen on in all subsequent runs")
|
.help("Port for the socket to listen on in all subsequent runs")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("vpn-mode")
|
|
||||||
.long("vpn-mode")
|
|
||||||
.help("Set the vpn mode of the client")
|
|
||||||
.long_help(
|
|
||||||
r#"
|
|
||||||
Special mode of the system such that all messages are sent as soon as they are received
|
|
||||||
and no cover traffic is generated. If set all message delays are set to 0 and overwriting
|
|
||||||
'Debug' values will have no effect.
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name("fastmode")
|
.arg(Arg::with_name("fastmode")
|
||||||
.long("fastmode")
|
.long("fastmode")
|
||||||
.hidden(true) // this will prevent this flag from being displayed in `--help`
|
.hidden(true) // this will prevent this flag from being displayed in `--help`
|
||||||
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
|
.help("Mostly debug-related option to increase default traffic rate so that you would not need to modify config post init")
|
||||||
)
|
);
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
let app = app
|
||||||
|
.arg(Arg::with_name("eth_endpoint")
|
||||||
|
.long("eth_endpoint")
|
||||||
|
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true))
|
||||||
|
.arg(Arg::with_name("eth_private_key")
|
||||||
|
.long("eth_private_key")
|
||||||
|
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true));
|
||||||
|
|
||||||
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn register_with_gateway(
|
async fn register_with_gateway(
|
||||||
gateway: &gateway::Node,
|
gateway: &gateway::Node,
|
||||||
our_identity: Arc<identity::KeyPair>,
|
our_identity: Arc<identity::KeyPair>,
|
||||||
) -> SharedKeys {
|
) -> Arc<SharedKeys> {
|
||||||
let timeout = Duration::from_millis(1500);
|
let timeout = Duration::from_millis(1500);
|
||||||
let mut gateway_client = GatewayClient::new_init(
|
let mut gateway_client = GatewayClient::new_init(
|
||||||
gateway.clients_address(),
|
gateway.clients_address(),
|
||||||
@@ -88,18 +87,19 @@ async fn register_with_gateway(
|
|||||||
.await
|
.await
|
||||||
.expect("failed to establish connection with the gateway!");
|
.expect("failed to establish connection with the gateway!");
|
||||||
gateway_client
|
gateway_client
|
||||||
.register()
|
.perform_initial_authentication()
|
||||||
.await
|
.await
|
||||||
.expect("failed to register with the gateway!")
|
.expect("failed to register with the gateway!")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn gateway_details(
|
async fn gateway_details(
|
||||||
validator_servers: Vec<String>,
|
validator_servers: Vec<Url>,
|
||||||
mixnet_contract: &str,
|
|
||||||
chosen_gateway_id: Option<&str>,
|
chosen_gateway_id: Option<&str>,
|
||||||
) -> gateway::Node {
|
) -> gateway::Node {
|
||||||
let validator_client_config = validator_client::Config::new(validator_servers, mixnet_contract);
|
let validator_api = validator_servers
|
||||||
let validator_client = validator_client::Client::new(validator_client_config);
|
.choose(&mut thread_rng())
|
||||||
|
.expect("The list of validator apis is empty");
|
||||||
|
let validator_client = validator_client::ApiClient::new(validator_api.clone());
|
||||||
|
|
||||||
let gateways = validator_client.get_cached_gateways().await.unwrap();
|
let gateways = validator_client.get_cached_gateways().await.unwrap();
|
||||||
let valid_gateways = gateways
|
let valid_gateways = gateways
|
||||||
@@ -196,8 +196,7 @@ pub fn execute(matches: &ArgMatches) {
|
|||||||
|
|
||||||
let registration_fut = async {
|
let registration_fut = async {
|
||||||
let gate_details = gateway_details(
|
let gate_details = gateway_details(
|
||||||
config.get_base().get_validator_rest_endpoints(),
|
config.get_base().get_validator_api_endpoints(),
|
||||||
&config.get_base().get_validator_mixnet_contract_address(),
|
|
||||||
chosen_gateway_id,
|
chosen_gateway_id,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@@ -3,14 +3,20 @@
|
|||||||
|
|
||||||
use crate::client::config::Config;
|
use crate::client::config::Config;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
pub(crate) mod init;
|
pub(crate) mod init;
|
||||||
pub(crate) mod run;
|
pub(crate) mod run;
|
||||||
pub(crate) mod upgrade;
|
pub(crate) mod upgrade;
|
||||||
|
|
||||||
fn parse_validators(raw: &str) -> Vec<String> {
|
fn parse_validators(raw: &str) -> Vec<Url> {
|
||||||
raw.split(',')
|
raw.split(',')
|
||||||
.map(|raw_validator| raw_validator.trim().into())
|
.map(|raw_validator| {
|
||||||
|
raw_validator
|
||||||
|
.trim()
|
||||||
|
.parse()
|
||||||
|
.expect("one of the provided validator api urls is invalid")
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,21 +24,13 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi
|
|||||||
if let Some(raw_validators) = matches.value_of("validators") {
|
if let Some(raw_validators) = matches.value_of("validators") {
|
||||||
config
|
config
|
||||||
.get_base_mut()
|
.get_base_mut()
|
||||||
.set_custom_validators(parse_validators(raw_validators));
|
.set_custom_validator_apis(parse_validators(raw_validators));
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(contract_address) = matches.value_of("mixnet-contract") {
|
|
||||||
config.get_base_mut().set_mixnet_contract(contract_address)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(gateway_id) = matches.value_of("gateway") {
|
if let Some(gateway_id) = matches.value_of("gateway") {
|
||||||
config.get_base_mut().with_gateway_id(gateway_id);
|
config.get_base_mut().with_gateway_id(gateway_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches.is_present("vpn-mode") {
|
|
||||||
config.get_base_mut().set_vpn_mode(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(port) = matches.value_of("port").map(|port| port.parse::<u16>()) {
|
if let Some(port) = matches.value_of("port").map(|port| port.parse::<u16>()) {
|
||||||
if let Err(err) = port {
|
if let Err(err) = port {
|
||||||
// if port was overridden, it must be parsable
|
// if port was overridden, it must be parsable
|
||||||
@@ -41,5 +39,14 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi
|
|||||||
config = config.with_port(port.unwrap());
|
config = config.with_port(port.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
if let Some(eth_endpoint) = matches.value_of("eth_endpoint") {
|
||||||
|
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
if let Some(eth_private_key) = matches.value_of("eth_private_key") {
|
||||||
|
config.get_base_mut().with_eth_private_key(eth_private_key);
|
||||||
|
}
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use log::*;
|
|||||||
use version_checker::is_minor_version_compatible;
|
use version_checker::is_minor_version_compatible;
|
||||||
|
|
||||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||||
App::new("run")
|
let app = App::new("run")
|
||||||
.about("Run the Nym client with provided configuration client optionally overriding set parameters")
|
.about("Run the Nym client with provided configuration client optionally overriding set parameters")
|
||||||
.arg(Arg::with_name("id")
|
.arg(Arg::with_name("id")
|
||||||
.long("id")
|
.long("id")
|
||||||
@@ -34,33 +34,29 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
.help("Comma separated list of rest endpoints of the validators")
|
.help("Comma separated list of rest endpoints of the validators")
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("mixnet-contract")
|
|
||||||
.long("mixnet-contract")
|
|
||||||
.help("Address of the validator contract managing the network")
|
|
||||||
.takes_value(true),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name("gateway")
|
.arg(Arg::with_name("gateway")
|
||||||
.long("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")
|
.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)
|
.takes_value(true)
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name("vpn-mode")
|
|
||||||
.long("vpn-mode")
|
|
||||||
.help("Set the vpn mode of the client")
|
|
||||||
.long_help(
|
|
||||||
r#"
|
|
||||||
Special mode of the system such that all messages are sent as soon as they are received
|
|
||||||
and no cover traffic is generated. If set all message delays are set to 0 and overwriting
|
|
||||||
'Debug' values will have no effect.
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name("port")
|
.arg(Arg::with_name("port")
|
||||||
.short("p")
|
.short("p")
|
||||||
.long("port")
|
.long("port")
|
||||||
.help("Port for the socket to listen on")
|
.help("Port for the socket to listen on")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
)
|
);
|
||||||
|
#[cfg(not(feature = "coconut"))]
|
||||||
|
let app = app
|
||||||
|
.arg(Arg::with_name("eth_endpoint")
|
||||||
|
.long("eth_endpoint")
|
||||||
|
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
|
||||||
|
.takes_value(true))
|
||||||
|
.arg(Arg::with_name("eth_private_key")
|
||||||
|
.long("eth_private_key")
|
||||||
|
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
|
||||||
|
.takes_value(true));
|
||||||
|
|
||||||
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
// this only checks compatibility between config the binary. It does not take into consideration
|
// this only checks compatibility between config the binary. It does not take into consideration
|
||||||
|
|||||||
@@ -3,11 +3,17 @@
|
|||||||
|
|
||||||
use crate::client::config::{Config, MISSING_VALUE};
|
use crate::client::config::{Config, MISSING_VALUE};
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use client_core::config::{default_validator_rest_endpoints, DEFAULT_MIXNET_CONTRACT_ADDRESS};
|
use config::defaults::default_api_endpoints;
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::process;
|
use std::process;
|
||||||
use version_checker::{parse_version, Version};
|
use version_checker::Version;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
|
||||||
|
print_failed_upgrade(from_version, to_version);
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||||
println!(
|
println!(
|
||||||
@@ -30,28 +36,29 @@ fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn command_args<'a, 'b>() -> App<'a, 'b> {
|
fn outdated_upgrade(config_version: &Version, package_version: &Version) -> ! {
|
||||||
App::new("upgrade").about("Try to upgrade the client")
|
eprintln!(
|
||||||
.arg(
|
"Cannot perform upgrade from {} to {}. Your version is too old to perform the upgrade.!",
|
||||||
Arg::with_name("id")
|
config_version, package_version
|
||||||
.long("id")
|
);
|
||||||
.help("Id of the nym-socks5-client we want to upgrade")
|
process::exit(1)
|
||||||
.takes_value(true)
|
|
||||||
.required(true),
|
|
||||||
)
|
|
||||||
// the rest of arguments depend on the upgrade path
|
|
||||||
.arg(Arg::with_name("current version")
|
|
||||||
.long("current-version")
|
|
||||||
.help("REQUIRED FOR PRE-0.9.0 UPGRADES. Specifies current version of the configuration file to help to determine a valid upgrade path. Valid formats include '0.8.1', 'v0.8.1' or 'V0.8.1'")
|
|
||||||
.takes_value(true)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unsupported_upgrade(config_version: Version, package_version: Version) -> ! {
|
fn unsupported_upgrade(current_version: &Version, config_version: &Version) -> ! {
|
||||||
eprintln!("Cannot perform upgrade from {} to {}. Please let the developers know about this issue if you expected it to work!", config_version, package_version);
|
eprintln!("Cannot perform upgrade from {} to {}. Please let the developers know about this issue if you expected it to work!", config_version, current_version);
|
||||||
process::exit(1)
|
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),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_config_version(config: &Config) -> Version {
|
fn parse_config_version(config: &Config) -> Version {
|
||||||
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
|
let version = Version::parse(config.get_base().get_version()).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to parse client version! - {:?}", err);
|
eprintln!("failed to parse client version! - {:?}", err);
|
||||||
@@ -86,190 +93,28 @@ fn parse_package_version() -> Version {
|
|||||||
version
|
version
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_090_upgrade(from: &str, mut config: Config) -> Config {
|
fn minor_0_12_upgrade(
|
||||||
// this is not extracted to separate function as you only have to manually pass version
|
|
||||||
// if upgrading from pre090 version
|
|
||||||
let from = match from.strip_prefix('v') {
|
|
||||||
Some(stripped) => stripped,
|
|
||||||
None => from,
|
|
||||||
};
|
|
||||||
|
|
||||||
let from = match from.strip_prefix('V') {
|
|
||||||
Some(stripped) => stripped,
|
|
||||||
None => from,
|
|
||||||
};
|
|
||||||
|
|
||||||
let from_version = parse_version(from).expect("invalid version provided!");
|
|
||||||
if from_version.major == 0 && from_version.minor < 8 {
|
|
||||||
// technically this could be implemented, but is there any point in that?
|
|
||||||
eprintln!("upgrading client from before v0.8.0 is not supported. Please run `init` with new binary instead");
|
|
||||||
process::exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (from_version.major == 0 && from_version.minor >= 9) || from_version.major >= 1 {
|
|
||||||
eprintln!("self reported version is higher than 0.9.0. Those releases should have already contained version numbers in config! Make sure you provided correct version");
|
|
||||||
process::exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: current is guaranteed to not have any `build` information suffix (nor pre-release
|
|
||||||
// information), as this was asserted at the beginning of this command)
|
|
||||||
//
|
|
||||||
// upgrade to current (if it's a 0.9.X) or try to upgrade to 0.9.0 as an intermediate
|
|
||||||
// step in future upgrades (so, for example, we might go 0.8.0 -> 0.9.0 -> 0.10.0)
|
|
||||||
// this way we don't need to have all the crazy paths on how to upgrade from any version to any
|
|
||||||
// other version. We just upgrade one minor version at a time.
|
|
||||||
let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
|
||||||
let to_version = if current.major == 0 && current.minor == 9 {
|
|
||||||
current
|
|
||||||
} else {
|
|
||||||
Version::new(0, 9, 0)
|
|
||||||
};
|
|
||||||
|
|
||||||
if config.get_base().get_validator_rest_endpoints()[0] != MISSING_VALUE {
|
|
||||||
eprintln!("existing config seems to have specified new validator rest endpoint which was only introduced in 0.9.0! Can't perform upgrade.");
|
|
||||||
print_failed_upgrade(&from_version, &to_version);
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
print_start_upgrade(&from_version, &to_version);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_version(to_version.to_string().as_ref());
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Setting validator REST endpoint to {:?}",
|
|
||||||
default_validator_rest_endpoints()
|
|
||||||
);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_validators(default_validator_rest_endpoints());
|
|
||||||
|
|
||||||
config.save_to_file(None).unwrap_or_else(|err| {
|
|
||||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
|
||||||
print_failed_upgrade(&from_version, &to_version);
|
|
||||||
process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
print_successful_upgrade(from_version, to_version);
|
|
||||||
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
changes:
|
|
||||||
- introduction of mixnet contract address field
|
|
||||||
- change to default validator rest endpoint
|
|
||||||
*/
|
|
||||||
fn minor_010_upgrade(
|
|
||||||
mut config: Config,
|
mut config: Config,
|
||||||
_matches: &ArgMatches,
|
_matches: &ArgMatches,
|
||||||
config_version: &Version,
|
config_version: &Version,
|
||||||
package_version: &Version,
|
package_version: &Version,
|
||||||
) -> Config {
|
) -> Config {
|
||||||
let to_version = if package_version.major == 0 && package_version.minor == 10 {
|
let to_version = if package_version.major == 0 && package_version.minor == 12 {
|
||||||
package_version.clone()
|
package_version.clone()
|
||||||
} else {
|
} else {
|
||||||
Version::new(0, 10, 0)
|
Version::new(0, 12, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
print_start_upgrade(&config_version, &to_version);
|
print_start_upgrade(&config_version, &to_version);
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_version(to_version.to_string().as_ref());
|
|
||||||
|
|
||||||
if config.get_base().get_validator_mixnet_contract_address() != MISSING_VALUE {
|
|
||||||
eprintln!("existing config seems to have specified mixnet contract address which was only introduced in 0.10.0! Can't perform upgrade.");
|
|
||||||
print_failed_upgrade(&config_version, &to_version);
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Setting mixnet contract address to {}",
|
"Setting validator API endpoints to {:?}",
|
||||||
DEFAULT_MIXNET_CONTRACT_ADDRESS
|
default_api_endpoints()
|
||||||
);
|
);
|
||||||
|
|
||||||
config
|
config
|
||||||
.get_base_mut()
|
.get_base_mut()
|
||||||
.set_mixnet_contract(DEFAULT_MIXNET_CONTRACT_ADDRESS);
|
.set_custom_validator_apis(default_api_endpoints());
|
||||||
|
|
||||||
// The default validator endpoint changed
|
|
||||||
println!(
|
|
||||||
"Setting validator REST endpoint to to {:?}",
|
|
||||||
default_validator_rest_endpoints()
|
|
||||||
);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_validators(default_validator_rest_endpoints());
|
|
||||||
|
|
||||||
config.save_to_file(None).unwrap_or_else(|err| {
|
|
||||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
|
||||||
print_failed_upgrade(&config_version, &to_version);
|
|
||||||
process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
print_successful_upgrade(config_version, to_version);
|
|
||||||
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
// no changes but version number
|
|
||||||
fn patch_010_upgrade(
|
|
||||||
mut config: Config,
|
|
||||||
_matches: &ArgMatches,
|
|
||||||
config_version: &Version,
|
|
||||||
package_version: &Version,
|
|
||||||
) -> Config {
|
|
||||||
let to_version = package_version;
|
|
||||||
|
|
||||||
print_start_upgrade(&config_version, &to_version);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_version(to_version.to_string().as_ref());
|
|
||||||
|
|
||||||
config.save_to_file(None).unwrap_or_else(|err| {
|
|
||||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
|
||||||
print_failed_upgrade(&config_version, &to_version);
|
|
||||||
process::exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
print_successful_upgrade(config_version, to_version);
|
|
||||||
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
fn minor_011_upgrade(
|
|
||||||
mut config: Config,
|
|
||||||
_matches: &ArgMatches,
|
|
||||||
config_version: &Version,
|
|
||||||
package_version: &Version,
|
|
||||||
) -> Config {
|
|
||||||
let to_version = package_version;
|
|
||||||
|
|
||||||
print_start_upgrade(&config_version, &to_version);
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Setting mixnet contract address to {}",
|
|
||||||
DEFAULT_MIXNET_CONTRACT_ADDRESS
|
|
||||||
);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_mixnet_contract(DEFAULT_MIXNET_CONTRACT_ADDRESS);
|
|
||||||
|
|
||||||
// The default validator endpoint changed
|
|
||||||
println!(
|
|
||||||
"Setting validator REST endpoint to {:?}",
|
|
||||||
default_validator_rest_endpoints()
|
|
||||||
);
|
|
||||||
|
|
||||||
config
|
|
||||||
.get_base_mut()
|
|
||||||
.set_custom_validators(default_validator_rest_endpoints());
|
|
||||||
|
|
||||||
config
|
config
|
||||||
.get_base_mut()
|
.get_base_mut()
|
||||||
@@ -297,18 +142,11 @@ fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version
|
|||||||
|
|
||||||
config = match config_version.major {
|
config = match config_version.major {
|
||||||
0 => match config_version.minor {
|
0 => match config_version.minor {
|
||||||
9 => minor_010_upgrade(config, matches, &config_version, &Version::new(0, 10, 0)),
|
9 | 10 => outdated_upgrade(&config_version, &package_version),
|
||||||
10 => match config_version.patch {
|
11 => minor_0_12_upgrade(config, matches, &config_version, &package_version),
|
||||||
0 => {
|
_ => unsupported_upgrade(&config_version, &package_version),
|
||||||
patch_010_upgrade(config, matches, &config_version, &Version::new(0, 10, 1))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
minor_011_upgrade(config, matches, &config_version, &Version::new(0, 11, 0))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => unsupported_upgrade(config_version, package_version),
|
|
||||||
},
|
},
|
||||||
_ => unsupported_upgrade(config_version, package_version),
|
_ => unsupported_upgrade(&config_version, &package_version),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,22 +156,14 @@ pub fn execute(matches: &ArgMatches) {
|
|||||||
|
|
||||||
let id = matches.value_of("id").unwrap();
|
let id = matches.value_of("id").unwrap();
|
||||||
|
|
||||||
let mut existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
|
let existing_config = Config::load_from_file(Some(id)).unwrap_or_else(|err| {
|
||||||
eprintln!("failed to load existing config file! - {:?}", err);
|
eprintln!("failed to load existing config file! - {:?}", err);
|
||||||
process::exit(1)
|
process::exit(1)
|
||||||
});
|
});
|
||||||
|
|
||||||
// versions fields were added in 0.9.0
|
|
||||||
if existing_config.get_base().get_version() == MISSING_VALUE {
|
if existing_config.get_base().get_version() == MISSING_VALUE {
|
||||||
let self_reported_version = matches.value_of("current version").unwrap_or_else(|| {
|
eprintln!("the existing configuration file does not seem to contain version number.");
|
||||||
eprintln!(
|
process::exit(1);
|
||||||
"trying to upgrade from pre v0.9.0 without providing current system version!"
|
|
||||||
);
|
|
||||||
process::exit(1)
|
|
||||||
});
|
|
||||||
|
|
||||||
// upgrades up to 0.9.0
|
|
||||||
existing_config = pre_090_upgrade(self_reported_version, existing_config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// here be upgrade path to 0.9.X and beyond based on version number from config
|
// here be upgrade path to 0.9.X and beyond based on version number from config
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.DS_Store
|
||||||
|
/node_modules/
|
||||||
|
/src/node_modules/@sapper/
|
||||||
|
yarn-error.log
|
||||||
|
/__sapper__/
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
On Ubuntu-ish systems (not tested on Debian but may work):
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt update && sudo apt install libwebkit2gtk-4.0-dev \
|
||||||
|
build-essential \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
libssl-dev \
|
||||||
|
libgtk-3-dev \
|
||||||
|
libappindicator3-dev \
|
||||||
|
patchelf \
|
||||||
|
librsvg2-dev```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
1. Start Sapper([docs](https://sapper.svelte.dev/docs/)) -> `yarn run dev`
|
||||||
|
2. Start Tauri([docs](https://tauri.studio/en/)) in another terminal -> `yarn tauri dev`
|
||||||
|
3. Start validator-api locally, or override `validator-urls` in `index.svelte`
|
||||||
|
|
||||||
|
## Getting around
|
||||||
|
|
||||||
|
+ Frontend -> `tauri-client/`
|
||||||
|
+ logic -> `tauri-client/src/routes/index.svelte`
|
||||||
|
+ assets -> `tauri-client/static`
|
||||||
|
+ Backend -> `tauri-client/src/src-tauri`
|
||||||
|
|
||||||
|
## Build standalone app
|
||||||
|
|
||||||
|
+ yarn tauri build [--debug]
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "TODO",
|
||||||
|
"description": "TODO",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "sapper dev",
|
||||||
|
"build": "sapper build --legacy",
|
||||||
|
"export": "sapper export --legacy",
|
||||||
|
"start": "node __sapper__/build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "^1.0.0-beta.4",
|
||||||
|
"compression": "^1.7.1",
|
||||||
|
"polka": "next",
|
||||||
|
"qrious": "^4.0.2",
|
||||||
|
"sirv": "^1.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.0.0",
|
||||||
|
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.0.0",
|
||||||
|
"@babel/preset-env": "^7.0.0",
|
||||||
|
"@babel/runtime": "^7.0.0",
|
||||||
|
"@rollup/plugin-babel": "^5.0.0",
|
||||||
|
"@rollup/plugin-commonjs": "^14.0.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^8.0.0",
|
||||||
|
"@rollup/plugin-replace": "^2.4.0",
|
||||||
|
"@rollup/plugin-url": "^5.0.0",
|
||||||
|
"@tauri-apps/cli": "^1.0.0-beta.5",
|
||||||
|
"rollup": "^2.3.4",
|
||||||
|
"rollup-plugin-svelte": "^7.0.0",
|
||||||
|
"rollup-plugin-terser": "^7.0.0",
|
||||||
|
"sapper": "^0.28.0",
|
||||||
|
"sass": "^1.35.2",
|
||||||
|
"svelte": "^3.17.3",
|
||||||
|
"svelte-preprocess": "^4.7.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
import path from "path";
|
||||||
|
import resolve from "@rollup/plugin-node-resolve";
|
||||||
|
import replace from "@rollup/plugin-replace";
|
||||||
|
import commonjs from "@rollup/plugin-commonjs";
|
||||||
|
import url from "@rollup/plugin-url";
|
||||||
|
import svelte from "rollup-plugin-svelte";
|
||||||
|
import babel from "@rollup/plugin-babel";
|
||||||
|
import { terser } from "rollup-plugin-terser";
|
||||||
|
import config from "sapper/config/rollup.js";
|
||||||
|
import pkg from "./package.json";
|
||||||
|
import sveltePreprocess from "svelte-preprocess";
|
||||||
|
|
||||||
|
const mode = process.env.NODE_ENV;
|
||||||
|
const dev = mode === "development";
|
||||||
|
const legacy = !!process.env.SAPPER_LEGACY_BUILD;
|
||||||
|
|
||||||
|
const onwarn = (warning, onwarn) =>
|
||||||
|
(warning.code === "MISSING_EXPORT" && /'preload'/.test(warning.message)) ||
|
||||||
|
(warning.code === "CIRCULAR_DEPENDENCY" &&
|
||||||
|
/[/\\]@sapper[/\\]/.test(warning.message)) ||
|
||||||
|
onwarn(warning);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
client: {
|
||||||
|
input: config.client.input(),
|
||||||
|
output: config.client.output(),
|
||||||
|
plugins: [
|
||||||
|
replace({
|
||||||
|
preventAssignment: true,
|
||||||
|
values: {
|
||||||
|
"process.browser": true,
|
||||||
|
"process.env.NODE_ENV": JSON.stringify(mode),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
svelte({
|
||||||
|
compilerOptions: {
|
||||||
|
dev,
|
||||||
|
hydratable: true,
|
||||||
|
},
|
||||||
|
preprocess: sveltePreprocess(),
|
||||||
|
}),
|
||||||
|
url({
|
||||||
|
sourceDir: path.resolve(__dirname, "src/node_modules/images"),
|
||||||
|
publicPath: "/client/",
|
||||||
|
}),
|
||||||
|
resolve({
|
||||||
|
browser: true,
|
||||||
|
dedupe: ["svelte"],
|
||||||
|
}),
|
||||||
|
commonjs(),
|
||||||
|
|
||||||
|
legacy &&
|
||||||
|
babel({
|
||||||
|
extensions: [".js", ".mjs", ".html", ".svelte"],
|
||||||
|
babelHelpers: "runtime",
|
||||||
|
exclude: ["node_modules/@babel/**"],
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{
|
||||||
|
targets: "> 0.25%, not dead",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
"@babel/plugin-syntax-dynamic-import",
|
||||||
|
[
|
||||||
|
"@babel/plugin-transform-runtime",
|
||||||
|
{
|
||||||
|
useESModules: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
!dev &&
|
||||||
|
terser({
|
||||||
|
module: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
|
||||||
|
preserveEntrySignatures: false,
|
||||||
|
onwarn,
|
||||||
|
},
|
||||||
|
|
||||||
|
server: {
|
||||||
|
input: config.server.input(),
|
||||||
|
output: config.server.output(),
|
||||||
|
plugins: [
|
||||||
|
replace({
|
||||||
|
preventAssignment: true,
|
||||||
|
values: {
|
||||||
|
"process.browser": false,
|
||||||
|
"process.env.NODE_ENV": JSON.stringify(mode),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
svelte({
|
||||||
|
compilerOptions: {
|
||||||
|
dev,
|
||||||
|
generate: "ssr",
|
||||||
|
hydratable: true,
|
||||||
|
},
|
||||||
|
emitCss: false,
|
||||||
|
}),
|
||||||
|
url({
|
||||||
|
sourceDir: path.resolve(__dirname, "src/node_modules/images"),
|
||||||
|
publicPath: "/client/",
|
||||||
|
emitFiles: false, // already emitted by client build
|
||||||
|
}),
|
||||||
|
resolve({
|
||||||
|
dedupe: ["svelte"],
|
||||||
|
}),
|
||||||
|
commonjs(),
|
||||||
|
],
|
||||||
|
external: Object.keys(pkg.dependencies).concat(
|
||||||
|
require("module").builtinModules
|
||||||
|
),
|
||||||
|
preserveEntrySignatures: "strict",
|
||||||
|
onwarn,
|
||||||
|
},
|
||||||
|
|
||||||
|
serviceworker: {
|
||||||
|
input: config.serviceworker.input(),
|
||||||
|
output: config.serviceworker.output(),
|
||||||
|
plugins: [
|
||||||
|
resolve(),
|
||||||
|
replace({
|
||||||
|
preventAssignment: true,
|
||||||
|
values: {
|
||||||
|
"process.browser": true,
|
||||||
|
"process.env.NODE_ENV": JSON.stringify(mode),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
commonjs(),
|
||||||
|
!dev && terser(),
|
||||||
|
],
|
||||||
|
preserveEntrySignatures: false,
|
||||||
|
onwarn,
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,304 @@
|
|||||||
|
/**
|
||||||
|
* Run this script to convert the project to TypeScript. This is only guaranteed to work
|
||||||
|
* on the unmodified default template; if you have done code changes you are likely need
|
||||||
|
* to touch up the generated project manually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// @ts-check
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { argv } = require('process');
|
||||||
|
|
||||||
|
const projectRoot = argv[2] || path.join(__dirname, '..');
|
||||||
|
|
||||||
|
const isRollup = fs.existsSync(path.join(projectRoot, "rollup.config.js"));
|
||||||
|
|
||||||
|
function warn(message) {
|
||||||
|
console.warn('Warning: ' + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceInFile(fileName, replacements) {
|
||||||
|
if (fs.existsSync(fileName)) {
|
||||||
|
let contents = fs.readFileSync(fileName, 'utf8');
|
||||||
|
let hadUpdates = false;
|
||||||
|
|
||||||
|
replacements.forEach(([from, to]) => {
|
||||||
|
const newContents = contents.replace(from, to);
|
||||||
|
|
||||||
|
const isAlreadyApplied = typeof to !== 'string' || contents.includes(to);
|
||||||
|
|
||||||
|
if (newContents !== contents) {
|
||||||
|
contents = newContents;
|
||||||
|
hadUpdates = true;
|
||||||
|
} else if (!isAlreadyApplied) {
|
||||||
|
warn(`Wanted to update "${from}" in ${fileName}, but did not find it.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hadUpdates) {
|
||||||
|
fs.writeFileSync(fileName, contents);
|
||||||
|
} else {
|
||||||
|
console.log(`${fileName} had already been updated.`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn(`Wanted to update ${fileName} but the file did not exist.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFile(fileName, contents) {
|
||||||
|
if (fs.existsSync(fileName)) {
|
||||||
|
warn(`Wanted to create ${fileName}, but it already existed. Leaving existing file.`);
|
||||||
|
} else {
|
||||||
|
fs.writeFileSync(fileName, contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDepsToPackageJson() {
|
||||||
|
const pkgJSONPath = path.join(projectRoot, 'package.json');
|
||||||
|
const packageJSON = JSON.parse(fs.readFileSync(pkgJSONPath, 'utf8'));
|
||||||
|
packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, {
|
||||||
|
...(isRollup ? { '@rollup/plugin-typescript': '^6.0.0' } : { 'ts-loader': '^8.0.4' }),
|
||||||
|
'@tsconfig/svelte': '^1.0.10',
|
||||||
|
'@types/compression': '^1.7.0',
|
||||||
|
'@types/node': '^14.11.1',
|
||||||
|
'@types/polka': '^0.5.1',
|
||||||
|
'svelte-check': '^1.0.46',
|
||||||
|
'svelte-preprocess': '^4.3.0',
|
||||||
|
tslib: '^2.0.1',
|
||||||
|
typescript: '^4.0.3'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add script for checking
|
||||||
|
packageJSON.scripts = Object.assign(packageJSON.scripts, {
|
||||||
|
validate: 'svelte-check --ignore src/node_modules/@sapper'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Write the package JSON
|
||||||
|
fs.writeFileSync(pkgJSONPath, JSON.stringify(packageJSON, null, ' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeJsExtensionToTs(dir) {
|
||||||
|
const elements = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
if (elements[i].isDirectory()) {
|
||||||
|
changeJsExtensionToTs(path.join(dir, elements[i].name));
|
||||||
|
} else if (elements[i].name.match(/^[^_]((?!json).)*js$/)) {
|
||||||
|
fs.renameSync(path.join(dir, elements[i].name), path.join(dir, elements[i].name).replace('.js', '.ts'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSingleSvelteFile({ view, vars, contextModule }) {
|
||||||
|
replaceInFile(path.join(projectRoot, 'src', `${view}.svelte`), [
|
||||||
|
[/(?:<script)(( .*?)*?)>/gm, (m, attrs) => `<script${attrs}${!attrs.includes('lang="ts"') ? ' lang="ts"' : ''}>`],
|
||||||
|
...(vars ? vars.map(({ name, type }) => [`export let ${name};`, `export let ${name}: ${type};`]) : []),
|
||||||
|
...(contextModule ? contextModule.map(({ js, ts }) => [js, ts]) : [])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch the *.svelte file to use TS
|
||||||
|
function updateSvelteFiles() {
|
||||||
|
[
|
||||||
|
{
|
||||||
|
view: 'components/Nav',
|
||||||
|
vars: [{ name: 'segment', type: 'string' }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
view: 'routes/_layout',
|
||||||
|
vars: [{ name: 'segment', type: 'string' }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
view: 'routes/_error',
|
||||||
|
vars: [
|
||||||
|
{ name: 'status', type: 'number' },
|
||||||
|
{ name: 'error', type: 'Error' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
view: 'routes/blog/index',
|
||||||
|
vars: [{ name: 'posts', type: '{ slug: string; title: string, html: any }[]' }],
|
||||||
|
contextModule: [
|
||||||
|
{
|
||||||
|
js: '.then(r => r.json())',
|
||||||
|
ts: '.then((r: { json: () => any; }) => r.json())'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
js: '.then(posts => {',
|
||||||
|
ts: '.then((posts: { slug: string; title: string, html: any }[]) => {'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
view: 'routes/blog/[slug]',
|
||||||
|
vars: [{ name: 'post', type: '{ slug: string; title: string, html: any }' }]
|
||||||
|
}
|
||||||
|
].forEach(updateSingleSvelteFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRollupConfig() {
|
||||||
|
// Edit rollup config
|
||||||
|
replaceInFile(path.join(projectRoot, 'rollup.config.js'), [
|
||||||
|
// Edit imports
|
||||||
|
[
|
||||||
|
/'rollup-plugin-terser';\n(?!import sveltePreprocess)/,
|
||||||
|
`'rollup-plugin-terser';
|
||||||
|
import sveltePreprocess from 'svelte-preprocess';
|
||||||
|
import typescript from '@rollup/plugin-typescript';
|
||||||
|
`
|
||||||
|
],
|
||||||
|
// Edit inputs
|
||||||
|
[
|
||||||
|
/(?<!THIS_IS_UNDEFINED[^\n]*\n\s*)onwarn\(warning\);/,
|
||||||
|
`(warning.code === 'THIS_IS_UNDEFINED') ||\n\tonwarn(warning);`
|
||||||
|
],
|
||||||
|
[/input: config.client.input\(\)(?!\.replace)/, `input: config.client.input().replace(/\\.js$/, '.ts')`],
|
||||||
|
[
|
||||||
|
/input: config.server.input\(\)(?!\.replace)/,
|
||||||
|
`input: { server: config.server.input().server.replace(/\\.js$/, ".ts") }`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/input: config.serviceworker.input\(\)(?!\.replace)/,
|
||||||
|
`input: config.serviceworker.input().replace(/\\.js$/, '.ts')`
|
||||||
|
],
|
||||||
|
// Add preprocess
|
||||||
|
[/compilerOptions/g, 'preprocess: sveltePreprocess({ sourceMap: dev }),\n\t\t\t\tcompilerOptions'],
|
||||||
|
// Add TypeScript
|
||||||
|
[/commonjs\(\)(?!,\n\s*typescript)/g, 'commonjs(),\n\t\t\ttypescript({ sourceMap: dev })']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateWebpackConfig() {
|
||||||
|
// Edit webpack config
|
||||||
|
replaceInFile(path.join(projectRoot, 'webpack.config.js'), [
|
||||||
|
// Edit imports
|
||||||
|
[
|
||||||
|
/require\('webpack-modules'\);\n(?!const sveltePreprocess)/,
|
||||||
|
`require('webpack-modules');\nconst sveltePreprocess = require('svelte-preprocess');\n`
|
||||||
|
],
|
||||||
|
// Edit extensions
|
||||||
|
[
|
||||||
|
/\['\.mjs', '\.js', '\.json', '\.svelte', '\.html'\]/,
|
||||||
|
`['.mjs', '.js', '.ts', '.json', '.svelte', '.html']`
|
||||||
|
],
|
||||||
|
// Edit entries
|
||||||
|
[
|
||||||
|
/entry: config\.client\.entry\(\)/,
|
||||||
|
`entry: { main: config.client.entry().main.replace(/\\.js$/, '.ts') }`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/entry: config\.server\.entry\(\)/,
|
||||||
|
`entry: { server: config.server.entry().server.replace(/\\.js$/, '.ts') }`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/entry: config\.serviceworker\.entry\(\)/,
|
||||||
|
`entry: { 'service-worker': config.serviceworker.entry()['service-worker'].replace(/\\.js$/, '.ts') }`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/loader: 'svelte-loader',\n\t\t\t\t\t\toptions: {/g,
|
||||||
|
'loader: \'svelte-loader\',\n\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\tpreprocess: sveltePreprocess({ sourceMap: dev }),'
|
||||||
|
],
|
||||||
|
// Add TypeScript rules for client and server
|
||||||
|
[
|
||||||
|
/module: {\n\s*rules: \[\n\s*(?!{\n\s*test: \/\\\.ts\$\/)/g,
|
||||||
|
`module: {\n\t\t\trules: [\n\t\t\t\t{\n\t\t\t\t\ttest: /\\.ts$/,\n\t\t\t\t\tloader: 'ts-loader'\n\t\t\t\t},\n\t\t\t\t`
|
||||||
|
],
|
||||||
|
// Add TypeScript rules for serviceworker
|
||||||
|
[
|
||||||
|
/output: config\.serviceworker\.output\(\),\n\s*(?!module)/,
|
||||||
|
`output: config.serviceworker.output(),\n\t\tmodule: {\n\t\t\trules: [\n\t\t\t\t{\n\t\t\t\t\ttest: /\\.ts$/,\n\t\t\t\t\tloader: 'ts-loader'\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t`
|
||||||
|
],
|
||||||
|
// Edit outputs
|
||||||
|
[
|
||||||
|
/output: config\.serviceworker\.output\(\),\n\s*(?!resolve)/,
|
||||||
|
`output: config.serviceworker.output(),\n\t\tresolve: { extensions: ['.mjs', '.js', '.ts', '.json'] },\n\t\t`
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateServiceWorker() {
|
||||||
|
replaceInFile(path.join(projectRoot, 'src', 'service-worker.ts'), [
|
||||||
|
[`shell.concat(files);`, `(shell as string[]).concat(files as string[]);`],
|
||||||
|
[`self.skipWaiting();`, `((self as any) as ServiceWorkerGlobalScope).skipWaiting();`],
|
||||||
|
[`self.clients.claim();`, `((self as any) as ServiceWorkerGlobalScope).clients.claim();`],
|
||||||
|
[`fetchAndCache(request)`, `fetchAndCache(request: Request)`],
|
||||||
|
[`self.addEventListener('activate', event =>`, `self.addEventListener('activate', (event: ExtendableEvent) =>`],
|
||||||
|
[`self.addEventListener('install', event =>`, `self.addEventListener('install', (event: ExtendableEvent) =>`],
|
||||||
|
[`addEventListener('fetch', event =>`, `addEventListener('fetch', (event: FetchEvent) =>`],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTsConfig() {
|
||||||
|
const tsconfig = `{
|
||||||
|
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["DOM", "ES2017", "WebWorker"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*", "src/node_modules/**/*"],
|
||||||
|
"exclude": ["node_modules/*", "__sapper__/*", "static/*"]
|
||||||
|
}`;
|
||||||
|
|
||||||
|
createFile(path.join(projectRoot, 'tsconfig.json'), tsconfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds the extension recommendation
|
||||||
|
function configureVsCode() {
|
||||||
|
const dir = path.join(projectRoot, '.vscode');
|
||||||
|
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
createFile(path.join(projectRoot, '.vscode', 'extensions.json'), `{"recommendations": ["svelte.svelte-vscode"]}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteThisScript() {
|
||||||
|
fs.unlinkSync(path.join(__filename));
|
||||||
|
|
||||||
|
// Check for Mac's DS_store file, and if it's the only one left remove it
|
||||||
|
const remainingFiles = fs.readdirSync(path.join(__dirname));
|
||||||
|
if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') {
|
||||||
|
fs.unlinkSync(path.join(__dirname, '.DS_store'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the scripts folder is empty
|
||||||
|
if (fs.readdirSync(path.join(__dirname)).length === 0) {
|
||||||
|
// Remove the scripts folder
|
||||||
|
fs.rmdirSync(path.join(__dirname));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Adding TypeScript with ${isRollup ? "Rollup" : "webpack" }...`);
|
||||||
|
|
||||||
|
addDepsToPackageJson();
|
||||||
|
|
||||||
|
changeJsExtensionToTs(path.join(projectRoot, 'src'));
|
||||||
|
|
||||||
|
updateSvelteFiles();
|
||||||
|
|
||||||
|
if (isRollup) {
|
||||||
|
updateRollupConfig();
|
||||||
|
} else {
|
||||||
|
updateWebpackConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateServiceWorker();
|
||||||
|
|
||||||
|
createTsConfig();
|
||||||
|
|
||||||
|
configureVsCode();
|
||||||
|
|
||||||
|
// Delete this script, but not during testing
|
||||||
|
if (!argv[2]) {
|
||||||
|
deleteThisScript();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Converted to TypeScript.');
|
||||||
|
|
||||||
|
if (fs.existsSync(path.join(projectRoot, 'node_modules'))) {
|
||||||
|
console.log(`
|
||||||
|
Next:
|
||||||
|
1. run 'npm install' again to install TypeScript dependencies
|
||||||
|
2. run 'npm run build' for the @sapper imports in your project to work
|
||||||
|
`);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
WixTools
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
[package]
|
||||||
|
name = "app"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A Tauri App"
|
||||||
|
authors = ["you"]
|
||||||
|
license = ""
|
||||||
|
repository = ""
|
||||||
|
default-run = "app"
|
||||||
|
edition = "2018"
|
||||||
|
build = "src/build.rs"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "1.0.0-beta.2" }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde_json = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
tauri = { version = "1.0.0-beta.4", features = [] }
|
||||||
|
tokio = "1.4"
|
||||||
|
url = "2.2"
|
||||||
|
|
||||||
|
coconut-interface = { path = "../../../common/coconut-interface" }
|
||||||
|
credentials = { path = "../../../common/credentials" }
|
||||||
|
validator-client = {path = "../../../common/client-libs/validator-client"}
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["custom-protocol"]
|
||||||
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 88 KiB |
@@ -0,0 +1,13 @@
|
|||||||
|
max_width = 100
|
||||||
|
hard_tabs = false
|
||||||
|
tab_spaces = 2
|
||||||
|
newline_style = "Auto"
|
||||||
|
use_small_heuristics = "Default"
|
||||||
|
reorder_imports = true
|
||||||
|
reorder_modules = true
|
||||||
|
remove_nested_parens = true
|
||||||
|
edition = "2018"
|
||||||
|
merge_derives = true
|
||||||
|
use_try_shorthand = false
|
||||||
|
use_field_init_shorthand = false
|
||||||
|
force_explicit_abi = true
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
tauri_build::build()
|
||||||
|
}
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
#![cfg_attr(
|
||||||
|
all(not(debug_assertions), target_os = "windows"),
|
||||||
|
windows_subsystem = "windows"
|
||||||
|
)]
|
||||||
|
|
||||||
|
use coconut_interface::{
|
||||||
|
self, hash_to_scalar, Attribute, Credential, Parameters, Signature, Theta, VerificationKey,
|
||||||
|
};
|
||||||
|
use credentials::{obtain_aggregate_signature, obtain_aggregate_verification_key};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
signatures: Vec<Signature>,
|
||||||
|
n_attributes: u32,
|
||||||
|
params: Parameters,
|
||||||
|
public_attributes_bytes: Vec<Vec<u8>>,
|
||||||
|
public_attributes: Vec<Attribute>,
|
||||||
|
private_attributes: Vec<Attribute>,
|
||||||
|
aggregated_verification_key: Option<VerificationKey>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn init(public_attributes_bytes: Vec<Vec<u8>>, private_attributes_bytes: Vec<Vec<u8>>) -> State {
|
||||||
|
let n_attributes = (public_attributes_bytes.len() + private_attributes_bytes.len()) as u32;
|
||||||
|
let params = Parameters::new(n_attributes).unwrap();
|
||||||
|
let public_attributes = public_attributes_bytes
|
||||||
|
.iter()
|
||||||
|
.map(hash_to_scalar)
|
||||||
|
.collect::<Vec<Attribute>>();
|
||||||
|
let private_attributes = private_attributes_bytes
|
||||||
|
.iter()
|
||||||
|
.map(hash_to_scalar)
|
||||||
|
.collect::<Vec<Attribute>>();
|
||||||
|
State {
|
||||||
|
signatures: Vec::new(),
|
||||||
|
n_attributes,
|
||||||
|
params,
|
||||||
|
public_attributes_bytes,
|
||||||
|
public_attributes,
|
||||||
|
private_attributes,
|
||||||
|
aggregated_verification_key: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_url_validators(raw: &[String]) -> Result<Vec<Url>, String> {
|
||||||
|
let mut parsed_urls = Vec::with_capacity(raw.len());
|
||||||
|
for url in raw {
|
||||||
|
let parsed_url: Url = url
|
||||||
|
.parse()
|
||||||
|
.map_err(|err| format!("one of validator urls is malformed - {}", err))?;
|
||||||
|
parsed_urls.push(parsed_url)
|
||||||
|
}
|
||||||
|
Ok(parsed_urls)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn randomise_credential(
|
||||||
|
idx: usize,
|
||||||
|
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||||
|
) -> Result<Vec<Signature>, String> {
|
||||||
|
let mut state = state.write().await;
|
||||||
|
let signature = state.signatures.remove(idx);
|
||||||
|
let new = signature.randomise(&state.params);
|
||||||
|
state.signatures.insert(idx, new);
|
||||||
|
Ok(state.signatures.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn delete_credential(
|
||||||
|
idx: usize,
|
||||||
|
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||||
|
) -> Result<Vec<Signature>, String> {
|
||||||
|
let mut state = state.write().await;
|
||||||
|
state.signatures.remove(idx);
|
||||||
|
Ok(state.signatures.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn list_credentials(
|
||||||
|
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||||
|
) -> Result<Vec<Signature>, String> {
|
||||||
|
let state = state.read().await;
|
||||||
|
Ok(state.signatures.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_aggregated_verification_key(
|
||||||
|
validator_urls: Vec<String>,
|
||||||
|
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||||
|
) -> Result<VerificationKey, String> {
|
||||||
|
if let Some(verification_key) = &state.read().await.aggregated_verification_key {
|
||||||
|
return Ok(verification_key.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsed_urls = parse_url_validators(&validator_urls)?;
|
||||||
|
let key = obtain_aggregate_verification_key(&parsed_urls)
|
||||||
|
.await
|
||||||
|
.map_err(|err| format!("failed to obtain aggregate verification key - {:?}", err))?;
|
||||||
|
|
||||||
|
state
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.aggregated_verification_key
|
||||||
|
.replace(key.clone());
|
||||||
|
|
||||||
|
Ok(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn prove_credential(
|
||||||
|
idx: usize,
|
||||||
|
validator_urls: Vec<String>,
|
||||||
|
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||||
|
) -> Result<Theta, String> {
|
||||||
|
let verification_key = get_aggregated_verification_key(validator_urls, state.clone()).await?;
|
||||||
|
let state = state.read().await;
|
||||||
|
|
||||||
|
if let Some(signature) = state.signatures.get(idx) {
|
||||||
|
match coconut_interface::prove_credential(
|
||||||
|
&state.params,
|
||||||
|
&verification_key,
|
||||||
|
signature,
|
||||||
|
&state.private_attributes,
|
||||||
|
) {
|
||||||
|
Ok(theta) => Ok(theta),
|
||||||
|
Err(e) => Err(format!("{}", e)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("Got invalid Signature idx".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn verify_credential(
|
||||||
|
idx: usize,
|
||||||
|
validator_urls: Vec<String>,
|
||||||
|
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||||
|
) -> Result<bool, String> {
|
||||||
|
// the API needs to be improved but at least it should compile (in theory)
|
||||||
|
let verification_key =
|
||||||
|
get_aggregated_verification_key(validator_urls.clone(), state.clone()).await?;
|
||||||
|
let theta = prove_credential(idx, validator_urls, state.clone()).await?;
|
||||||
|
|
||||||
|
let state = state.read().await;
|
||||||
|
|
||||||
|
let credential = Credential::new(
|
||||||
|
state.n_attributes,
|
||||||
|
theta,
|
||||||
|
state.public_attributes_bytes.clone(),
|
||||||
|
state
|
||||||
|
.signatures
|
||||||
|
.get(idx)
|
||||||
|
.ok_or("Got invalid signature idx")?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(credential.verify(&verification_key))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn get_credential(
|
||||||
|
validator_urls: Vec<String>,
|
||||||
|
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||||
|
) -> Result<Vec<Signature>, String> {
|
||||||
|
let guard = state.read().await;
|
||||||
|
let parsed_urls = parse_url_validators(&validator_urls)?;
|
||||||
|
|
||||||
|
let signature = obtain_aggregate_signature(
|
||||||
|
&guard.params,
|
||||||
|
&guard.public_attributes,
|
||||||
|
&guard.private_attributes,
|
||||||
|
&parsed_urls,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|err| format!("failed to obtain aggregate signature - {:?}", err))?;
|
||||||
|
|
||||||
|
let mut state = state.write().await;
|
||||||
|
state.signatures.push(signature);
|
||||||
|
Ok(state.signatures.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let public_attributes = vec![b"public_key".to_vec()];
|
||||||
|
let private_attributes = vec![b"private_key".to_vec()];
|
||||||
|
tauri::Builder::default()
|
||||||
|
.manage(Arc::new(RwLock::new(State::init(
|
||||||
|
public_attributes,
|
||||||
|
private_attributes,
|
||||||
|
))))
|
||||||
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
get_credential,
|
||||||
|
randomise_credential,
|
||||||
|
delete_credential,
|
||||||
|
list_credentials,
|
||||||
|
verify_credential
|
||||||
|
])
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running tauri application");
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"package": {
|
||||||
|
"productName": "nym-tauri-client",
|
||||||
|
"version": "0.1.0"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"distDir": "../__sapper__/export",
|
||||||
|
"devPath": "http://localhost:3000",
|
||||||
|
"beforeDevCommand": "",
|
||||||
|
"beforeBuildCommand": "yarn run export"
|
||||||
|
},
|
||||||
|
"tauri": {
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": "all",
|
||||||
|
"identifier": "com.tauri.dev",
|
||||||
|
"icon": [
|
||||||
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/128x128@2x.png",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
|
],
|
||||||
|
"resources": [],
|
||||||
|
"externalBin": [],
|
||||||
|
"copyright": "",
|
||||||
|
"category": "DeveloperTool",
|
||||||
|
"shortDescription": "",
|
||||||
|
"longDescription": "",
|
||||||
|
"deb": {
|
||||||
|
"depends": [],
|
||||||
|
"useBootstrapper": false
|
||||||
|
},
|
||||||
|
"macOS": {
|
||||||
|
"frameworks": [],
|
||||||
|
"minimumSystemVersion": "",
|
||||||
|
"useBootstrapper": false,
|
||||||
|
"exceptionDomain": "",
|
||||||
|
"signingIdentity": null,
|
||||||
|
"entitlements": null
|
||||||
|
},
|
||||||
|
"windows": {
|
||||||
|
"certificateThumbprint": null,
|
||||||
|
"digestAlgorithm": "sha256",
|
||||||
|
"timestampUrl": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"updater": {
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
"allowlist": {},
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "Nym Client",
|
||||||
|
"width": 800,
|
||||||
|
"height": 1000,
|
||||||
|
"resizable": true,
|
||||||
|
"fullscreen": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"security": {
|
||||||
|
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* These declarations tell TypeScript that we allow import of images, e.g.
|
||||||
|
* ```
|
||||||
|
<script lang='ts'>
|
||||||
|
import successkid from 'images/successkid.jpg';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<img src="{successkid}">
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
declare module "*.gif" {
|
||||||
|
const value: string;
|
||||||
|
export default value;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.jpg" {
|
||||||
|
const value: string;
|
||||||
|
export default value;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.jpeg" {
|
||||||
|
const value: string;
|
||||||
|
export default value;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.png" {
|
||||||
|
const value: string;
|
||||||
|
export default value;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.svg" {
|
||||||
|
const value: string;
|
||||||
|
export default value;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.webp" {
|
||||||
|
const value: string;
|
||||||
|
export default value;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import * as sapper from '@sapper/app';
|
||||||
|
|
||||||
|
sapper.start({
|
||||||
|
target: document.querySelector('#sapper')
|
||||||
|
});
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<nav>
|
||||||
|
<img src="coconut-120.png" style="width: 120px" alt=""/>
|
||||||
|
</nav>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<script>
|
||||||
|
export let status;
|
||||||
|
export let error;
|
||||||
|
|
||||||
|
const dev = process.env.NODE_ENV === 'development';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h1, p {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.8em;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0 0 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 480px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 4em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{status}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<h1>{status}</h1>
|
||||||
|
|
||||||
|
<p>{error.message}</p>
|
||||||
|
|
||||||
|
{#if dev && error.stack}
|
||||||
|
<pre>{error.stack}</pre>
|
||||||
|
{/if}
|
||||||