Compare commits
363 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61f8b1710a | |||
| 496b284bcd | |||
| 1f249a3386 | |||
| 8ca7c48094 | |||
| abbcfbb6c2 | |||
| c0cc019b97 | |||
| e8896352a1 | |||
| 278b2e1657 | |||
| 87437785f9 | |||
| 788a67e9f4 | |||
| 4f58a63cb6 | |||
| 35e3961f75 | |||
| 2dac633873 | |||
| 23d08b993c | |||
| 42b5472886 | |||
| 48071f11ab | |||
| 72b5aedad3 | |||
| 4fa5a1ad37 | |||
| bcc450eb9f | |||
| ef1a453b9d | |||
| 1c8685681e | |||
| 0801b3c6f8 | |||
| 1f66670cc3 | |||
| 515054f1c6 | |||
| 76d4036883 | |||
| a5fa5dc05c | |||
| e3fd26ca4e | |||
| 8139fcfe74 | |||
| 86fe4d1c1b | |||
| 6abeb9e3ca | |||
| d3ce3794b0 | |||
| 89746e7dfe | |||
| 700cd16641 | |||
| b72649e296 | |||
| e9449b9135 | |||
| caff2aa9d2 | |||
| 6eac899167 | |||
| ba4ade1750 | |||
| b131de3bea | |||
| a618668b63 | |||
| 71e9fa178d | |||
| 67f13be3f7 | |||
| af89261992 | |||
| d94a0454ae | |||
| 732720c306 | |||
| 58792e53de | |||
| 8c54ebb6d1 | |||
| b4229d22d5 | |||
| 4533834177 | |||
| 42d3c3eec5 | |||
| 56e4b13e63 | |||
| 26217f53ae | |||
| d79eda40a4 | |||
| f4a17ac698 | |||
| 11e0b085d5 | |||
| 7df87a9c22 | |||
| 6b674fb53e | |||
| e7e68dafb5 | |||
| d5cabb10d6 | |||
| cd425412cc | |||
| 7cafd25036 | |||
| 4b68f8b725 | |||
| e2d816defb | |||
| d80333c819 | |||
| 4d8d40f288 | |||
| 1e13dc542d | |||
| d103cefed2 | |||
| ddd7c7058c | |||
| bbb6919bf1 | |||
| c3ffadf53f | |||
| c41872d5a4 | |||
| b5ca5b4417 | |||
| 6bff864444 | |||
| a0e3978927 | |||
| 735751b0d4 | |||
| bf500948b2 | |||
| 85d172e54a | |||
| d9f088f36e | |||
| 396112bc8b | |||
| 9ba2b28654 | |||
| 89fad5c667 | |||
| 474c496226 | |||
| 8bd497ae09 | |||
| 107cec39f4 | |||
| 6a9b9cd0dd | |||
| f328f3fa9e | |||
| 21a2b5f320 | |||
| 29dd931289 | |||
| bbfb1f4346 | |||
| ee02583cd2 | |||
| 4141a7844f | |||
| 1de86f7ad7 | |||
| 833502ee35 | |||
| 9095da1e10 | |||
| b5eb8e94f4 | |||
| 8377c17838 | |||
| af018180d2 | |||
| 5102fe9797 | |||
| 188e766106 | |||
| 5729123dd1 | |||
| 1935df960b | |||
| c39fd49b1f | |||
| bc6634fb6f | |||
| 09941eb741 | |||
| 2fc0d51377 | |||
| 1e1b69c3b5 | |||
| 82070b4ccb | |||
| 829296c0bb | |||
| c3571e53d9 | |||
| 7a8c9317bc | |||
| 11ca9dd34e | |||
| 3e48b8db92 | |||
| 38377ca776 | |||
| 529ad0e146 | |||
| d14337b9db | |||
| 855ae2fe78 | |||
| e328898971 | |||
| 72a6de18ae | |||
| 52dc25b0ea | |||
| a6180a54bf | |||
| f9971fbc8d | |||
| 0c7df5bd22 | |||
| 96a925c040 | |||
| 20bc1e9caf | |||
| 2ae61ae79f | |||
| 53ab49cdd0 | |||
| e341a37dd3 | |||
| e6ca58b7c8 | |||
| 8a3959b1e1 | |||
| 4ebeada604 | |||
| 6a43b95e5e | |||
| 7ebb39c401 | |||
| 66aff5bf2d | |||
| 15e029c380 | |||
| a17df31eaa | |||
| fa60c83691 | |||
| e4dda5e541 | |||
| c598082335 | |||
| 2e0cc8fa5f | |||
| b3caa7e28d | |||
| 9a30708bea | |||
| dd5d13ce91 | |||
| 30bfc24386 | |||
| 3a86e9ecb7 | |||
| 80fb3066e8 | |||
| 977338d1ba | |||
| d5d889727d | |||
| 93d4f91008 | |||
| c053dc5903 | |||
| da847b6ee8 | |||
| 0fea2e09ae | |||
| 751e3f739a | |||
| 01b3204a49 | |||
| f5808fab83 | |||
| 48e698d080 | |||
| 022687526a | |||
| 3a624070ad | |||
| 2e5a7c3912 | |||
| 78f0633669 | |||
| 8fa54c4ad5 | |||
| 7e07724085 | |||
| 42f0337e7f | |||
| 7cab3d58a8 | |||
| 495f2eb543 | |||
| 0baaa2f847 | |||
| 4adf922b3f | |||
| f3dfbeb2b7 | |||
| 6619aed3b7 | |||
| 422b9a3a86 | |||
| 618d0bdd34 | |||
| aec136edc8 | |||
| 88fa090e23 | |||
| 988fb174fc | |||
| 8961d19fb8 | |||
| 627ebf4614 | |||
| 03099493aa | |||
| f8c3f784c8 | |||
| 42fe861fbc | |||
| a0ca5fcf55 | |||
| 938e5ba19c | |||
| 962d43ba3f | |||
| cd5888636c | |||
| 305a374917 | |||
| 02a74aa448 | |||
| f86cb859a7 | |||
| 4bf7096ee7 | |||
| 4b830ce38b | |||
| 65125b5f1e | |||
| 281c94c6b2 | |||
| 160ffbbcdd | |||
| a6dc10ceec | |||
| 81edbab511 | |||
| 272909d250 | |||
| 91f1552a88 | |||
| 4aa7d08e65 | |||
| 587f500a0c | |||
| 98898054c3 | |||
| 4753766d16 | |||
| 502acd5b20 | |||
| d569bf6b09 | |||
| bfdf9942f0 | |||
| 63d2ed2fec | |||
| 9627fa0500 | |||
| 7938c41fcf | |||
| f3fa86deb4 | |||
| 3cdfcfff2c | |||
| 1412ca8fdd | |||
| 611a945a3b | |||
| 7d3e2f9870 | |||
| f08521a705 | |||
| 3be06f813e | |||
| 2c8187eb6c | |||
| 82c107f7ad | |||
| 3f5ec9e7be | |||
| ea606857c2 | |||
| a12c733b01 | |||
| f9063a298b | |||
| edc100e67e | |||
| e381e9e37f | |||
| 1abcad05c1 | |||
| ffcfa9435f | |||
| 52d5eb444b | |||
| b5cc7b8e49 | |||
| 180927bcac | |||
| 078365c467 | |||
| 85938113b7 | |||
| 0f38f35aba | |||
| 0a7826d286 | |||
| e02eae8fb3 | |||
| fe4870199e | |||
| 3d506cfa01 | |||
| 393d348306 | |||
| 3ce936edac | |||
| 3b634fe64e | |||
| 12a058d91b | |||
| 95f0dd8979 | |||
| 02dfd775a9 | |||
| 82d6d203f0 | |||
| d180f7063c | |||
| 70f5d476f2 | |||
| 445f3b0adb | |||
| 42836b3e0e | |||
| 7c2318a096 | |||
| b3739c02c7 | |||
| 5b7b3c0bdc | |||
| f2c2f273d7 | |||
| 3913e3e56b | |||
| 5f3f97f3be | |||
| fb2b7d3480 | |||
| 7577ec9cb2 | |||
| 1e900c32df | |||
| cb4eaddc9f | |||
| 1b4bf74107 | |||
| 52151c5fb4 | |||
| 24fe628e15 | |||
| 369330f517 | |||
| ada30f5483 | |||
| c906770370 | |||
| 6f94124256 | |||
| e15c243202 | |||
| c5599bf07d | |||
| 70de88d53a | |||
| ef30a6706b | |||
| c784d95088 | |||
| d57a4bc242 | |||
| 750a59461c | |||
| f244cff810 | |||
| 63bbdfa523 | |||
| 1774606ec6 | |||
| e6c0b48819 | |||
| cabadcb5cc | |||
| 0cec9f636f | |||
| 56ddadc4c4 | |||
| 2ab5b63086 | |||
| ffcb7348ff | |||
| 05739b4d84 | |||
| 3a6f1bec79 | |||
| 538fd7c5ee | |||
| 12c931be36 | |||
| ca0525d949 | |||
| 053fee7fdc | |||
| 97981e536d | |||
| a7595ff176 | |||
| 14fbf8e064 | |||
| bb1fc9bb6a | |||
| 581edbf0b3 | |||
| 03c33b1ee5 | |||
| e13eeeb561 | |||
| e51881dbdf | |||
| 2a8ccace26 | |||
| 1fd02ede95 | |||
| c324804aa9 | |||
| d5b961be5b | |||
| 1e26d4c88e | |||
| f91fa95888 | |||
| b67d1e7a99 | |||
| cc9b444394 | |||
| 22c1d80712 | |||
| 0b38e20298 | |||
| 38f78c9983 | |||
| 6e3bb2ec18 | |||
| 412776e336 | |||
| 68d363af3c | |||
| 7415cf1934 | |||
| 8c6421f240 | |||
| c3f03a657f | |||
| 95646e5770 | |||
| 34ec1149d7 | |||
| 13f095a587 | |||
| 696a27ec76 | |||
| c0b8ddd48e | |||
| f35396481f | |||
| be8b9e5a83 | |||
| 7429487f30 | |||
| 23d11ce523 | |||
| 4f4cb83456 | |||
| 9fe1ee6436 | |||
| 9ae5ee38f6 | |||
| 0f8d1f6439 | |||
| b92527437a | |||
| d936c66e1f | |||
| e63de3f52b | |||
| bded08dce9 | |||
| 9fcffb1d94 | |||
| ed24afa207 | |||
| d966eab085 | |||
| e67c6613c0 | |||
| 2111251d35 | |||
| c60b52e9c4 | |||
| 8b14e2b1b6 | |||
| a59295f036 | |||
| d988fe02b5 | |||
| 9bdc3b260f | |||
| 2381e52d3b | |||
| aa2e0b662e | |||
| 6c7c9e46f4 | |||
| ee27dfe06c | |||
| 600b89b5c7 | |||
| 092268def9 | |||
| 80f7175abe | |||
| a90378f987 | |||
| a6278e9ae7 | |||
| 825c30a547 | |||
| fd54f8a32f | |||
| 9488b8ba6a | |||
| c1f167bbd4 | |||
| dbeeeb9796 | |||
| 81fbcdfdb2 | |||
| 13313d705f | |||
| e7c9c2b319 | |||
| 236a441036 | |||
| d8bef263b5 | |||
| e04c759c14 | |||
| 9d22387b18 | |||
| 6254656ab6 | |||
| afda62a5cf | |||
| a322becfec | |||
| 9b4e25221f | |||
| f46cc9d1bb | |||
| bf53a107af | |||
| 25ebdbb6eb | |||
| 47d045b1c7 | |||
| 0b0bb8175f |
@@ -0,0 +1,37 @@
|
||||
name: 'Install wasm-opt'
|
||||
description: 'Installs wasm-opt from binaryen'
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version of wasm-opt to install'
|
||||
default: '116'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Check platform compatibility
|
||||
run: |
|
||||
if [[ "$(uname)" != "Linux" ]]; then
|
||||
echo "Error: This action is only compatible with Linux."
|
||||
exit 1
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Download wasm-opt
|
||||
run: |
|
||||
set -e
|
||||
SOURCE="https://github.com/WebAssembly/binaryen/releases/download/version_${{ inputs.version }}/binaryen-version_${{ inputs.version }}-x86_64-linux.tar.gz"
|
||||
TEMP_ARCHIVE="$RUNNER_TEMP/binaryen-version_${{ inputs.version }}-x86_64-linux.tar.gz"
|
||||
curl -L -o "$TEMP_ARCHIVE" "$SOURCE"
|
||||
tar -xvzf $TEMP_ARCHIVE -C $RUNNER_TEMP
|
||||
echo "$RUNNER_TEMP/binaryen-version_${{ inputs.version }}/bin" >> $GITHUB_PATH
|
||||
shell: bash
|
||||
id: install-binary
|
||||
|
||||
- name: Verify installation
|
||||
run: |
|
||||
if ! command -v wasm-opt &> /dev/null; then
|
||||
echo "Error: wasm-opt binary was not installed successfully."
|
||||
exit 1
|
||||
fi
|
||||
shell: bash
|
||||
id: verify-installation
|
||||
|
||||
+7
-5
@@ -1,16 +1,16 @@
|
||||
name: Build and upload binaries to artifact storage
|
||||
name: build-upload-binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
inputs:
|
||||
add_tokio_unstable:
|
||||
description: 'True to add RUSTFLAGS="--cfg tokio_unstable"'
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
NETWORK: mainnet
|
||||
NETWORK: mainnet
|
||||
|
||||
jobs:
|
||||
publish-nym:
|
||||
@@ -20,6 +20,8 @@ jobs:
|
||||
platform: [ubuntu-20.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install rsync
|
||||
|
||||
@@ -31,8 +31,8 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [custom-runner-linux]
|
||||
|
||||
platform: [custom-linux]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -45,12 +45,12 @@ jobs:
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
|
||||
- name: Branch name
|
||||
run: echo running on branch ${GITHUB_REF##*/}
|
||||
|
||||
|
||||
- name: Run tests against binaries
|
||||
run: ./build_and_run.sh ${{ github.head_ref || github.ref_name }}
|
||||
working-directory: tests/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
name: ci-build-ts
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'ts-packages/**'
|
||||
- "ts-packages/**"
|
||||
- "sdk/typescript/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: ubuntu-20.04-16-core
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rsync
|
||||
@@ -20,7 +21,7 @@ jobs:
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
- name: Build
|
||||
run: yarn && yarn build && yarn build:ci
|
||||
run: yarn && yarn build && yarn build:ci:storybook
|
||||
- name: Deploy branch to CI www (storybook)
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
|
||||
@@ -2,20 +2,6 @@ name: ci-build-upload-binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'clients/**'
|
||||
- 'common/**'
|
||||
- 'explorer-api/**'
|
||||
- 'gateway/**'
|
||||
- 'integrations/**'
|
||||
- 'mixnode/**'
|
||||
- 'sdk/rust/nym-sdk/**'
|
||||
- 'service-providers/**'
|
||||
- 'nym-api/**'
|
||||
- 'nym-outfox/**'
|
||||
- 'tools/nym-cli/**'
|
||||
- 'tools/ts-rs-cli/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'clients/**'
|
||||
@@ -31,9 +17,6 @@ on:
|
||||
- 'tools/nym-cli/**'
|
||||
- 'tools/ts-rs-cli/**'
|
||||
|
||||
env:
|
||||
NETWORK: mainnet
|
||||
|
||||
jobs:
|
||||
publish-nym:
|
||||
strategy:
|
||||
@@ -42,8 +25,8 @@ jobs:
|
||||
platform: [ubuntu-20.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
# a push event from the origin repo, or a PR from external repo
|
||||
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'nymtech/nym' }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -57,8 +40,7 @@ jobs:
|
||||
echo $OUTPUT_DIR
|
||||
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||
continue-on-error: true
|
||||
run: sudo apt update && sudo apt install libudev-dev
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
|
||||
@@ -37,17 +37,22 @@ on:
|
||||
- 'tools/nym-nr-query/**'
|
||||
- 'tools/ts-rs-cli/**'
|
||||
- 'Cargo.toml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
# Enable sccache via environment variable
|
||||
# env:
|
||||
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [custom-linux, custom-runner-mac-m1]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||
continue-on-error: true
|
||||
if: matrix.os == 'custom-linux'
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
@@ -70,36 +75,40 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace
|
||||
# Enable wireguard by default on linux only
|
||||
args: --workspace --features wireguard
|
||||
|
||||
- name: Build all examples
|
||||
if: matrix.os == 'custom-linux'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples
|
||||
args: --workspace --examples --features wireguard
|
||||
|
||||
- name: Run all tests
|
||||
if: matrix.os == 'custom-linux'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace
|
||||
args: --workspace --features wireguard
|
||||
|
||||
- name: Run expensive tests
|
||||
if: github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master'
|
||||
if: (github.ref == 'refs/heads/develop' || github.event.pull_request.base.ref == 'develop' || github.event.pull_request.base.ref == 'master') && matrix.os == 'custom-linux'
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace -- --ignored
|
||||
args: --workspace --features wireguard -- --ignored
|
||||
|
||||
- name: Annotate with clippy checks
|
||||
if: matrix.os == 'custom-linux'
|
||||
uses: actions-rs/clippy-check@v1
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --workspace
|
||||
args: --workspace --features wireguard
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --workspace --all-targets -- -D warnings
|
||||
args: --workspace --all-targets --features wireguard -- -D warnings
|
||||
|
||||
@@ -2,10 +2,6 @@ name: ci-contracts-schema
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'contracts/**'
|
||||
- 'common/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'contracts/**'
|
||||
@@ -14,7 +10,9 @@ on:
|
||||
jobs:
|
||||
check-schema:
|
||||
name: Generate and check schema
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-linux
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
@@ -24,9 +22,8 @@ jobs:
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
|
||||
- name: Generate the schema
|
||||
run: make contract-schema
|
||||
|
||||
- name: Check for diff
|
||||
run: git diff --exit-code -- contracts/*/schema
|
||||
run: git diff --exit-code -- contracts/**/schema
|
||||
|
||||
@@ -2,10 +2,6 @@ name: ci-contracts-upload-binaries
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'common/**'
|
||||
- 'contracts/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'common/**'
|
||||
@@ -22,6 +18,8 @@ jobs:
|
||||
platform: [ubuntu-20.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -34,10 +32,6 @@ jobs:
|
||||
mkdir -p $OUTPUT_DIR
|
||||
echo $OUTPUT_DIR
|
||||
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||
continue-on-error: true
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -46,7 +40,9 @@ jobs:
|
||||
override: true
|
||||
|
||||
- name: Install wasm-opt
|
||||
run: cargo install --version 0.112.0 wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '112'
|
||||
|
||||
- name: Build release contracts
|
||||
run: make contracts
|
||||
|
||||
@@ -27,6 +27,8 @@ jobs:
|
||||
# since it's going to be compiled into wasm, there's absolutely
|
||||
# no point in running CI on different OS-es
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
needs: matrix_prep
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install rsync
|
||||
@@ -28,15 +28,20 @@ jobs:
|
||||
command: build
|
||||
args: --workspace --release --all
|
||||
- name: Install mdbook
|
||||
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook)
|
||||
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.35" mdbook)
|
||||
- name: Install mdbook plugins
|
||||
run: |
|
||||
cargo install --vers "=0.2.2" mdbook-variables && cargo install \
|
||||
--vers "^1.8.0" mdbook-admonish && cargo install --vers \
|
||||
--vers "^1.8.0" mdbook-admonish --force && cargo install --vers \
|
||||
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \
|
||||
&& cargo install --vers "^0.7.7" mdbook-linkcheck
|
||||
&& cargo install --vers "^0.7.7" mdbook-linkcheck \
|
||||
# && cd documentation \
|
||||
# && mdbook-admonish install dev-portal \
|
||||
# && mdbook-admonish install docs \
|
||||
# && mdbook-admonish install operators
|
||||
|
||||
- name: Build all projects in documentation/ & move to ~/dist/docs/
|
||||
run: cd documentation && ./build_all_to_dist.sh
|
||||
run: cd documentation && ./build_all_to_dist.sh
|
||||
continue-on-error: false
|
||||
- name: Deploy branch to CI www
|
||||
continue-on-error: true
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
name: ci-lint-typescript
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "ts-packages/**"
|
||||
- "sdk/typescript/**"
|
||||
- "nym-connect/desktop/src/**"
|
||||
- "nym-connect/desktop/package.json"
|
||||
- "nym-wallet/src/**"
|
||||
- "nym-wallet/package.json"
|
||||
- "explorer/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "ts-packages/**"
|
||||
@@ -22,7 +13,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: ubuntu-20.04-16-core
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
@@ -37,9 +28,15 @@ jobs:
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Install wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '116'
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
@@ -49,7 +46,7 @@ jobs:
|
||||
run: yarn
|
||||
|
||||
- name: Build packages
|
||||
run: yarn build:ci:sdk
|
||||
run: yarn build:ci
|
||||
|
||||
- name: Lint
|
||||
run: yarn lint
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
name: ci-nym-connect-desktop-rust
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "nym-connect/desktop/src-tauri/**"
|
||||
- "nym-connect/desktop/src-tauri/Cargo.toml"
|
||||
- "clients/client-core/**"
|
||||
- "clients/socks5/**"
|
||||
- "common/**"
|
||||
- "gateway/gateway-requests/**"
|
||||
- "contracts/vesting/**"
|
||||
- "nym-api/nym-api-requests/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "nym-connect/desktop/src-tauri/**"
|
||||
@@ -25,8 +15,8 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [self-hosted, custom-linux]
|
||||
# env:
|
||||
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: ci-nym-connect-desktop
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'nym-connect/desktop/**'
|
||||
|
||||
@@ -11,7 +11,7 @@ defaults:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rsync
|
||||
|
||||
@@ -12,7 +12,7 @@ defaults:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rsync
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
name: ci-nym-vpn-ui-js
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'nym-vpn/ui/src/**'
|
||||
- 'nym-vpn/ui/package.json'
|
||||
- 'nym-vpn/ui/index.html'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'nym-vpn/ui/src/**'
|
||||
- 'nym-vpn/ui/package.json'
|
||||
- 'nym-vpn/ui/index.html'
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install Yarn
|
||||
run: npm install -g yarn
|
||||
- name: Install dependencies
|
||||
working-directory: nym-vpn/ui
|
||||
run: yarn
|
||||
- name: Type-check
|
||||
working-directory: nym-vpn/ui
|
||||
run: yarn typecheck
|
||||
- name: Check lint
|
||||
working-directory: nym-vpn/ui
|
||||
run: yarn lint
|
||||
- name: Check formatting
|
||||
working-directory: nym-vpn/ui
|
||||
run: yarn fmt:check
|
||||
# - name: Run tests
|
||||
# working-directory: nym-vpn/ui
|
||||
# run: yarn test
|
||||
- name: Check build
|
||||
working-directory: nym-vpn/ui
|
||||
run: yarn build
|
||||
@@ -0,0 +1,64 @@
|
||||
name: ci-nym-vpn-ui-rust
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'nym-vpn/ui/src-tauri/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'nym-vpn/ui/src-tauri/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [self-hosted, custom-linux]
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
CARGOTOML_PATH: ./nym-vpn/ui/src-tauri/Cargo.toml
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libayatana-appindicator3-dev
|
||||
continue-on-error: true
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Prepare build
|
||||
working-directory: nym-vpn/ui/
|
||||
run: mkdir dist
|
||||
|
||||
- name: Check build
|
||||
working-directory: nym-vpn/ui/src-tauri
|
||||
run: cargo build --release --lib --features custom-protocol
|
||||
|
||||
# - name: Run all tests
|
||||
# uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: test
|
||||
# args: --manifest-path ${{ env.CARGOTOML_PATH }} --workspace
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --all -- --check
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --workspace --all-features
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path ${{ env.CARGOTOML_PATH }} --workspace --all-features -- -D warnings
|
||||
@@ -17,8 +17,8 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
# env:
|
||||
# RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: 1.71.0
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
|
||||
@@ -1,35 +1,44 @@
|
||||
name: Nym Wallet Storybook
|
||||
name: ci-nym-wallet-storybook
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'nym-wallet/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install rsync
|
||||
run: sudo apt-get install rsync
|
||||
continue-on-error: true
|
||||
|
||||
- uses: rlespinasse/github-slug-action@v3.x
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Setup yarn
|
||||
run: npm install -g yarn
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn && yarn build
|
||||
|
||||
- name: Build storybook
|
||||
run: yarn storybook:build
|
||||
working-directory: ./nym-wallet
|
||||
|
||||
- name: Deploy branch to CI www (storybook)
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
@@ -41,9 +50,11 @@ jobs:
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/wallet-${{ env.GITHUB_REF_SLUG }}
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
|
||||
- name: Matrix - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
|
||||
- name: Matrix - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: nym-wallet
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
name: ci-sdk-docs-typescript
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "sdk/typescript/**"
|
||||
- "wasm/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "sdk/typescript/**"
|
||||
@@ -12,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install rsync
|
||||
@@ -34,6 +30,14 @@ jobs:
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Install wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '116'
|
||||
|
||||
- name: Build branch WASM packages
|
||||
run: make sdk-wasm-build
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ on:
|
||||
|
||||
jobs:
|
||||
wasm:
|
||||
runs-on: [custom-runner-linux]
|
||||
runs-on: [custom-linux]
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -30,12 +32,13 @@ jobs:
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Install wasm-opt
|
||||
run: cargo install wasm-opt
|
||||
uses: ./.github/actions/install-wasm-opt
|
||||
with:
|
||||
version: '116'
|
||||
|
||||
- name: Install wasm-bindgen-cli
|
||||
run: cargo install wasm-bindgen-cli
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Greetings
|
||||
name: greetings
|
||||
|
||||
on: [pull_request_target, issues]
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ jobs:
|
||||
rust: [stable, beta]
|
||||
os: [custom-linux, windows10, custom-runner-mac-m1]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
@@ -68,7 +70,7 @@ jobs:
|
||||
|
||||
notification:
|
||||
needs: build
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
|
||||
@@ -14,8 +14,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [custom-linux, macos-latest, windows10]
|
||||
os: [custom-ubuntu-20.04, macos-latest, windows10]
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
@@ -23,17 +25,20 @@ jobs:
|
||||
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||
if: matrix.os == 'custom-linux'
|
||||
if: matrix.os == 'custom-ubuntu-20.04'
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
# There is an issue with 1.72.0 where clippy crashes on nym-wallet-types. Pin to 1.71.0 for now
|
||||
toolchain: 1.71.0
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install Protoc
|
||||
uses: arduino/setup-protoc@v2
|
||||
if: matrix.os == 'macos-latest'
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -67,7 +72,7 @@ jobs:
|
||||
|
||||
notification:
|
||||
needs: build
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Daily security audit
|
||||
name: nightly-security-audit
|
||||
|
||||
on:
|
||||
schedule:
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
path: .github/workflows/support-files/notifications/deny.message
|
||||
notification:
|
||||
needs: cargo-deny
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [custom-runner-linux]
|
||||
platform: [custom-ubuntu-20.04]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [macos-latest]
|
||||
platform: [macos-12-large]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [custom-runner-linux]
|
||||
platform: [custom-ubuntu-20.04]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
if: ${{ (startsWith(github.ref, 'refs/tags/nym-contracts-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||
runs-on: [self-hosted, custom-runner-linux]
|
||||
runs-on: [self-hosted, custom-ubuntu-20.04]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [macos-latest]
|
||||
platform: [macos-12-large]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [custom-runner-linux]
|
||||
platform: [custom-ubuntu-20.04]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Nyms5 Android
|
||||
name: publish-nyms5-android-apk
|
||||
# unsigned APKs only, supported archs:
|
||||
# - arm64-v8a (arm64)
|
||||
# - x86_64
|
||||
@@ -12,7 +12,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build APK
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-ubuntu-20.04
|
||||
env:
|
||||
ANDROID_HOME: ${{ github.workspace }}/android-sdk
|
||||
NDK_VERSION: 25.2.9519653
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
gh-release:
|
||||
name: Publish APK (GH release)
|
||||
needs: build
|
||||
runs-on: custom-runner-linux
|
||||
runs-on: custom-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: [custom-runner-linux]
|
||||
runs-on: ubuntu-20.04-16-core
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -25,6 +25,9 @@ jobs:
|
||||
- name: Install wasm-pack
|
||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
||||
|
||||
- name: Install wasm-opt
|
||||
run: cargo install wasm-opt
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
|
||||
@@ -3,6 +3,31 @@
|
||||
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
- add client registry to Gateway ([#3955])
|
||||
- add HTTP API to Gateway ([#3955])
|
||||
- add `/client/<pub-key>`, `clients` and `register` routes to the gateway ([#3955])
|
||||
|
||||
## [2023.1-milka] (2023-09-24)
|
||||
|
||||
- custom Debug impl for mix::Node and gateway::Node ([#3930])
|
||||
- added forceTls argument to 'MixFetchOptsSimple' ([#3907])
|
||||
- Enable loop cover traffic by default in NR ([#3904])
|
||||
- Fix all the cargo warnings ([#3899])
|
||||
- [Issue] nym-socks5-client crash on UDP request ([#3898])
|
||||
- Feature/gateway inbuilt nr ([#3877])
|
||||
- removed queued mixnet migration that was already run ([#3872])
|
||||
- [feat] Socks5 and Native client: run with hardcoded topology ([#3866])
|
||||
- Introduce a local network requester directly inside a gateway ([#3838])
|
||||
|
||||
[#3930]: https://github.com/nymtech/nym/pull/3930
|
||||
[#3907]: https://github.com/nymtech/nym/pull/3907
|
||||
[#3904]: https://github.com/nymtech/nym/pull/3904
|
||||
[#3899]: https://github.com/nymtech/nym/pull/3899
|
||||
[#3898]: https://github.com/nymtech/nym/issues/3898
|
||||
[#3877]: https://github.com/nymtech/nym/pull/3877
|
||||
[#3872]: https://github.com/nymtech/nym/pull/3872
|
||||
[#3866]: https://github.com/nymtech/nym/pull/3866
|
||||
[#3838]: https://github.com/nymtech/nym/issues/3838
|
||||
|
||||
## [v1.1.31-kitkat] (2023-09-12)
|
||||
|
||||
|
||||
Generated
+1250
-1281
File diff suppressed because it is too large
Load Diff
+18
-5
@@ -46,7 +46,8 @@ members = [
|
||||
"common/crypto",
|
||||
"common/dkg",
|
||||
"common/execute",
|
||||
"common/http-requests",
|
||||
"common/exit-policy",
|
||||
"common/http-api-client",
|
||||
"common/inclusion-probability",
|
||||
"common/ledger",
|
||||
"common/mixnode-common",
|
||||
@@ -78,6 +79,7 @@ members = [
|
||||
"common/wasm/storage",
|
||||
"common/wasm/utils",
|
||||
"common/wireguard",
|
||||
"common/wireguard-types",
|
||||
"explorer-api",
|
||||
"explorer-api/explorer-api-requests",
|
||||
"explorer-api/explorer-client",
|
||||
@@ -93,6 +95,8 @@ members = [
|
||||
"nym-api",
|
||||
"nym-browser-extension/storage",
|
||||
"nym-api/nym-api-requests",
|
||||
"nym-node",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-outfox",
|
||||
"tools/internal/ssl-inject",
|
||||
"tools/internal/sdk-version-bump",
|
||||
@@ -116,7 +120,7 @@ default-members = [
|
||||
"explorer-api",
|
||||
]
|
||||
|
||||
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "cpu-cycles"]
|
||||
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "nym-vpn/ui/src-tauri", "cpu-cycles"]
|
||||
|
||||
[workspace.package]
|
||||
authors = ["Nym Technologies SA"]
|
||||
@@ -129,7 +133,10 @@ license = "Apache-2.0"
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.71"
|
||||
async-trait = "0.1.68"
|
||||
axum = "0.6.20"
|
||||
base64 = "0.21.4"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
boringtun = { git = "https://github.com/cloudflare/boringtun", rev = "e1d6360d6ab4529fc942a078e4c54df107abe2ba" }
|
||||
cfg-if = "1.0.0"
|
||||
cosmwasm-derive = "=1.3.0"
|
||||
cosmwasm-schema = "=1.3.0"
|
||||
@@ -151,21 +158,27 @@ dotenvy = "0.15.6"
|
||||
futures = "0.3.28"
|
||||
generic-array = "0.14.7"
|
||||
getrandom = "0.2.10"
|
||||
hyper = "0.14.27"
|
||||
k256 = "0.13"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
once_cell = "1.7.2"
|
||||
parking_lot = "0.12.1"
|
||||
rand = "0.8.5"
|
||||
reqwest = "0.11.18"
|
||||
reqwest = "0.11.22"
|
||||
schemars = "0.8.1"
|
||||
serde = "1.0.152"
|
||||
serde_json = "1.0.91"
|
||||
sqlx = "0.7.2"
|
||||
tap = "1.0.1"
|
||||
tendermint-rpc = "0.32" # same version as used by cosmrs
|
||||
thiserror = "1.0.38"
|
||||
thiserror = "1.0.48"
|
||||
tokio = "1.24.1"
|
||||
tokio-tungstenite = "0.20.1"
|
||||
tracing = "0.1.37"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
ts-rs = "7.0.0"
|
||||
utoipa = "3.5.0"
|
||||
utoipa-swagger-ui = "3.1.5"
|
||||
url = "2.4"
|
||||
zeroize = "1.6.0"
|
||||
|
||||
|
||||
@@ -46,21 +46,9 @@ clippy:
|
||||
# -----------------------------------------------------------------------------
|
||||
define add_cargo_workspace
|
||||
|
||||
clippy-$(1):
|
||||
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
|
||||
|
||||
clippy-extra-$(1):
|
||||
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace --examples --tests -- -D warnings
|
||||
|
||||
check-$(1):
|
||||
cargo check --manifest-path $(2)/Cargo.toml --workspace $(3)
|
||||
|
||||
test-$(1):
|
||||
cargo test --manifest-path $(2)/Cargo.toml --workspace
|
||||
|
||||
test-expensive-$(1):
|
||||
cargo test --manifest-path $(2)/Cargo.toml --workspace -- --ignored
|
||||
|
||||
build-$(1):
|
||||
cargo build --manifest-path $(2)/Cargo.toml --workspace $(3)
|
||||
|
||||
@@ -70,15 +58,27 @@ build-extra-$(1):
|
||||
build-release-$(1):
|
||||
$(4) cargo $$($(1)_BUILD_RELEASE_TOOLCHAIN) build --manifest-path $(2)/Cargo.toml --workspace --release $(3)
|
||||
|
||||
test-$(1):
|
||||
cargo test --manifest-path $(2)/Cargo.toml --workspace
|
||||
|
||||
test-expensive-$(1):
|
||||
cargo test --manifest-path $(2)/Cargo.toml --workspace -- --ignored
|
||||
|
||||
clippy-$(1):
|
||||
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace $(3) -- -D warnings
|
||||
|
||||
clippy-extra-$(1):
|
||||
cargo $$($(1)_CLIPPY_TOOLCHAIN) clippy --manifest-path $(2)/Cargo.toml --workspace --examples --tests -- -D warnings
|
||||
|
||||
fmt-$(1):
|
||||
cargo fmt --manifest-path $(2)/Cargo.toml --all
|
||||
|
||||
clippy: clippy-$(1) clippy-extra-$(1)
|
||||
check: check-$(1)
|
||||
cargo-test: test-$(1)
|
||||
cargo-test-expensive: test-expensive-$(1)
|
||||
build: build-$(1) build-extra-$(1)
|
||||
build-release-all: build-release-$(1)
|
||||
cargo-test: test-$(1)
|
||||
cargo-test-expensive: test-expensive-$(1)
|
||||
clippy: clippy-$(1) clippy-extra-$(1)
|
||||
fmt: fmt-$(1)
|
||||
endef
|
||||
|
||||
@@ -93,10 +93,6 @@ $(eval $(call add_cargo_workspace,contracts,contracts,--lib --target wasm32-unkn
|
||||
$(eval $(call add_cargo_workspace,wallet,nym-wallet))
|
||||
$(eval $(call add_cargo_workspace,connect,nym-connect/desktop))
|
||||
|
||||
# OVERRIDE: there is an issue where clippy crashes on nym-wallet-types with the latest
|
||||
# stable toolchain. So pin to 1.71.0 until that is resolved.
|
||||
wallet_CLIPPY_TOOLCHAIN := +1.71.0
|
||||
|
||||
# OVERRIDE: wasm-opt fails if the binary has been built with the latest rustc.
|
||||
# Pin to the last working version.
|
||||
contracts_BUILD_RELEASE_TOOLCHAIN := +1.69.0
|
||||
|
||||
@@ -50,10 +50,10 @@ Node, node operator and delegator rewards are determined according to the princi
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=\lambda_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\lambda_{i}#gh-dark-mode-only">|ratio of stake operator has pledged to their node to the token circulating supply.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=\omega_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\omega_{i}#gh-dark-mode-only">|fraction of total effort undertaken by node `i`, set to `1/k`.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=k#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}k#gh-dark-mode-only">|number of nodes stakeholders are incentivised to create, set by the validators, a matter of governance. Currently determined by the `reward set` size, and set to 720 in testnet Sandbox.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=\alpha#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\alpha#gh-dark-mode-only">|Sybil attack resistance parameter - the higher this parameter is set the stronger the reduction in competitiveness gets for a Sybil attacker.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=PM_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PM_{i}#gh-dark-mode-only">|declared profit margin of operator `i`, defaults to 10% in.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=\alpha#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\alpha#gh-dark-mode-only">|A Sybil attack resistance parameter - the higher this parameter is set, the stronger the reduction in competitiveness for a Sybil attacker.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=PM_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PM_{i}#gh-dark-mode-only">|declared profit margin of operator `i`, defaults to 10%.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=PF_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PF_{i}#gh-dark-mode-only">|uptime of node `i`, scaled to 0 - 1, for the rewarding epoch
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=PP_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PP_{i}#gh-dark-mode-only">|cost of operating node `i` for the duration of the rewarding epoch, set to 40 NYMT.
|
||||
|<img src="https://render.githubusercontent.com/render/math?math=PP_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PP_{i}#gh-dark-mode-only">|cost of operating node `i` for the duration of the rewarding epoch, set to 40 NYMs.
|
||||
|
||||
Node reward for node `i` is determined as:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.29"
|
||||
version = "1.1.30"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
@@ -30,8 +30,8 @@ serde = { workspace = true, features = ["derive"] } # for config serialization/d
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tap = "1.0.1"
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
||||
tokio-tungstenite = "0.14" # websocket
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "net", "signal"] } # async runtime
|
||||
tokio-tungstenite = { workspace = true }
|
||||
|
||||
## internal
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::{
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
|
||||
use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseConfigV1_1_20_2;
|
||||
use nym_client_core::config::old_config_v1_1_30::ConfigV1_1_30 as BaseConfigV1_1_30;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
use nym_config::read_config_from_toml_file;
|
||||
use nym_network_defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
|
||||
@@ -51,7 +52,7 @@ impl ConfigV1_1_20_2 {
|
||||
pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig), ClientError> {
|
||||
let gateway_details = self.base.client.gateway_endpoint.clone().into();
|
||||
let config = Config {
|
||||
base: self.base.into(),
|
||||
base: BaseConfigV1_1_30::from(self.base).into(),
|
||||
socket: self.socket.into(),
|
||||
storage_paths: ClientPaths {
|
||||
common_paths: self.storage_paths.common_paths.upgrade_default()?,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.29"
|
||||
version = "1.1.30"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -22,6 +22,7 @@ use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_topology::NymTopology;
|
||||
use serde::Serialize;
|
||||
use std::fmt::Display;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
use tap::TapFallible;
|
||||
@@ -64,7 +65,12 @@ pub(crate) struct Init {
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(long, alias = "api_validators", value_delimiter = ',')]
|
||||
#[clap(
|
||||
long,
|
||||
alias = "api_validators",
|
||||
value_delimiter = ',',
|
||||
group = "network"
|
||||
)]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
|
||||
@@ -72,6 +78,10 @@ pub(crate) struct Init {
|
||||
#[clap(short, long)]
|
||||
port: Option<u16>,
|
||||
|
||||
/// The custom host on which the socks5 client will be listening for requests
|
||||
#[clap(long)]
|
||||
host: Option<IpAddr>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[clap(long, group = "network", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
@@ -98,6 +108,7 @@ impl From<Init> for OverrideConfig {
|
||||
fn from(init_config: Init) -> Self {
|
||||
OverrideConfig {
|
||||
nym_apis: init_config.nym_apis,
|
||||
ip: init_config.host,
|
||||
port: init_config.port,
|
||||
use_anonymous_replies: init_config.use_reply_surbs,
|
||||
fastmode: init_config.fastmode,
|
||||
@@ -115,7 +126,7 @@ impl From<Init> for OverrideConfig {
|
||||
pub struct InitResults {
|
||||
#[serde(flatten)]
|
||||
client_core: nym_client_core::init::types::InitResults,
|
||||
socks5_listening_port: u16,
|
||||
socks5_listening_address: SocketAddr,
|
||||
client_address: String,
|
||||
}
|
||||
|
||||
@@ -127,7 +138,7 @@ impl InitResults {
|
||||
address,
|
||||
gateway,
|
||||
),
|
||||
socks5_listening_port: config.core.socks5.listening_port,
|
||||
socks5_listening_address: config.core.socks5.bind_adddress,
|
||||
client_address: address.to_string(),
|
||||
}
|
||||
}
|
||||
@@ -136,7 +147,11 @@ impl InitResults {
|
||||
impl Display for InitResults {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "{}", self.client_core)?;
|
||||
writeln!(f, "SOCKS5 listening port: {}", self.socks5_listening_port)?;
|
||||
writeln!(
|
||||
f,
|
||||
"SOCKS5 listening address: {}",
|
||||
self.socks5_listening_address
|
||||
)?;
|
||||
write!(f, "Address of this client: {}", self.client_address)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
|
||||
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
|
||||
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
|
||||
use crate::config::{BaseClientConfig, Config};
|
||||
use crate::config::old_config_v1_1_30::ConfigV1_1_30;
|
||||
use crate::config::{BaseClientConfig, Config, SocksClientPaths};
|
||||
use crate::error::Socks5ClientError;
|
||||
use clap::CommandFactory;
|
||||
use clap::{Parser, Subcommand};
|
||||
@@ -22,6 +23,7 @@ use nym_client_core::error::ClientCoreError;
|
||||
use nym_config::OptionalSet;
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use std::error::Error;
|
||||
use std::net::IpAddr;
|
||||
|
||||
pub(crate) mod build_info;
|
||||
pub mod init;
|
||||
@@ -72,6 +74,7 @@ pub(crate) enum Commands {
|
||||
// Configuration that can be overridden.
|
||||
pub(crate) struct OverrideConfig {
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
ip: Option<IpAddr>,
|
||||
port: Option<u16>,
|
||||
use_anonymous_replies: Option<bool>,
|
||||
fastmode: bool,
|
||||
@@ -145,6 +148,7 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
)
|
||||
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
|
||||
.with_optional(Config::with_port, args.port)
|
||||
.with_optional(Config::with_ip, args.ip)
|
||||
.with_optional_base_custom_env(
|
||||
BaseClientConfig::with_custom_nym_apis,
|
||||
args.nym_apis,
|
||||
@@ -164,12 +168,11 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
|
||||
}
|
||||
|
||||
fn persist_gateway_details(
|
||||
config: &Config,
|
||||
storage_paths: &SocksClientPaths,
|
||||
details: GatewayEndpointConfig,
|
||||
) -> Result<(), Socks5ClientError> {
|
||||
let details_store =
|
||||
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
|
||||
let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
|
||||
let details_store = OnDiskGatewayDetails::new(&storage_paths.common_paths.gateway_details);
|
||||
let keys_store = OnDiskKeys::new(storage_paths.common_paths.keys.clone());
|
||||
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
|
||||
Socks5ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
|
||||
source: Box::new(source),
|
||||
@@ -199,9 +202,10 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
|
||||
let updated_step1: ConfigV1_1_20 = old_config.into();
|
||||
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
|
||||
let (updated, gateway_config) = updated_step2.upgrade()?;
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
let (updated_step3, gateway_config) = updated_step2.upgrade()?;
|
||||
persist_gateway_details(&updated_step3.storage_paths, gateway_config)?;
|
||||
|
||||
let updated: Config = updated_step3.into();
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
}
|
||||
@@ -219,9 +223,10 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated_step1: ConfigV1_1_20_2 = old_config.into();
|
||||
let (updated, gateway_config) = updated_step1.upgrade()?;
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
let (updated_step2, gateway_config) = updated_step1.upgrade()?;
|
||||
persist_gateway_details(&updated_step2.storage_paths, gateway_config)?;
|
||||
|
||||
let updated: Config = updated_step2.into();
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
}
|
||||
@@ -236,9 +241,25 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
info!("It seems the client is using <= v1.1.20_2 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let (updated, gateway_config) = old_config.upgrade()?;
|
||||
persist_gateway_details(&updated, gateway_config)?;
|
||||
let (updated_step1, gateway_config) = old_config.upgrade()?;
|
||||
persist_gateway_details(&updated_step1.storage_paths, gateway_config)?;
|
||||
|
||||
let updated: Config = updated_step1.into();
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn try_upgrade_v1_1_30_config(id: &str) -> Result<bool, Socks5ClientError> {
|
||||
// explicitly load it as v1.1.30 (which is incompatible with the current one, i.e. +1.1.31)
|
||||
let Ok(old_config) = ConfigV1_1_30::read_from_default_path(id) else {
|
||||
// if we failed to load it, there might have been nothing to upgrade
|
||||
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
|
||||
return Ok(false);
|
||||
};
|
||||
info!("It seems the client is using <= v1.1.30 config template.");
|
||||
info!("It is going to get updated to the current specification.");
|
||||
|
||||
let updated: Config = old_config.into();
|
||||
updated.save_to_default_location()?;
|
||||
Ok(true)
|
||||
}
|
||||
@@ -253,6 +274,9 @@ fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
|
||||
if try_upgrade_v1_1_20_2_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
if try_upgrade_v1_1_30_config(id)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_socks5_client_core::NymClient;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
@@ -53,6 +54,10 @@ pub(crate) struct Run {
|
||||
#[clap(short, long)]
|
||||
port: Option<u16>,
|
||||
|
||||
/// The custom host on which the socks5 client will be listening for requests
|
||||
#[clap(long)]
|
||||
host: Option<IpAddr>,
|
||||
|
||||
/// Path to .json file containing custom network specification.
|
||||
#[clap(long, group = "network", group = "routing", hide = true)]
|
||||
custom_mixnet: Option<PathBuf>,
|
||||
@@ -88,6 +93,7 @@ impl From<Run> for OverrideConfig {
|
||||
fn from(run_config: Run) -> Self {
|
||||
OverrideConfig {
|
||||
nym_apis: run_config.nym_apis,
|
||||
ip: run_config.host,
|
||||
port: run_config.port,
|
||||
use_anonymous_replies: run_config.use_anonymous_replies,
|
||||
fastmode: run_config.fastmode,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::SocksClientPaths;
|
||||
use crate::config::template::CONFIG_TEMPLATE;
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::{
|
||||
@@ -11,15 +10,18 @@ use nym_config::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::io;
|
||||
use std::net::IpAddr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub use crate::config::persistence::SocksClientPaths;
|
||||
pub use nym_client_core::config::Config as BaseClientConfig;
|
||||
pub use nym_socks5_client_core::config::Config as CoreConfig;
|
||||
|
||||
pub mod old_config_v1_1_13;
|
||||
pub mod old_config_v1_1_20;
|
||||
pub mod old_config_v1_1_20_2;
|
||||
pub mod old_config_v1_1_30;
|
||||
mod persistence;
|
||||
mod template;
|
||||
|
||||
@@ -102,8 +104,15 @@ impl Config {
|
||||
self.core.validate()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_port(mut self, port: u16) -> Self {
|
||||
self.core.socks5.listening_port = port;
|
||||
self.core = self.core.with_port(port);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_ip(mut self, ip: IpAddr) -> Self {
|
||||
self.core = self.core.with_ip(ip);
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::old_config_v1_1_30::ConfigV1_1_30;
|
||||
use crate::{
|
||||
config::{default_config_filepath, persistence::SocksClientPaths, Config},
|
||||
config::{default_config_filepath, persistence::SocksClientPaths},
|
||||
error::Socks5ClientError,
|
||||
};
|
||||
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
|
||||
use nym_client_core::config::GatewayEndpointConfig;
|
||||
@@ -43,9 +43,9 @@ impl ConfigV1_1_20_2 {
|
||||
|
||||
// in this upgrade, gateway endpoint configuration was moved out of the config file,
|
||||
// so its returned to be stored elsewhere.
|
||||
pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig), Socks5ClientError> {
|
||||
pub fn upgrade(self) -> Result<(ConfigV1_1_30, GatewayEndpointConfig), Socks5ClientError> {
|
||||
let gateway_details = self.core.base.client.gateway_endpoint.clone().into();
|
||||
let config = Config {
|
||||
let config = ConfigV1_1_30 {
|
||||
core: self.core.into(),
|
||||
storage_paths: SocksClientPaths {
|
||||
common_paths: self.storage_paths.common_paths.upgrade_default()?,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::persistence::SocksClientPaths;
|
||||
use crate::config::{default_config_filepath, Config};
|
||||
use nym_bin_common::logging::LoggingSettings;
|
||||
use nym_config::read_config_from_toml_file;
|
||||
use nym_socks5_client_core::config::old_config_v1_1_30::ConfigV1_1_30 as CoreConfigV1_1_30;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_30 {
|
||||
pub core: CoreConfigV1_1_30,
|
||||
|
||||
// I'm leaving a landmine here for when the paths actually do change the next time,
|
||||
// but propagating the change right now (in ALL clients) would be such a hassle...,
|
||||
// so sorry for the next person looking at it : )
|
||||
pub storage_paths: SocksClientPaths,
|
||||
|
||||
pub logging: LoggingSettings,
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_30> for Config {
|
||||
fn from(value: ConfigV1_1_30) -> Self {
|
||||
Config {
|
||||
core: value.core.into(),
|
||||
storage_paths: value.storage_paths,
|
||||
logging: LoggingSettings::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigV1_1_30 {
|
||||
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
|
||||
read_config_from_toml_file(path)
|
||||
}
|
||||
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
Self::read_from_toml_file(default_config_filepath(id))
|
||||
}
|
||||
}
|
||||
@@ -75,8 +75,9 @@ gateway_details = '{{ storage_paths.gateway_details }}'
|
||||
# The mix address of the provider to which all requests are going to be sent.
|
||||
provider_mix_address = '{{ core.socks5.provider_mix_address }}'
|
||||
|
||||
# The port on which the client will be listening for incoming requests
|
||||
listening_port = {{ core.socks5.listening_port }}
|
||||
# The address on which the client will be listening for incoming requests
|
||||
# (default: 127.0.0.1:1080)
|
||||
bind_adddress = '{{ core.socks5.bind_adddress }}'
|
||||
|
||||
# Specifies whether this client is going to use an anonymous sender tag for communication with the service provider.
|
||||
# While this is going to hide its actual address information, it will make the actual communication
|
||||
|
||||
@@ -8,7 +8,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
bip39 = { workspace = true }
|
||||
rand = "0.7.3"
|
||||
thiserror = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
nym-coconut-interface = { path = "../coconut-interface" }
|
||||
|
||||
@@ -15,6 +15,7 @@ clap_complete_fig = "4.0"
|
||||
log = { workspace = true }
|
||||
pretty_env_logger = "0.4.0"
|
||||
semver = "0.11"
|
||||
schemars = { workspace = true, features = ["preserve_order"], optional = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
|
||||
@@ -29,6 +30,7 @@ opentelemetry-jaeger = { version = "0.18.0", optional = true, features = [
|
||||
"isahc_collector_client",
|
||||
] }
|
||||
tracing-opentelemetry = { version = "0.19.0", optional = true }
|
||||
utoipa = { workspace = true, optional = true }
|
||||
opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
|
||||
|
||||
|
||||
@@ -42,7 +44,9 @@ vergen = { version = "=7.4.3", default-features = false, features = [
|
||||
|
||||
[features]
|
||||
default = []
|
||||
openapi = ["utoipa"]
|
||||
output_format = ["serde_json"]
|
||||
bin_info_schema = ["schemars"]
|
||||
tracing = [
|
||||
"tracing-subscriber",
|
||||
"tracing-tree",
|
||||
|
||||
@@ -81,6 +81,8 @@ impl BinaryBuildInformation {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
#[cfg_attr(feature = "bin_info_schema", derive(schemars::JsonSchema))]
|
||||
pub struct BinaryBuildInformationOwned {
|
||||
/// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable.
|
||||
pub binary_name: String,
|
||||
|
||||
@@ -24,7 +24,7 @@ sha2 = "0.10.6"
|
||||
tap = "1.0.1"
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
tungstenite = { version = "0.13.0", default-features = false }
|
||||
tungstenite = { workspace = true, default-features = false }
|
||||
tokio = { workspace = true, features = ["macros"]}
|
||||
time = "0.3.17"
|
||||
zeroize = { workspace = true }
|
||||
@@ -54,10 +54,10 @@ workspace = true
|
||||
features = ["time"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
version = "0.14"
|
||||
version = "0.20.1"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
workspace = true
|
||||
version = "0.6.2"
|
||||
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
|
||||
optional = true
|
||||
|
||||
@@ -88,7 +88,7 @@ tempfile = "3.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -47,11 +47,7 @@ impl<T> PersistedGatewayDetails<T> {
|
||||
pub fn validate(&self, shared_key: Option<&SharedKeys>) -> Result<(), ClientCoreError> {
|
||||
match self {
|
||||
PersistedGatewayDetails::Default(details) => {
|
||||
if !details.verify(
|
||||
shared_key
|
||||
.ok_or(ClientCoreError::UnavailableSharedKey)?
|
||||
.deref(),
|
||||
) {
|
||||
if !details.verify(shared_key.ok_or(ClientCoreError::UnavailableSharedKey)?) {
|
||||
Err(ClientCoreError::MismatchedGatewayDetails {
|
||||
gateway_id: details.details.gateway_id.clone(),
|
||||
})
|
||||
|
||||
@@ -30,11 +30,11 @@ impl StorageManager {
|
||||
})?;
|
||||
}
|
||||
|
||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
.filename(database_path)
|
||||
.create_if_missing(fresh);
|
||||
|
||||
let opts = opts.disable_statement_logging();
|
||||
opts.disable_statement_logging();
|
||||
|
||||
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
|
||||
Ok(pool) => pool,
|
||||
|
||||
@@ -199,7 +199,7 @@ fn group_mixnodes_by_country_code(
|
||||
if let Some(ref location) = m.location {
|
||||
let country_code = location.two_letter_iso_country_code.clone();
|
||||
let group_code = CountryGroup::new(country_code.as_str());
|
||||
let mixnodes = acc.entry(group_code).or_insert_with(Vec::new);
|
||||
let mixnodes = acc.entry(group_code).or_default();
|
||||
mixnodes.push(m.mix_id);
|
||||
}
|
||||
acc
|
||||
|
||||
@@ -69,7 +69,7 @@ impl NymApiTopologyProvider {
|
||||
Ok(mixes) => mixes,
|
||||
};
|
||||
|
||||
let gateways = match self.validator_client.get_cached_gateways().await {
|
||||
let gateways = match self.validator_client.get_cached_described_gateways().await {
|
||||
Err(err) => {
|
||||
error!("failed to get network gateways - {err}");
|
||||
return None;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{client::topology_control::geo_aware_provider::CountryGroup, error::ClientCoreError};
|
||||
use nym_config::defaults::NymNetworkDetails;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_gateway_client::client::GatewayConfig;
|
||||
@@ -12,7 +13,6 @@ use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
use crate::{client::topology_control::geo_aware_provider::CountryGroup, error::ClientCoreError};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
@@ -20,6 +20,7 @@ pub mod disk_persistence;
|
||||
pub mod old_config_v1_1_13;
|
||||
pub mod old_config_v1_1_20;
|
||||
pub mod old_config_v1_1_20_2;
|
||||
pub mod old_config_v1_1_30;
|
||||
|
||||
// 'DEBUG'
|
||||
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
|
||||
@@ -280,29 +281,24 @@ impl GatewayEndpointConfig {
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)
|
||||
}
|
||||
|
||||
pub fn from_node(node: nym_topology::gateway::Node, use_tls: bool) -> Self {
|
||||
// TODO: in the future this shall return a Result and explicit `use_tls` will be removed in favour of the tls info being available on the struct
|
||||
if use_tls {
|
||||
Self::from_topology_node_tls(node)
|
||||
pub fn from_node(
|
||||
node: nym_topology::gateway::Node,
|
||||
must_use_tls: bool,
|
||||
) -> Result<Self, ClientCoreError> {
|
||||
let gateway_listener = if must_use_tls {
|
||||
node.clients_address_tls()
|
||||
.ok_or(ClientCoreError::UnsupportedWssProtocol {
|
||||
gateway: node.identity_key.to_base58_string(),
|
||||
})?
|
||||
} else {
|
||||
Self::from_topology_node_no_tls(node)
|
||||
}
|
||||
}
|
||||
node.clients_address()
|
||||
};
|
||||
|
||||
pub fn from_topology_node_no_tls(node: nym_topology::gateway::Node) -> Self {
|
||||
GatewayEndpointConfig {
|
||||
Ok(GatewayEndpointConfig {
|
||||
gateway_id: node.identity_key.to_base58_string(),
|
||||
gateway_listener: node.clients_address(),
|
||||
gateway_listener,
|
||||
gateway_owner: node.owner,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_topology_node_tls(node: nym_topology::gateway::Node) -> Self {
|
||||
GatewayEndpointConfig {
|
||||
gateway_id: node.identity_key.to_base58_string(),
|
||||
gateway_listener: node.clients_address_tls(),
|
||||
gateway_owner: node.owner,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::{
|
||||
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection,
|
||||
GatewayEndpointConfig, ReplySurbs, Topology, Traffic,
|
||||
use crate::config::old_config_v1_1_30::{
|
||||
AcknowledgementsV1_1_30, ClientV1_1_30, ConfigV1_1_30, CoverTrafficV1_1_30, DebugConfigV1_1_30,
|
||||
GatewayConnectionV1_1_30, ReplySurbsV1_1_30, TopologyV1_1_30, TrafficV1_1_30,
|
||||
};
|
||||
use crate::config::GatewayEndpointConfig;
|
||||
use nym_sphinx::params::{PacketSize, PacketType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
@@ -58,9 +59,9 @@ pub struct ConfigV1_1_20_2 {
|
||||
pub debug: DebugConfigV1_1_20_2,
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_20_2> for Config {
|
||||
impl From<ConfigV1_1_20_2> for ConfigV1_1_30 {
|
||||
fn from(value: ConfigV1_1_20_2) -> Self {
|
||||
Config {
|
||||
ConfigV1_1_30 {
|
||||
client: value.client.into(),
|
||||
debug: value.debug.into(),
|
||||
}
|
||||
@@ -107,9 +108,9 @@ pub struct ClientV1_1_20_2 {
|
||||
pub gateway_endpoint: GatewayEndpointConfigV1_1_20_2,
|
||||
}
|
||||
|
||||
impl From<ClientV1_1_20_2> for Client {
|
||||
impl From<ClientV1_1_20_2> for ClientV1_1_30 {
|
||||
fn from(value: ClientV1_1_20_2) -> Self {
|
||||
Client {
|
||||
ClientV1_1_30 {
|
||||
version: value.version,
|
||||
id: value.id,
|
||||
disabled_credentials_mode: value.disabled_credentials_mode,
|
||||
@@ -132,9 +133,9 @@ pub struct TrafficV1_1_20_2 {
|
||||
pub packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl From<TrafficV1_1_20_2> for Traffic {
|
||||
impl From<TrafficV1_1_20_2> for TrafficV1_1_30 {
|
||||
fn from(value: TrafficV1_1_20_2) -> Self {
|
||||
Traffic {
|
||||
TrafficV1_1_30 {
|
||||
average_packet_delay: value.average_packet_delay,
|
||||
message_sending_average_delay: value.message_sending_average_delay,
|
||||
disable_main_poisson_packet_distribution: value
|
||||
@@ -168,9 +169,9 @@ pub struct CoverTrafficV1_1_20_2 {
|
||||
pub disable_loop_cover_traffic_stream: bool,
|
||||
}
|
||||
|
||||
impl From<CoverTrafficV1_1_20_2> for CoverTraffic {
|
||||
impl From<CoverTrafficV1_1_20_2> for CoverTrafficV1_1_30 {
|
||||
fn from(value: CoverTrafficV1_1_20_2) -> Self {
|
||||
CoverTraffic {
|
||||
CoverTrafficV1_1_30 {
|
||||
loop_cover_traffic_average_delay: value.loop_cover_traffic_average_delay,
|
||||
cover_traffic_primary_size_ratio: value.cover_traffic_primary_size_ratio,
|
||||
disable_loop_cover_traffic_stream: value.disable_loop_cover_traffic_stream,
|
||||
@@ -195,9 +196,9 @@ pub struct GatewayConnectionV1_1_20_2 {
|
||||
pub gateway_response_timeout: Duration,
|
||||
}
|
||||
|
||||
impl From<GatewayConnectionV1_1_20_2> for GatewayConnection {
|
||||
impl From<GatewayConnectionV1_1_20_2> for GatewayConnectionV1_1_30 {
|
||||
fn from(value: GatewayConnectionV1_1_20_2) -> Self {
|
||||
GatewayConnection {
|
||||
GatewayConnectionV1_1_30 {
|
||||
gateway_response_timeout: value.gateway_response_timeout,
|
||||
}
|
||||
}
|
||||
@@ -221,9 +222,9 @@ pub struct AcknowledgementsV1_1_20_2 {
|
||||
pub ack_wait_addition: Duration,
|
||||
}
|
||||
|
||||
impl From<AcknowledgementsV1_1_20_2> for Acknowledgements {
|
||||
impl From<AcknowledgementsV1_1_20_2> for AcknowledgementsV1_1_30 {
|
||||
fn from(value: AcknowledgementsV1_1_20_2) -> Self {
|
||||
Acknowledgements {
|
||||
AcknowledgementsV1_1_30 {
|
||||
average_ack_delay: value.average_ack_delay,
|
||||
ack_wait_multiplier: value.ack_wait_multiplier,
|
||||
ack_wait_addition: value.ack_wait_addition,
|
||||
@@ -261,9 +262,9 @@ impl Default for TopologyV1_1_20_2 {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TopologyV1_1_20_2> for Topology {
|
||||
impl From<TopologyV1_1_20_2> for TopologyV1_1_30 {
|
||||
fn from(value: TopologyV1_1_20_2) -> Self {
|
||||
Topology {
|
||||
TopologyV1_1_30 {
|
||||
topology_refresh_rate: value.topology_refresh_rate,
|
||||
topology_resolution_timeout: value.topology_resolution_timeout,
|
||||
disable_refreshing: value.disable_refreshing,
|
||||
@@ -307,9 +308,9 @@ impl Default for ReplySurbsV1_1_20_2 {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ReplySurbsV1_1_20_2> for ReplySurbs {
|
||||
impl From<ReplySurbsV1_1_20_2> for ReplySurbsV1_1_30 {
|
||||
fn from(value: ReplySurbsV1_1_20_2) -> Self {
|
||||
ReplySurbs {
|
||||
ReplySurbsV1_1_30 {
|
||||
minimum_reply_surb_storage_threshold: value.minimum_reply_surb_storage_threshold,
|
||||
maximum_reply_surb_storage_threshold: value.maximum_reply_surb_storage_threshold,
|
||||
minimum_reply_surb_request_size: value.minimum_reply_surb_request_size,
|
||||
@@ -335,9 +336,9 @@ pub struct DebugConfigV1_1_20_2 {
|
||||
pub reply_surbs: ReplySurbsV1_1_20_2,
|
||||
}
|
||||
|
||||
impl From<DebugConfigV1_1_20_2> for DebugConfig {
|
||||
impl From<DebugConfigV1_1_20_2> for DebugConfigV1_1_30 {
|
||||
fn from(value: DebugConfigV1_1_20_2) -> Self {
|
||||
DebugConfig {
|
||||
DebugConfigV1_1_30 {
|
||||
traffic: value.traffic.into(),
|
||||
cover_traffic: value.cover_traffic.into(),
|
||||
gateway_connection: value.gateway_connection.into(),
|
||||
|
||||
@@ -0,0 +1,472 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::topology_control::geo_aware_provider::CountryGroup;
|
||||
use crate::config::{
|
||||
Acknowledgements, Client, Config, CoverTraffic, DebugConfig, GatewayConnection, GroupBy,
|
||||
ReplySurbs, Topology, TopologyStructure, Traffic,
|
||||
};
|
||||
use nym_sphinx::{
|
||||
addressing::clients::Recipient,
|
||||
params::{PacketSize, PacketType},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use url::Url;
|
||||
|
||||
// 'DEBUG'
|
||||
const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5;
|
||||
|
||||
const DEFAULT_ACK_WAIT_ADDITION: Duration = Duration::from_millis(1_500);
|
||||
const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(200);
|
||||
const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: Duration = Duration::from_millis(20);
|
||||
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_RESOLUTION_TIMEOUT: Duration = Duration::from_millis(5_000);
|
||||
const DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD: Duration = Duration::from_secs(70 * 60); // 70min -> full epoch (1h) + a bit of overhead
|
||||
|
||||
// Set this to a high value for now, so that we don't risk sporadic timeouts that might cause
|
||||
// bought bandwidth tokens to not have time to be spent; Once we remove the gateway from the
|
||||
// bandwidth bridging protocol, we can come back to a smaller timeout value
|
||||
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
|
||||
|
||||
const DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO: f64 = 0.70;
|
||||
|
||||
// reply-surbs related:
|
||||
|
||||
// define when to request
|
||||
// clients/client-core/src/client/replies/reply_storage/surb_storage.rs
|
||||
const DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 10;
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD: usize = 200;
|
||||
|
||||
// define how much to request at once
|
||||
// clients/client-core/src/client/replies/reply_controller.rs
|
||||
const DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 10;
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE: u32 = 100;
|
||||
|
||||
const DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE: u32 = 500;
|
||||
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD: Duration = Duration::from_secs(10);
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD: Duration = Duration::from_secs(5 * 60);
|
||||
|
||||
// 12 hours
|
||||
const DEFAULT_MAXIMUM_REPLY_SURB_AGE: Duration = Duration::from_secs(12 * 60 * 60);
|
||||
|
||||
// 24 hours
|
||||
const DEFAULT_MAXIMUM_REPLY_KEY_AGE: Duration = Duration::from_secs(24 * 60 * 60);
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigV1_1_30 {
|
||||
pub client: ClientV1_1_30,
|
||||
|
||||
#[serde(default)]
|
||||
pub debug: DebugConfigV1_1_30,
|
||||
}
|
||||
|
||||
impl From<ConfigV1_1_30> for Config {
|
||||
fn from(value: ConfigV1_1_30) -> Self {
|
||||
Config {
|
||||
client: Client {
|
||||
version: value.client.version,
|
||||
id: value.client.id,
|
||||
disabled_credentials_mode: value.client.disabled_credentials_mode,
|
||||
nyxd_urls: value.client.nyxd_urls,
|
||||
nym_api_urls: value.client.nym_api_urls,
|
||||
},
|
||||
debug: DebugConfig {
|
||||
traffic: Traffic {
|
||||
average_packet_delay: value.debug.traffic.average_packet_delay,
|
||||
message_sending_average_delay: value
|
||||
.debug
|
||||
.traffic
|
||||
.message_sending_average_delay,
|
||||
disable_main_poisson_packet_distribution: value
|
||||
.debug
|
||||
.traffic
|
||||
.disable_main_poisson_packet_distribution,
|
||||
primary_packet_size: value.debug.traffic.primary_packet_size,
|
||||
secondary_packet_size: value.debug.traffic.secondary_packet_size,
|
||||
packet_type: value.debug.traffic.packet_type,
|
||||
},
|
||||
cover_traffic: CoverTraffic {
|
||||
loop_cover_traffic_average_delay: value
|
||||
.debug
|
||||
.cover_traffic
|
||||
.loop_cover_traffic_average_delay,
|
||||
cover_traffic_primary_size_ratio: value
|
||||
.debug
|
||||
.cover_traffic
|
||||
.cover_traffic_primary_size_ratio,
|
||||
disable_loop_cover_traffic_stream: value
|
||||
.debug
|
||||
.cover_traffic
|
||||
.disable_loop_cover_traffic_stream,
|
||||
},
|
||||
gateway_connection: GatewayConnection {
|
||||
gateway_response_timeout: value
|
||||
.debug
|
||||
.gateway_connection
|
||||
.gateway_response_timeout,
|
||||
},
|
||||
acknowledgements: Acknowledgements {
|
||||
average_ack_delay: value.debug.acknowledgements.average_ack_delay,
|
||||
ack_wait_multiplier: value.debug.acknowledgements.ack_wait_multiplier,
|
||||
ack_wait_addition: value.debug.acknowledgements.ack_wait_addition,
|
||||
},
|
||||
topology: Topology {
|
||||
topology_refresh_rate: value.debug.topology.topology_refresh_rate,
|
||||
topology_resolution_timeout: value.debug.topology.topology_resolution_timeout,
|
||||
disable_refreshing: value.debug.topology.disable_refreshing,
|
||||
max_startup_gateway_waiting_period: value
|
||||
.debug
|
||||
.topology
|
||||
.max_startup_gateway_waiting_period,
|
||||
topology_structure: value.debug.topology.topology_structure.into(),
|
||||
},
|
||||
reply_surbs: ReplySurbs {
|
||||
minimum_reply_surb_storage_threshold: value
|
||||
.debug
|
||||
.reply_surbs
|
||||
.minimum_reply_surb_storage_threshold,
|
||||
maximum_reply_surb_storage_threshold: value
|
||||
.debug
|
||||
.reply_surbs
|
||||
.maximum_reply_surb_storage_threshold,
|
||||
minimum_reply_surb_request_size: value
|
||||
.debug
|
||||
.reply_surbs
|
||||
.minimum_reply_surb_request_size,
|
||||
maximum_reply_surb_request_size: value
|
||||
.debug
|
||||
.reply_surbs
|
||||
.maximum_reply_surb_request_size,
|
||||
maximum_allowed_reply_surb_request_size: value
|
||||
.debug
|
||||
.reply_surbs
|
||||
.maximum_allowed_reply_surb_request_size,
|
||||
maximum_reply_surb_rerequest_waiting_period: value
|
||||
.debug
|
||||
.reply_surbs
|
||||
.maximum_reply_surb_rerequest_waiting_period,
|
||||
maximum_reply_surb_drop_waiting_period: value
|
||||
.debug
|
||||
.reply_surbs
|
||||
.maximum_reply_surb_drop_waiting_period,
|
||||
maximum_reply_surb_age: value.debug.reply_surbs.maximum_reply_surb_age,
|
||||
maximum_reply_key_age: value.debug.reply_surbs.maximum_reply_key_age,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
|
||||
// note: the deny_unknown_fields is VITAL here to allow upgrades from v1.1.20_2
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ClientV1_1_30 {
|
||||
/// Version of the client for which this configuration was created.
|
||||
pub version: String,
|
||||
|
||||
/// ID specifies the human readable ID of this particular client.
|
||||
pub id: String,
|
||||
|
||||
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
|
||||
/// to claim bandwidth without presenting bandwidth credentials.
|
||||
// TODO: this should be moved to `debug.gateway_connection`
|
||||
#[serde(default)]
|
||||
pub disabled_credentials_mode: bool,
|
||||
|
||||
/// Addresses to nyxd validators via which the client can communicate with the chain.
|
||||
#[serde(alias = "validator_urls")]
|
||||
pub nyxd_urls: Vec<Url>,
|
||||
|
||||
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
||||
#[serde(alias = "validator_api_urls")]
|
||||
pub nym_api_urls: Vec<Url>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct TrafficV1_1_30 {
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// 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
|
||||
/// until the packet reaches its destination.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub average_packet_delay: Duration,
|
||||
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// it is going to take another 'real traffic stream' message to be sent.
|
||||
/// If no real packets are available and cover traffic is enabled,
|
||||
/// a loop cover message is sent instead in order to preserve the rate.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub message_sending_average_delay: Duration,
|
||||
|
||||
/// Controls whether the main packet stream constantly produces packets according to the predefined
|
||||
/// poisson distribution.
|
||||
pub disable_main_poisson_packet_distribution: bool,
|
||||
|
||||
/// Specifies the packet size used for sent messages.
|
||||
/// Do not override it unless you understand the consequences of that change.
|
||||
pub primary_packet_size: PacketSize,
|
||||
|
||||
/// Specifies the optional auxiliary packet size for optimizing message streams.
|
||||
/// Note that its use decreases overall anonymity.
|
||||
/// Do not set it it unless you understand the consequences of that change.
|
||||
pub secondary_packet_size: Option<PacketSize>,
|
||||
|
||||
pub packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl Default for TrafficV1_1_30 {
|
||||
fn default() -> Self {
|
||||
TrafficV1_1_30 {
|
||||
average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||
message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY,
|
||||
disable_main_poisson_packet_distribution: false,
|
||||
primary_packet_size: PacketSize::RegularPacket,
|
||||
secondary_packet_size: None,
|
||||
packet_type: PacketType::Mix,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct CoverTrafficV1_1_30 {
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// it is going to take for another loop cover traffic message to be sent.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub loop_cover_traffic_average_delay: Duration,
|
||||
|
||||
/// Specifies the ratio of `primary_packet_size` to `secondary_packet_size` used in cover traffic.
|
||||
/// Only applicable if `secondary_packet_size` is enabled.
|
||||
pub cover_traffic_primary_size_ratio: f64,
|
||||
|
||||
/// Controls whether the dedicated loop cover traffic stream should be enabled.
|
||||
/// (and sending packets, on average, every [Self::loop_cover_traffic_average_delay])
|
||||
pub disable_loop_cover_traffic_stream: bool,
|
||||
}
|
||||
|
||||
impl Default for CoverTrafficV1_1_30 {
|
||||
fn default() -> Self {
|
||||
CoverTrafficV1_1_30 {
|
||||
loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY,
|
||||
cover_traffic_primary_size_ratio: DEFAULT_COVER_TRAFFIC_PRIMARY_SIZE_RATIO,
|
||||
disable_loop_cover_traffic_stream: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct GatewayConnectionV1_1_30 {
|
||||
/// How long we're willing to wait for a response to a message sent to the gateway,
|
||||
/// before giving up on it.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub gateway_response_timeout: Duration,
|
||||
}
|
||||
|
||||
impl Default for GatewayConnectionV1_1_30 {
|
||||
fn default() -> Self {
|
||||
GatewayConnectionV1_1_30 {
|
||||
gateway_response_timeout: DEFAULT_GATEWAY_RESPONSE_TIMEOUT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct AcknowledgementsV1_1_30 {
|
||||
/// The parameter of Poisson distribution determining how long, on average,
|
||||
/// 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
|
||||
/// until the packet reaches its destination.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub average_ack_delay: Duration,
|
||||
|
||||
/// Value multiplied with the expected round trip time of an acknowledgement packet before
|
||||
/// it is assumed it was lost and retransmission of the data packet happens.
|
||||
/// In an ideal network with 0 latency, this value would have been 1.
|
||||
pub ack_wait_multiplier: f64,
|
||||
|
||||
/// 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.
|
||||
/// In an ideal network with 0 latency, this value would have been 0.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub ack_wait_addition: Duration,
|
||||
}
|
||||
|
||||
impl Default for AcknowledgementsV1_1_30 {
|
||||
fn default() -> Self {
|
||||
AcknowledgementsV1_1_30 {
|
||||
average_ack_delay: DEFAULT_AVERAGE_PACKET_DELAY,
|
||||
ack_wait_multiplier: DEFAULT_ACK_WAIT_MULTIPLIER,
|
||||
ack_wait_addition: DEFAULT_ACK_WAIT_ADDITION,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct TopologyV1_1_30 {
|
||||
/// The uniform delay every which clients are querying the directory server
|
||||
/// to try to obtain a compatible network topology to send sphinx packets through.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub topology_refresh_rate: Duration,
|
||||
|
||||
/// 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
|
||||
/// did not reach its destination.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub topology_resolution_timeout: Duration,
|
||||
|
||||
/// Specifies whether the client should not refresh the network topology after obtaining
|
||||
/// the first valid instance.
|
||||
/// Supersedes `topology_refresh_rate_ms`.
|
||||
pub disable_refreshing: bool,
|
||||
|
||||
/// Defines how long the client is going to wait on startup for its gateway to come online,
|
||||
/// before abandoning the procedure.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub max_startup_gateway_waiting_period: Duration,
|
||||
|
||||
/// Specifies the mixnode topology to be used for sending packets.
|
||||
pub topology_structure: TopologyStructureV1_1_30,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum TopologyStructureV1_1_30 {
|
||||
#[default]
|
||||
NymApi,
|
||||
GeoAware(GroupByV1_1_30),
|
||||
}
|
||||
|
||||
impl From<TopologyStructureV1_1_30> for TopologyStructure {
|
||||
fn from(value: TopologyStructureV1_1_30) -> Self {
|
||||
match value {
|
||||
TopologyStructureV1_1_30::NymApi => TopologyStructure::NymApi,
|
||||
TopologyStructureV1_1_30::GeoAware(group_by) => {
|
||||
TopologyStructure::GeoAware(group_by.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum GroupByV1_1_30 {
|
||||
CountryGroup(CountryGroup),
|
||||
NymAddress(Recipient),
|
||||
}
|
||||
|
||||
impl From<GroupByV1_1_30> for GroupBy {
|
||||
fn from(value: GroupByV1_1_30) -> Self {
|
||||
match value {
|
||||
GroupByV1_1_30::CountryGroup(country) => GroupBy::CountryGroup(country),
|
||||
GroupByV1_1_30::NymAddress(addr) => GroupBy::NymAddress(addr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GroupByV1_1_30 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
GroupByV1_1_30::CountryGroup(group) => write!(f, "group: {}", group),
|
||||
GroupByV1_1_30::NymAddress(address) => write!(f, "address: {}", address),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TopologyV1_1_30 {
|
||||
fn default() -> Self {
|
||||
TopologyV1_1_30 {
|
||||
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
|
||||
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
|
||||
disable_refreshing: false,
|
||||
max_startup_gateway_waiting_period: DEFAULT_MAX_STARTUP_GATEWAY_WAITING_PERIOD,
|
||||
topology_structure: TopologyStructureV1_1_30::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct ReplySurbsV1_1_30 {
|
||||
/// Defines the minimum number of reply surbs the client wants to keep in its storage at all times.
|
||||
/// It can only allow to go below that value if its to request additional reply surbs.
|
||||
pub minimum_reply_surb_storage_threshold: usize,
|
||||
|
||||
/// Defines the maximum number of reply surbs the client wants to keep in its storage at any times.
|
||||
pub maximum_reply_surb_storage_threshold: usize,
|
||||
|
||||
/// Defines the minimum number of reply surbs the client would request.
|
||||
pub minimum_reply_surb_request_size: u32,
|
||||
|
||||
/// Defines the maximum number of reply surbs the client would request.
|
||||
pub maximum_reply_surb_request_size: u32,
|
||||
|
||||
/// Defines the maximum number of reply surbs a remote party is allowed to request from this client at once.
|
||||
pub maximum_allowed_reply_surb_request_size: u32,
|
||||
|
||||
/// Defines maximum amount of time the client is going to wait for reply surbs before explicitly asking
|
||||
/// for more even though in theory they wouldn't need to.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_reply_surb_rerequest_waiting_period: Duration,
|
||||
|
||||
/// Defines maximum amount of time the client is going to wait for reply surbs before
|
||||
/// deciding it's never going to get them and would drop all pending messages
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_reply_surb_drop_waiting_period: Duration,
|
||||
|
||||
/// Defines maximum amount of time given reply surb is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_reply_surb_age: Duration,
|
||||
|
||||
/// Defines maximum amount of time given reply key is going to be valid for.
|
||||
/// This is going to be superseded by key rotation once implemented.
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub maximum_reply_key_age: Duration,
|
||||
}
|
||||
|
||||
impl Default for ReplySurbsV1_1_30 {
|
||||
fn default() -> Self {
|
||||
ReplySurbsV1_1_30 {
|
||||
minimum_reply_surb_storage_threshold: DEFAULT_MINIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
||||
maximum_reply_surb_storage_threshold: DEFAULT_MAXIMUM_REPLY_SURB_STORAGE_THRESHOLD,
|
||||
minimum_reply_surb_request_size: DEFAULT_MINIMUM_REPLY_SURB_REQUEST_SIZE,
|
||||
maximum_reply_surb_request_size: DEFAULT_MAXIMUM_REPLY_SURB_REQUEST_SIZE,
|
||||
maximum_allowed_reply_surb_request_size: DEFAULT_MAXIMUM_ALLOWED_SURB_REQUEST_SIZE,
|
||||
maximum_reply_surb_rerequest_waiting_period:
|
||||
DEFAULT_MAXIMUM_REPLY_SURB_REREQUEST_WAITING_PERIOD,
|
||||
maximum_reply_surb_drop_waiting_period: DEFAULT_MAXIMUM_REPLY_SURB_DROP_WAITING_PERIOD,
|
||||
maximum_reply_surb_age: DEFAULT_MAXIMUM_REPLY_SURB_AGE,
|
||||
maximum_reply_key_age: DEFAULT_MAXIMUM_REPLY_KEY_AGE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct DebugConfigV1_1_30 {
|
||||
/// Defines all configuration options related to traffic streams.
|
||||
pub traffic: TrafficV1_1_30,
|
||||
|
||||
/// Defines all configuration options related to cover traffic stream(s).
|
||||
pub cover_traffic: CoverTrafficV1_1_30,
|
||||
|
||||
/// Defines all configuration options related to the gateway connection.
|
||||
pub gateway_connection: GatewayConnectionV1_1_30,
|
||||
|
||||
/// Defines all configuration options related to acknowledgements, such as delays or wait timeouts.
|
||||
pub acknowledgements: AcknowledgementsV1_1_30,
|
||||
|
||||
/// Defines all configuration options related topology, such as refresh rates or timeouts.
|
||||
pub topology: TopologyV1_1_30,
|
||||
|
||||
/// Defines all configuration options related to reply SURBs.
|
||||
pub reply_surbs: ReplySurbsV1_1_30,
|
||||
}
|
||||
@@ -126,6 +126,12 @@ pub enum ClientCoreError {
|
||||
|
||||
#[error("this client has performed gateway initialisation in another session")]
|
||||
NoInitClientPresent,
|
||||
|
||||
#[error("there are no gateways supporting the wss protocol available")]
|
||||
NoWssGateways,
|
||||
|
||||
#[error("the specified gateway '{gateway}' does not support the wss protocol")]
|
||||
UnsupportedWssProtocol { gateway: String },
|
||||
}
|
||||
|
||||
/// Set of messages that the client can send to listeners via the task manager
|
||||
|
||||
@@ -67,7 +67,7 @@ pub async fn current_gateways<R: Rng>(
|
||||
|
||||
log::trace!("Fetching list of gateways from: {nym_api}");
|
||||
|
||||
let gateways = client.get_cached_gateways().await?;
|
||||
let gateways = client.get_cached_described_gateways().await?;
|
||||
let valid_gateways = gateways
|
||||
.into_iter()
|
||||
.filter_map(|gateway| gateway.try_into().ok())
|
||||
@@ -174,7 +174,10 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
|
||||
pub async fn choose_gateway_by_latency<R: Rng>(
|
||||
rng: &mut R,
|
||||
gateways: &[gateway::Node],
|
||||
must_use_tls: bool,
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
let gateways = filter_by_tls(gateways, must_use_tls)?;
|
||||
|
||||
info!(
|
||||
"choosing gateway by latency, pinging {} gateways ...",
|
||||
gateways.len()
|
||||
@@ -210,28 +213,57 @@ pub async fn choose_gateway_by_latency<R: Rng>(
|
||||
Ok(chosen.gateway.clone())
|
||||
}
|
||||
|
||||
fn filter_by_tls(
|
||||
gateways: &[gateway::Node],
|
||||
must_use_tls: bool,
|
||||
) -> Result<Vec<&gateway::Node>, ClientCoreError> {
|
||||
if must_use_tls {
|
||||
let filtered = gateways
|
||||
.iter()
|
||||
.filter(|g| g.clients_wss_port.is_some())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if filtered.is_empty() {
|
||||
return Err(ClientCoreError::NoWssGateways);
|
||||
}
|
||||
|
||||
Ok(filtered)
|
||||
} else {
|
||||
Ok(gateways.iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn uniformly_random_gateway<R: Rng>(
|
||||
rng: &mut R,
|
||||
gateways: &[gateway::Node],
|
||||
must_use_tls: bool,
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
gateways
|
||||
filter_by_tls(gateways, must_use_tls)?
|
||||
.choose(rng)
|
||||
.ok_or(ClientCoreError::NoGatewaysOnNetwork)
|
||||
.cloned()
|
||||
.map(|&r| r.clone())
|
||||
}
|
||||
|
||||
pub(super) fn get_specified_gateway(
|
||||
gateway_identity: IdentityKeyRef,
|
||||
gateways: &[gateway::Node],
|
||||
must_use_tls: bool,
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||
|
||||
gateways
|
||||
let gateway = gateways
|
||||
.iter()
|
||||
.find(|gateway| gateway.identity_key == user_gateway)
|
||||
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))
|
||||
.cloned()
|
||||
.ok_or_else(|| ClientCoreError::NoGatewayWithId(gateway_identity.to_string()))?;
|
||||
|
||||
if must_use_tls && gateway.clients_wss_port.is_none() {
|
||||
return Err(ClientCoreError::UnsupportedWssProtocol {
|
||||
gateway: gateway_identity.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(gateway.clone())
|
||||
}
|
||||
|
||||
pub(super) async fn register_with_gateway(
|
||||
|
||||
@@ -108,19 +108,20 @@ where
|
||||
|
||||
let gateway_details = match selection_specification {
|
||||
GatewaySelectionSpecification::UniformRemote { must_use_tls } => {
|
||||
let gateway = uniformly_random_gateway(&mut rng, &available_gateways)?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
|
||||
let gateway = uniformly_random_gateway(&mut rng, &available_gateways, must_use_tls)?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
|
||||
}
|
||||
GatewaySelectionSpecification::RemoteByLatency { must_use_tls } => {
|
||||
let gateway = choose_gateway_by_latency(&mut rng, &available_gateways).await?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
|
||||
let gateway =
|
||||
choose_gateway_by_latency(&mut rng, &available_gateways, must_use_tls).await?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
|
||||
}
|
||||
GatewaySelectionSpecification::Specified {
|
||||
must_use_tls,
|
||||
identity,
|
||||
} => {
|
||||
let gateway = get_specified_gateway(&identity, &available_gateways)?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls))
|
||||
let gateway = get_specified_gateway(&identity, &available_gateways, must_use_tls)?;
|
||||
GatewayDetails::Configured(GatewayEndpointConfig::from_node(gateway, must_use_tls)?)
|
||||
}
|
||||
GatewaySelectionSpecification::Custom {
|
||||
gateway_identity,
|
||||
|
||||
@@ -31,7 +31,7 @@ serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
|
||||
[dependencies.tungstenite]
|
||||
version = "0.13"
|
||||
workspace = true
|
||||
default-features = false
|
||||
|
||||
# non-wasm-only dependencies
|
||||
@@ -44,7 +44,10 @@ version = "0.1.11"
|
||||
features = ["net", "sync", "time"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
version = "0.14"
|
||||
workspace = true
|
||||
# the choice of this particular tls feature was arbitrary;
|
||||
# if you reckon a different one would be more appropriate, feel free to change it
|
||||
features = ["native-tls"]
|
||||
|
||||
# wasm-only dependencies
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
||||
|
||||
@@ -74,7 +74,7 @@ impl PartiallyDelegated {
|
||||
|
||||
fn route_socket_messages(
|
||||
ws_msgs: Vec<Message>,
|
||||
packet_router: &mut PacketRouter,
|
||||
packet_router: &PacketRouter,
|
||||
shared_key: &SharedKeys,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
let plaintexts = Self::recover_received_plaintexts(ws_msgs, shared_key);
|
||||
@@ -97,7 +97,6 @@ impl PartiallyDelegated {
|
||||
let mixnet_receiver_future = async move {
|
||||
let mut notify_receiver = notify_receiver;
|
||||
let mut chunk_stream = (&mut stream).ready_chunks(8);
|
||||
let mut packet_router = packet_router;
|
||||
|
||||
let ret_err = loop {
|
||||
tokio::select! {
|
||||
@@ -115,7 +114,7 @@ impl PartiallyDelegated {
|
||||
Ok(msgs) => msgs
|
||||
};
|
||||
|
||||
if let Err(err) = Self::route_socket_messages(ws_msgs, &mut packet_router, shared_key.as_ref()) {
|
||||
if let Err(err) = Self::route_socket_messages(ws_msgs, &packet_router, shared_key.as_ref()) {
|
||||
log::warn!("Route socket messages failed: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ pub trait GatewayPacketRouter {
|
||||
}
|
||||
|
||||
n if n
|
||||
== PacketSize::OutfoxRegularPacket.plaintext_size() - outfox_ack_overhead =>
|
||||
== PacketSize::OutfoxRegularPacket
|
||||
.plaintext_size()
|
||||
.saturating_sub(outfox_ack_overhead) =>
|
||||
{
|
||||
trace!("received regular outfox packet");
|
||||
received_messages.push(received_packet);
|
||||
|
||||
@@ -24,6 +24,7 @@ nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
http-api-client = { path = "../../../common/http-api-client"}
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
@@ -86,4 +87,5 @@ required-features = ["http-client"]
|
||||
default = ["http-client"]
|
||||
http-client = ["cosmrs/rpc", "openssl"]
|
||||
generate-ts = []
|
||||
contract-testing = ["nym-mixnet-contract-common/contract-testing"]
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use nym_validator_client::nyxd::contract_traits::{
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
setup_env(Some("../../../envs/qa-qwerty.env"));
|
||||
setup_env(Some("../../../envs/qa.env"));
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let config =
|
||||
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
|
||||
|
||||
@@ -9,7 +9,7 @@ use nym_validator_client::nyxd::contract_traits::{
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
setup_env(Some("../../../envs/qa-qwerty.env"));
|
||||
setup_env(Some("../../../envs/qa.env"));
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let config =
|
||||
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use nym_api_requests::models::MixNodeBondAnnotated;
|
||||
use nym_api_requests::models::{DescribedGateway, MixNodeBondAnnotated};
|
||||
use nym_api_requests::models::{
|
||||
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse,
|
||||
@@ -19,6 +19,7 @@ use nym_api_requests::models::{
|
||||
use nym_network_defaults::NymNetworkDetails;
|
||||
use url::Url;
|
||||
|
||||
pub use crate::nym_api::NymApiClientExt;
|
||||
pub use nym_mixnet_contract_common::{
|
||||
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, MixId,
|
||||
};
|
||||
@@ -147,7 +148,7 @@ impl Client<ReqwestRpcClient> {
|
||||
|
||||
impl<C> Client<C> {
|
||||
pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self {
|
||||
let nym_api_client = nym_api::Client::new(config.api_url.clone());
|
||||
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
|
||||
|
||||
Client {
|
||||
nym_api: nym_api_client,
|
||||
@@ -161,7 +162,7 @@ impl<C, S> Client<C, S> {
|
||||
where
|
||||
S: OfflineSigner,
|
||||
{
|
||||
let nym_api_client = nym_api::Client::new(config.api_url.clone());
|
||||
let nym_api_client = nym_api::Client::new(config.api_url.clone(), None);
|
||||
|
||||
Client {
|
||||
nym_api: nym_api_client,
|
||||
@@ -177,7 +178,7 @@ impl<C, S> Client<C, S> {
|
||||
}
|
||||
|
||||
pub fn change_nym_api(&mut self, new_endpoint: Url) {
|
||||
self.nym_api.change_url(new_endpoint)
|
||||
self.nym_api.change_base_url(new_endpoint)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||
@@ -241,7 +242,7 @@ pub struct NymApiClient {
|
||||
|
||||
impl NymApiClient {
|
||||
pub fn new(api_url: Url) -> Self {
|
||||
let nym_api = nym_api::Client::new(api_url);
|
||||
let nym_api = nym_api::Client::new(api_url, None);
|
||||
|
||||
NymApiClient { nym_api }
|
||||
}
|
||||
@@ -251,7 +252,7 @@ impl NymApiClient {
|
||||
}
|
||||
|
||||
pub fn change_nym_api(&mut self, new_endpoint: Url) {
|
||||
self.nym_api.change_url(new_endpoint);
|
||||
self.nym_api.change_base_url(new_endpoint);
|
||||
}
|
||||
|
||||
pub async fn get_cached_active_mixnodes(
|
||||
@@ -274,6 +275,12 @@ impl NymApiClient {
|
||||
Ok(self.nym_api.get_gateways().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_described_gateways(
|
||||
&self,
|
||||
) -> Result<Vec<DescribedGateway>, ValidatorClientError> {
|
||||
Ok(self.nym_api.get_gateways_described().await?)
|
||||
}
|
||||
|
||||
pub async fn get_gateway_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use http_api_client::HttpClientError;
|
||||
use nym_api_requests::models::RequestError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NymAPIError {
|
||||
#[error("There was an issue with the REST request - {source}")]
|
||||
ReqwestClientError {
|
||||
#[from]
|
||||
source: reqwest::Error,
|
||||
},
|
||||
|
||||
#[error("Not found")]
|
||||
NotFound,
|
||||
|
||||
#[error("Request failed with error message - {0}")]
|
||||
GenericRequestFailure(String),
|
||||
|
||||
#[error("The nym API has failed to resolve our request. It returned status code {status} and additional error message: {}", error.message())]
|
||||
ApiRequestFailure { status: u16, error: RequestError },
|
||||
}
|
||||
pub type NymAPIError = HttpClientError<RequestError>;
|
||||
|
||||
@@ -3,140 +3,38 @@
|
||||
|
||||
use crate::nym_api::error::NymAPIError;
|
||||
use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
||||
use async_trait::async_trait;
|
||||
use http_api_client::{ApiClient, NO_PARAMS};
|
||||
use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use nym_api_requests::models::{
|
||||
ComputeRewardEstParam, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
|
||||
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
|
||||
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse,
|
||||
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse, UptimeResponse,
|
||||
};
|
||||
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
|
||||
use nym_name_service_common::response::NamesListResponse;
|
||||
use nym_service_provider_directory_common::response::ServicesListResponse;
|
||||
use reqwest::{Response, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
pub mod error;
|
||||
pub mod routes;
|
||||
|
||||
type PathSegments<'a> = &'a [&'a str];
|
||||
type Params<'a, K, V> = &'a [(K, V)];
|
||||
pub use http_api_client::Client;
|
||||
|
||||
const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Client {
|
||||
url: Url,
|
||||
reqwest_client: reqwest::Client,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(url: Url) -> Self {
|
||||
let reqwest_client = reqwest::Client::new();
|
||||
Self {
|
||||
url,
|
||||
reqwest_client,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_url(&mut self, new_url: Url) {
|
||||
self.url = new_url
|
||||
}
|
||||
|
||||
pub fn current_url(&self) -> &Url {
|
||||
&self.url
|
||||
}
|
||||
|
||||
async fn send_get_request<K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<Response, NymAPIError>
|
||||
where
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = create_api_url(&self.url, path, params);
|
||||
Ok(self.reqwest_client.get(url).send().await?)
|
||||
}
|
||||
|
||||
async fn query_nym_api<T, K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, NymAPIError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let res = self.send_get_request(path, params).await?;
|
||||
if res.status().is_success() {
|
||||
Ok(res.json().await?)
|
||||
} else if res.status() == StatusCode::NOT_FOUND {
|
||||
Err(NymAPIError::NotFound)
|
||||
} else {
|
||||
Err(NymAPIError::GenericRequestFailure(res.text().await?))
|
||||
}
|
||||
}
|
||||
|
||||
// This works for endpoints returning Result<Json<T>, ErrorResponse>
|
||||
async fn query_nym_api_fallible<T, K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, NymAPIError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let res = self.send_get_request(path, params).await?;
|
||||
let status = res.status();
|
||||
if res.status().is_success() {
|
||||
Ok(res.json().await?)
|
||||
} else {
|
||||
let request_error: RequestError = res.json().await?;
|
||||
Err(NymAPIError::ApiRequestFailure {
|
||||
status: status.as_u16(),
|
||||
error: request_error,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn post_nym_api<B, T, K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
json_body: &B,
|
||||
) -> Result<T, NymAPIError>
|
||||
where
|
||||
B: Serialize + ?Sized,
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = create_api_url(&self.url, path, params);
|
||||
let response = self.reqwest_client.post(url).json(json_body).send().await?;
|
||||
if response.status().is_success() {
|
||||
Ok(response.json().await?)
|
||||
} else {
|
||||
Err(NymAPIError::GenericRequestFailure(response.text().await?))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.query_nym_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait NymApiClientExt: ApiClient {
|
||||
async fn get_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.get_json(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
async fn get_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -148,8 +46,8 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateways_detailed(&self) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
async fn get_gateways_detailed(&self) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -161,10 +59,10 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes_detailed_unfiltered(
|
||||
async fn get_mixnodes_detailed_unfiltered(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -176,23 +74,29 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
|
||||
self.query_nym_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
|
||||
async fn get_gateways(&self) -> Result<Vec<GatewayBond>, NymAPIError> {
|
||||
self.get_json(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
async fn get_gateways_described(&self) -> Result<Vec<DescribedGateway>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::GATEWAYS, routes::DESCRIBED],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_active_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
async fn get_active_mixnodes_detailed(&self) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -205,19 +109,19 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeDetails>, NymAPIError> {
|
||||
self.get_json(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_report(
|
||||
async fn get_mixnode_report(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<MixnodeStatusReportResponse, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -230,11 +134,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateway_report(
|
||||
async fn get_gateway_report(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<GatewayStatusReportResponse, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -247,11 +151,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_history(
|
||||
async fn get_mixnode_history(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<MixnodeUptimeHistoryResponse, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -264,11 +168,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateway_history(
|
||||
async fn get_gateway_history(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<GatewayUptimeHistoryResponse, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -281,10 +185,10 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_rewarded_mixnodes_detailed(
|
||||
async fn get_rewarded_mixnodes_detailed(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS,
|
||||
@@ -297,13 +201,13 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateway_core_status_count(
|
||||
async fn get_gateway_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<GatewayCoreStatusResponse, NymAPIError> {
|
||||
if let Some(since) = since {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -315,7 +219,7 @@ impl Client {
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -328,13 +232,13 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_core_status_count(
|
||||
async fn get_mixnode_core_status_count(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
since: Option<i64>,
|
||||
) -> Result<MixnodeCoreStatusResponse, NymAPIError> {
|
||||
if let Some(since) = since {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -346,7 +250,7 @@ impl Client {
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -360,11 +264,11 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_status(
|
||||
async fn get_mixnode_status(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<MixnodeStatusResponse, NymAPIError> {
|
||||
self.query_nym_api(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -377,11 +281,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_reward_estimation(
|
||||
async fn get_mixnode_reward_estimation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<RewardEstimationResponse, NymAPIError> {
|
||||
self.query_nym_api_fallible(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -394,12 +298,12 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn compute_mixnode_reward_estimation(
|
||||
async fn compute_mixnode_reward_estimation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
request_body: &ComputeRewardEstParam,
|
||||
) -> Result<RewardEstimationResponse, NymAPIError> {
|
||||
self.post_nym_api(
|
||||
self.post_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -413,11 +317,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_stake_saturation(
|
||||
async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<StakeSaturationResponse, NymAPIError> {
|
||||
self.query_nym_api_fallible(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -430,11 +334,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_inclusion_probability(
|
||||
async fn get_mixnode_inclusion_probability(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<InclusionProbabilityResponse, NymAPIError> {
|
||||
self.query_nym_api_fallible(
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -447,11 +351,8 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_avg_uptime(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
) -> Result<UptimeResponse, NymAPIError> {
|
||||
self.query_nym_api_fallible(
|
||||
async fn get_mixnode_avg_uptime(&self, mix_id: MixId) -> Result<UptimeResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
@@ -464,11 +365,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn blind_sign(
|
||||
async fn blind_sign(
|
||||
&self,
|
||||
request_body: &BlindSignRequestBody,
|
||||
) -> Result<BlindedSignatureResponse, NymAPIError> {
|
||||
self.post_nym_api(
|
||||
self.post_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
@@ -481,11 +382,11 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn verify_bandwidth_credential(
|
||||
async fn verify_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
) -> Result<VerifyCredentialResponse, NymAPIError> {
|
||||
self.post_nym_api(
|
||||
self.post_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
@@ -498,118 +399,20 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
|
||||
async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
|
||||
log::trace!("Getting service providers");
|
||||
self.query_nym_api(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
|
||||
self.get_json(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
//pub async fn get_registered_names(&self) -> Result<Vec<NameEntry>, NymAPIError> {
|
||||
pub async fn get_registered_names(&self) -> Result<NamesListResponse, NymAPIError> {
|
||||
//async fn get_registered_names(&self) -> Result<Vec<NameEntry>, NymAPIError> {
|
||||
async fn get_registered_names(&self) -> Result<NamesListResponse, NymAPIError> {
|
||||
log::trace!("Getting registered names");
|
||||
self.query_nym_api(&[routes::API_VERSION, routes::REGISTERED_NAMES], NO_PARAMS)
|
||||
self.get_json(&[routes::API_VERSION, routes::REGISTERED_NAMES], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
// utility function that should solve the double slash problem in validator API forever.
|
||||
fn create_api_url<K: AsRef<str>, V: AsRef<str>>(
|
||||
base: &Url,
|
||||
segments: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Url {
|
||||
let mut url = base.clone();
|
||||
let mut path_segments = url
|
||||
.path_segments_mut()
|
||||
.expect("provided validator url does not have a base!");
|
||||
for segment in segments {
|
||||
let segment = segment.strip_prefix('/').unwrap_or(segment);
|
||||
let segment = segment.strip_suffix('/').unwrap_or(segment);
|
||||
|
||||
path_segments.push(segment);
|
||||
}
|
||||
// I don't understand why compiler couldn't figure out that it's no longer used
|
||||
// and can be dropped
|
||||
drop(path_segments);
|
||||
|
||||
if !params.is_empty() {
|
||||
url.query_pairs_mut().extend_pairs(params);
|
||||
}
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn creating_api_path() {
|
||||
let base_url: Url = "http://foomp.com".parse().unwrap();
|
||||
|
||||
// works with 1 segment
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with 2 segments
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with leading slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["/foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with trailing slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with both leading and trailing slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// adds params
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar?foomp=baz",
|
||||
create_api_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
|
||||
create_api_url(
|
||||
&base_url,
|
||||
&["/foo/", "/bar/"],
|
||||
&[("arg1", "val1"), ("arg2", "val2")]
|
||||
)
|
||||
.as_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl NymApiClientExt for Client {}
|
||||
|
||||
@@ -6,6 +6,7 @@ use nym_network_defaults::NYM_API_VERSION;
|
||||
pub const API_VERSION: &str = NYM_API_VERSION;
|
||||
pub const MIXNODES: &str = "mixnodes";
|
||||
pub const GATEWAYS: &str = "gateways";
|
||||
pub const DESCRIBED: &str = "described";
|
||||
|
||||
pub const DETAILED: &str = "detailed";
|
||||
pub const DETAILED_UNFILTERED: &str = "detailed-unfiltered";
|
||||
|
||||
+5
-4
@@ -683,13 +683,14 @@ pub trait MixnetSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
|
||||
#[cfg(feature = "contract-testing")]
|
||||
async fn testing_resolve_all_pending_events(
|
||||
&self,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_mixnet_contract(
|
||||
fee,
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents {},
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents { limit: None },
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
@@ -928,8 +929,8 @@ mod tests {
|
||||
.withdraw_delegator_reward_on_behalf(owner.parse().unwrap(), mix_id, None)
|
||||
.ignore(),
|
||||
|
||||
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents {} => {
|
||||
#[cfg(feature = "contract-testing")]
|
||||
MixnetExecuteMsg::TestingResolveAllPendingEvents { .. } => {
|
||||
client.testing_resolve_all_pending_events(None).ignore()
|
||||
}
|
||||
};
|
||||
|
||||
+1
-1
@@ -8,6 +8,7 @@ use crate::nyxd::cosmwasm_client::types::{
|
||||
Account, CodeDetails, Contract, ContractCodeId, SequenceResponse, SimulateResponse,
|
||||
};
|
||||
use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::Query;
|
||||
use crate::rpc::TendermintRpcClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::cosmwasm::{CodeInfoResponse, ContractCodeHistoryEntry};
|
||||
@@ -35,7 +36,6 @@ use std::convert::TryFrom;
|
||||
use std::time::Duration;
|
||||
use tendermint_rpc::{
|
||||
endpoint::{block::Response as BlockResponse, broadcast, tx::Response as TxResponse},
|
||||
query::Query,
|
||||
Order,
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient, Reqwes
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::cosmwasm;
|
||||
use cosmrs::tendermint::{abci, evidence::Evidence, Genesis};
|
||||
use cosmrs::tx::{Msg, Raw, SignDoc};
|
||||
use cosmrs::tx::{Raw, SignDoc};
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_network_defaults::{ChainDetails, NymNetworkDetails};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
@@ -39,6 +39,7 @@ pub use cosmrs::tendermint::block::Height;
|
||||
pub use cosmrs::tendermint::hash::{self, Algorithm, Hash};
|
||||
pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
|
||||
pub use cosmrs::tendermint::Time as TendermintTime;
|
||||
pub use cosmrs::tx::Msg;
|
||||
pub use cosmrs::tx::{self};
|
||||
pub use cosmrs::Coin as CosmosCoin;
|
||||
pub use cosmrs::Gas;
|
||||
@@ -47,6 +48,7 @@ pub use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
|
||||
pub use tendermint_rpc::{
|
||||
endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse},
|
||||
query::Query,
|
||||
Paging,
|
||||
};
|
||||
pub use tendermint_rpc::{Request, Response, SimpleRequest};
|
||||
@@ -57,7 +59,6 @@ use crate::http_client;
|
||||
use crate::{DirectSigningHttpRpcNyxdClient, QueryHttpRpcNyxdClient};
|
||||
#[cfg(feature = "http-client")]
|
||||
use cosmrs::rpc::{HttpClient, HttpClientUrl};
|
||||
use tendermint_rpc::query::Query;
|
||||
|
||||
pub mod coin;
|
||||
pub mod contract_traits;
|
||||
|
||||
@@ -8,6 +8,6 @@ description = "Crutch library until there is proper SerDe support for coconut st
|
||||
bs58 = "0.4.0"
|
||||
getset = "0.1.1"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = "1"
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-coconut = {path = "../nymcoconut" }
|
||||
|
||||
@@ -60,7 +60,7 @@ pub async fn init(args: Args, client: SigningClient, network_details: &NymNetwor
|
||||
// by default we make ourselves an admin, let me know if you don't like that behaviour
|
||||
let opts = Some(InstantiateOptions {
|
||||
funds,
|
||||
admin: Some(args.admin.unwrap_or_else(|| client.address().clone())),
|
||||
admin: Some(args.admin.unwrap_or_else(|| client.address())),
|
||||
});
|
||||
|
||||
let msg: serde_json::Value =
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
|
||||
use crate::context::QueryClientWithNyxd;
|
||||
use crate::utils::{pretty_cosmwasm_coin, show_error};
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
|
||||
use crate::context::QueryClientWithNyxd;
|
||||
use crate::utils::{pretty_decimal_with_denom, show_error};
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::nym_api::error::NymAPIError;
|
||||
|
||||
use crate::context::QueryClientWithNyxd;
|
||||
use crate::utils::show_error;
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use nym_validator_client::nym_api::error::NymAPIError;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::nym_api::error::NymAPIError;
|
||||
|
||||
use crate::context::QueryClientWithNyxd;
|
||||
use crate::utils::show_error;
|
||||
use clap::Parser;
|
||||
use comfy_table::Table;
|
||||
use nym_validator_client::client::NymApiClientExt;
|
||||
use nym_validator_client::nym_api::error::NymAPIError;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
|
||||
@@ -15,6 +15,7 @@ pub use toml::de::Error as TomlDeError;
|
||||
pub mod defaults;
|
||||
pub mod helpers;
|
||||
pub mod legacy_helpers;
|
||||
pub mod serde_helpers;
|
||||
|
||||
pub const NYM_DIR: &str = ".nym";
|
||||
pub const DEFAULT_NYM_APIS_DIR: &str = "nym-api";
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::fmt::Display;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub fn de_maybe_stringified<'de, D, T, E>(deserializer: D) -> Result<Option<T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
T: FromStr<Err = E>,
|
||||
E: Display,
|
||||
{
|
||||
let raw = String::deserialize(deserializer)?;
|
||||
if raw.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(raw.parse().map_err(serde::de::Error::custom)?))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn de_maybe_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
de_maybe_stringified(deserializer)
|
||||
}
|
||||
|
||||
pub fn de_maybe_path<'de, D>(deserializer: D) -> Result<Option<PathBuf>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
de_maybe_stringified(deserializer)
|
||||
}
|
||||
|
||||
pub fn de_maybe_port<'de, D>(deserializer: D) -> Result<Option<u16>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let port = u16::deserialize(deserializer)?;
|
||||
if port == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(port))
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ serde_repr = "0.1"
|
||||
|
||||
# we still have to preserve that import for `JsonSchema` for `Layer` type (since we can't use cw_serde macro due to custom serde impl)
|
||||
schemars = "0.8"
|
||||
thiserror = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
|
||||
serde-json-wasm = { workspace = true }
|
||||
humantime-serde = "1.1.1"
|
||||
|
||||
@@ -572,7 +572,7 @@ mod tests {
|
||||
let env = mock_env();
|
||||
|
||||
// epoch just begun
|
||||
let interval = Interval {
|
||||
let mut interval = Interval {
|
||||
id: 0,
|
||||
epochs_in_interval: 100,
|
||||
current_epoch_start: OffsetDateTime::from_unix_timestamp(
|
||||
@@ -586,19 +586,16 @@ mod tests {
|
||||
assert!(!interval.is_current_epoch_over(&env));
|
||||
|
||||
// current time == current epoch start
|
||||
let mut interval = interval;
|
||||
interval.current_epoch_start =
|
||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64).unwrap();
|
||||
assert!(!interval.is_current_epoch_over(&env));
|
||||
|
||||
// epoch HASN'T yet begun (weird edge case, but can happen if we decide to manually adjust things)
|
||||
let mut interval = interval;
|
||||
interval.current_epoch_start =
|
||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64 + 100).unwrap();
|
||||
assert!(!interval.is_current_epoch_over(&env));
|
||||
|
||||
// current_time = EXACTLY end of the epoch
|
||||
let mut interval = interval;
|
||||
interval.current_epoch_start =
|
||||
OffsetDateTime::from_unix_timestamp(env.block.time.seconds() as i64).unwrap()
|
||||
- interval.epoch_length;
|
||||
|
||||
@@ -14,4 +14,4 @@ cosmwasm-schema = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.23" }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -14,7 +14,7 @@ cw2 = { workspace = true, optional = true }
|
||||
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.6.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
ts-rs = { workspace = true, optional = true}
|
||||
|
||||
[features]
|
||||
|
||||
@@ -9,11 +9,11 @@ edition = "2021"
|
||||
async-trait = { workspace = true }
|
||||
|
||||
log = { workspace = true }
|
||||
thiserror = "1.0"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = ["sync"]}
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
workspace = true
|
||||
version = "0.5"
|
||||
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
@@ -22,5 +22,5 @@ features = [ "rt-multi-thread", "net", "signal", "fs" ]
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
||||
|
||||
@@ -29,11 +29,11 @@ impl PersistentStorage {
|
||||
database_path.as_ref().as_os_str()
|
||||
);
|
||||
|
||||
let opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
.filename(database_path)
|
||||
.create_if_missing(true);
|
||||
|
||||
let opts = opts.disable_statement_logging();
|
||||
opts.disable_statement_logging();
|
||||
|
||||
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
|
||||
Ok(db) => db,
|
||||
|
||||
@@ -23,7 +23,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"], optional = true }
|
||||
serde_bytes = { version = "0.11.6", optional = true }
|
||||
serde_crate = { version = "1.0", optional = true, default_features = false, features = ["derive"], package = "serde" }
|
||||
subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
|
||||
thiserror = "1.0.37"
|
||||
thiserror = { workspace = true }
|
||||
zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] }
|
||||
|
||||
# internal
|
||||
|
||||
@@ -145,8 +145,12 @@ impl PublicKey {
|
||||
Self::from_bytes(&bytes)
|
||||
}
|
||||
|
||||
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
|
||||
self.0.verify(message, &signature.0)
|
||||
pub fn verify<M: AsRef<[u8]>>(
|
||||
&self,
|
||||
message: M,
|
||||
signature: &Signature,
|
||||
) -> Result<(), SignatureError> {
|
||||
self.0.verify(message.as_ref(), &signature.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,16 +243,16 @@ impl PrivateKey {
|
||||
Self::from_bytes(&bytes)
|
||||
}
|
||||
|
||||
pub fn sign(&self, message: &[u8]) -> Signature {
|
||||
pub fn sign<M: AsRef<[u8]>>(&self, message: M) -> Signature {
|
||||
let expanded_secret_key = ed25519_dalek::ExpandedSecretKey::from(&self.0);
|
||||
let public_key: PublicKey = self.into();
|
||||
let sig = expanded_secret_key.sign(message, &public_key.0);
|
||||
let sig = expanded_secret_key.sign(message.as_ref(), &public_key.0);
|
||||
Signature(sig)
|
||||
}
|
||||
|
||||
/// Signs text with the provided Ed25519 private key, returning a base58 signature
|
||||
pub fn sign_text(&self, text: &str) -> String {
|
||||
let signature_bytes = self.sign(text.as_ref()).to_bytes();
|
||||
let signature_bytes = self.sign(text).to_bytes();
|
||||
bs58::encode(signature_bytes).into_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ rand = { version = "0.8.5", default-features = false}
|
||||
rand_chacha = "0.3"
|
||||
rand_core = "0.6.3"
|
||||
sha2 = "0.9"
|
||||
serde = "1.0"
|
||||
serde = { workspace = true }
|
||||
serde_derive = "1.0"
|
||||
thiserror = "1.0"
|
||||
zeroize = { version = "1.4", features = ["zeroize_derive"] }
|
||||
thiserror = { workspace = true }
|
||||
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
|
||||
nym-pemstore = { path = "../pemstore" }
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
[package]
|
||||
name = "nym-exit-policy"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
# feature-specific dependencies:
|
||||
|
||||
## client feature
|
||||
reqwest = { workspace = true, optional = true }
|
||||
|
||||
## openapi feature
|
||||
serde_json = { workspace = true, optional = true }
|
||||
utoipa = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
client = ["reqwest"]
|
||||
openapi = ["utoipa", "serde_json"]
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::policy::PolicyError;
|
||||
use crate::ExitPolicy;
|
||||
use reqwest::IntoUrl;
|
||||
|
||||
pub async fn get_exit_policy(url: impl IntoUrl) -> Result<ExitPolicy, PolicyError> {
|
||||
ExitPolicy::parse_from_torrc(reqwest::get(url).await?.text().await?)
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod policy;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
pub mod client;
|
||||
|
||||
pub use crate::policy::{
|
||||
AddressPolicy, AddressPolicyAction, AddressPolicyRule, AddressPortPattern, PolicyError,
|
||||
PortRange,
|
||||
};
|
||||
|
||||
pub(crate) const EXIT_POLICY_FIELD_NAME: &str = "ExitPolicy";
|
||||
const COMMENT_CHAR: char = '#';
|
||||
|
||||
pub type ExitPolicy = AddressPolicy;
|
||||
|
||||
pub fn parse_exit_policy<S: AsRef<str>>(exit_policy: S) -> Result<ExitPolicy, PolicyError> {
|
||||
let rules = exit_policy
|
||||
.as_ref()
|
||||
.lines()
|
||||
.map(|maybe_rule| {
|
||||
if let Some(comment_start) = maybe_rule.find(COMMENT_CHAR) {
|
||||
&maybe_rule[..comment_start]
|
||||
} else {
|
||||
maybe_rule
|
||||
}
|
||||
.trim()
|
||||
})
|
||||
.filter(|maybe_rule| !maybe_rule.is_empty())
|
||||
.map(parse_address_policy_rule)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(AddressPolicy { rules })
|
||||
}
|
||||
|
||||
pub fn format_exit_policy(policy: &ExitPolicy) -> String {
|
||||
policy
|
||||
.rules
|
||||
.iter()
|
||||
.map(|rule| format!("{EXIT_POLICY_FIELD_NAME} {rule}"))
|
||||
.fold(String::new(), |accumulator, rule| {
|
||||
accumulator + &rule + "\n"
|
||||
})
|
||||
.trim_end()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn parse_address_policy_rule(rule: &str) -> Result<AddressPolicyRule, PolicyError> {
|
||||
// each exit policy rule must begin with 'ExitPolicy' followed by the actual rule
|
||||
rule.strip_prefix(EXIT_POLICY_FIELD_NAME)
|
||||
.ok_or(PolicyError::NoExitPolicyPrefix {
|
||||
entry: rule.to_string(),
|
||||
})?
|
||||
.trim()
|
||||
.parse()
|
||||
}
|
||||
|
||||
// for each line, ignore everything after the comment
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::policy::AddressPolicyAction::{Accept, Accept6, Reject, Reject6};
|
||||
use crate::policy::{AddressPortPattern, IpPattern, PortRange};
|
||||
|
||||
#[test]
|
||||
fn parsing_policy() {
|
||||
let sample = r#"
|
||||
ExitPolicy reject 1.2.3.4/32:*#comment
|
||||
ExitPolicy reject 1.2.3.5:* #comment
|
||||
ExitPolicy reject 1.2.3.6/16:*
|
||||
ExitPolicy reject 1.2.3.6/16:123-456 # comment
|
||||
|
||||
ExitPolicy accept *:53 # DNS
|
||||
|
||||
# random comment
|
||||
|
||||
ExitPolicy accept6 *6:119
|
||||
ExitPolicy accept *4:120
|
||||
ExitPolicy reject6 [FC00::]/7:*
|
||||
|
||||
#ExitPolicy accept *:8080 #and another comment here
|
||||
|
||||
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
|
||||
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8328:1234
|
||||
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8328/64:1235
|
||||
|
||||
#another comment
|
||||
#ExitPolicy accept *:8080
|
||||
|
||||
ExitPolicy reject *:*
|
||||
"#;
|
||||
|
||||
let res = parse_exit_policy(sample).unwrap();
|
||||
|
||||
let mut expected = AddressPolicy::new();
|
||||
|
||||
// ExitPolicy reject 1.2.3.4/32:*#comment
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V4 {
|
||||
addr_prefix: "1.2.3.4".parse().unwrap(),
|
||||
mask: 32,
|
||||
},
|
||||
ports: PortRange::new_all(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy reject 1.2.3.5:* #comment
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V4 {
|
||||
addr_prefix: "1.2.3.5".parse().unwrap(),
|
||||
mask: 32,
|
||||
},
|
||||
ports: PortRange::new_all(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy reject 1.2.3.6/16:*
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V4 {
|
||||
addr_prefix: "1.2.3.6".parse().unwrap(),
|
||||
mask: 16,
|
||||
},
|
||||
ports: PortRange::new_all(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy reject 1.2.3.6/16:123-456
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V4 {
|
||||
addr_prefix: "1.2.3.6".parse().unwrap(),
|
||||
mask: 16,
|
||||
},
|
||||
ports: PortRange::new(123, 456).unwrap(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy accept *:53
|
||||
expected.push(
|
||||
Accept,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::Star,
|
||||
ports: PortRange::new_singleton(53),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy accept6 *6:119
|
||||
expected.push(
|
||||
Accept6,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V6Star,
|
||||
ports: PortRange::new_singleton(119),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy accept *4:120
|
||||
expected.push(
|
||||
Accept,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V4Star,
|
||||
ports: PortRange::new_singleton(120),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy reject6 [FC00::]/7:*
|
||||
expected.push(
|
||||
Reject6,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V6 {
|
||||
addr_prefix: "FC00::".parse().unwrap(),
|
||||
mask: 7,
|
||||
},
|
||||
ports: PortRange::new_all(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V6 {
|
||||
addr_prefix: "FE80:0000:0000:0000:0202:B3FF:FE1E:8329".parse().unwrap(),
|
||||
mask: 128,
|
||||
},
|
||||
ports: PortRange::new_all(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy FE80:0000:0000:0000:0202:B3FF:FE1E:8328:1234
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V6 {
|
||||
addr_prefix: "FE80:0000:0000:0000:0202:B3FF:FE1E:8328".parse().unwrap(),
|
||||
mask: 128,
|
||||
},
|
||||
ports: PortRange::new_singleton(1234),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy FE80:0000:0000:0000:0202:B3FF:FE1E:8328/64:1235
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V6 {
|
||||
addr_prefix: "FE80:0000:0000:0000:0202:B3FF:FE1E:8328".parse().unwrap(),
|
||||
mask: 64,
|
||||
},
|
||||
ports: PortRange::new_singleton(1235),
|
||||
},
|
||||
);
|
||||
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::Star,
|
||||
ports: PortRange::new_all(),
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(res, expected)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,726 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//! Implements address policies, based on a series of accept/reject
|
||||
//! rules.
|
||||
|
||||
use crate::policy::error::PolicyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use std::str::FromStr;
|
||||
use tracing::trace;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum AddressPolicyAction {
|
||||
/// A rule that accepts matching address:port combinations on IPv4 and IPv6.
|
||||
Accept,
|
||||
|
||||
/// A rule that rejects matching address:port combinations on IPv4 and IPv6.
|
||||
Reject,
|
||||
|
||||
/// A rule that accepts matching address:port combinations on IPv6 only.
|
||||
Accept6,
|
||||
|
||||
/// A rule that rejects matching address:port combinations on IPv6 only.
|
||||
Reject6,
|
||||
}
|
||||
|
||||
impl AddressPolicyAction {
|
||||
pub fn is_accept(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
AddressPolicyAction::Accept | AddressPolicyAction::Accept6
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for AddressPolicyAction {
|
||||
type Err = PolicyError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"accept" => Ok(AddressPolicyAction::Accept),
|
||||
"reject" => Ok(AddressPolicyAction::Reject),
|
||||
"accept6" => Ok(AddressPolicyAction::Accept6),
|
||||
"reject6" => Ok(AddressPolicyAction::Reject6),
|
||||
other => Err(PolicyError::InvalidPolicyAction {
|
||||
action: other.to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AddressPolicyAction {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AddressPolicyAction::Accept => write!(f, "accept"),
|
||||
AddressPolicyAction::Reject => write!(f, "reject"),
|
||||
AddressPolicyAction::Accept6 => write!(f, "accept6"),
|
||||
AddressPolicyAction::Reject6 => write!(f, "reject6"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A sequence of rules that are applied to an address:port until one
|
||||
/// matches.
|
||||
///
|
||||
/// Each rule is of the form "accept(6) PATTERN" or "reject(6) PATTERN",
|
||||
/// where every pattern describes a set of addresses and ports.
|
||||
/// Address sets are given as a prefix of 0-128 bits that the address
|
||||
/// must have; port sets are given as a low-bound and high-bound that
|
||||
/// the target port might lie between.
|
||||
///
|
||||
/// An example IPv4 policy might be:
|
||||
///
|
||||
/// ```text
|
||||
/// reject *:25
|
||||
/// reject 127.0.0.0/8:*
|
||||
/// reject 192.168.0.0/16:*
|
||||
/// accept *:80
|
||||
/// accept *:443
|
||||
/// accept *:9000-65535
|
||||
/// reject *:*
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
#[cfg_attr(feature = "openapi", aliases(ExitPolicy))]
|
||||
pub struct AddressPolicy {
|
||||
/// A list of rules to apply to find out whether an address is
|
||||
/// contained by this policy.
|
||||
///
|
||||
/// The rules apply in order; the first one to match determines
|
||||
/// whether the address is accepted or rejected.
|
||||
pub(crate) rules: Vec<AddressPolicyRule>,
|
||||
}
|
||||
|
||||
impl AddressPolicy {
|
||||
/// Create a new AddressPolicy that matches nothing.
|
||||
pub const fn new() -> Self {
|
||||
AddressPolicy { rules: Vec::new() }
|
||||
}
|
||||
|
||||
/// Create a new AddressPolicy that matches everything.
|
||||
pub fn new_open() -> Self {
|
||||
AddressPolicy {
|
||||
rules: vec![AddressPolicyRule::new(
|
||||
AddressPolicyAction::Accept,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::Star,
|
||||
ports: PortRange::new_all(),
|
||||
},
|
||||
)],
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether this AddressPolicy matches all patterns.
|
||||
pub fn is_open(&self) -> bool {
|
||||
if self.rules.len() != 1 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let rule = &self.rules[0];
|
||||
|
||||
rule.action == AddressPolicyAction::Accept
|
||||
&& rule.pattern.ip_pattern == IpPattern::Star
|
||||
&& rule.pattern.ports.is_all()
|
||||
}
|
||||
|
||||
/// Attempts to parse the AddressPolicy out of raw torrc representation.
|
||||
pub fn parse_from_torrc<S: AsRef<str>>(raw: S) -> Result<Self, PolicyError> {
|
||||
crate::parse_exit_policy(raw)
|
||||
}
|
||||
|
||||
/// Formats the AddressPolicy with torrc representation
|
||||
pub fn format_as_torrc(&self) -> String {
|
||||
crate::format_exit_policy(self)
|
||||
}
|
||||
|
||||
/// Apply this policy to an address:port combination
|
||||
///
|
||||
/// We do this by applying each rule in sequence, until one
|
||||
/// matches.
|
||||
///
|
||||
/// Returns None if no rule matches.
|
||||
pub fn allows(&self, addr: &IpAddr, port: u16) -> Option<bool> {
|
||||
self.rules
|
||||
.iter()
|
||||
.find(|rule| rule.pattern.matches(addr, port))
|
||||
.map(|rule| {
|
||||
trace!("'{addr}:{port}' is covered by rule '{rule}'");
|
||||
rule.action.is_accept()
|
||||
})
|
||||
}
|
||||
|
||||
/// As allows, but accept a SocketAddr.
|
||||
pub fn allows_sockaddr(&self, addr: &SocketAddr) -> Option<bool> {
|
||||
self.allows(&addr.ip(), addr.port())
|
||||
}
|
||||
|
||||
/// Add a new rule to this policy.
|
||||
///
|
||||
/// The newly added rule is applied _after_ all previous rules.
|
||||
/// It matches all addresses and ports covered by AddressPortPattern.
|
||||
///
|
||||
/// If accept is true, the rule is to accept addresses that match;
|
||||
/// if accept is false, the rule rejects such addresses.
|
||||
pub fn push(&mut self, action: AddressPolicyAction, pattern: AddressPortPattern) {
|
||||
self.rules.push(AddressPolicyRule { action, pattern })
|
||||
}
|
||||
|
||||
/// As push, but accepts a AddressPolicyRule.
|
||||
pub fn push_rule(&mut self, rule: AddressPolicyRule) {
|
||||
self.rules.push(rule)
|
||||
}
|
||||
}
|
||||
|
||||
/// A single rule in an address policy.
|
||||
///
|
||||
/// Contains a pattern, what to do with things that match it.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
pub struct AddressPolicyRule {
|
||||
/// What do we do with items that match the pattern?
|
||||
action: AddressPolicyAction,
|
||||
|
||||
/// What pattern are we trying to match?
|
||||
pattern: AddressPortPattern,
|
||||
}
|
||||
|
||||
impl FromStr for AddressPolicyRule {
|
||||
type Err = PolicyError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
// split on the first space, i.e. separation between the action and the pattern
|
||||
let Some((action, pattern)) = s.split_once(' ') else {
|
||||
return Err(PolicyError::MalformedAddressPolicy { raw: s.to_string() });
|
||||
};
|
||||
|
||||
Ok(AddressPolicyRule {
|
||||
action: action.parse()?,
|
||||
pattern: pattern.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressPolicyRule {
|
||||
pub fn new(action: AddressPolicyAction, pattern: AddressPortPattern) -> Self {
|
||||
AddressPolicyRule { action, pattern }
|
||||
}
|
||||
}
|
||||
impl Display for AddressPolicyRule {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} {}", self.action, self.pattern)
|
||||
}
|
||||
}
|
||||
|
||||
/// A pattern that may or may not match an address and port.
|
||||
///
|
||||
/// Each AddrPortPattern has an IP pattern, which matches a set of
|
||||
/// addresses by prefix, and a port pattern, which matches a range of
|
||||
/// ports.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use nym_exit_policy::policy::AddressPortPattern;
|
||||
/// use std::net::{IpAddr,Ipv4Addr};
|
||||
/// let localhost = IpAddr::V4(Ipv4Addr::new(127,3,4,5));
|
||||
/// let not_localhost = IpAddr::V4(Ipv4Addr::new(192,0,2,16));
|
||||
/// let pat: AddressPortPattern = "127.0.0.0/8:*".parse().unwrap();
|
||||
///
|
||||
/// assert!(pat.matches(&localhost, 22));
|
||||
/// assert!(!pat.matches(¬_localhost, 22));
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
pub struct AddressPortPattern {
|
||||
/// A pattern to match somewhere between zero and all IP addresses.
|
||||
#[serde(with = "stringified_ip_pattern")]
|
||||
#[cfg_attr(feature = "openapi", schema(example = "1.2.3.6/16", value_type = String))]
|
||||
pub(crate) ip_pattern: IpPattern,
|
||||
|
||||
/// A pattern to match a range of ports.
|
||||
pub(crate) ports: PortRange,
|
||||
}
|
||||
|
||||
mod stringified_ip_pattern {
|
||||
use super::IpPattern;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub fn serialize<S: Serializer>(pattern: &IpPattern, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&pattern.to_string())
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<IpPattern, D::Error> {
|
||||
let s = <String>::deserialize(deserializer)?;
|
||||
IpPattern::from_str(&s).map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressPortPattern {
|
||||
/// Return true iff this pattern matches a given address and port.
|
||||
pub fn matches(&self, addr: &IpAddr, port: u16) -> bool {
|
||||
self.ip_pattern.matches(addr) && self.ports.contains(port)
|
||||
}
|
||||
|
||||
/// As matches, but accept a SocketAddr.
|
||||
pub fn matches_sockaddr(&self, addr: &SocketAddr) -> bool {
|
||||
self.matches(&addr.ip(), addr.port())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AddressPortPattern {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}:{}", self.ip_pattern, self.ports)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for AddressPortPattern {
|
||||
type Err = PolicyError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, PolicyError> {
|
||||
let last_colon = s
|
||||
.rfind(':')
|
||||
.ok_or(PolicyError::MalformedAddressPortPattern { raw: s.to_string() })?;
|
||||
|
||||
// doesn't have enough chars to cover the port, even if its a wildcard
|
||||
if s.len() < last_colon + 2 {
|
||||
return Err(PolicyError::MalformedAddressPortPattern { raw: s.to_string() });
|
||||
}
|
||||
|
||||
let ip_pattern = s[..last_colon].parse()?;
|
||||
let ports = s[last_colon + 1..].parse()?;
|
||||
|
||||
Ok(AddressPortPattern { ip_pattern, ports })
|
||||
}
|
||||
}
|
||||
|
||||
/// A pattern that matches one or more IP addresses.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum IpPattern {
|
||||
/// Match all addresses.
|
||||
Star,
|
||||
|
||||
/// Match all IPv4 addresses.
|
||||
V4Star,
|
||||
|
||||
/// Match all IPv6 addresses.
|
||||
V6Star,
|
||||
|
||||
/// Match all IPv4 addresses beginning with a given prefix and mask.
|
||||
V4 { addr_prefix: Ipv4Addr, mask: u8 },
|
||||
|
||||
/// Match all IPv6 addresses beginning with a given prefix and mask.
|
||||
V6 { addr_prefix: Ipv6Addr, mask: u8 },
|
||||
}
|
||||
|
||||
impl IpPattern {
|
||||
/// Construct an IpPattern that matches the first `mask` bits of `addr`.
|
||||
fn from_addr_and_mask(address: IpAddr, target_mask: u8) -> Result<Self, PolicyError> {
|
||||
match (address, target_mask) {
|
||||
(IpAddr::V4(_), 0) => Ok(IpPattern::V4Star),
|
||||
(IpAddr::V6(_), 0) => Ok(IpPattern::V6Star),
|
||||
(IpAddr::V4(addr_prefix), mask) if mask <= 32 => {
|
||||
Ok(IpPattern::V4 { addr_prefix, mask })
|
||||
}
|
||||
(IpAddr::V6(addr_prefix), mask) if mask <= 128 => {
|
||||
Ok(IpPattern::V6 { addr_prefix, mask })
|
||||
}
|
||||
(addr, mask) => {
|
||||
if addr.is_ipv4() {
|
||||
Err(PolicyError::InvalidIpV4Mask { mask })
|
||||
} else {
|
||||
Err(PolicyError::InvalidIpV6Mask { mask })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true iff `addr` is matched by this pattern.
|
||||
fn matches(&self, addr: &IpAddr) -> bool {
|
||||
match (self, addr) {
|
||||
(IpPattern::Star, _) => true,
|
||||
(IpPattern::V4Star, IpAddr::V4(_)) => true,
|
||||
(IpPattern::V6Star, IpAddr::V6(_)) => true,
|
||||
(IpPattern::V4 { addr_prefix, mask }, IpAddr::V4(addr)) => {
|
||||
let p1 = u32::from_be_bytes(addr_prefix.octets());
|
||||
let p2 = u32::from_be_bytes(addr.octets());
|
||||
|
||||
let shift = 32 - mask;
|
||||
(p1 >> shift) == (p2 >> shift)
|
||||
}
|
||||
(IpPattern::V6 { addr_prefix, mask }, IpAddr::V6(addr)) => {
|
||||
let p1 = u128::from_be_bytes(addr_prefix.octets());
|
||||
let p2 = u128::from_be_bytes(addr.octets());
|
||||
|
||||
let shift = 128 - mask;
|
||||
(p1 >> shift) == (p2 >> shift)
|
||||
}
|
||||
(_, _) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for IpPattern {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
IpPattern::Star => write!(f, "*"),
|
||||
IpPattern::V4Star => write!(f, "*4"),
|
||||
IpPattern::V6Star => write!(f, "*6"),
|
||||
IpPattern::V4 { addr_prefix, mask } => {
|
||||
write!(f, "{addr_prefix}/{mask}")
|
||||
}
|
||||
IpPattern::V6 { addr_prefix, mask } => {
|
||||
write!(f, "{addr_prefix}/{mask}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper: try to parse a plain ipv4 address, or an IPv6 address
|
||||
/// wrapped in brackets.
|
||||
fn parse_addr(s: &str) -> Result<IpAddr, PolicyError> {
|
||||
if s.starts_with('[') && s.ends_with(']') {
|
||||
Ipv6Addr::from_str(&s[1..s.len() - 1]).map(IpAddr::V6)
|
||||
} else {
|
||||
IpAddr::from_str(s)
|
||||
}
|
||||
.map_err(|source| PolicyError::MalformedIpAddress {
|
||||
addr: s.to_string(),
|
||||
source,
|
||||
})
|
||||
}
|
||||
|
||||
/// Helper: try to parse a port making sure it's non-zero
|
||||
fn parse_port(s: &str) -> Result<u16, PolicyError> {
|
||||
let port = s
|
||||
.parse::<u16>()
|
||||
.map_err(|_| PolicyError::InvalidPort { raw: s.to_string() })?;
|
||||
|
||||
if port == 0 {
|
||||
Err(PolicyError::InvalidPort {
|
||||
raw: port.to_string(),
|
||||
})
|
||||
} else {
|
||||
Ok(port)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for IpPattern {
|
||||
type Err = PolicyError;
|
||||
fn from_str(s: &str) -> Result<Self, PolicyError> {
|
||||
let (ip_s, mask_s) = match s.find('/') {
|
||||
Some(slash_idx) => (&s[..slash_idx], Some(&s[slash_idx + 1..])),
|
||||
None => (s, None),
|
||||
};
|
||||
|
||||
match (ip_s, mask_s) {
|
||||
// '*' patterns
|
||||
("*", Some(m)) => Err(PolicyError::MaskWithStar {
|
||||
mask: m.to_string(),
|
||||
}),
|
||||
("*", None) => Ok(IpPattern::Star),
|
||||
|
||||
// '*4' patterns
|
||||
("*4", Some(m)) => Err(PolicyError::MaskWithV4Star {
|
||||
mask: m.to_string(),
|
||||
}),
|
||||
("*4", None) => Ok(IpPattern::V4Star),
|
||||
|
||||
// '*6' patterns
|
||||
("*6", Some(m)) => Err(PolicyError::MaskWithV6Star {
|
||||
mask: m.to_string(),
|
||||
}),
|
||||
("*6", None) => Ok(IpPattern::V6Star),
|
||||
|
||||
(s, Some(m)) => {
|
||||
let a: IpAddr = parse_addr(s)?;
|
||||
let m: u8 = m.parse().map_err(|_| PolicyError::InvalidMask {
|
||||
mask: m.to_string(),
|
||||
})?;
|
||||
IpPattern::from_addr_and_mask(a, m)
|
||||
}
|
||||
(s, None) => {
|
||||
let a: IpAddr = parse_addr(s)?;
|
||||
let m = if a.is_ipv4() { 32 } else { 128 };
|
||||
IpPattern::from_addr_and_mask(a, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A PortRange is a set of consecutively numbered TCP or UDP ports.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use nym_exit_policy::policy::PortRange;
|
||||
///
|
||||
/// let r: PortRange = "22-8000".parse().unwrap();
|
||||
/// assert!(r.contains(128));
|
||||
/// assert!(r.contains(22));
|
||||
/// assert!(r.contains(8000));
|
||||
///
|
||||
/// assert!(! r.contains(21));
|
||||
/// assert!(! r.contains(8001));
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
pub struct PortRange {
|
||||
/// The first port in this range.
|
||||
#[cfg_attr(feature = "openapi", schema(example = 80))]
|
||||
pub start: u16,
|
||||
|
||||
/// The last port in this range.
|
||||
#[cfg_attr(feature = "openapi", schema(example = 81))]
|
||||
pub end: u16,
|
||||
}
|
||||
|
||||
impl PortRange {
|
||||
/// Create a new port range spanning from start to end, asserting that
|
||||
/// the correct invariants hold.
|
||||
fn new_unchecked(start: u16, end: u16) -> Self {
|
||||
assert_ne!(start, 0);
|
||||
assert!(start <= end);
|
||||
|
||||
PortRange { start, end }
|
||||
}
|
||||
|
||||
/// Create a port range containing all ports.
|
||||
pub fn new_all() -> Self {
|
||||
PortRange::new_unchecked(1, 65535)
|
||||
}
|
||||
|
||||
/// Create a new PortRange.
|
||||
///
|
||||
/// The Portrange contains all ports between `start` and `end` inclusive.
|
||||
///
|
||||
/// Returns None if lo is greater than end, or if either is zero.
|
||||
pub const fn new(start: u16, end: u16) -> Option<Self> {
|
||||
if start != 0 && start <= end {
|
||||
Some(PortRange { start, end })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new singleton PortRange.
|
||||
pub const fn new_singleton(value: u16) -> Self {
|
||||
PortRange {
|
||||
start: value,
|
||||
end: value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if a port is in this range.
|
||||
pub fn contains(&self, port: u16) -> bool {
|
||||
self.start <= port && port <= self.end
|
||||
}
|
||||
|
||||
/// Return true if this range contains all ports.
|
||||
pub fn is_all(&self) -> bool {
|
||||
self.start == 1 && self.end == 65535
|
||||
}
|
||||
}
|
||||
|
||||
/// A PortRange is displayed as a number if it contains a single port,
|
||||
/// and as a start point and end point separated by a dash if it contains
|
||||
/// more than one port.
|
||||
impl Display for PortRange {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.is_all() {
|
||||
write!(f, "*")
|
||||
} else if self.start == self.end {
|
||||
write!(f, "{}", self.start)
|
||||
} else {
|
||||
write!(f, "{}-{}", self.start, self.end)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PortRange {
|
||||
type Err = PolicyError;
|
||||
fn from_str(s: &str) -> Result<Self, PolicyError> {
|
||||
// check is if it's a star range
|
||||
if s == "*" {
|
||||
return Ok(PortRange::new_all());
|
||||
}
|
||||
|
||||
if let Some(pos) = s.find('-') {
|
||||
// This is a range; parse each part
|
||||
let start = parse_port(&s[..pos])?;
|
||||
let end = parse_port(&s[pos + 1..])?;
|
||||
PortRange::new(start, end).ok_or(PolicyError::InvalidRange { start, end })
|
||||
} else {
|
||||
// There was no hyphen, so try to parse this range as a singleton.
|
||||
let value = parse_port(s)?;
|
||||
Ok(PortRange::new_singleton(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_bad_rules() {
|
||||
fn check(s: &str) {
|
||||
assert!(s.parse::<AddressPortPattern>().is_err());
|
||||
}
|
||||
|
||||
check("marzipan:80");
|
||||
check("1.2.3.4:90-80");
|
||||
check("1.2.3.4/100:8888");
|
||||
check("[1.2.3.4]/16:80");
|
||||
check("[::1]/130:8888");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rule_matches() {
|
||||
fn check(address: &str, yes: &[&str], no: &[&str]) {
|
||||
use std::net::SocketAddr;
|
||||
let policy = address.parse::<AddressPortPattern>().unwrap();
|
||||
for s in yes {
|
||||
let sa = s.parse::<SocketAddr>().unwrap();
|
||||
assert!(policy.matches_sockaddr(&sa));
|
||||
}
|
||||
for s in no {
|
||||
let sa = s.parse::<SocketAddr>().unwrap();
|
||||
assert!(!policy.matches_sockaddr(&sa));
|
||||
}
|
||||
}
|
||||
|
||||
check(
|
||||
"1.2.3.4/16:80",
|
||||
&["1.2.3.4:80", "1.2.44.55:80"],
|
||||
&["9.9.9.9:80", "1.3.3.4:80", "1.2.3.4:81"],
|
||||
);
|
||||
check(
|
||||
"*:443-8000",
|
||||
&["1.2.3.4:443", "[::1]:500"],
|
||||
&["9.0.0.0:80", "[::1]:80"],
|
||||
);
|
||||
check(
|
||||
"[face::]/8:80",
|
||||
&["[fab0::7]:80"],
|
||||
&["[dd00::]:80", "[face::7]:443"],
|
||||
);
|
||||
|
||||
check("0.0.0.0/0:*", &["127.0.0.1:80"], &["[f00b::]:80"]);
|
||||
check("[::]/0:*", &["[f00b::]:80"], &["127.0.0.1:80"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_policy_matches() -> Result<(), PolicyError> {
|
||||
let mut policy = AddressPolicy::default();
|
||||
policy.push(AddressPolicyAction::Accept, "*:443".parse()?);
|
||||
policy.push(AddressPolicyAction::Accept, "[::1]:80".parse()?);
|
||||
policy.push(AddressPolicyAction::Reject, "*:80".parse()?);
|
||||
|
||||
let policy = policy; // drop mut
|
||||
assert!(policy
|
||||
.allows_sockaddr(&"[::6]:443".parse().unwrap())
|
||||
.unwrap());
|
||||
assert!(policy
|
||||
.allows_sockaddr(&"127.0.0.1:443".parse().unwrap())
|
||||
.unwrap());
|
||||
assert!(policy
|
||||
.allows_sockaddr(&"[::1]:80".parse().unwrap())
|
||||
.unwrap());
|
||||
assert!(!policy
|
||||
.allows_sockaddr(&"[::2]:80".parse().unwrap())
|
||||
.unwrap());
|
||||
assert!(!policy
|
||||
.allows_sockaddr(&"127.0.0.1:80".parse().unwrap())
|
||||
.unwrap());
|
||||
assert!(policy
|
||||
.allows_sockaddr(&"127.0.0.1:66".parse().unwrap())
|
||||
.is_none());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_portrange() {
|
||||
assert_eq!(
|
||||
"1-100".parse::<PortRange>().unwrap(),
|
||||
PortRange::new(1, 100).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"01-100".parse::<PortRange>().unwrap(),
|
||||
PortRange::new(1, 100).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"1-65535".parse::<PortRange>().unwrap(),
|
||||
PortRange::new_all()
|
||||
);
|
||||
assert_eq!(
|
||||
"10-30".parse::<PortRange>().unwrap(),
|
||||
PortRange::new(10, 30).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"9001".parse::<PortRange>().unwrap(),
|
||||
PortRange::new(9001, 9001).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
"9001-9001".parse::<PortRange>().unwrap(),
|
||||
PortRange::new(9001, 9001).unwrap()
|
||||
);
|
||||
assert_eq!("*".parse::<PortRange>().unwrap(), PortRange::new_all());
|
||||
|
||||
assert!("hello".parse::<PortRange>().is_err());
|
||||
assert!("0".parse::<PortRange>().is_err());
|
||||
assert!("65536".parse::<PortRange>().is_err());
|
||||
assert!("65537".parse::<PortRange>().is_err());
|
||||
assert!("1-2-3".parse::<PortRange>().is_err());
|
||||
assert!("10-5".parse::<PortRange>().is_err());
|
||||
assert!("1-".parse::<PortRange>().is_err());
|
||||
assert!("-2".parse::<PortRange>().is_err());
|
||||
assert!("-".parse::<PortRange>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_portrange() {
|
||||
assert!(PortRange::new_all().is_all());
|
||||
assert!(!PortRange::new(2, 65535).unwrap().is_all());
|
||||
|
||||
assert!(PortRange::new_all().contains(1));
|
||||
assert!(PortRange::new_all().contains(65535));
|
||||
assert!(PortRange::new_all().contains(7777));
|
||||
|
||||
assert!(PortRange::new(20, 30).unwrap().contains(20));
|
||||
assert!(PortRange::new(20, 30).unwrap().contains(25));
|
||||
assert!(PortRange::new(20, 30).unwrap().contains(30));
|
||||
assert!(!PortRange::new(20, 30).unwrap().contains(19));
|
||||
assert!(!PortRange::new(20, 30).unwrap().contains(31));
|
||||
}
|
||||
|
||||
// this test exists due to manually implemented 'stringified_ip_pattern' on 'AddressPortPattern'
|
||||
#[test]
|
||||
fn policy_serde_json_roundtrip() {
|
||||
let policy = AddressPolicy::parse_from_torrc(
|
||||
r#"
|
||||
ExitPolicy reject 1.2.3.4/32:*
|
||||
ExitPolicy reject 1.2.3.5:*
|
||||
ExitPolicy reject 1.2.3.6/16:*
|
||||
ExitPolicy reject 1.2.3.6/16:123-456
|
||||
ExitPolicy accept *:53
|
||||
ExitPolicy accept6 *6:119
|
||||
ExitPolicy accept *4:120
|
||||
ExitPolicy reject6 [FC00::]/7:*
|
||||
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
|
||||
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8328:1234
|
||||
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8328/64:1235
|
||||
ExitPolicy reject *:*"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let json = serde_json::to_string(&policy).unwrap();
|
||||
let recovered: AddressPolicy = serde_json::from_str(&json).unwrap();
|
||||
|
||||
assert_eq!(recovered, policy);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::EXIT_POLICY_FIELD_NAME;
|
||||
use std::net::AddrParseError;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Error from an unparsable or invalid policy.
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum PolicyError {
|
||||
#[cfg(feature = "client")]
|
||||
#[error("failed to fetch the remote policy: {source}")]
|
||||
ClientError {
|
||||
#[from]
|
||||
source: reqwest::Error,
|
||||
},
|
||||
|
||||
#[error("/{mask} is not a valid mask for an IpV4 address")]
|
||||
InvalidIpV4Mask { mask: u8 },
|
||||
|
||||
#[error("/{mask} is not a valid mask for an IpV6 address")]
|
||||
InvalidIpV6Mask { mask: u8 },
|
||||
|
||||
#[error("'{action}' is not a valid policy action")]
|
||||
InvalidPolicyAction { action: String },
|
||||
|
||||
#[error("'{addr}' is not a valid Ip address: {source}")]
|
||||
MalformedIpAddress {
|
||||
addr: String,
|
||||
#[source]
|
||||
source: AddrParseError,
|
||||
},
|
||||
|
||||
/// Attempted to use a bitmask with the address "*".
|
||||
#[error("attempted to use a bitmask ('/{mask}') with the address '*'")]
|
||||
MaskWithStar { mask: String },
|
||||
|
||||
/// Attempted to use a bitmask with the address "*4".
|
||||
#[error("attempted to use a bitmask ('/{mask}') with the address '*4'")]
|
||||
MaskWithV4Star { mask: String },
|
||||
|
||||
/// Attempted to use a bitmask with the address "*6".
|
||||
#[error("attempted to use a bitmask ('/{mask}') with the address '*6'")]
|
||||
MaskWithV6Star { mask: String },
|
||||
|
||||
#[error("'/{mask}' is not a valid mask")]
|
||||
InvalidMask { mask: String },
|
||||
|
||||
/// A port was not a number in the range 1..65535
|
||||
#[error(
|
||||
"the provided port '{raw}' was either malformed or was not in the valid 1..65535 range"
|
||||
)]
|
||||
InvalidPort { raw: String },
|
||||
|
||||
/// A port range had its starting-point higher than its ending point.
|
||||
#[error("the provided port range ({start}-{end}) was invalid. either the start was 0 or it was greater than the end.")]
|
||||
InvalidRange { start: u16, end: u16 },
|
||||
|
||||
#[error("could not parse '{raw}' into a valid policy address:port pattern")]
|
||||
MalformedAddressPortPattern { raw: String },
|
||||
|
||||
#[error("could not parse '{raw}' into a valid address policy")]
|
||||
MalformedAddressPolicy { raw: String },
|
||||
|
||||
#[error(
|
||||
"the provided exit policy entry does not start with the expected '{}' prefix: '{entry}'",
|
||||
EXIT_POLICY_FIELD_NAME
|
||||
)]
|
||||
NoExitPolicyPrefix { entry: String },
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// adapted from: https://github.com/dgoulet-tor/arti/tree/781dc4bd64f515f0c13ae9907c473c2bad8fbf71
|
||||
// and https://github.com/torproject/tor/blob/3cb6a690be60fcdab60130402ff88dcfc0657596/contrib/or-tools/exitlist
|
||||
// + https://github.com/torproject/tor/blob/3cb6a690be60fcdab60130402ff88dcfc0657596/src/feature/dirparse/policy_parse.c
|
||||
|
||||
mod address_policy;
|
||||
mod error;
|
||||
|
||||
pub use address_policy::{
|
||||
AddressPolicy, AddressPolicyAction, AddressPolicyRule, AddressPortPattern, IpPattern, PortRange,
|
||||
};
|
||||
pub use error::PolicyError;
|
||||
@@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "http-api-client"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json"] }
|
||||
url = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
# for request timeout until https://github.com/seanmonstar/reqwest/issues/1135 is fixed
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer]
|
||||
workspace = true
|
||||
features = ["tokio"]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user