Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 07b566071b | |||
| 5f231e3eac | |||
| 9d3cad2771 | |||
| ae0f9b1b7b | |||
| 8a1e225365 | |||
| 2a6d0f3113 | |||
| d81e1fe44d | |||
| ef62ec6560 | |||
| 6649f859e6 | |||
| 617113555b | |||
| b9e8ba3215 | |||
| 2693ccafd1 | |||
| 8d78d23060 | |||
| 9543247def | |||
| 618c54ede3 | |||
| c91b63a6f8 | |||
| 4e63f2ce60 | |||
| bc80511863 | |||
| bc08b26ed4 | |||
| 81c5cafc60 | |||
| a9f25c2492 | |||
| 0ab5320656 | |||
| 28e7ce11e3 | |||
| 03364b848b | |||
| 8926d622b3 | |||
| a58d3dc6b2 | |||
| ece189773d | |||
| de2c3851ee | |||
| 6869de5640 | |||
| 580b677489 | |||
| 2093789c5c | |||
| d09864b99f | |||
| f6e8278592 | |||
| b085a8a636 | |||
| e6ce531aeb | |||
| 1a860eb3f5 | |||
| 09cfd9ff05 | |||
| 2c88ca137a | |||
| ca3bfc859a |
@@ -1,112 +0,0 @@
|
||||
name: Build and upload binaries to CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'clients/**'
|
||||
- 'common/**'
|
||||
- 'contracts/**'
|
||||
- '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/**'
|
||||
- 'common/**'
|
||||
- 'contracts/**'
|
||||
- 'explorer-api/**'
|
||||
- 'gateway/**'
|
||||
- 'integrations/**'
|
||||
- 'mixnode/**'
|
||||
- 'sdk/rust/nym-sdk/**'
|
||||
- 'service-providers/**'
|
||||
- 'nym-api/**'
|
||||
- 'nym-outfox/**'
|
||||
- 'tools/nym-cli/**'
|
||||
- 'tools/ts-rs-cli/**'
|
||||
|
||||
env:
|
||||
NETWORK: mainnet
|
||||
|
||||
jobs:
|
||||
publish-nym:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-20.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare build output directory
|
||||
shell: bash
|
||||
env:
|
||||
OUTPUT_DIR: ci-builds/${{ github.ref_name }}
|
||||
run: |
|
||||
rm -rf ci-builds || true
|
||||
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
|
||||
continue-on-error: true
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --release
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Build release contracts
|
||||
run: make wasm
|
||||
|
||||
- name: Prepare build output
|
||||
shell: bash
|
||||
env:
|
||||
OUTPUT_DIR: ci-builds/${{ github.ref_name }}
|
||||
run: |
|
||||
cp target/release/nym-client $OUTPUT_DIR
|
||||
cp target/release/nym-gateway $OUTPUT_DIR
|
||||
cp target/release/nym-mixnode $OUTPUT_DIR
|
||||
cp target/release/nym-socks5-client $OUTPUT_DIR
|
||||
cp target/release/nym-api $OUTPUT_DIR
|
||||
cp target/release/nym-network-requester $OUTPUT_DIR
|
||||
cp target/release/nym-network-statistics $OUTPUT_DIR
|
||||
cp target/release/nym-cli $OUTPUT_DIR
|
||||
|
||||
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
|
||||
|
||||
- name: Deploy branch to CI www
|
||||
continue-on-error: true
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
|
||||
ARGS: "-avzr"
|
||||
SOURCE: "ci-builds/"
|
||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
@@ -1,134 +0,0 @@
|
||||
name: NC Android APK Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- "release/nc-android-v[0-9].[0-9].[0-9]*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build APK
|
||||
runs-on: custom-runner-linux
|
||||
env:
|
||||
ANDROID_HOME: ${{ github.workspace }}/android-sdk
|
||||
NDK_VERSION: 25.1.8937393
|
||||
NDK_HOME: ${{ github.workspace }}/android-sdk/ndk/25.1.8937393
|
||||
SDK_PLATFORM_VERSION: android-33
|
||||
SDK_BUILDTOOLS_VERSION: 33.0.1
|
||||
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
# https://next--tauri.netlify.app/next/guides/getting-started/prerequisites/linux/#1-system-dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install \
|
||||
libwebkit2gtk-4.0-dev \
|
||||
build-essential \
|
||||
unzip \
|
||||
curl \
|
||||
wget \
|
||||
libssl-dev \
|
||||
libgtk-3-dev \
|
||||
squashfs-tools \
|
||||
libayatana-appindicator3-dev \
|
||||
librsvg2-dev
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "17"
|
||||
|
||||
- name: Install Android SDK manager
|
||||
# https://developer.android.com/studio/command-line/sdkmanager
|
||||
run: |
|
||||
curl -sS https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -o cmdline-tools.zip
|
||||
unzip cmdline-tools.zip
|
||||
mkdir -p $ANDROID_HOME/cmdline-tools/latest
|
||||
mv cmdline-tools/* $ANDROID_HOME/cmdline-tools/latest
|
||||
rm -rf cmdline-tools
|
||||
|
||||
- name: Install Android S/NDK
|
||||
run: |
|
||||
echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses
|
||||
echo y | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager \
|
||||
"platforms;$SDK_PLATFORM_VERSION" \
|
||||
"platform-tools" \
|
||||
"ndk;$NDK_VERSION" \
|
||||
"build-tools;$SDK_BUILDTOOLS_VERSION"
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Install tauri cli
|
||||
run: cargo install tauri-cli --version "^2.0.0-alpha.2"
|
||||
|
||||
- name: Install rust android targets
|
||||
run: |
|
||||
rustup target add aarch64-linux-android \
|
||||
armv7-linux-androideabi \
|
||||
i686-linux-android \
|
||||
x86_64-linux-android
|
||||
|
||||
- name: Setup Nodejs
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install yarn
|
||||
run: |
|
||||
npm i -g yarn
|
||||
yarn --version
|
||||
|
||||
- name: Build frontend code
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
yarn build
|
||||
yarn workspace @nym/nym-connect-android webpack:prod
|
||||
|
||||
- name: Build APK
|
||||
working-directory: nym-connect-android
|
||||
env:
|
||||
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
|
||||
WRY_ANDROID_PACKAGE: net.nymtech.nym_connect_android
|
||||
WRY_ANDROID_LIBRARY: nym_connect_android
|
||||
# TODO build with release profile (--release), it will requires
|
||||
# to sign the APK. For now build with debug profile to avoid that
|
||||
run: cargo tauri android build --debug --apk --split-per-abi -t aarch64
|
||||
|
||||
# TODO add the version number to APK name
|
||||
- name: Rename APK artifact
|
||||
run: |
|
||||
mkdir apk/
|
||||
mv nym-connect-android/src-tauri/gen/android/nym_connect_android/app/build/outputs/apk/arm64/debug/app-arm64-debug.apk \
|
||||
apk/nym-connect-arm64-debug.apk
|
||||
mv nym-connect-android/src-tauri/gen/android/nym_connect_android/app/build/outputs/apk/x86_64/debug/app-x86_64-debug.apk \
|
||||
apk/nym-connect-x86_64-debug.apk
|
||||
|
||||
- name: Upload APK artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nc-apk-debug
|
||||
path: |
|
||||
apk/nym-connect-arm64-debug.apk
|
||||
apk/nym-connect-x86_64-debug.apk
|
||||
|
||||
# publish:
|
||||
# name: Publish APK
|
||||
# needs: build
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v3
|
||||
# - name: Download binary artifact
|
||||
# uses: actions/download-artifact@v3
|
||||
# with:
|
||||
# name: nc-apk-debug
|
||||
# path: apk
|
||||
# # TODO add a step to upload the APK somewhere
|
||||
# - name: Publish
|
||||
# uses: ???
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
# creates the matrix strategy from nightly_build_matrix_includes.json
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- id: set-matrix
|
||||
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||
with:
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
- name: install npm
|
||||
uses: actions/setup-node@v3
|
||||
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||
|
||||
@@ -54,12 +54,6 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -72,18 +66,6 @@ jobs:
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Build all examples
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -92,7 +74,7 @@ jobs:
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
@@ -101,7 +83,13 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace -- --ignored
|
||||
args: --workspace --all-features -- --ignored
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -114,7 +102,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --workspace
|
||||
args: --all-features
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
|
||||
@@ -54,12 +54,6 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -72,18 +66,6 @@ jobs:
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Build all examples
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --workspace --examples
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -92,7 +74,7 @@ jobs:
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' || matrix.os == 'ubuntu-20.04' }}
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
@@ -101,7 +83,13 @@ jobs:
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace -- --ignored
|
||||
args: --workspace --all-features -- --ignored
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -114,7 +102,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --workspace
|
||||
args: --all-features
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
|
||||
+1
-1
@@ -42,4 +42,4 @@ envs/qwerty.env
|
||||
Cargo.lock
|
||||
nym-connect/Cargo.lock
|
||||
.parcel-cache
|
||||
**/.DS_Store
|
||||
.DS_Store
|
||||
|
||||
+2
-18
@@ -6,27 +6,11 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
### Added
|
||||
|
||||
- dkg resharing mode ([#2936])
|
||||
|
||||
[#2936]: https://github.com/nymtech/nym/pull/2936
|
||||
|
||||
|
||||
# [v1.1.9] (2023-02-07)
|
||||
|
||||
### Added
|
||||
|
||||
- Separate `nym-api` endpoints with values of "total-supply" and "circulating-supply" in `nym` ([#2964])
|
||||
- Add `host` option to client init ([#2912])
|
||||
- Remove Coconut feature flag ([#2793])
|
||||
- Don't drop in mixnet connection handler ([#2963])
|
||||
|
||||
### Changed
|
||||
- remove coconut feature and unify builds ([#2890])
|
||||
- native-client: is now capable of listening for requests on sockets different than `127.0.0.1` ([#2939]). This can be specified via `--host` flag during `init` or `run`. Alternatively a custom `host` can be set in `config.toml` file under `socket` section.
|
||||
- mixnode, gateway: fix unexpected shutdown on corrupted connection ([#2963])
|
||||
|
||||
[#2890]: https://github.com/nymtech/nym/pull/2890
|
||||
[#2939]: https://github.com/nymtech/nym/pull/2939
|
||||
[#2963]: https://github.com/nymtech/nym/pull/2963
|
||||
|
||||
|
||||
# [v1.1.8] (2023-01-31)
|
||||
|
||||
|
||||
Generated
+142
-175
@@ -99,9 +99,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.69"
|
||||
version = "1.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
|
||||
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
@@ -138,9 +138,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.64"
|
||||
version = "0.1.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
|
||||
checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -290,6 +290,15 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bip32"
|
||||
version = "0.3.0"
|
||||
@@ -449,8 +458,7 @@ dependencies = [
|
||||
name = "build-information"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"vergen 7.5.1",
|
||||
"vergen 7.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -473,9 +481,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.4.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
||||
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
@@ -485,9 +493,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
@@ -620,9 +628,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.1.4"
|
||||
version = "4.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
|
||||
checksum = "d8d93d855ce6a0aa87b8473ef9169482f40abaa2e9e0993024c35c902cbd5920"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
@@ -639,7 +647,7 @@ version = "4.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75"
|
||||
dependencies = [
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -648,7 +656,7 @@ version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf0c76d8fcf782a1102ccfcd10ca8246e7fdd609c1cd6deddbb96cb638e9bb5c"
|
||||
dependencies = [
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"clap_complete",
|
||||
]
|
||||
|
||||
@@ -658,7 +666,7 @@ version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"heck 0.4.0",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -809,7 +817,7 @@ dependencies = [
|
||||
name = "completions"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
]
|
||||
@@ -969,9 +977,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-derive"
|
||||
version = "1.2.1"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5abeeb891e6d0098402e4d3d042f90451db52651d2fe14b170e69a1dd3e4115"
|
||||
checksum = "6fca30d51f7e5fbfa6440d8b10d7df0231bdf77e97fd3fe5d0cb79cc4822e50c"
|
||||
dependencies = [
|
||||
"syn",
|
||||
]
|
||||
@@ -1013,9 +1021,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.0.1"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
|
||||
checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3"
|
||||
dependencies = [
|
||||
"crc-catalog 2.2.0",
|
||||
]
|
||||
@@ -1046,15 +1054,13 @@ name = "credential"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"coconut-interface",
|
||||
"completions",
|
||||
"config",
|
||||
"credential-storage",
|
||||
"credentials",
|
||||
"crypto",
|
||||
"log",
|
||||
"logging",
|
||||
"network-defaults",
|
||||
"pemstore",
|
||||
"rand 0.7.3",
|
||||
@@ -1464,9 +1470,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.89"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9"
|
||||
checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@@ -1476,9 +1482,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.89"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d"
|
||||
checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -1491,15 +1497,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.89"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a"
|
||||
checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.89"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2"
|
||||
checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1561,7 +1567,7 @@ dependencies = [
|
||||
"hashbrown 0.12.3",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core 0.9.7",
|
||||
"parking_lot_core 0.9.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1755,9 +1761,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
@@ -1779,9 +1785,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.32"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
|
||||
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -1888,7 +1894,7 @@ name = "explorer-api"
|
||||
version = "1.1.8"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"contracts-common",
|
||||
"dotenv",
|
||||
"humantime-serde",
|
||||
@@ -1999,7 +2005,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project",
|
||||
"spin 0.9.5",
|
||||
"spin 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2052,9 +2058,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.26"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
|
||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -2067,9 +2073,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.26"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -2077,15 +2083,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.26"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.26"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
|
||||
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -2105,15 +2111,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.26"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.26"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2122,21 +2128,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.26"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.26"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.26"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -2288,6 +2294,19 @@ dependencies = [
|
||||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.16.1"
|
||||
@@ -2476,9 +2495,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
@@ -2501,12 +2520,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
@@ -2636,9 +2649,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.24"
|
||||
version = "0.14.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c"
|
||||
checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@@ -2825,12 +2838,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.5"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
|
||||
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2859,14 +2872,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.3"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef"
|
||||
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.1",
|
||||
"hermit-abi 0.2.6",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3201,7 +3214,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3284,7 +3297,7 @@ dependencies = [
|
||||
"log",
|
||||
"memchr",
|
||||
"mime",
|
||||
"spin 0.9.5",
|
||||
"spin 0.9.4",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"version_check",
|
||||
@@ -3425,7 +3438,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.9"
|
||||
version = "1.1.8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3433,7 +3446,7 @@ dependencies = [
|
||||
"bs58",
|
||||
"build-information",
|
||||
"cfg-if",
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"coconut-bandwidth-contract-common",
|
||||
"coconut-dkg-common",
|
||||
"coconut-interface",
|
||||
@@ -3529,7 +3542,7 @@ dependencies = [
|
||||
"base64 0.13.1",
|
||||
"bip39",
|
||||
"bs58",
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
"dotenv",
|
||||
@@ -3553,7 +3566,7 @@ dependencies = [
|
||||
"bip39",
|
||||
"bs58",
|
||||
"cfg-if",
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"coconut-bandwidth-contract-common",
|
||||
"coconut-dkg-common",
|
||||
"comfy-table",
|
||||
@@ -3584,7 +3597,7 @@ name = "nym-client"
|
||||
version = "1.1.8"
|
||||
dependencies = [
|
||||
"build-information",
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"client-connections",
|
||||
"client-core",
|
||||
"coconut-interface",
|
||||
@@ -3629,7 +3642,7 @@ dependencies = [
|
||||
"bip39",
|
||||
"bs58",
|
||||
"build-information",
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"coconut-interface",
|
||||
"colored",
|
||||
"completions",
|
||||
@@ -3679,7 +3692,7 @@ dependencies = [
|
||||
"atty",
|
||||
"bs58",
|
||||
"build-information",
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"colored",
|
||||
"completions",
|
||||
"config",
|
||||
@@ -3721,8 +3734,7 @@ name = "nym-network-requester"
|
||||
version = "1.1.8"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"build-information",
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"client-connections",
|
||||
"completions",
|
||||
"dirs",
|
||||
@@ -3739,7 +3751,6 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"service-providers-common",
|
||||
"socks5-requests",
|
||||
"sqlx 0.6.2",
|
||||
"statistics-common",
|
||||
@@ -3812,7 +3823,7 @@ name = "nym-socks5-client"
|
||||
version = "1.1.8"
|
||||
dependencies = [
|
||||
"build-information",
|
||||
"clap 4.1.4",
|
||||
"clap 4.1.3",
|
||||
"client-connections",
|
||||
"client-core",
|
||||
"coconut-interface",
|
||||
@@ -3839,7 +3850,6 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"service-providers-common",
|
||||
"socks5-requests",
|
||||
"tap",
|
||||
"task",
|
||||
@@ -3901,6 +3911,7 @@ dependencies = [
|
||||
name = "nymcoconut"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bls12_381 0.6.0",
|
||||
"bs58",
|
||||
"criterion 0.3.6",
|
||||
@@ -3924,7 +3935,6 @@ dependencies = [
|
||||
name = "nymsphinx"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"criterion 0.3.6",
|
||||
"crypto",
|
||||
"mixnet-contract-common",
|
||||
"nymsphinx-acknowledgements",
|
||||
@@ -4185,7 +4195,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.7",
|
||||
"parking_lot_core 0.9.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4204,15 +4214,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.7"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
|
||||
checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec 1.10.0",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4306,9 +4316,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.5.5"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660"
|
||||
checksum = "4257b4a04d91f7e9e6290be5d3da4804dd5784fafde3a497d73eb2b4a158c30a"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
@@ -4316,9 +4326,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.5.5"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69"
|
||||
checksum = "241cda393b0cdd65e62e07e12454f1f25d57017dcc514b1514cd3c4645e3a0a6"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
@@ -4326,9 +4336,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.5.5"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202"
|
||||
checksum = "46b53634d8c8196302953c74d5352f33d0c512a9499bd2ce468fc9f4128fa27c"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
@@ -4339,9 +4349,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.5.5"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616"
|
||||
checksum = "0ef4f1332a8d4678b41966bb4cc1d0676880e84183a1ecc3f4b69f03e99c7a51"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
@@ -4490,9 +4500,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -5164,16 +5174,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.8"
|
||||
version = "0.36.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
|
||||
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.45.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5249,7 +5259,7 @@ version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
|
||||
dependencies = [
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5330,9 +5340,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.8.2"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
|
||||
checksum = "645926f31b250a2dca3c232496c2d898d91036e45ca0e97e0e2390c54e11be36"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
@@ -5410,9 +5420,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.9"
|
||||
version = "0.11.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294"
|
||||
checksum = "718dc5fff5b36f99093fc49b280cfc96ce6fc824317783bff5a1fed0c7a64819"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -5451,9 +5461,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.93"
|
||||
version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
|
||||
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
|
||||
dependencies = [
|
||||
"itoa 1.0.5",
|
||||
"ryu",
|
||||
@@ -5483,22 +5493,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "service-providers-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"build-information",
|
||||
"log",
|
||||
"nym-sdk",
|
||||
"nymsphinx-anonymous-replies",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.9.8"
|
||||
@@ -5659,7 +5653,7 @@ version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "475b3bbe5245c26f2d8a6f62d67c1f30eb9fffeccee721c45d162c3ebbdf81b2"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"heck 0.4.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -5680,9 +5674,6 @@ name = "socks5-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nymsphinx-addressing",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"service-providers-common",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -5717,9 +5708,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.5"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc"
|
||||
checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
@@ -5834,7 +5825,7 @@ dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc 3.0.1",
|
||||
"crc 3.0.0",
|
||||
"crossbeam-queue",
|
||||
"dotenvy",
|
||||
"either",
|
||||
@@ -5877,7 +5868,7 @@ checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1"
|
||||
dependencies = [
|
||||
"dotenv",
|
||||
"either",
|
||||
"heck 0.4.1",
|
||||
"heck 0.4.0",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5896,7 +5887,7 @@ checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9"
|
||||
dependencies = [
|
||||
"dotenvy",
|
||||
"either",
|
||||
"heck 0.4.1",
|
||||
"heck 0.4.0",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6017,7 +6008,7 @@ version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"heck 0.4.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
@@ -6058,9 +6049,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
@@ -6321,9 +6312,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.25.0"
|
||||
version = "1.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
|
||||
checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"bytes",
|
||||
@@ -6337,7 +6328,7 @@ dependencies = [
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"tracing",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6363,9 +6354,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
@@ -6806,9 +6797,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.1"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
@@ -6931,7 +6922,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"enum-iterator 0.8.1",
|
||||
"getset",
|
||||
"git2",
|
||||
"git2 0.16.1",
|
||||
"rustc_version 0.4.0",
|
||||
"rustversion",
|
||||
"thiserror",
|
||||
@@ -6939,15 +6930,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "7.5.1"
|
||||
version = "7.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f21b881cd6636ece9735721cf03c1fe1e774fe258683d084bb2812ab67435749"
|
||||
checksum = "571b69f690c855821462709b6f41d42ceccc316fbd17b60bd06d06928cfe6a99"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
"enum-iterator 1.2.0",
|
||||
"getset",
|
||||
"git2",
|
||||
"git2 0.15.0",
|
||||
"rustc_version 0.4.0",
|
||||
"rustversion",
|
||||
"thiserror",
|
||||
@@ -7241,30 +7232,6 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.42.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc 0.42.1",
|
||||
"windows_i686_gnu 0.42.1",
|
||||
"windows_i686_msvc 0.42.1",
|
||||
"windows_x86_64_gnu 0.42.1",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc 0.42.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.1"
|
||||
|
||||
@@ -76,7 +76,6 @@ members = [
|
||||
"integrations/bity",
|
||||
"mixnode",
|
||||
"sdk/rust/nym-sdk",
|
||||
"service-providers/common",
|
||||
"service-providers/network-requester",
|
||||
"service-providers/network-statistics",
|
||||
"nym-api",
|
||||
@@ -106,12 +105,4 @@ homepage = "https://nymtech.net"
|
||||
edition = "2021"
|
||||
|
||||
[workspace.dependencies]
|
||||
async-trait = "0.1.63"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
serde = "1.0.152"
|
||||
serde_json = "1.0.91"
|
||||
tap = "1.0.1"
|
||||
thiserror = "1.0.38"
|
||||
tokio = "1.24.1"
|
||||
url = "2.2"
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -51,22 +51,11 @@ pub mod non_wasm_helpers;
|
||||
|
||||
pub mod helpers;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClientInput {
|
||||
pub connection_command_sender: ConnectionCommandSender,
|
||||
pub input_sender: InputMessageSender,
|
||||
}
|
||||
|
||||
impl ClientInput {
|
||||
pub async fn send(
|
||||
&self,
|
||||
message: InputMessage,
|
||||
) -> Result<(), tokio::sync::mpsc::error::SendError<InputMessage>> {
|
||||
self.input_sender.send(message).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClientOutput {
|
||||
pub received_buffer_request_sender: ReceivedBufferRequestSender,
|
||||
}
|
||||
@@ -314,7 +303,6 @@ where
|
||||
let shared_key = if self.key_manager.is_gateway_key_set() {
|
||||
Some(self.key_manager.gateway_shared_key())
|
||||
} else {
|
||||
log::info!("Gateway key not set! Will proceed anyway.");
|
||||
None
|
||||
};
|
||||
|
||||
|
||||
@@ -77,12 +77,4 @@ impl InputMessage {
|
||||
lane,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lane(&self) -> &TransmissionLane {
|
||||
match self {
|
||||
InputMessage::Regular { lane, .. }
|
||||
| InputMessage::Anonymous { lane, .. }
|
||||
| InputMessage::Reply { lane, .. } => lane,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ use crate::client::replies::reply_storage::backend::Empty;
|
||||
use crate::client::replies::reply_storage::{CombinedReplyStorage, ReplyStorageBackend};
|
||||
use async_trait::async_trait;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
// well, right now we don't have the browser storage : (
|
||||
// so we keep everything in memory
|
||||
#[derive(Debug)]
|
||||
@@ -29,18 +27,6 @@ impl Backend {
|
||||
impl ReplyStorageBackend for Backend {
|
||||
type StorageError = <Empty as ReplyStorageBackend>::StorageError;
|
||||
|
||||
async fn new(
|
||||
debug_config: &crate::config::DebugConfig,
|
||||
_db_path: Option<PathBuf>,
|
||||
) -> Result<Self, Self::StorageError> {
|
||||
Ok(Backend {
|
||||
empty: Empty {
|
||||
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
|
||||
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn flush_surb_storage(
|
||||
&mut self,
|
||||
storage: &CombinedReplyStorage,
|
||||
|
||||
@@ -56,9 +56,4 @@ pub enum StorageError {
|
||||
details: String,
|
||||
// err: Option<Box<dyn std::error::Error>>
|
||||
},
|
||||
|
||||
#[error("failed to create storage")]
|
||||
FailedToCreateStorage {
|
||||
source: Box<dyn std::error::Error + Send + Sync>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::base_client::non_wasm_helpers;
|
||||
use crate::client::replies::reply_storage::backend::fs_backend::manager::StorageManager;
|
||||
use crate::client::replies::reply_storage::backend::fs_backend::models::{
|
||||
ReplySurbStorageMetadata, StoredReplyKey, StoredReplySurb, StoredSenderTag, StoredSurbSender,
|
||||
@@ -368,20 +367,6 @@ impl Backend {
|
||||
impl ReplyStorageBackend for Backend {
|
||||
type StorageError = error::StorageError;
|
||||
|
||||
async fn new(
|
||||
debug_config: &crate::config::DebugConfig,
|
||||
db_path: Option<PathBuf>,
|
||||
) -> Result<Self, Self::StorageError> {
|
||||
non_wasm_helpers::setup_fs_reply_surb_backend(db_path, debug_config)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
log::error!("Failed to create storage: {err}");
|
||||
Self::StorageError::FailedToCreateStorage {
|
||||
source: Box::new(err),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.manager.is_active()
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::client::replies::reply_storage::CombinedReplyStorage;
|
||||
use async_trait::async_trait;
|
||||
use std::{error::Error, path::PathBuf};
|
||||
use std::error::Error;
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@@ -30,16 +30,6 @@ pub struct Empty {
|
||||
impl ReplyStorageBackend for Empty {
|
||||
type StorageError = UndefinedError;
|
||||
|
||||
async fn new(
|
||||
debug_config: &crate::config::DebugConfig,
|
||||
_db_path: Option<PathBuf>,
|
||||
) -> Result<Self, Self::StorageError> {
|
||||
Ok(Self {
|
||||
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
|
||||
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
|
||||
})
|
||||
}
|
||||
|
||||
async fn flush_surb_storage(
|
||||
&mut self,
|
||||
_storage: &CombinedReplyStorage,
|
||||
@@ -73,11 +63,6 @@ impl ReplyStorageBackend for Empty {
|
||||
pub trait ReplyStorageBackend: Sized {
|
||||
type StorageError: Error + 'static;
|
||||
|
||||
async fn new(
|
||||
debug_config: &crate::config::DebugConfig,
|
||||
db_path: Option<PathBuf>,
|
||||
) -> Result<Self, Self::StorageError>;
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
@@ -171,15 +171,10 @@ impl<T> Config<T> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpointConfig) {
|
||||
pub fn with_gateway_endpoint(&mut self, gateway_endpoint: GatewayEndpointConfig) {
|
||||
self.client.gateway_endpoint = gateway_endpoint;
|
||||
}
|
||||
|
||||
pub fn with_gateway_endpoint(mut self, gateway_endpoint: GatewayEndpointConfig) -> Self {
|
||||
self.client.gateway_endpoint = gateway_endpoint;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
|
||||
self.client.gateway_endpoint.gateway_id = id.into();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ edition = "2021"
|
||||
[dependencies]
|
||||
bip39 = "1.0.1"
|
||||
clap = { version = "4.0", features = ["cargo", "derive"] }
|
||||
log = "0.4"
|
||||
rand = "0.7.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "1.0"
|
||||
@@ -21,7 +20,6 @@ completions = { path = "../../common/completions" }
|
||||
credentials = { path = "../../common/credentials" }
|
||||
credential-storage = { path = "../../common/credential-storage" }
|
||||
crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
||||
logging = { path = "../../common/logging"}
|
||||
network-defaults = { path = "../../common/network-defaults" }
|
||||
pemstore = { path = "../../common/pemstore" }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{ArgGroup, Args, Subcommand};
|
||||
use clap::{Args, Subcommand};
|
||||
use completions::ArgShell;
|
||||
use log::*;
|
||||
use rand::rngs::OsRng;
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -13,20 +12,18 @@ use credential_storage::PersistentStorage;
|
||||
use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
|
||||
use credentials::coconut::utils::obtain_aggregate_signature;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use network_defaults::VOUCHER_INFO;
|
||||
use network_defaults::{NymNetworkDetails, VOUCHER_INFO};
|
||||
use validator_client::nyxd::traits::DkgQueryClient;
|
||||
use validator_client::nyxd::tx::Hash;
|
||||
use validator_client::nyxd::CosmWasmClient;
|
||||
use validator_client::CoconutApiClient;
|
||||
use validator_client::{CoconutApiClient, Config};
|
||||
|
||||
use crate::client::Client;
|
||||
use crate::error::{CredentialClientError, Result};
|
||||
use crate::recovery_storage::RecoveryStorage;
|
||||
use crate::state::{KeyPair, State};
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum Command {
|
||||
/// Run the binary to obtain a credential
|
||||
/// Run the binary
|
||||
Run(Run),
|
||||
|
||||
/// Generate shell completions
|
||||
@@ -37,11 +34,6 @@ pub(crate) enum Command {
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[clap(group(
|
||||
ArgGroup::new("recov")
|
||||
.required(true)
|
||||
.args(&["amount", "recovery_mode"]),
|
||||
))]
|
||||
pub(crate) struct Run {
|
||||
/// Home directory of the client that is supposed to use the credential.
|
||||
#[clap(long)]
|
||||
@@ -55,25 +47,15 @@ pub(crate) struct Run {
|
||||
#[clap(long)]
|
||||
pub(crate) mnemonic: String,
|
||||
|
||||
/// The amount of utokens the credential will hold. If recovery mode is enabled, this value
|
||||
/// is not needed
|
||||
#[clap(long, default_value = "0")]
|
||||
/// The amount of utokens the credential will hold
|
||||
#[clap(long)]
|
||||
pub(crate) amount: u64,
|
||||
|
||||
/// Path to a directory used to store recovery files for unconsumed deposits
|
||||
#[clap(long)]
|
||||
pub(crate) recovery_dir: std::path::PathBuf,
|
||||
|
||||
/// Recovery mode, when enabled, tries to recover any deposit data dumped in recovery_dir
|
||||
#[clap(long)]
|
||||
pub(crate) recovery_mode: bool,
|
||||
}
|
||||
|
||||
pub(crate) async fn deposit(nyxd_url: &str, mnemonic: &str, amount: u64) -> Result<State> {
|
||||
let mut rng = OsRng;
|
||||
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
|
||||
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
|
||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
||||
|
||||
let client = Client::new(nyxd_url, mnemonic);
|
||||
let tx_hash = client
|
||||
@@ -85,47 +67,46 @@ pub(crate) async fn deposit(nyxd_url: &str, mnemonic: &str, amount: u64) -> Resu
|
||||
)
|
||||
.await?;
|
||||
|
||||
let voucher = BandwidthVoucher::new(
|
||||
¶ms,
|
||||
amount.to_string(),
|
||||
VOUCHER_INFO.to_string(),
|
||||
Hash::from_str(&tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?,
|
||||
identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?,
|
||||
encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?,
|
||||
);
|
||||
|
||||
let state = State { voucher, params };
|
||||
let state = State {
|
||||
amount,
|
||||
tx_hash,
|
||||
signing_keypair,
|
||||
encryption_keypair,
|
||||
};
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_credential<C: Clone + CosmWasmClient + Send + Sync>(
|
||||
state: &State,
|
||||
client: validator_client::Client<C>,
|
||||
shared_storage: PersistentStorage,
|
||||
) -> Result<()> {
|
||||
pub(crate) async fn get_credential(state: &State, shared_storage: PersistentStorage) -> Result<()> {
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let config = Config::try_from_nym_network_details(&network_details)?;
|
||||
let client = validator_client::Client::new_query(config)?;
|
||||
let epoch_id = client.nyxd.get_current_epoch().await?.epoch_id;
|
||||
let threshold = client
|
||||
.nyxd
|
||||
.get_current_epoch_threshold()
|
||||
.await?
|
||||
.ok_or(CredentialClientError::NoThreshold)?;
|
||||
let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client, epoch_id).await?;
|
||||
|
||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
||||
let bandwidth_credential_attributes = BandwidthVoucher::new(
|
||||
¶ms,
|
||||
state.amount.to_string(),
|
||||
VOUCHER_INFO.to_string(),
|
||||
Hash::from_str(&state.tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?,
|
||||
identity::PrivateKey::from_base58_string(&state.signing_keypair.private_key)?,
|
||||
encryption::PrivateKey::from_base58_string(&state.encryption_keypair.private_key)?,
|
||||
);
|
||||
|
||||
let signature = obtain_aggregate_signature(
|
||||
&state.params,
|
||||
&state.voucher,
|
||||
¶ms,
|
||||
&bandwidth_credential_attributes,
|
||||
&coconut_api_clients,
|
||||
threshold,
|
||||
)
|
||||
.await?;
|
||||
info!("Signature: {:?}", signature.to_bs58());
|
||||
println!("Signature: {:?}", signature.to_bs58());
|
||||
shared_storage
|
||||
.insert_coconut_credential(
|
||||
state.voucher.get_voucher_value(),
|
||||
state.amount.to_string(),
|
||||
VOUCHER_INFO.to_string(),
|
||||
state.voucher.get_private_attributes()[0].to_bs58(),
|
||||
state.voucher.get_private_attributes()[1].to_bs58(),
|
||||
bandwidth_credential_attributes.get_private_attributes()[0].to_bs58(),
|
||||
bandwidth_credential_attributes.get_private_attributes()[1].to_bs58(),
|
||||
signature.to_bs58(),
|
||||
epoch_id.to_string(),
|
||||
)
|
||||
@@ -133,33 +114,3 @@ pub(crate) async fn get_credential<C: Clone + CosmWasmClient + Send + Sync>(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn recover_credentials<C: Clone + CosmWasmClient + Send + Sync>(
|
||||
client: validator_client::Client<C>,
|
||||
recovery_storage: &RecoveryStorage,
|
||||
shared_storage: PersistentStorage,
|
||||
) -> Result<()> {
|
||||
for voucher in recovery_storage.unconsumed_vouchers()? {
|
||||
let state = State {
|
||||
voucher,
|
||||
params: Parameters::new(TOTAL_ATTRIBUTES).unwrap(),
|
||||
};
|
||||
if let Err(e) = get_credential(&state, client.clone(), shared_storage.clone()).await {
|
||||
error!(
|
||||
"Could not recover deposit {} due to {:?}, try again later",
|
||||
state.voucher.tx_hash(),
|
||||
e
|
||||
)
|
||||
} else {
|
||||
info!(
|
||||
"Converted deposit {} to a credential, removing recovery data for it",
|
||||
state.voucher.tx_hash()
|
||||
);
|
||||
if let Err(e) = recovery_storage.remove_voucher(state.voucher.tx_hash().to_string()) {
|
||||
warn!("Could not remove recovery data - {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::time::SystemTimeError;
|
||||
use thiserror::Error;
|
||||
|
||||
use credential_storage::error::StorageError;
|
||||
@@ -15,9 +14,6 @@ pub type Result<T> = std::result::Result<T, CredentialClientError>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CredentialClientError {
|
||||
#[error("IO error: {0}")]
|
||||
IOError(#[from] std::io::Error),
|
||||
|
||||
#[error("Nyxd error: {0}")]
|
||||
Nyxd(#[from] NyxdError),
|
||||
|
||||
@@ -38,10 +34,4 @@ pub enum CredentialClientError {
|
||||
|
||||
#[error("Could not use shared storage")]
|
||||
SharedStorageError(#[from] StorageError),
|
||||
|
||||
#[error("Could not get system time")]
|
||||
SysTimeError(#[from] SystemTimeError),
|
||||
|
||||
#[error("Threshold not set yet")]
|
||||
NoThreshold,
|
||||
}
|
||||
|
||||
@@ -4,25 +4,15 @@
|
||||
mod client;
|
||||
mod commands;
|
||||
mod error;
|
||||
mod recovery_storage;
|
||||
mod state;
|
||||
|
||||
use commands::*;
|
||||
use completions::fig_generate;
|
||||
use config::{DATA_DIR, DB_FILE_NAME};
|
||||
use error::Result;
|
||||
use log::*;
|
||||
use network_defaults::{setup_env, NymNetworkDetails};
|
||||
use std::process::exit;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use network_defaults::setup_env;
|
||||
|
||||
use clap::{CommandFactory, Parser};
|
||||
use logging::setup_logging;
|
||||
use validator_client::nyxd::traits::DkgQueryClient;
|
||||
use validator_client::nyxd::CosmWasmClient;
|
||||
use validator_client::Config;
|
||||
|
||||
const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author = "Nymtech", version, about)]
|
||||
@@ -35,36 +25,9 @@ struct Cli {
|
||||
pub(crate) command: Command,
|
||||
}
|
||||
|
||||
async fn block_until_coconut_is_available<C: Clone + CosmWasmClient + Send + Sync>(
|
||||
client: &validator_client::Client<C>,
|
||||
) -> Result<()> {
|
||||
loop {
|
||||
let epoch = client.nyxd.get_current_epoch().await?;
|
||||
let current_timestamp_secs = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)?
|
||||
.as_secs();
|
||||
if epoch.state.is_final() {
|
||||
if current_timestamp_secs + SAFETY_BUFFER_SECS >= epoch.finish_timestamp.seconds() {
|
||||
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
// Use 20 additional seconds to avoid the exact moment of going into the final epoch state
|
||||
let secs_until_final = epoch.final_timestamp_secs() + 20 - current_timestamp_secs;
|
||||
info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final);
|
||||
std::thread::sleep(Duration::from_secs(secs_until_final));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let args = Cli::parse();
|
||||
setup_logging();
|
||||
setup_env(args.config_env_file.as_ref());
|
||||
let bin_name = "nym-credential-client";
|
||||
|
||||
@@ -72,34 +35,9 @@ async fn main() -> Result<()> {
|
||||
Command::Run(r) => {
|
||||
let db_path = r.client_home_directory.join(DATA_DIR).join(DB_FILE_NAME);
|
||||
let shared_storage = credential_storage::initialise_storage(db_path).await;
|
||||
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
|
||||
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let config = Config::try_from_nym_network_details(&network_details)?;
|
||||
let client = validator_client::Client::new_query(config)?;
|
||||
|
||||
block_until_coconut_is_available(&client).await?;
|
||||
info!("Starting depositing funds, don't kill the process");
|
||||
|
||||
if !r.recovery_mode {
|
||||
let state = deposit(&r.nyxd_url, &r.mnemonic, r.amount).await?;
|
||||
if get_credential(&state, client, shared_storage)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
warn!("Failed to obtain credential. Dumping recovery data.",);
|
||||
match recovery_storage.insert_voucher(&state.voucher) {
|
||||
Ok(file_path) => {
|
||||
warn!("Dumped recovery data to {:?}. Try using recovery mode to convert it to a credential", file_path);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Could not dump recovery data to file system due to {:?}, the deposit will be lost!", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
recover_credentials(client, &recovery_storage, shared_storage).await?;
|
||||
}
|
||||
let state = deposit(&r.nyxd_url, &r.mnemonic, r.amount).await?;
|
||||
get_credential(&state, shared_storage).await?;
|
||||
}
|
||||
Command::Completions(c) => c.generate(&mut crate::Cli::command(), bin_name),
|
||||
Command::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name),
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use credentials::coconut::bandwidth::BandwidthVoucher;
|
||||
use std::fs::{create_dir_all, read_dir, File};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct RecoveryStorage {
|
||||
recovery_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl RecoveryStorage {
|
||||
pub fn new(recovery_dir: PathBuf) -> std::io::Result<Self> {
|
||||
create_dir_all(&recovery_dir)?;
|
||||
Ok(Self { recovery_dir })
|
||||
}
|
||||
|
||||
pub fn unconsumed_vouchers(&self) -> std::io::Result<impl Iterator<Item = BandwidthVoucher>> {
|
||||
Ok(read_dir(&self.recovery_dir)?
|
||||
.filter_map(|entry| entry.ok())
|
||||
.filter_map(|entry| {
|
||||
let path = entry.path();
|
||||
if path.is_file() {
|
||||
Some(path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|path| File::open(path).ok())
|
||||
.filter_map(|mut f| {
|
||||
let mut buff = Vec::new();
|
||||
if f.read_to_end(&mut buff).is_ok() {
|
||||
Some(buff)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.filter_map(|buff| BandwidthVoucher::try_from_bytes(&buff).ok()))
|
||||
}
|
||||
|
||||
pub fn insert_voucher(&self, voucher: &BandwidthVoucher) -> std::io::Result<PathBuf> {
|
||||
let file_name = voucher.tx_hash().to_string();
|
||||
let file_path = self.recovery_dir.join(file_name);
|
||||
let mut file = File::create(&file_path)?;
|
||||
let buff = voucher.to_bytes();
|
||||
file.write_all(&buff)?;
|
||||
|
||||
Ok(file_path)
|
||||
}
|
||||
|
||||
pub fn remove_voucher(&self, file_name: String) -> std::io::Result<()> {
|
||||
let file_path = self.recovery_dir.join(file_name);
|
||||
std::fs::remove_file(file_path)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use coconut_interface::Parameters;
|
||||
use credentials::coconut::bandwidth::BandwidthVoucher;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub(crate) struct KeyPair {
|
||||
pub public_key: String,
|
||||
pub private_key: String,
|
||||
@@ -29,7 +29,10 @@ impl From<encryption::KeyPair> for KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub(crate) struct State {
|
||||
pub voucher: BandwidthVoucher,
|
||||
pub params: Parameters,
|
||||
pub amount: u64,
|
||||
pub tx_hash: String,
|
||||
pub signing_keypair: KeyPair,
|
||||
pub encryption_keypair: KeyPair,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::template::config_template;
|
||||
use client_core::config::ClientCoreConfigTrait;
|
||||
pub use client_core::config::Config as BaseConfig;
|
||||
pub use client_core::config::MISSING_VALUE;
|
||||
use client_core::config::{ClientCoreConfigTrait, DebugConfig};
|
||||
use config::defaults::DEFAULT_WEBSOCKET_LISTENING_PORT;
|
||||
use config::{NymConfig, OptionalSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -11,10 +13,6 @@ use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub use client_core::config::Config as BaseConfig;
|
||||
pub use client_core::config::MISSING_VALUE;
|
||||
pub use client_core::config::{DebugConfig, GatewayEndpointConfig};
|
||||
|
||||
mod template;
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone, Copy)]
|
||||
|
||||
@@ -11,22 +11,19 @@ use client_core::client::base_client::{
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
};
|
||||
use client_core::client::inbound_messages::InputMessage;
|
||||
use client_core::client::received_buffer::{
|
||||
ReceivedBufferMessage, ReceivedBufferRequestSender, ReconstructedMessagesReceiver,
|
||||
};
|
||||
use client_core::client::key_manager::KeyManager;
|
||||
use client_core::client::received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver};
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
use futures::channel::mpsc;
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
use log::*;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::anonymous_replies::requests::AnonymousSenderTag;
|
||||
use nymsphinx::receiver::ReconstructedMessage;
|
||||
use task::TaskManager;
|
||||
use tokio::sync::watch::error::SendError;
|
||||
use validator_client::nyxd::QueryNyxdClient;
|
||||
|
||||
pub use client_core::client::key_manager::KeyManager;
|
||||
pub use nymsphinx::addressing::clients::Recipient;
|
||||
pub use nymsphinx::receiver::ReconstructedMessage;
|
||||
pub mod config;
|
||||
pub(crate) mod config;
|
||||
|
||||
pub struct SocketClient {
|
||||
/// Client configuration options, including, among other things, packet sending rates,
|
||||
@@ -48,13 +45,6 @@ impl SocketClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_keys(config: Config, key_manager: KeyManager) -> Self {
|
||||
SocketClient {
|
||||
config,
|
||||
key_manager,
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_bandwidth_controller(config: &Config) -> BandwidthController<QueryNyxdClient> {
|
||||
let details = network_defaults::NymNetworkDetails::new_from_env();
|
||||
let mut client_config = validator_client::Config::try_from_nym_network_details(&details)
|
||||
@@ -130,17 +120,10 @@ impl SocketClient {
|
||||
return Err(ClientError::InvalidSocketMode);
|
||||
}
|
||||
|
||||
// don't create bandwidth controller if credentials are disabled
|
||||
let bandwidth_controller = if self.config.get_base().get_disabled_credentials_mode() {
|
||||
None
|
||||
} else {
|
||||
Some(Self::create_bandwidth_controller(&self.config).await)
|
||||
};
|
||||
|
||||
let base_builder = BaseClientBuilder::new_from_base_config(
|
||||
self.config.get_base(),
|
||||
self.key_manager,
|
||||
bandwidth_controller,
|
||||
Some(Self::create_bandwidth_controller(&self.config).await),
|
||||
non_wasm_helpers::setup_fs_reply_surb_backend(
|
||||
Some(self.config.get_base().get_reply_surb_database_path()),
|
||||
self.config.get_debug_settings(),
|
||||
@@ -174,17 +157,10 @@ impl SocketClient {
|
||||
return Err(ClientError::InvalidSocketMode);
|
||||
}
|
||||
|
||||
// don't create bandwidth controller if credentials are disabled
|
||||
let bandwidth_controller = if self.config.get_base().get_disabled_credentials_mode() {
|
||||
None
|
||||
} else {
|
||||
Some(Self::create_bandwidth_controller(&self.config).await)
|
||||
};
|
||||
|
||||
let base_client = BaseClientBuilder::new_from_base_config(
|
||||
self.config.get_base(),
|
||||
self.key_manager,
|
||||
bandwidth_controller,
|
||||
Some(Self::create_bandwidth_controller(&self.config).await),
|
||||
non_wasm_helpers::setup_fs_reply_surb_backend(
|
||||
Some(self.config.get_base().get_reply_surb_database_path()),
|
||||
self.config.get_debug_settings(),
|
||||
@@ -192,8 +168,6 @@ impl SocketClient {
|
||||
.await?,
|
||||
);
|
||||
|
||||
let address = base_client.as_mix_recipient();
|
||||
|
||||
let mut started_client = base_client.start_base().await?;
|
||||
let client_input = started_client.client_input.register_producer();
|
||||
let client_output = started_client.client_output.register_consumer();
|
||||
@@ -211,38 +185,21 @@ impl SocketClient {
|
||||
|
||||
Ok(DirectClient {
|
||||
client_input,
|
||||
_received_buffer_request_sender: client_output.received_buffer_request_sender,
|
||||
reconstructed_receiver,
|
||||
address,
|
||||
shutdown_notifier: started_client.task_manager,
|
||||
_shutdown_notifier: started_client.task_manager,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DirectClient {
|
||||
client_input: ClientInput,
|
||||
// make sure to not drop the channel
|
||||
_received_buffer_request_sender: ReceivedBufferRequestSender,
|
||||
reconstructed_receiver: ReconstructedMessagesReceiver,
|
||||
address: Recipient,
|
||||
|
||||
// we need to keep reference to this guy otherwise things will start dropping
|
||||
shutdown_notifier: TaskManager,
|
||||
_shutdown_notifier: TaskManager,
|
||||
}
|
||||
|
||||
impl DirectClient {
|
||||
pub fn address(&self) -> &Recipient {
|
||||
&self.address
|
||||
}
|
||||
|
||||
pub fn signal_shutdown(&self) -> Result<(), SendError<()>> {
|
||||
self.shutdown_notifier.signal_shutdown()
|
||||
}
|
||||
|
||||
pub async fn wait_for_shutdown(&mut self) {
|
||||
self.shutdown_notifier.wait_for_shutdown().await
|
||||
}
|
||||
|
||||
/// EXPERIMENTAL DIRECT RUST API
|
||||
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
|
||||
/// well enough in local tests)
|
||||
|
||||
@@ -31,7 +31,7 @@ pub(crate) struct Init {
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
@@ -62,7 +62,7 @@ pub(crate) struct Init {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long, hide = true)]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Save a summary of the initialization to a json file
|
||||
@@ -147,7 +147,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
|
||||
.await
|
||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
||||
|
||||
config.get_base_mut().set_gateway_endpoint(gateway);
|
||||
config.get_base_mut().with_gateway_endpoint(gateway);
|
||||
|
||||
config.save_to_file(None).tap_err(|_| {
|
||||
log::error!("Failed to save the config file");
|
||||
|
||||
@@ -23,7 +23,7 @@ pub(crate) struct Run {
|
||||
id: String,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
@@ -59,7 +59,7 @@ pub(crate) struct Run {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long, hide = true)]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ log = { workspace = true }
|
||||
pin-project = "1.0"
|
||||
pretty_env_logger = "0.4"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
|
||||
serde_json = { workspace = true }
|
||||
serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization
|
||||
serde_json = "1.0.89"
|
||||
tap = "1.0.1"
|
||||
thiserror = "1.0.34"
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||
@@ -45,7 +45,6 @@ nymsphinx = { path = "../../common/nymsphinx" }
|
||||
ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
|
||||
pemstore = { path = "../../common/pemstore" }
|
||||
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
||||
service-providers-common = { path = "../../service-providers/common" }
|
||||
socks5-requests = { path = "../../common/socks5/requests" }
|
||||
task = { path = "../../common/task" }
|
||||
topology = { path = "../../common/topology" }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::config::template::config_template;
|
||||
@@ -9,8 +9,6 @@ use config::defaults::DEFAULT_SOCKS5_LISTENING_PORT;
|
||||
use config::{NymConfig, OptionalSet};
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use service_providers_common::interface::ProviderInterfaceVersion;
|
||||
use socks5_requests::Socks5ProtocolVersion;
|
||||
use std::fmt::Debug;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
@@ -92,16 +90,6 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_provider_interface_version(mut self, version: ProviderInterfaceVersion) -> Self {
|
||||
self.socks5.provider_interface_version = version;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_socks5_protocol_version(mut self, version: Socks5ProtocolVersion) -> Self {
|
||||
self.socks5.socks5_protocol_version = version;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_anonymous_replies(mut self, anonymous_replies: bool) -> Self {
|
||||
self.socks5.send_anonymously = anonymous_replies;
|
||||
self
|
||||
@@ -129,14 +117,6 @@ impl Config {
|
||||
.expect("malformed provider address")
|
||||
}
|
||||
|
||||
pub fn get_provider_interface_version(&self) -> ProviderInterfaceVersion {
|
||||
self.socks5.provider_interface_version
|
||||
}
|
||||
|
||||
pub fn get_socks5_protocol_version(&self) -> Socks5ProtocolVersion {
|
||||
self.socks5.socks5_protocol_version
|
||||
}
|
||||
|
||||
pub fn get_send_anonymously(&self) -> bool {
|
||||
self.socks5.send_anonymously
|
||||
}
|
||||
@@ -207,15 +187,6 @@ pub struct Socks5 {
|
||||
/// The mix address of the provider to which all requests are going to be sent.
|
||||
provider_mix_address: String,
|
||||
|
||||
/// The version of the 'service provider' this client is going to use in its communication with the
|
||||
/// specified socks5 provider.
|
||||
// if in doubt, use the legacy version as initially nobody will be using the updated binaries
|
||||
#[serde(default = "ProviderInterfaceVersion::new_legacy")]
|
||||
provider_interface_version: ProviderInterfaceVersion,
|
||||
|
||||
#[serde(default = "Socks5ProtocolVersion::new_legacy")]
|
||||
socks5_protocol_version: Socks5ProtocolVersion,
|
||||
|
||||
/// 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
|
||||
/// slower and consume nearly double the bandwidth as it will require sending reply SURBs.
|
||||
@@ -230,8 +201,6 @@ impl Socks5 {
|
||||
Socks5 {
|
||||
listening_port: DEFAULT_SOCKS5_LISTENING_PORT,
|
||||
provider_mix_address: provider_mix_address.into(),
|
||||
provider_interface_version: ProviderInterfaceVersion::Legacy,
|
||||
socks5_protocol_version: Socks5ProtocolVersion::Legacy,
|
||||
send_anonymously: false,
|
||||
}
|
||||
}
|
||||
@@ -242,8 +211,6 @@ impl Default for Socks5 {
|
||||
Socks5 {
|
||||
listening_port: DEFAULT_SOCKS5_LISTENING_PORT,
|
||||
provider_mix_address: "".into(),
|
||||
provider_interface_version: ProviderInterfaceVersion::Legacy,
|
||||
socks5_protocol_version: Socks5ProtocolVersion::Legacy,
|
||||
send_anonymously: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ use client_core::client::key_manager::KeyManager;
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
#[cfg(not(feature = "mobile"))]
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
use log::*;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
@@ -70,7 +69,6 @@ impl NymClient {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "mobile"))]
|
||||
async fn create_bandwidth_controller(config: &Config) -> BandwidthController<QueryNyxdClient> {
|
||||
let details = network_defaults::NymNetworkDetails::new_from_env();
|
||||
let mut client_config = validator_client::Config::try_from_nym_network_details(&details)
|
||||
@@ -134,8 +132,6 @@ impl NymClient {
|
||||
self_address,
|
||||
shared_lane_queue_lengths,
|
||||
socks::client::Config::new(
|
||||
config.get_provider_interface_version(),
|
||||
config.get_socks5_protocol_version(),
|
||||
config.get_send_anonymously(),
|
||||
config.get_connection_start_surbs(),
|
||||
config.get_per_request_surbs(),
|
||||
|
||||
@@ -43,7 +43,7 @@ pub(crate) struct Init {
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
@@ -66,7 +66,7 @@ pub(crate) struct Init {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long, hide = true)]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Save a summary of the initialization to a json file
|
||||
@@ -153,9 +153,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
|
||||
.await
|
||||
.tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?;
|
||||
|
||||
config.get_base_mut().set_gateway_endpoint(gateway);
|
||||
|
||||
// TODO: ask the service provider we specified for its interface version and set it in the config
|
||||
config.get_base_mut().with_gateway_endpoint(gateway);
|
||||
|
||||
config.save_to_file(None).tap_err(|_| {
|
||||
log::error!("Failed to save the config file");
|
||||
|
||||
@@ -43,7 +43,7 @@ pub(crate) struct Run {
|
||||
gateway: Option<identity::PublicKey>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the nyxd validators
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',', hide = true)]
|
||||
#[clap(long, alias = "nymd_validators", value_delimiter = ',')]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the Nym APIs
|
||||
@@ -65,7 +65,7 @@ pub(crate) struct Run {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[clap(long, hide = true)]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::socks::types::SocksProxyError;
|
||||
use client_core::error::ClientCoreError;
|
||||
use socks5_requests::{ConnectionError, ConnectionId};
|
||||
use socks5_requests::ConnectionId;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Socks5ClientError {
|
||||
@@ -28,12 +28,3 @@ pub enum Socks5ClientError {
|
||||
error: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ConnectionError> for Socks5ClientError {
|
||||
fn from(value: ConnectionError) -> Self {
|
||||
Socks5ClientError::NetworkRequesterError {
|
||||
connection_id: value.connection_id,
|
||||
error: value.network_requester_error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,7 @@ use proxy_helpers::connection_controller::{
|
||||
};
|
||||
use proxy_helpers::proxy_runner::ProxyRunner;
|
||||
use rand::RngCore;
|
||||
use service_providers_common::interface::{ProviderInterfaceVersion, RequestVersion};
|
||||
use socks5_requests::{
|
||||
ConnectionId, RemoteAddress, Socks5ProtocolVersion, Socks5ProviderRequest, Socks5Request,
|
||||
};
|
||||
use socks5_requests::{ConnectionId, Message, RemoteAddress, Request};
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::pin::Pin;
|
||||
@@ -131,8 +128,6 @@ impl AsyncWrite for StreamState {
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct Config {
|
||||
provider_interface_version: ProviderInterfaceVersion,
|
||||
socks5_protocol_version: Socks5ProtocolVersion,
|
||||
use_surbs_for_responses: bool,
|
||||
connection_start_surbs: u32,
|
||||
per_request_surbs: u32,
|
||||
@@ -140,27 +135,16 @@ pub(crate) struct Config {
|
||||
|
||||
impl Config {
|
||||
pub(crate) fn new(
|
||||
provider_interface_version: ProviderInterfaceVersion,
|
||||
socks5_protocol_version: Socks5ProtocolVersion,
|
||||
use_surbs_for_responses: bool,
|
||||
connection_start_surbs: u32,
|
||||
per_request_surbs: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
provider_interface_version,
|
||||
socks5_protocol_version,
|
||||
use_surbs_for_responses,
|
||||
connection_start_surbs,
|
||||
per_request_surbs,
|
||||
}
|
||||
}
|
||||
|
||||
fn request_version(&self) -> RequestVersion<Socks5Request> {
|
||||
RequestVersion {
|
||||
provider_interface: self.provider_interface_version,
|
||||
provider_protocol: self.socks5_protocol_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A client connecting to the Socks proxy server, because
|
||||
@@ -189,9 +173,7 @@ impl Drop for SocksClient {
|
||||
// if we never managed to start a proxy, the entry will not exist in the controller
|
||||
if self.started_proxy {
|
||||
self.controller_sender
|
||||
.unbounded_send(ControllerCommand::Remove {
|
||||
connection_id: self.connection_id,
|
||||
})
|
||||
.unbounded_send(ControllerCommand::Remove(self.connection_id))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
@@ -266,26 +248,19 @@ impl SocksClient {
|
||||
|
||||
// Send an error back to the client
|
||||
pub async fn send_error_v4(&mut self, r: ResponseCodeV4) -> Result<(), SocksProxyError> {
|
||||
self.stream
|
||||
.write_all(&[SOCKS4_VERSION, r as u8])
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketWriteError { source })
|
||||
self.stream.write_all(&[SOCKS4_VERSION, r as u8]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_error_v5(&mut self, r: ResponseCodeV5) -> Result<(), SocksProxyError> {
|
||||
self.stream
|
||||
.write_all(&[SOCKS5_VERSION, r as u8])
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketWriteError { source })
|
||||
self.stream.write_all(&[SOCKS5_VERSION, r as u8]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Shutdown the `TcpStream` to the client and end the session
|
||||
pub async fn shutdown(&mut self) -> Result<(), SocksProxyError> {
|
||||
info!("client is shutting down its TCP stream");
|
||||
self.stream
|
||||
.shutdown()
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketShutdownFailure { source })?;
|
||||
self.stream.shutdown().await?;
|
||||
self.shutdown_listener.mark_as_success();
|
||||
Ok(())
|
||||
}
|
||||
@@ -293,20 +268,11 @@ impl SocksClient {
|
||||
/// Initializes the new client, checking that the correct Socks version (5)
|
||||
/// is in use and that the client is authenticated, then runs the request.
|
||||
pub async fn run(&mut self) -> Result<(), SocksProxyError> {
|
||||
debug!(
|
||||
"New connection from: {}",
|
||||
self.stream
|
||||
.peer_addr()
|
||||
.map_err(|source| SocksProxyError::PeerAddrExtractionFailure { source })?
|
||||
.ip()
|
||||
);
|
||||
debug!("New connection from: {}", self.stream.peer_addr()?.ip());
|
||||
|
||||
// Read a byte from the stream and determine the version being requested
|
||||
let mut header = [0u8];
|
||||
self.stream
|
||||
.read_exact(&mut header)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
self.stream.read_exact(&mut header).await?;
|
||||
|
||||
self.socks_version = match SocksVersion::try_from(header[0]) {
|
||||
Ok(version) => Some(version),
|
||||
@@ -318,10 +284,7 @@ impl SocksClient {
|
||||
|
||||
if self.socks_version == Some(SocksVersion::V5) {
|
||||
let mut auth = [0u8];
|
||||
self.stream
|
||||
.read_exact(&mut auth)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
self.stream.read_exact(&mut auth).await?;
|
||||
self.auth_nmethods = auth[0];
|
||||
self.authenticate_socks5().await?;
|
||||
}
|
||||
@@ -330,15 +293,8 @@ impl SocksClient {
|
||||
}
|
||||
|
||||
async fn send_anonymous_connect_to_mixnet(&mut self, remote_address: RemoteAddress) {
|
||||
// TODO: simplify by using `request_version`
|
||||
let req = Socks5Request::new_connect(
|
||||
self.config.socks5_protocol_version,
|
||||
self.connection_id,
|
||||
remote_address,
|
||||
None,
|
||||
);
|
||||
let msg =
|
||||
Socks5ProviderRequest::new_provider_data(self.config.provider_interface_version, req);
|
||||
let req = Request::new_connect(self.connection_id, remote_address, None);
|
||||
let msg = Message::Request(req);
|
||||
|
||||
let input_message = InputMessage::new_anonymous(
|
||||
self.service_provider,
|
||||
@@ -353,15 +309,8 @@ impl SocksClient {
|
||||
}
|
||||
|
||||
async fn send_connect_to_mixnet_with_return_address(&mut self, remote_address: RemoteAddress) {
|
||||
// TODO: simplify by using `request_version`
|
||||
let req = Socks5Request::new_connect(
|
||||
self.config.socks5_protocol_version,
|
||||
self.connection_id,
|
||||
remote_address,
|
||||
Some(self.self_address),
|
||||
);
|
||||
let msg =
|
||||
Socks5ProviderRequest::new_provider_data(self.config.provider_interface_version, req);
|
||||
let req = Request::new_connect(self.connection_id, remote_address, Some(self.self_address));
|
||||
let msg = Message::Request(req);
|
||||
|
||||
let input_message = InputMessage::new_regular(
|
||||
self.service_provider,
|
||||
@@ -401,7 +350,6 @@ impl SocksClient {
|
||||
let input_sender = self.input_sender.clone();
|
||||
let anonymous = self.config.use_surbs_for_responses;
|
||||
let per_request_surbs = self.config.per_request_surbs;
|
||||
let request_version = self.config.request_version();
|
||||
|
||||
let recipient = self.service_provider;
|
||||
let (stream, _) = ProxyRunner::new(
|
||||
@@ -415,16 +363,8 @@ impl SocksClient {
|
||||
self.shutdown_listener.clone(),
|
||||
)
|
||||
.run(move |conn_id, read_data, socket_closed| {
|
||||
let provider_request = Socks5Request::new_send(
|
||||
request_version.provider_protocol,
|
||||
conn_id,
|
||||
read_data,
|
||||
socket_closed,
|
||||
);
|
||||
let provider_message = Socks5ProviderRequest::new_provider_data(
|
||||
request_version.provider_interface,
|
||||
provider_request,
|
||||
);
|
||||
let provider_request = Request::new_send(conn_id, read_data, socket_closed);
|
||||
let provider_message = Message::Request(provider_request);
|
||||
let lane = TransmissionLane::ConnectionId(conn_id);
|
||||
if anonymous {
|
||||
InputMessage::new_anonymous(
|
||||
@@ -473,10 +413,7 @@ impl SocksClient {
|
||||
|
||||
self.started_proxy = true;
|
||||
self.controller_sender
|
||||
.unbounded_send(ControllerCommand::Insert {
|
||||
connection_id: self.connection_id,
|
||||
connection_sender: mix_sender,
|
||||
})
|
||||
.unbounded_send(ControllerCommand::Insert(self.connection_id, mix_sender))
|
||||
.unwrap();
|
||||
|
||||
info!(
|
||||
@@ -554,13 +491,7 @@ impl SocksClient {
|
||||
/// into the Authenticator (where it'll be more easily testable)
|
||||
/// would be a good next step.
|
||||
async fn authenticate_socks5(&mut self) -> Result<(), SocksProxyError> {
|
||||
debug!(
|
||||
"Authenticating w/ {}",
|
||||
self.stream
|
||||
.peer_addr()
|
||||
.map_err(|source| SocksProxyError::PeerAddrExtractionFailure { source })?
|
||||
.ip()
|
||||
);
|
||||
debug!("Authenticating w/ {}", self.stream.peer_addr()?.ip());
|
||||
// Get valid auth methods
|
||||
let methods = self.get_available_methods().await?;
|
||||
trace!("methods: {:?}", methods);
|
||||
@@ -574,45 +505,27 @@ impl SocksClient {
|
||||
response[1] = AuthenticationMethods::UserPass as u8;
|
||||
|
||||
debug!("Sending USER/PASS packet");
|
||||
self.stream
|
||||
.write_all(&response)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketWriteError { source })?;
|
||||
self.stream.write_all(&response).await?;
|
||||
|
||||
let mut header = [0u8; 2];
|
||||
|
||||
// Read a byte from the stream and determine the version being requested
|
||||
self.stream
|
||||
.read_exact(&mut header)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
self.stream.read_exact(&mut header).await?;
|
||||
|
||||
// debug!("Auth Header: [{}, {}]", header[0], header[1]);
|
||||
|
||||
// Username parsing
|
||||
let ulen = header[1];
|
||||
let mut username = vec![0; ulen as usize];
|
||||
self.stream
|
||||
.read_exact(&mut username)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
self.stream.read_exact(&mut username).await?;
|
||||
|
||||
// Password Parsing
|
||||
let plen = self
|
||||
.stream
|
||||
.read_u8()
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
let plen = self.stream.read_u8().await?;
|
||||
let mut password = vec![0; plen as usize];
|
||||
self.stream
|
||||
.read_exact(&mut password)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
self.stream.read_exact(&mut password).await?;
|
||||
|
||||
let username_str = String::from_utf8(username)
|
||||
.map_err(|source| SocksProxyError::MalformedAuthUsername { source })?;
|
||||
let password_str = String::from_utf8(password)
|
||||
.map_err(|source| SocksProxyError::MalformedAuthPassword { source })?;
|
||||
let username_str = String::from_utf8(username)?;
|
||||
let password_str = String::from_utf8(password)?;
|
||||
|
||||
let user = User {
|
||||
username: username_str,
|
||||
@@ -623,17 +536,11 @@ impl SocksClient {
|
||||
if self.authenticator.is_allowed(&user) {
|
||||
debug!("Access Granted. User: {}", user.username);
|
||||
let response = [1, ResponseCodeV5::Success as u8];
|
||||
self.stream
|
||||
.write_all(&response)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketWriteError { source })?;
|
||||
self.stream.write_all(&response).await?;
|
||||
} else {
|
||||
debug!("Access Denied. User: {}", user.username);
|
||||
let response = [1, ResponseCodeV5::Failure as u8];
|
||||
self.stream
|
||||
.write_all(&response)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketWriteError { source })?;
|
||||
self.stream.write_all(&response).await?;
|
||||
|
||||
// Shutdown
|
||||
self.shutdown().await?;
|
||||
@@ -644,18 +551,12 @@ impl SocksClient {
|
||||
// set the default auth method (no auth)
|
||||
response[1] = AuthenticationMethods::NoAuth as u8;
|
||||
debug!("Sending NOAUTH packet");
|
||||
self.stream
|
||||
.write_all(&response)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketWriteError { source })?;
|
||||
self.stream.write_all(&response).await?;
|
||||
Ok(())
|
||||
} else {
|
||||
warn!("Client has no suitable authentication methods!");
|
||||
response[1] = AuthenticationMethods::NoMethods as u8;
|
||||
self.stream
|
||||
.write_all(&response)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketWriteError { source })?;
|
||||
self.stream.write_all(&response).await?;
|
||||
self.shutdown().await?;
|
||||
Err(ResponseCodeV5::Failure.into())
|
||||
}
|
||||
@@ -666,10 +567,7 @@ impl SocksClient {
|
||||
let mut methods: Vec<u8> = Vec::with_capacity(self.auth_nmethods as usize);
|
||||
for _ in 0..self.auth_nmethods {
|
||||
let mut method = [0u8; 1];
|
||||
self.stream
|
||||
.read_exact(&mut method)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
self.stream.read_exact(&mut method).await?;
|
||||
if self.authenticator.auth_methods.contains(&method[0]) {
|
||||
methods.append(&mut method.to_vec());
|
||||
}
|
||||
|
||||
@@ -5,9 +5,8 @@ use log::*;
|
||||
use client_core::client::received_buffer::ReconstructedMessagesReceiver;
|
||||
use client_core::client::received_buffer::{ReceivedBufferMessage, ReceivedBufferRequestSender};
|
||||
use nymsphinx::receiver::ReconstructedMessage;
|
||||
use proxy_helpers::connection_controller::ControllerSender;
|
||||
use service_providers_common::interface::{ControlResponse, ResponseContent};
|
||||
use socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent};
|
||||
use proxy_helpers::connection_controller::{ControllerCommand, ControllerSender};
|
||||
use socks5_requests::Message;
|
||||
use task::TaskClient;
|
||||
|
||||
use crate::error::Socks5ClientError;
|
||||
@@ -53,39 +52,6 @@ impl MixnetResponseListener {
|
||||
}
|
||||
}
|
||||
|
||||
fn on_control_response(
|
||||
&self,
|
||||
control_response: ControlResponse,
|
||||
) -> Result<(), Socks5ClientError> {
|
||||
error!("received a control response which we don't know how to handle yet!");
|
||||
error!("got: {:?}", control_response);
|
||||
|
||||
// I guess we'd need another channel here to forward those to where they need to go
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_provider_data_response(
|
||||
&self,
|
||||
provider_response: Socks5Response,
|
||||
) -> Result<(), Socks5ClientError> {
|
||||
match provider_response.content {
|
||||
Socks5ResponseContent::ConnectionError(err_response) => {
|
||||
error!(
|
||||
"Network requester failed on connection id {} with error: {}",
|
||||
err_response.connection_id, err_response.network_requester_error
|
||||
);
|
||||
Err(err_response.into())
|
||||
}
|
||||
Socks5ResponseContent::NetworkData(response) => {
|
||||
self.controller_sender
|
||||
.unbounded_send(response.into())
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_message(
|
||||
&self,
|
||||
reconstructed_message: ReconstructedMessage,
|
||||
@@ -94,28 +60,38 @@ impl MixnetResponseListener {
|
||||
if reconstructed_message.sender_tag.is_some() {
|
||||
warn!("this message was sent anonymously - it couldn't have come from the service provider");
|
||||
}
|
||||
match Socks5ProviderResponse::try_from_bytes(&raw_message) {
|
||||
|
||||
let response = match Message::try_from_bytes(&raw_message) {
|
||||
Err(err) => {
|
||||
warn!("failed to parse received response: {err}");
|
||||
Ok(())
|
||||
warn!("failed to parse received response - {err}");
|
||||
return Ok(());
|
||||
}
|
||||
Ok(response) => {
|
||||
// as long as the client used the same (or older) interface than the service provider,
|
||||
// the response should have used exactly the same version
|
||||
trace!(
|
||||
"the received response was sent with {:?} interface version",
|
||||
response.interface_version
|
||||
Ok(Message::Request(_)) => {
|
||||
warn!("unexpected request");
|
||||
return Ok(());
|
||||
}
|
||||
Ok(Message::Response(data)) => data,
|
||||
Ok(Message::NetworkRequesterResponse(r)) => {
|
||||
error!(
|
||||
"Network requester failed on connection id {} with error: {}",
|
||||
r.connection_id, r.network_requester_error
|
||||
);
|
||||
match response.content {
|
||||
ResponseContent::Control(control_response) => {
|
||||
self.on_control_response(control_response)
|
||||
}
|
||||
ResponseContent::ProviderData(provider_response) => {
|
||||
self.on_provider_data_response(provider_response)
|
||||
}
|
||||
}
|
||||
return Err(Socks5ClientError::NetworkRequesterError {
|
||||
connection_id: r.connection_id,
|
||||
error: r.network_requester_error,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.controller_sender
|
||||
.unbounded_send(ControllerCommand::Send(
|
||||
response.connection_id,
|
||||
response.data,
|
||||
response.is_closed,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn run(&mut self) {
|
||||
|
||||
@@ -33,7 +33,7 @@ impl TryFrom<u8> for SocksVersion {
|
||||
match version {
|
||||
SOCKS4_VERSION => Ok(Self::V4),
|
||||
SOCKS5_VERSION => Ok(Self::V5),
|
||||
_ => Err(SocksProxyError::UnsupportedProxyVersion { version }),
|
||||
_ => Err(SocksProxyError::UnsupportedProxyVersion(version)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,7 @@ impl SocksRequest {
|
||||
log::trace!("read from stream socks4");
|
||||
|
||||
let mut packet = [0u8; 3];
|
||||
stream
|
||||
.read_exact(&mut packet)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
stream.read_exact(&mut packet).await?;
|
||||
|
||||
// CD (command)
|
||||
let Some(command) = SocksCommand::from(packet[0] as usize) else {
|
||||
@@ -46,10 +43,7 @@ impl SocksRequest {
|
||||
|
||||
// DSTIP
|
||||
let mut ip = [0u8; 4];
|
||||
stream
|
||||
.read_exact(&mut ip)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
stream.read_exact(&mut ip).await?;
|
||||
|
||||
// USERID
|
||||
let _userid = read_until_zero(stream).await;
|
||||
@@ -82,17 +76,12 @@ impl SocksRequest {
|
||||
|
||||
let mut packet = [0u8; 4];
|
||||
// Read a byte from the stream and determine the version being requested
|
||||
stream
|
||||
.read_exact(&mut packet)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
stream.read_exact(&mut packet).await?;
|
||||
|
||||
// VER
|
||||
if packet[0] != SOCKS5_VERSION {
|
||||
warn!("Unsupported version: SOCKS{}", packet[0]);
|
||||
return Err(SocksProxyError::UnsupportedProxyVersion {
|
||||
version: (packet[0]),
|
||||
});
|
||||
return Err(SocksProxyError::UnsupportedProxyVersion(packet[0]));
|
||||
}
|
||||
|
||||
// CMD
|
||||
@@ -114,41 +103,26 @@ impl SocksRequest {
|
||||
let addr = match addr_type {
|
||||
AddrType::Domain => {
|
||||
let mut domain_length = [0u8];
|
||||
stream
|
||||
.read_exact(&mut domain_length)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
stream.read_exact(&mut domain_length).await?;
|
||||
let mut domain = vec![0u8; domain_length[0] as usize];
|
||||
stream
|
||||
.read_exact(&mut domain)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
stream.read_exact(&mut domain).await?;
|
||||
domain
|
||||
}
|
||||
AddrType::V4 => {
|
||||
let mut addr = [0u8; 4];
|
||||
stream
|
||||
.read_exact(&mut addr)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
stream.read_exact(&mut addr).await?;
|
||||
addr.to_vec()
|
||||
}
|
||||
AddrType::V6 => {
|
||||
let mut addr = [0u8; 16];
|
||||
stream
|
||||
.read_exact(&mut addr)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
stream.read_exact(&mut addr).await?;
|
||||
addr.to_vec()
|
||||
}
|
||||
};
|
||||
|
||||
// DST.PORT
|
||||
let mut port = [0u8; 2];
|
||||
stream
|
||||
.read_exact(&mut port)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
stream.read_exact(&mut port).await?;
|
||||
let port = merge_u8_into_u16(port[0], port[1]);
|
||||
|
||||
Ok(SocksRequest {
|
||||
@@ -205,10 +179,7 @@ where
|
||||
let mut result = Vec::new();
|
||||
let mut char = [0u8];
|
||||
loop {
|
||||
stream
|
||||
.read_exact(&mut char)
|
||||
.await
|
||||
.map_err(|source| SocksProxyError::SocketReadError { source })?;
|
||||
stream.read_exact(&mut char).await?;
|
||||
if char[0] == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -86,11 +86,6 @@ impl SphinxSocksServer {
|
||||
mixnet_response_listener.run().await;
|
||||
});
|
||||
|
||||
// TODO:, if required, there should be another task here responsible for control requests.
|
||||
// it should get `input_sender` to send actual requests into the mixnet
|
||||
// and some channel that connects it from `MixnetResponseListener` to receive
|
||||
// any control responses
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
Ok((stream, _remote)) = listener.accept() => {
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
use socks5_requests::Socks5RequestError;
|
||||
use std::string::FromUtf8Error;
|
||||
use thiserror::Error;
|
||||
|
||||
/// SOCKS4 Response codes
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum ResponseCodeV4 {
|
||||
@@ -12,8 +8,9 @@ pub(crate) enum ResponseCodeV4 {
|
||||
}
|
||||
|
||||
/// Possible SOCKS5 Response Codes
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ResponseCodeV5 {
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub(crate) enum ResponseCodeV5 {
|
||||
#[error("SOCKS5 Server Success")]
|
||||
Success = 0x00,
|
||||
#[error("SOCKS5 Server Failure")]
|
||||
@@ -34,55 +31,30 @@ pub enum ResponseCodeV5 {
|
||||
AddrTypeNotSupported = 0x08,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub enum SocksProxyError {
|
||||
#[error("{version} of the socks protocol is not supported by this client")]
|
||||
UnsupportedProxyVersion { version: u8 },
|
||||
GenericError(Box<dyn std::error::Error + Send + Sync>),
|
||||
UnsupportedProxyVersion(u8),
|
||||
}
|
||||
|
||||
#[error("failed to write to the socket: {source}")]
|
||||
SocketWriteError {
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
impl std::fmt::Display for SocksProxyError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SocksProxyError::GenericError(err) => write!(f, "GenericError - {err}"),
|
||||
SocksProxyError::UnsupportedProxyVersion(version) => {
|
||||
write!(f, "Unsupported proxy version {}", version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[error("failed to read from the socket: {source}")]
|
||||
SocketReadError {
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
|
||||
#[error("failed to shutdown underlying socket stream: {source}")]
|
||||
SocketShutdownFailure {
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
|
||||
#[error("failed to extract ip address of the connected peer: {source}")]
|
||||
PeerAddrExtractionFailure {
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
},
|
||||
|
||||
#[error("failed to authenticate user due to malformed username: {source}")]
|
||||
MalformedAuthUsername {
|
||||
#[source]
|
||||
source: FromUtf8Error,
|
||||
},
|
||||
|
||||
#[error("failed to authenticate user due to malformed password: {source}")]
|
||||
MalformedAuthPassword {
|
||||
#[source]
|
||||
source: FromUtf8Error,
|
||||
},
|
||||
|
||||
#[error(transparent)]
|
||||
Socks5ResponseFailure(#[from] ResponseCodeV5),
|
||||
|
||||
#[error("could not complete the provider request: {source}")]
|
||||
ProviderRequestFailure {
|
||||
#[from]
|
||||
source: Socks5RequestError,
|
||||
},
|
||||
impl<E> From<E> for SocksProxyError
|
||||
where
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
fn from(err: E) -> Self {
|
||||
SocksProxyError::GenericError(Box::new(err))
|
||||
}
|
||||
}
|
||||
|
||||
/// DST.addr variant types
|
||||
|
||||
@@ -6,10 +6,6 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true, features = ["derive"], optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "7", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
@@ -4,7 +4,6 @@
|
||||
// TODO: at a later date this crate should probably also expose `ContractBuildInformation`
|
||||
// and be used by our smart contracts
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BinaryBuildInformation {
|
||||
// VERGEN_BUILD_TIMESTAMP
|
||||
/// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.
|
||||
@@ -54,19 +53,6 @@ impl BinaryBuildInformation {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> BinaryBuildInformationOwned {
|
||||
BinaryBuildInformationOwned {
|
||||
build_timestamp: self.build_timestamp.to_owned(),
|
||||
build_version: self.build_version.to_owned(),
|
||||
commit_sha: self.commit_sha.to_owned(),
|
||||
commit_timestamp: self.commit_timestamp.to_owned(),
|
||||
commit_branch: self.commit_branch.to_owned(),
|
||||
rustc_version: self.rustc_version.to_owned(),
|
||||
rustc_channel: self.rustc_channel.to_owned(),
|
||||
cargo_profile: self.cargo_profile.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pretty_print(&self) -> String {
|
||||
format!(
|
||||
r#"
|
||||
@@ -98,39 +84,3 @@ impl BinaryBuildInformation {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct BinaryBuildInformationOwned {
|
||||
// VERGEN_BUILD_TIMESTAMP
|
||||
/// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.
|
||||
pub build_timestamp: String,
|
||||
|
||||
// VERGEN_BUILD_SEMVER
|
||||
/// Provides the build version, for example `0.1.0-9-g46f83e1`.
|
||||
pub build_version: String,
|
||||
|
||||
// VERGEN_GIT_SHA
|
||||
/// Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`.
|
||||
pub commit_sha: String,
|
||||
|
||||
// VERGEN_GIT_COMMIT_TIMESTAMP
|
||||
/// Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`.
|
||||
pub commit_timestamp: String,
|
||||
|
||||
// VERGEN_GIT_BRANCH
|
||||
/// Provides the name of the git branch that was used for the build, for example `master`.
|
||||
pub commit_branch: String,
|
||||
|
||||
// VERGEN_RUSTC_SEMVER
|
||||
/// Provides the rustc version that was used for the build, for example `1.52.0-nightly`.
|
||||
pub rustc_version: String,
|
||||
|
||||
// VERGEN_RUSTC_CHANNEL
|
||||
/// Provides the rustc channel that was used for the build, for example `nightly`.
|
||||
pub rustc_channel: String,
|
||||
|
||||
// VERGEN_CARGO_PROFILE
|
||||
/// Provides the cargo profile that was used for the build, for example `debug`.
|
||||
pub cargo_profile: String,
|
||||
}
|
||||
|
||||
@@ -441,7 +441,6 @@ where
|
||||
}
|
||||
|
||||
debug_assert!(self.connection.is_available());
|
||||
log::trace!("Registering gateway");
|
||||
|
||||
// it's fine to instantiate it here as it's only used once (during authentication or registration)
|
||||
// and putting it into the GatewayClient struct would be a hassle
|
||||
|
||||
@@ -934,6 +934,16 @@ impl NymApiClient {
|
||||
Ok(self.nym_api_client.blind_sign(request_body).await?)
|
||||
}
|
||||
|
||||
pub async fn partial_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &str,
|
||||
) -> Result<BlindedSignatureResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.nym_api_client
|
||||
.partial_bandwidth_credential(request_body)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn verify_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
|
||||
@@ -449,6 +449,23 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn partial_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &str,
|
||||
) -> Result<BlindedSignatureResponse, NymAPIError> {
|
||||
self.post_nym_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
routes::BANDWIDTH,
|
||||
routes::COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL,
|
||||
],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn verify_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
|
||||
@@ -14,6 +14,7 @@ pub const COCONUT_ROUTES: &str = "coconut";
|
||||
pub const BANDWIDTH: &str = "bandwidth";
|
||||
|
||||
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
|
||||
pub const COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL: &str = "partial-bandwidth-credential";
|
||||
pub const COCONUT_VERIFY_BANDWIDTH_CREDENTIAL: &str = "verify-bandwidth-credential";
|
||||
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
|
||||
@@ -8,7 +8,7 @@ use coconut_dkg_common::dealer::{
|
||||
DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse,
|
||||
};
|
||||
use coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
|
||||
use coconut_dkg_common::types::{Epoch, EpochId, InitialReplacementData};
|
||||
use coconut_dkg_common::types::{Epoch, EpochId};
|
||||
use coconut_dkg_common::verification_key::PagedVKSharesResponse;
|
||||
use cosmrs::AccountId;
|
||||
|
||||
@@ -16,7 +16,6 @@ use cosmrs::AccountId;
|
||||
pub trait DkgQueryClient {
|
||||
async fn get_current_epoch(&self) -> Result<Epoch, NyxdError>;
|
||||
async fn get_current_epoch_threshold(&self) -> Result<Option<u64>, NyxdError>;
|
||||
async fn get_initial_dealers(&self) -> Result<Option<InitialReplacementData>, NyxdError>;
|
||||
async fn get_dealer_details(
|
||||
&self,
|
||||
address: &AccountId,
|
||||
@@ -63,14 +62,6 @@ where
|
||||
.query_contract_smart(self.coconut_dkg_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_initial_dealers(&self) -> Result<Option<InitialReplacementData>, NyxdError> {
|
||||
let request = DkgQueryMsg::GetInitialDealers {};
|
||||
self.client
|
||||
.query_contract_smart(self.coconut_dkg_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_dealer_details(
|
||||
&self,
|
||||
address: &AccountId,
|
||||
|
||||
@@ -17,21 +17,18 @@ pub trait DkgSigningClient {
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
|
||||
async fn submit_dealing_bytes(
|
||||
&self,
|
||||
commitment: ContractSafeBytes,
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
|
||||
async fn submit_verification_key_share(
|
||||
&self,
|
||||
share: VerificationKeyShare,
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
}
|
||||
@@ -60,13 +57,11 @@ where
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof: bte_key,
|
||||
announce_address,
|
||||
resharing,
|
||||
};
|
||||
|
||||
self.client
|
||||
@@ -84,13 +79,9 @@ where
|
||||
async fn submit_dealing_bytes(
|
||||
&self,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::CommitDealing {
|
||||
dealing_bytes,
|
||||
resharing,
|
||||
};
|
||||
let req = DkgExecuteMsg::CommitDealing { dealing_bytes };
|
||||
|
||||
self.client
|
||||
.execute(
|
||||
@@ -107,10 +98,9 @@ where
|
||||
async fn submit_verification_key_share(
|
||||
&self,
|
||||
share: VerificationKeyShare,
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::CommitVerificationKeyShare { share, resharing };
|
||||
let req = DkgExecuteMsg::CommitVerificationKeyShare { share };
|
||||
|
||||
self.client
|
||||
.execute(
|
||||
|
||||
@@ -21,22 +21,18 @@ pub enum ExecuteMsg {
|
||||
RegisterDealer {
|
||||
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
},
|
||||
|
||||
CommitDealing {
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
resharing: bool,
|
||||
},
|
||||
|
||||
CommitVerificationKeyShare {
|
||||
share: VerificationKeyShare,
|
||||
resharing: bool,
|
||||
},
|
||||
|
||||
VerifyVerificationKeyShare {
|
||||
owner: Addr,
|
||||
resharing: bool,
|
||||
},
|
||||
|
||||
SurpassedThreshold {},
|
||||
@@ -49,7 +45,6 @@ pub enum ExecuteMsg {
|
||||
pub enum QueryMsg {
|
||||
GetCurrentEpochState {},
|
||||
GetCurrentEpochThreshold {},
|
||||
GetInitialDealers {},
|
||||
GetDealerDetails {
|
||||
dealer_address: String,
|
||||
},
|
||||
|
||||
@@ -18,12 +18,6 @@ pub type EpochId = u64;
|
||||
// 2 public attributes, 2 private attributes, 1 fixed for coconut credential
|
||||
pub const TOTAL_DEALINGS: usize = 2 + 2 + 1;
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct InitialReplacementData {
|
||||
pub initial_dealers: Vec<Addr>,
|
||||
pub initial_height: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, JsonSchema,
|
||||
)]
|
||||
@@ -92,17 +86,15 @@ impl Epoch {
|
||||
current_timestamp: Timestamp,
|
||||
) -> Self {
|
||||
let duration = match state {
|
||||
EpochState::PublicKeySubmission { .. } => {
|
||||
time_configuration.public_key_submission_time_secs
|
||||
}
|
||||
EpochState::DealingExchange { .. } => time_configuration.dealing_exchange_time_secs,
|
||||
EpochState::VerificationKeySubmission { .. } => {
|
||||
EpochState::PublicKeySubmission => time_configuration.public_key_submission_time_secs,
|
||||
EpochState::DealingExchange => time_configuration.dealing_exchange_time_secs,
|
||||
EpochState::VerificationKeySubmission => {
|
||||
time_configuration.verification_key_submission_time_secs
|
||||
}
|
||||
EpochState::VerificationKeyValidation { .. } => {
|
||||
EpochState::VerificationKeyValidation => {
|
||||
time_configuration.verification_key_validation_time_secs
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { .. } => {
|
||||
EpochState::VerificationKeyFinalization => {
|
||||
time_configuration.verification_key_finalization_time_secs
|
||||
}
|
||||
EpochState::InProgress => time_configuration.in_progress_time_secs,
|
||||
@@ -114,33 +106,6 @@ impl Epoch {
|
||||
finish_timestamp: current_timestamp.plus_seconds(duration),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn final_timestamp_secs(&self) -> u64 {
|
||||
let mut finish = self.finish_timestamp.seconds();
|
||||
let time_configuration = self.time_configuration;
|
||||
let mut curr_epoch_state = self.state;
|
||||
while let Some(state) = curr_epoch_state.next() {
|
||||
curr_epoch_state = state;
|
||||
let adding = match curr_epoch_state {
|
||||
EpochState::PublicKeySubmission { .. } => {
|
||||
time_configuration.public_key_submission_time_secs
|
||||
}
|
||||
EpochState::DealingExchange { .. } => time_configuration.dealing_exchange_time_secs,
|
||||
EpochState::VerificationKeySubmission { .. } => {
|
||||
time_configuration.verification_key_submission_time_secs
|
||||
}
|
||||
EpochState::VerificationKeyValidation { .. } => {
|
||||
time_configuration.verification_key_validation_time_secs
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { .. } => {
|
||||
time_configuration.verification_key_finalization_time_secs
|
||||
}
|
||||
EpochState::InProgress { .. } => 0,
|
||||
};
|
||||
finish += adding;
|
||||
}
|
||||
finish
|
||||
}
|
||||
}
|
||||
|
||||
// currently (it is still extremely likely to change, we might be able to get rid of verification key-related complaints),
|
||||
@@ -158,36 +123,28 @@ impl Epoch {
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum EpochState {
|
||||
PublicKeySubmission { resharing: bool },
|
||||
DealingExchange { resharing: bool },
|
||||
VerificationKeySubmission { resharing: bool },
|
||||
VerificationKeyValidation { resharing: bool },
|
||||
VerificationKeyFinalization { resharing: bool },
|
||||
PublicKeySubmission,
|
||||
DealingExchange,
|
||||
VerificationKeySubmission,
|
||||
VerificationKeyValidation,
|
||||
VerificationKeyFinalization,
|
||||
InProgress,
|
||||
}
|
||||
|
||||
impl Default for EpochState {
|
||||
fn default() -> Self {
|
||||
Self::PublicKeySubmission { resharing: false }
|
||||
Self::PublicKeySubmission
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EpochState {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
EpochState::PublicKeySubmission { resharing } => {
|
||||
write!(f, "PublicKeySubmission with resharing {resharing}")
|
||||
}
|
||||
EpochState::DealingExchange { resharing } => write!(f, "DealingExchange {resharing}"),
|
||||
EpochState::VerificationKeySubmission { resharing } => {
|
||||
write!(f, "VerificationKeySubmission with resharing {resharing}")
|
||||
}
|
||||
EpochState::VerificationKeyValidation { resharing } => {
|
||||
write!(f, "VerificationKeyValidation with resharing {resharing}")
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { resharing } => {
|
||||
write!(f, "VerificationKeyFinalization with resharing {resharing}")
|
||||
}
|
||||
EpochState::PublicKeySubmission => write!(f, "PublicKeySubmission"),
|
||||
EpochState::DealingExchange => write!(f, "DealingExchange"),
|
||||
EpochState::VerificationKeySubmission => write!(f, "VerificationKeySubmission"),
|
||||
EpochState::VerificationKeyValidation => write!(f, "VerificationKeyValidation"),
|
||||
EpochState::VerificationKeyFinalization => write!(f, "VerificationKeyFinalization"),
|
||||
EpochState::InProgress => write!(f, "InProgress"),
|
||||
}
|
||||
}
|
||||
@@ -196,19 +153,11 @@ impl Display for EpochState {
|
||||
impl EpochState {
|
||||
pub fn next(self) -> Option<Self> {
|
||||
match self {
|
||||
EpochState::PublicKeySubmission { resharing } => {
|
||||
Some(EpochState::DealingExchange { resharing })
|
||||
}
|
||||
EpochState::DealingExchange { resharing } => {
|
||||
Some(EpochState::VerificationKeySubmission { resharing })
|
||||
}
|
||||
EpochState::VerificationKeySubmission { resharing } => {
|
||||
Some(EpochState::VerificationKeyValidation { resharing })
|
||||
}
|
||||
EpochState::VerificationKeyValidation { resharing } => {
|
||||
Some(EpochState::VerificationKeyFinalization { resharing })
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { .. } => Some(EpochState::InProgress),
|
||||
EpochState::PublicKeySubmission => Some(EpochState::DealingExchange),
|
||||
EpochState::DealingExchange => Some(EpochState::VerificationKeySubmission),
|
||||
EpochState::VerificationKeySubmission => Some(EpochState::VerificationKeyValidation),
|
||||
EpochState::VerificationKeyValidation => Some(EpochState::VerificationKeyFinalization),
|
||||
EpochState::VerificationKeyFinalization => Some(EpochState::InProgress),
|
||||
EpochState::InProgress => None,
|
||||
}
|
||||
}
|
||||
@@ -222,8 +171,4 @@ impl EpochState {
|
||||
|
||||
states
|
||||
}
|
||||
|
||||
pub fn is_final(&self) -> bool {
|
||||
*self == EpochState::InProgress
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +30,11 @@ pub struct PagedVKSharesResponse {
|
||||
|
||||
pub fn to_cosmos_msg(
|
||||
owner: Addr,
|
||||
resharing: bool,
|
||||
coconut_dkg_addr: String,
|
||||
multisig_addr: String,
|
||||
expiration_time: Timestamp,
|
||||
) -> StdResult<CosmosMsg> {
|
||||
let verify_vk_share_req = ExecuteMsg::VerifyVerificationKeyShare { owner, resharing };
|
||||
let verify_vk_share_req = ExecuteMsg::VerifyVerificationKeyShare { owner };
|
||||
let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: coconut_dkg_addr,
|
||||
msg: to_binary(&verify_vk_share_req)?,
|
||||
@@ -63,8 +62,7 @@ pub fn owner_from_cosmos_msgs(msgs: &[CosmosMsg]) -> Option<Addr> {
|
||||
funds: _,
|
||||
})) = msgs.get(0)
|
||||
{
|
||||
if let Ok(ExecuteMsg::VerifyVerificationKeyShare { owner, .. }) =
|
||||
from_binary::<ExecuteMsg>(msg)
|
||||
if let Ok(ExecuteMsg::VerifyVerificationKeyShare { owner }) = from_binary::<ExecuteMsg>(msg)
|
||||
{
|
||||
return Some(owner);
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -42,9 +42,41 @@ pub struct BandwidthVoucher {
|
||||
encryption_key: encryption::PrivateKey,
|
||||
pedersen_commitments_openings: Vec<Attribute>,
|
||||
blind_sign_request: BlindSignRequest,
|
||||
use_request: bool,
|
||||
}
|
||||
|
||||
impl BandwidthVoucher {
|
||||
pub fn new_with_blind_sign_req(
|
||||
private_attributes: [PrivateAttribute; PRIVATE_ATTRIBUTES as usize],
|
||||
public_attributes_plain: [&str; PUBLIC_ATTRIBUTES as usize],
|
||||
tx_hash: Hash,
|
||||
signing_key: identity::PrivateKey,
|
||||
encryption_key: encryption::PrivateKey,
|
||||
pedersen_commitments_openings: Vec<Attribute>,
|
||||
blind_sign_request: BlindSignRequest,
|
||||
) -> Self {
|
||||
let voucher_value = public_attributes_plain[0];
|
||||
let voucher_info = public_attributes_plain[1];
|
||||
let voucher_value_plain = voucher_value.to_string();
|
||||
let voucher_info_plain = voucher_info.to_string();
|
||||
let voucher_value = hash_to_scalar(voucher_value.as_bytes());
|
||||
let voucher_info = hash_to_scalar(voucher_info.as_bytes());
|
||||
|
||||
BandwidthVoucher {
|
||||
serial_number: private_attributes[0],
|
||||
binding_number: private_attributes[1],
|
||||
voucher_value,
|
||||
voucher_value_plain,
|
||||
voucher_info,
|
||||
voucher_info_plain,
|
||||
tx_hash,
|
||||
signing_key,
|
||||
encryption_key,
|
||||
pedersen_commitments_openings,
|
||||
blind_sign_request,
|
||||
use_request: false,
|
||||
}
|
||||
}
|
||||
pub fn new(
|
||||
params: &Parameters,
|
||||
voucher_value: String,
|
||||
@@ -77,135 +109,10 @@ impl BandwidthVoucher {
|
||||
encryption_key,
|
||||
pedersen_commitments_openings,
|
||||
blind_sign_request,
|
||||
use_request: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let serial_number_b = self.serial_number.to_bytes();
|
||||
let binding_number_b = self.binding_number.to_bytes();
|
||||
let voucher_value_plain_b = self.voucher_value_plain.as_bytes();
|
||||
let voucher_info_plain_b = self.voucher_info_plain.as_bytes();
|
||||
let tx_hash_b = self.tx_hash.as_bytes();
|
||||
let signing_key_b = self.signing_key.to_bytes();
|
||||
let encryption_key_b = self.encryption_key.to_bytes();
|
||||
let blind_sign_request_b = self.blind_sign_request.to_bytes();
|
||||
|
||||
let mut ret = Vec::new();
|
||||
|
||||
ret.extend_from_slice(&serial_number_b);
|
||||
ret.extend_from_slice(&binding_number_b);
|
||||
ret.extend_from_slice(tx_hash_b);
|
||||
ret.extend_from_slice(&signing_key_b);
|
||||
ret.extend_from_slice(&encryption_key_b);
|
||||
ret.extend_from_slice(&(voucher_value_plain_b.len() as u64).to_be_bytes());
|
||||
ret.extend_from_slice(&(voucher_info_plain_b.len() as u64).to_be_bytes());
|
||||
ret.extend_from_slice(&(blind_sign_request_b.len() as u64).to_be_bytes());
|
||||
ret.extend_from_slice(&(self.pedersen_commitments_openings.len() as u64).to_be_bytes());
|
||||
ret.extend_from_slice(voucher_value_plain_b);
|
||||
ret.extend_from_slice(voucher_info_plain_b);
|
||||
ret.extend_from_slice(&blind_sign_request_b);
|
||||
for commitment in self.pedersen_commitments_openings.iter() {
|
||||
ret.extend_from_slice(&commitment.to_bytes());
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, Error> {
|
||||
if bytes.len() < 32 * 5 + 4 * 8 {
|
||||
return Err(Error::BandwidthVoucherDeserializationError(format!(
|
||||
"Less then {} bytes needed",
|
||||
32 * 5 + 4 * 8
|
||||
)));
|
||||
}
|
||||
let mut buff = [0u8; 32];
|
||||
let mut small_buff = [0u8; 8];
|
||||
let scalar_err =
|
||||
|| Error::BandwidthVoucherDeserializationError(String::from("Invalid Scalar"));
|
||||
buff.copy_from_slice(&bytes[..32]);
|
||||
let serial_number = Option::<PrivateAttribute>::from(PrivateAttribute::from_bytes(&buff))
|
||||
.ok_or_else(scalar_err)?;
|
||||
buff.copy_from_slice(&bytes[32..2 * 32]);
|
||||
let binding_number = Option::<PrivateAttribute>::from(PrivateAttribute::from_bytes(&buff))
|
||||
.ok_or_else(scalar_err)?;
|
||||
buff.copy_from_slice(&bytes[2 * 32..3 * 32]);
|
||||
let tx_hash = Hash::new(buff);
|
||||
buff.copy_from_slice(&bytes[3 * 32..4 * 32]);
|
||||
let signing_key = identity::PrivateKey::from_bytes(&buff).map_err(|_| {
|
||||
Error::BandwidthVoucherDeserializationError(String::from("Invalid key"))
|
||||
})?;
|
||||
buff.copy_from_slice(&bytes[4 * 32..5 * 32]);
|
||||
let encryption_key = encryption::PrivateKey::from_bytes(&buff).map_err(|_| {
|
||||
Error::BandwidthVoucherDeserializationError(String::from("Invalid key"))
|
||||
})?;
|
||||
small_buff.copy_from_slice(&bytes[5 * 32..5 * 32 + 8]);
|
||||
let voucher_value_plain_no = u64::from_be_bytes(small_buff) as usize;
|
||||
small_buff.copy_from_slice(&bytes[5 * 32 + 8..5 * 32 + 2 * 8]);
|
||||
let voucher_info_plain_no = u64::from_be_bytes(small_buff) as usize;
|
||||
small_buff.copy_from_slice(&bytes[5 * 32 + 2 * 8..5 * 32 + 3 * 8]);
|
||||
let blind_sign_request_no = u64::from_be_bytes(small_buff) as usize;
|
||||
small_buff.copy_from_slice(&bytes[5 * 32 + 3 * 8..5 * 32 + 4 * 8]);
|
||||
let pedersen_commitments_openings_no = u64::from_be_bytes(small_buff) as usize;
|
||||
|
||||
let total_length = 32 * 5
|
||||
+ 4 * 8
|
||||
+ voucher_value_plain_no
|
||||
+ voucher_info_plain_no
|
||||
+ blind_sign_request_no
|
||||
+ pedersen_commitments_openings_no * 32;
|
||||
if bytes.len() != total_length {
|
||||
return Err(Error::BandwidthVoucherDeserializationError(format!(
|
||||
"Expected {total_length} bytes",
|
||||
)));
|
||||
}
|
||||
|
||||
let utf_err = |_| {
|
||||
Err(Error::BandwidthVoucherDeserializationError(String::from(
|
||||
"Invalid UTF8 string",
|
||||
)))
|
||||
};
|
||||
let mut var_length_pointer = 5 * 32 + 4 * 8;
|
||||
let voucher_value_plain = String::from_utf8(
|
||||
bytes[var_length_pointer..var_length_pointer + voucher_value_plain_no].to_vec(),
|
||||
)
|
||||
.or_else(utf_err)?;
|
||||
let voucher_value = hash_to_scalar(&voucher_value_plain);
|
||||
var_length_pointer += voucher_value_plain_no;
|
||||
let voucher_info_plain = String::from_utf8(
|
||||
bytes[var_length_pointer..var_length_pointer + voucher_info_plain_no].to_vec(),
|
||||
)
|
||||
.or_else(utf_err)?;
|
||||
let voucher_info = hash_to_scalar(&voucher_info_plain);
|
||||
var_length_pointer += voucher_info_plain_no;
|
||||
let blind_sign_request = BlindSignRequest::from_bytes(
|
||||
&bytes[var_length_pointer..var_length_pointer + blind_sign_request_no],
|
||||
)?;
|
||||
var_length_pointer += blind_sign_request_no;
|
||||
|
||||
let mut pedersen_commitments_openings = Vec::new();
|
||||
for _ in 0..pedersen_commitments_openings_no {
|
||||
buff.copy_from_slice(&bytes[var_length_pointer..var_length_pointer + 32]);
|
||||
let commitment =
|
||||
Option::<Attribute>::from(Attribute::from_bytes(&buff)).ok_or_else(scalar_err)?;
|
||||
var_length_pointer += 32;
|
||||
pedersen_commitments_openings.push(commitment);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
serial_number,
|
||||
binding_number,
|
||||
voucher_value,
|
||||
voucher_value_plain,
|
||||
voucher_info,
|
||||
voucher_info_plain,
|
||||
tx_hash,
|
||||
signing_key,
|
||||
encryption_key,
|
||||
pedersen_commitments_openings,
|
||||
blind_sign_request,
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if the plain values correspond to the PublicAttributes
|
||||
pub fn verify_against_plain(values: &[PublicAttribute], plain_values: &[String]) -> bool {
|
||||
values.len() == 2
|
||||
@@ -234,8 +141,8 @@ impl BandwidthVoucher {
|
||||
&self.blind_sign_request
|
||||
}
|
||||
|
||||
pub fn get_voucher_value(&self) -> String {
|
||||
self.voucher_value_plain.clone()
|
||||
pub fn use_request(&self) -> bool {
|
||||
self.use_request
|
||||
}
|
||||
|
||||
pub fn get_public_attributes_plain(&self) -> Vec<String> {
|
||||
@@ -282,13 +189,13 @@ pub fn prepare_for_spending(
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use coconut_interface::Base58;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
fn voucher_fixture() -> BandwidthVoucher {
|
||||
#[test]
|
||||
fn voucher_consistency() {
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let mut rng = OsRng;
|
||||
BandwidthVoucher::new(
|
||||
let voucher = BandwidthVoucher::new(
|
||||
¶ms,
|
||||
"1234".to_string(),
|
||||
"voucher info".to_string(),
|
||||
@@ -303,48 +210,7 @@ mod test {
|
||||
&encryption::KeyPair::new(&mut rng).private_key().to_bytes(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_voucher() {
|
||||
let voucher = voucher_fixture();
|
||||
let bytes = voucher.to_bytes();
|
||||
let deserialized_voucher = BandwidthVoucher::try_from_bytes(&bytes).unwrap();
|
||||
assert_eq!(voucher.serial_number, deserialized_voucher.serial_number);
|
||||
assert_eq!(voucher.binding_number, deserialized_voucher.binding_number);
|
||||
assert_eq!(voucher.voucher_value, deserialized_voucher.voucher_value);
|
||||
assert_eq!(
|
||||
voucher.voucher_value_plain,
|
||||
deserialized_voucher.voucher_value_plain
|
||||
);
|
||||
assert_eq!(voucher.voucher_info, deserialized_voucher.voucher_info);
|
||||
assert_eq!(
|
||||
voucher.voucher_info_plain,
|
||||
deserialized_voucher.voucher_info_plain
|
||||
);
|
||||
assert_eq!(voucher.tx_hash, deserialized_voucher.tx_hash);
|
||||
assert_eq!(
|
||||
voucher.signing_key.to_string(),
|
||||
deserialized_voucher.signing_key.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
voucher.encryption_key.to_string(),
|
||||
deserialized_voucher.encryption_key.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
voucher.pedersen_commitments_openings,
|
||||
deserialized_voucher.pedersen_commitments_openings
|
||||
);
|
||||
assert_eq!(
|
||||
voucher.blind_sign_request.to_bs58(),
|
||||
deserialized_voucher.blind_sign_request.to_bs58()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn voucher_consistency() {
|
||||
let voucher = voucher_fixture();
|
||||
assert!(!BandwidthVoucher::verify_against_plain(
|
||||
&[],
|
||||
&voucher.get_public_attributes_plain()
|
||||
|
||||
@@ -45,15 +45,21 @@ async fn obtain_partial_credential(
|
||||
let private_attributes = attributes.get_private_attributes();
|
||||
let blind_sign_request = attributes.blind_sign_request();
|
||||
|
||||
let blind_sign_request_body = BlindSignRequestBody::new(
|
||||
blind_sign_request,
|
||||
attributes.tx_hash().to_string(),
|
||||
attributes.sign(blind_sign_request).to_base58_string(),
|
||||
&public_attributes,
|
||||
public_attributes_plain,
|
||||
(public_attributes.len() + private_attributes.len()) as u32,
|
||||
);
|
||||
let response = client.blind_sign(&blind_sign_request_body).await?;
|
||||
let response = if attributes.use_request() {
|
||||
let blind_sign_request_body = BlindSignRequestBody::new(
|
||||
blind_sign_request,
|
||||
attributes.tx_hash().to_string(),
|
||||
attributes.sign(blind_sign_request).to_base58_string(),
|
||||
&public_attributes,
|
||||
public_attributes_plain,
|
||||
(public_attributes.len() + private_attributes.len()) as u32,
|
||||
);
|
||||
client.blind_sign(&blind_sign_request_body).await?
|
||||
} else {
|
||||
client
|
||||
.partial_bandwidth_credential(&attributes.tx_hash().to_string())
|
||||
.await?
|
||||
};
|
||||
let encrypted_signature = response.encrypted_signature;
|
||||
let remote_key = PublicKey::from_bytes(&response.remote_key)?;
|
||||
|
||||
@@ -86,7 +92,6 @@ pub async fn obtain_aggregate_signature(
|
||||
params: &Parameters,
|
||||
attributes: &BandwidthVoucher,
|
||||
coconut_api_clients: &[CoconutApiClient],
|
||||
threshold: u64,
|
||||
) -> Result<Signature, Error> {
|
||||
if coconut_api_clients.is_empty() {
|
||||
return Err(Error::NoValidatorsAvailable);
|
||||
@@ -103,32 +108,32 @@ pub async fn obtain_aggregate_signature(
|
||||
.iter()
|
||||
.map(|api_client| api_client.node_id)
|
||||
.collect();
|
||||
let verification_key =
|
||||
aggregate_verification_keys(&validators_partial_vks, Some(indices.as_ref()))?;
|
||||
|
||||
for coconut_api_client in coconut_api_clients.iter() {
|
||||
if let Ok(signature) = obtain_partial_credential(
|
||||
let signature = obtain_partial_credential(
|
||||
params,
|
||||
attributes,
|
||||
&coconut_api_client.api_client,
|
||||
&coconut_api_client.verification_key,
|
||||
)
|
||||
.await
|
||||
{
|
||||
let share = SignatureShare::new(signature, coconut_api_client.node_id);
|
||||
shares.push(share)
|
||||
}
|
||||
}
|
||||
if shares.len() < threshold as usize {
|
||||
return Err(Error::NotEnoughShares);
|
||||
.await?;
|
||||
let share = SignatureShare::new(signature, coconut_api_client.node_id);
|
||||
shares.push(share)
|
||||
}
|
||||
|
||||
let mut attributes = Vec::with_capacity(private_attributes.len() + public_attributes.len());
|
||||
attributes.extend_from_slice(&private_attributes);
|
||||
attributes.extend_from_slice(&public_attributes);
|
||||
|
||||
aggregate_signature_shares(params, &verification_key, &attributes, &shares)
|
||||
.map_err(Error::SignatureAggregationError)
|
||||
let verification_key =
|
||||
aggregate_verification_keys(&validators_partial_vks, Some(indices.as_ref()))?;
|
||||
|
||||
Ok(aggregate_signature_shares(
|
||||
params,
|
||||
&verification_key,
|
||||
&attributes,
|
||||
&shares,
|
||||
)?)
|
||||
}
|
||||
|
||||
// TODO: better type flow
|
||||
|
||||
@@ -9,9 +9,6 @@ use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("IO error")]
|
||||
IOError(#[from] std::io::Error),
|
||||
|
||||
#[error("The detailed description is yet to be determined")]
|
||||
BandwidthCredentialError,
|
||||
|
||||
@@ -32,13 +29,4 @@ pub enum Error {
|
||||
|
||||
#[error("Could not parse the key - {0}")]
|
||||
ParsePublicKey(#[from] KeyRecoveryError),
|
||||
|
||||
#[error("Could not gather enough signature shares. Try again using the recovery command")]
|
||||
NotEnoughShares,
|
||||
|
||||
#[error("Could not aggregate signature shares - {0}. Try again using the recovery command")]
|
||||
SignatureAggregationError(CoconutError),
|
||||
|
||||
#[error("Could not deserialize bandwidth voucher - {0}")]
|
||||
BandwidthVoucherDeserializationError(String),
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@ criterion = { version="0.3", features=["html_reports"] }
|
||||
doc-comment = "0.3"
|
||||
rand_chacha = "0.3"
|
||||
|
||||
[dev-dependencies.bincode]
|
||||
version = "1"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmarks"
|
||||
harness = false
|
||||
|
||||
@@ -93,10 +93,6 @@ impl SecretKey {
|
||||
Self { x, ys }
|
||||
}
|
||||
|
||||
pub fn into_raw(&self) -> (Scalar, Vec<Scalar>) {
|
||||
(self.x, self.ys.clone())
|
||||
}
|
||||
|
||||
/// Derive verification key using this secret key.
|
||||
pub fn verification_key(&self, params: &Parameters) -> VerificationKey {
|
||||
let g1 = params.gen1();
|
||||
|
||||
@@ -27,11 +27,6 @@ topology = { path = "../topology" }
|
||||
|
||||
[dev-dependencies]
|
||||
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
|
||||
criterion = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "benchmarks"
|
||||
harness = false
|
||||
|
||||
# do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require
|
||||
# net2 via tokio-util -> tokio -> mio -> net2
|
||||
|
||||
@@ -12,7 +12,7 @@ bs58 = "0.4"
|
||||
serde = "1.0"
|
||||
thiserror = "1"
|
||||
|
||||
crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
|
||||
crypto = { path = "../../crypto" }
|
||||
nymsphinx-addressing = { path = "../addressing" }
|
||||
nymsphinx-params = { path = "../params" }
|
||||
nymsphinx-types = { path = "../types" }
|
||||
|
||||
@@ -1,269 +0,0 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use criterion::{black_box, Criterion, criterion_group, criterion_main};
|
||||
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use crypto::asymmetric::encryption::{KeyPair, PrivateKey};
|
||||
use crypto::asymmetric::identity::PublicKey;
|
||||
use mixnet_contract_common::Layer;
|
||||
use nymsphinx::{delays, Node, NODE_ADDRESS_LENGTH, NodeAddressBytes, NymsphinxPayloadBuilder, PAYLOAD_OVERHEAD_SIZE, SphinxPacket};
|
||||
use nymsphinx::acknowledgements::AckKey;
|
||||
use nymsphinx::acknowledgements::surb_ack::SurbAck;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::builder::SphinxPacketBuilder;
|
||||
use nymsphinx::chunking::fragment::{Fragment, FragmentHeader, FragmentIdentifier};
|
||||
use nymsphinx::cover::generate_loop_cover_packet;
|
||||
use nymsphinx::crypto::keygen;
|
||||
use nymsphinx::params::packet_sizes::PacketSize::{ExtendedPacket16, ExtendedPacket32, ExtendedPacket8, RegularPacket};
|
||||
use nymsphinx::params::PacketSize;
|
||||
use topology::{gateway, mix, MixLayer, NymTopology};
|
||||
|
||||
const REGULAR_PACKET_SIZE: usize = PAYLOAD_OVERHEAD_SIZE + 2 * 1024;
|
||||
const EXTENDED_PACKET_SIZE_8: usize = PAYLOAD_OVERHEAD_SIZE + 8 * 1024;
|
||||
const EXTENDED_PACKET_SIZE_16: usize = PAYLOAD_OVERHEAD_SIZE + 16 * 1024;
|
||||
const EXTENDED_PACKET_SIZE_32: usize = PAYLOAD_OVERHEAD_SIZE + 32 * 1024;
|
||||
|
||||
struct BenchCase {
|
||||
packet_size: PacketSize,
|
||||
}
|
||||
|
||||
fn feature_topology(sender_gateway_id: PublicKey, recipient_gateway_id: PublicKey) -> (NymTopology, KeyPair) {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let gateway1 = gateway::Node {
|
||||
owner: "N/A".to_string(),
|
||||
stake: 1000,
|
||||
location: "N/A".to_string(),
|
||||
host: "1.1.1.1".parse().unwrap(),
|
||||
mix_host: "1.1.1.1:1789".parse().unwrap(),
|
||||
clients_port: 8888,
|
||||
identity_key: sender_gateway_id,
|
||||
sphinx_key: encryption::PublicKey::from_base58_string(
|
||||
"C7cown6dYCLZpLiMFC1PaBmhvLvmJmLDJGeRTbPD45bX",
|
||||
)
|
||||
.unwrap(),
|
||||
version: "0.x.0".to_string(),
|
||||
};
|
||||
|
||||
let gateway2 = gateway::Node {
|
||||
identity_key: recipient_gateway_id,
|
||||
..gateway1.clone()
|
||||
};
|
||||
|
||||
let node1_enc_keys = KeyPair::new(&mut rng);
|
||||
let node1 = mix::Node {
|
||||
mix_id: 42,
|
||||
owner: "N/A".to_string(),
|
||||
host: "3.3.3.3".parse().unwrap(),
|
||||
mix_host: "3.3.3.3:1789".parse().unwrap(),
|
||||
identity_key: identity::PublicKey::from_base58_string(
|
||||
"3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7",
|
||||
)
|
||||
.unwrap(),
|
||||
sphinx_key: *node1_enc_keys.public_key(),
|
||||
layer: Layer::One,
|
||||
version: "0.x.0".to_string(),
|
||||
};
|
||||
|
||||
let node2 = mix::Node {
|
||||
owner: "Alice".to_string(),
|
||||
..node1.clone()
|
||||
};
|
||||
|
||||
let node3 = mix::Node {
|
||||
owner: "Bob".to_string(),
|
||||
..node1.clone()
|
||||
};
|
||||
|
||||
let mut mixes: HashMap<MixLayer, Vec<mix::Node>> = HashMap::new();
|
||||
mixes.insert(1, vec![node1]);
|
||||
mixes.insert(2, vec![node2]);
|
||||
mixes.insert(3, vec![node3]);
|
||||
|
||||
let topology = NymTopology::new(mixes, vec![gateway1, gateway2]);
|
||||
(topology, node1_enc_keys)
|
||||
}
|
||||
|
||||
fn make_packet_copy(packet: &SphinxPacket) -> SphinxPacket {
|
||||
SphinxPacket::from_bytes(&packet.to_bytes()).unwrap()
|
||||
}
|
||||
|
||||
fn bench_loop_packet_create(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("benchmark-sphinx");
|
||||
// group.sample_size(200);
|
||||
group.measurement_time(Duration::from_secs(500));
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let case = BenchCase {
|
||||
packet_size: RegularPacket,
|
||||
};
|
||||
|
||||
// create sender
|
||||
let sender_client_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let sender_client_enc_pair = encryption::KeyPair::new(&mut rng);
|
||||
let sender_gateway_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let packet_sender = Recipient::new(
|
||||
*sender_client_id_pair.public_key(),
|
||||
*sender_client_enc_pair.public_key(),
|
||||
*sender_gateway_id_pair.public_key(),
|
||||
);
|
||||
|
||||
// build topology
|
||||
let (topology, node_keypair) = feature_topology(*sender_gateway_id_pair.public_key(), *sender_gateway_id_pair.public_key());
|
||||
// generate the encryption key for the ack
|
||||
let ack_key = AckKey::new(&mut rng);
|
||||
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Sphinx] create_loop_cover_packet_with_payload_size_{}",
|
||||
case.packet_size.payload_size(),
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
generate_loop_cover_packet(
|
||||
&mut rng,
|
||||
&topology,
|
||||
&ack_key,
|
||||
&packet_sender,
|
||||
Duration::from_millis(50),
|
||||
Duration::from_millis(50),
|
||||
case.packet_size)
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// let's create the packet to later benchmark the processing
|
||||
let packet = generate_loop_cover_packet(
|
||||
&mut rng,
|
||||
&topology,
|
||||
&ack_key,
|
||||
&packet_sender,
|
||||
Duration::from_millis(50),
|
||||
Duration::from_millis(50),
|
||||
case.packet_size).unwrap();
|
||||
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Sphinx] process_loop_cover_packet_with_payload_size_{}",
|
||||
case.packet_size.payload_size(),
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
make_packet_copy(&packet.sphinx_packet).process(&node_keypair.private_key().into())
|
||||
})
|
||||
},
|
||||
);
|
||||
// let new_packet = packet.sphinx_packet.process(&node_keypair.private_key().into());
|
||||
}
|
||||
|
||||
fn bench_new_no_surb(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("benchmark-sphinx");
|
||||
// group.sample_size(200);
|
||||
group.measurement_time(Duration::from_secs(500));
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let case = BenchCase {
|
||||
packet_size: ExtendedPacket8,
|
||||
};
|
||||
|
||||
// create sender
|
||||
let sender_client_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let sender_client_enc_pair = encryption::KeyPair::new(&mut rng);
|
||||
let sender_gateway_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let packet_sender = Recipient::new(
|
||||
*sender_client_id_pair.public_key(),
|
||||
*sender_client_enc_pair.public_key(),
|
||||
*sender_gateway_id_pair.public_key(),
|
||||
);
|
||||
|
||||
// create recipient
|
||||
let recipient_client_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let recipient_client_enc_pair = encryption::KeyPair::new(&mut rng);
|
||||
let recipient_gateway_id_pair = identity::KeyPair::new(&mut rng);
|
||||
let packet_recipient = Recipient::new(
|
||||
*recipient_client_id_pair.public_key(),
|
||||
*recipient_client_enc_pair.public_key(),
|
||||
*recipient_gateway_id_pair.public_key(),
|
||||
);
|
||||
|
||||
// build topology
|
||||
let (topology, node_keypair) = feature_topology(*sender_gateway_id_pair.public_key(), *recipient_gateway_id_pair.public_key());
|
||||
|
||||
// generate pseudorandom route for the packet
|
||||
let route = topology.random_route_to_gateway(
|
||||
&mut rng,
|
||||
3,
|
||||
packet_recipient.gateway(),
|
||||
).unwrap();
|
||||
|
||||
// generate some payload
|
||||
let mlen = 40;
|
||||
let mut msg = vec![0u8; mlen];
|
||||
let fragment = Fragment {
|
||||
header: FragmentHeader::try_new(
|
||||
12345,
|
||||
u8::max_value(),
|
||||
u8::max_value(),
|
||||
None,
|
||||
Some(1234),
|
||||
)
|
||||
.unwrap(),
|
||||
payload: msg,
|
||||
};
|
||||
|
||||
|
||||
let ack_key = AckKey::new(&mut rng);
|
||||
let surb_ack = SurbAck::construct(
|
||||
&mut rng,
|
||||
&packet_sender,
|
||||
&ack_key,
|
||||
fragment.fragment_identifier().to_bytes(),
|
||||
Duration::from_millis(50),
|
||||
&topology,
|
||||
).unwrap();
|
||||
|
||||
|
||||
let packet_payload = NymsphinxPayloadBuilder::new(fragment, surb_ack)
|
||||
.build_regular(&mut rng, packet_recipient.encryption_key());
|
||||
|
||||
let delays = delays::generate_from_average_duration(route.len(), Duration::from_millis(50));
|
||||
let destination = packet_recipient.as_sphinx_destination();
|
||||
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Sphinx] create_packet_no_reply_surbs_with_payload_size_{}",
|
||||
case.packet_size.payload_size(),
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
SphinxPacketBuilder::new()
|
||||
.with_payload_size(case.packet_size.payload_size())
|
||||
.build_packet(packet_payload.clone(), &route, &destination, &delays)
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// let's create the packet to later benchmark the processing
|
||||
let sphinx_packet = SphinxPacketBuilder::new()
|
||||
.with_payload_size(case.packet_size.payload_size())
|
||||
.build_packet(packet_payload.clone(), &route, &destination, &delays)
|
||||
.unwrap();
|
||||
|
||||
group.bench_function(
|
||||
&format!(
|
||||
"[Sphinx] process_packet_with_payload_size_{}",
|
||||
case.packet_size.payload_size(),
|
||||
),
|
||||
|b| {
|
||||
b.iter(|| {
|
||||
make_packet_copy(&sphinx_packet).process(&node_keypair.private_key().into())
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
criterion_group!(sphinx, bench_loop_packet_create, bench_new_no_surb);
|
||||
criterion_main!(sphinx);
|
||||
@@ -1,13 +1,11 @@
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::ChunkingError;
|
||||
use nymsphinx_params::{SerializedFragmentIdentifier, FRAG_ID_LEN};
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use nymsphinx_params::{FRAG_ID_LEN, SerializedFragmentIdentifier};
|
||||
|
||||
use crate::ChunkingError;
|
||||
|
||||
// Personal reflection: In hindsight I've spent too much time on relatively too little
|
||||
// gain here, as even though I might have saved couple of bytes per packet, the gain
|
||||
// is negligible in the context of having to include SURB-ACKs and reply-SURBs in the packets.
|
||||
@@ -112,8 +110,8 @@ impl FragmentIdentifier {
|
||||
/// header used to reconstruct the message after being received.
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub struct Fragment {
|
||||
pub header: FragmentHeader,
|
||||
pub payload: Vec<u8>,
|
||||
header: FragmentHeader,
|
||||
payload: Vec<u8>,
|
||||
}
|
||||
|
||||
// manual implementation to hide detailed payload that we don't care about
|
||||
@@ -292,7 +290,7 @@ impl Fragment {
|
||||
/// and for the longest messages, without upper bound, there is usually also only 7 bytes
|
||||
/// of overhead apart from first and last fragments in each set that instead have 10 bytes of overhead.
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct FragmentHeader {
|
||||
pub(crate) struct FragmentHeader {
|
||||
/// ID associated with `FragmentSet` to which this particular `Fragment` belongs.
|
||||
/// Its value is restricted to (0, i32::max_value()].
|
||||
/// Note that it *excludes* 0, but *includes* i32::max_value().
|
||||
@@ -321,7 +319,7 @@ impl FragmentHeader {
|
||||
/// Tries to create a new `FragmentHeader` using provided metadata. Bunch of logical
|
||||
/// checks are performed to see if the data is not self-contradictory,
|
||||
/// for example if current_fragment > total_fragments.
|
||||
pub fn try_new(
|
||||
fn try_new(
|
||||
id: i32,
|
||||
total_fragments: u8,
|
||||
current_fragment: u8,
|
||||
@@ -462,11 +460,9 @@ impl FragmentHeader {
|
||||
|
||||
#[cfg(test)]
|
||||
mod fragment_tests {
|
||||
use rand::{RngCore, thread_rng};
|
||||
|
||||
use nymsphinx_params::packet_sizes::PacketSize;
|
||||
|
||||
use super::*;
|
||||
use nymsphinx_params::packet_sizes::PacketSize;
|
||||
use rand::{thread_rng, RngCore};
|
||||
|
||||
fn max_plaintext_size() -> usize {
|
||||
PacketSize::default().plaintext_size() - PacketSize::AckPacket.size()
|
||||
@@ -589,7 +585,7 @@ mod fragment_tests {
|
||||
None,
|
||||
Some(1234),
|
||||
)
|
||||
.unwrap(),
|
||||
.unwrap(),
|
||||
payload: msg,
|
||||
};
|
||||
let packet_bytes = fragment.clone().into_bytes();
|
||||
@@ -606,7 +602,7 @@ mod fragment_tests {
|
||||
None,
|
||||
Some(1234),
|
||||
)
|
||||
.unwrap(),
|
||||
.unwrap(),
|
||||
payload: msg,
|
||||
};
|
||||
let packet_bytes = fragment.clone().into_bytes();
|
||||
@@ -648,7 +644,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload,
|
||||
id,
|
||||
@@ -658,7 +654,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
@@ -669,7 +665,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
id,
|
||||
@@ -679,7 +675,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -701,7 +697,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload,
|
||||
id,
|
||||
@@ -711,7 +707,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
@@ -722,7 +718,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
id,
|
||||
@@ -732,7 +728,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
id,
|
||||
@@ -742,7 +738,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
@@ -753,7 +749,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
id,
|
||||
@@ -763,7 +759,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -784,7 +780,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(Fragment::try_new(
|
||||
&full_payload,
|
||||
id,
|
||||
@@ -794,7 +790,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload,
|
||||
id,
|
||||
@@ -804,7 +800,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
id,
|
||||
@@ -814,7 +810,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&full_payload,
|
||||
@@ -825,7 +821,7 @@ mod fragment_tests {
|
||||
Some(link_id),
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -846,7 +842,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
id,
|
||||
@@ -856,7 +852,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
id,
|
||||
@@ -866,7 +862,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
id,
|
||||
@@ -876,7 +872,7 @@ mod fragment_tests {
|
||||
None,
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload,
|
||||
@@ -887,7 +883,7 @@ mod fragment_tests {
|
||||
Some(link_id),
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
assert!(Fragment::try_new(
|
||||
&non_full_payload2,
|
||||
id,
|
||||
@@ -897,7 +893,7 @@ mod fragment_tests {
|
||||
Some(link_id),
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
|
||||
assert!(Fragment::try_new(
|
||||
&too_much_payload,
|
||||
@@ -908,7 +904,7 @@ mod fragment_tests {
|
||||
Some(link_id),
|
||||
max_plaintext_size(),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1012,7 +1008,7 @@ mod fragment_header {
|
||||
None,
|
||||
Some(0),
|
||||
)
|
||||
.is_err());
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1070,7 +1066,7 @@ mod fragment_header {
|
||||
None,
|
||||
Some(1234),
|
||||
)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
assert!(FragmentHeader::try_new(12345, 10, 2, Some(1234), None).is_err());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
use nymsphinx_addressing::nodes::{NymNodeRoutingAddress, NymNodeRoutingAddressError};
|
||||
use nymsphinx_params::{PacketMode, PacketSize};
|
||||
use nymsphinx_types::SphinxPacket;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MixPacketFormattingError {
|
||||
@@ -47,7 +46,7 @@ impl From<NymNodeRoutingAddressError> for MixPacketFormattingError {
|
||||
|
||||
pub struct MixPacket {
|
||||
next_hop: NymNodeRoutingAddress,
|
||||
pub sphinx_packet: SphinxPacket,
|
||||
sphinx_packet: SphinxPacket,
|
||||
packet_mode: PacketMode,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
use crypto::aes::cipher::{KeyIvInit, StreamCipher};
|
||||
use crypto::asymmetric::encryption;
|
||||
use crypto::shared_key::new_ephemeral_shared_key;
|
||||
@@ -14,6 +12,7 @@ use nymsphinx_chunking::fragment::Fragment;
|
||||
use nymsphinx_params::{
|
||||
PacketEncryptionAlgorithm, PacketHkdfAlgorithm, ReplySurbEncryptionAlgorithm,
|
||||
};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
pub struct NymsphinxPayloadBuilder {
|
||||
fragment: Fragment,
|
||||
@@ -28,10 +27,10 @@ impl NymsphinxPayloadBuilder {
|
||||
fn build<C>(
|
||||
self,
|
||||
packet_encryption_key: &CipherKey<C>,
|
||||
variant_data: impl IntoIterator<Item=u8>,
|
||||
variant_data: impl IntoIterator<Item = u8>,
|
||||
) -> NymsphinxPayload
|
||||
where
|
||||
C: StreamCipher + KeyIvInit,
|
||||
where
|
||||
C: StreamCipher + KeyIvInit,
|
||||
{
|
||||
let (_, surb_ack_bytes) = self.surb_ack.prepare_for_sending();
|
||||
|
||||
@@ -69,8 +68,8 @@ impl NymsphinxPayloadBuilder {
|
||||
rng: &mut R,
|
||||
recipient_encryption_key: &encryption::PublicKey,
|
||||
) -> NymsphinxPayload
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
// create keys for 'payload' encryption
|
||||
let (ephemeral_keypair, shared_key) = new_ephemeral_shared_key::<
|
||||
@@ -89,7 +88,6 @@ impl NymsphinxPayloadBuilder {
|
||||
// the actual byte data that will be put into the sphinx packet paylaod.
|
||||
// no more transformations are going to happen to it
|
||||
// TODO: use that fact for some better compile time assertions
|
||||
#[derive(Clone)]
|
||||
pub struct NymsphinxPayload(Vec<u8>);
|
||||
|
||||
impl AsRef<[u8]> for NymsphinxPayload {
|
||||
|
||||
@@ -6,7 +6,7 @@ use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use ordered_buffer::{OrderedMessage, OrderedMessageBuffer, ReadContiguousData};
|
||||
use socks5_requests::{ConnectionId, NetworkData, SendRequest};
|
||||
use socks5_requests::ConnectionId;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
time::Duration,
|
||||
@@ -36,38 +36,9 @@ pub type ControllerSender = mpsc::UnboundedSender<ControllerCommand>;
|
||||
pub type ControllerReceiver = mpsc::UnboundedReceiver<ControllerCommand>;
|
||||
|
||||
pub enum ControllerCommand {
|
||||
Insert {
|
||||
connection_id: ConnectionId,
|
||||
connection_sender: ConnectionSender,
|
||||
},
|
||||
Remove {
|
||||
connection_id: ConnectionId,
|
||||
},
|
||||
Send {
|
||||
connection_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
is_closed: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<NetworkData> for ControllerCommand {
|
||||
fn from(value: NetworkData) -> Self {
|
||||
ControllerCommand::Send {
|
||||
connection_id: value.connection_id,
|
||||
data: value.data,
|
||||
is_closed: value.is_closed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SendRequest> for ControllerCommand {
|
||||
fn from(value: SendRequest) -> Self {
|
||||
ControllerCommand::Send {
|
||||
connection_id: value.conn_id,
|
||||
data: value.data,
|
||||
is_closed: value.local_closed,
|
||||
}
|
||||
}
|
||||
Insert(ConnectionId, ConnectionSender),
|
||||
Remove(ConnectionId),
|
||||
Send(ConnectionId, Vec<u8>, bool),
|
||||
}
|
||||
|
||||
struct ActiveConnection {
|
||||
@@ -264,13 +235,13 @@ impl Controller {
|
||||
loop {
|
||||
tokio::select! {
|
||||
command = self.receiver.next() => match command {
|
||||
Some(ControllerCommand::Send{connection_id, data, is_closed}) => {
|
||||
self.send_to_connection(connection_id, data, is_closed)
|
||||
Some(ControllerCommand::Send(conn_id, data, is_closed)) => {
|
||||
self.send_to_connection(conn_id, data, is_closed)
|
||||
}
|
||||
Some(ControllerCommand::Insert{connection_id, connection_sender}) => {
|
||||
self.insert_connection(connection_id, connection_sender)
|
||||
Some(ControllerCommand::Insert(conn_id, sender)) => {
|
||||
self.insert_connection(conn_id, sender)
|
||||
}
|
||||
Some(ControllerCommand::Remove{ connection_id }) => self.remove_connection(connection_id),
|
||||
Some(ControllerCommand::Remove(conn_id)) => self.remove_connection(conn_id),
|
||||
None => {
|
||||
log::trace!("SOCKS5 Controller: Stopping since channel closed");
|
||||
break;
|
||||
|
||||
@@ -7,9 +7,5 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
thiserror = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
nymsphinx-addressing = { path = "../../../common/nymsphinx/addressing" }
|
||||
service-providers-common = { path = "../../../service-providers/common" }
|
||||
thiserror = "1"
|
||||
|
||||
@@ -1,128 +1,12 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use service_providers_common::interface;
|
||||
use service_providers_common::interface::ServiceProviderMessagingError;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use request::*;
|
||||
pub use response::*;
|
||||
pub use version::*;
|
||||
|
||||
pub mod msg;
|
||||
pub mod network_requester_response;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod version;
|
||||
|
||||
pub type Socks5ProviderRequest = interface::Request<Socks5Request>;
|
||||
pub type Socks5ProviderResponse = interface::Response<Socks5Request>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Socks5RequestError {
|
||||
#[error("failed to deserialize received request: {source}")]
|
||||
RequestDeserialization {
|
||||
#[from]
|
||||
source: RequestDeserializationError,
|
||||
},
|
||||
|
||||
#[error("failed to deserialize received response: {source}")]
|
||||
ResponseDeserialization {
|
||||
#[from]
|
||||
source: ResponseDeserializationError,
|
||||
},
|
||||
|
||||
#[error(transparent)]
|
||||
ProviderInterfaceError(#[from] ServiceProviderMessagingError),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use service_providers_common::interface::RequestContent;
|
||||
|
||||
#[cfg(test)]
|
||||
mod interface_backwards_compatibility {
|
||||
use super::*;
|
||||
use service_providers_common::interface::ProviderInterfaceVersion;
|
||||
|
||||
#[test]
|
||||
fn old_client_vs_new_service_provider() {
|
||||
let old_serialized_connect = vec![
|
||||
0, 0, 2, 254, 34, 100, 192, 20, 13, 171, 0, 16, 56, 48, 46, 50, 52, 57, 46, 57, 57,
|
||||
46, 49, 52, 56, 58, 56, 48, 34, 112, 17, 182, 225, 6, 174, 216, 160, 41, 72, 236,
|
||||
160, 90, 156, 3, 250, 41, 243, 53, 191, 178, 218, 53, 170, 14, 185, 33, 94, 153,
|
||||
25, 41, 6, 82, 169, 187, 88, 246, 211, 57, 68, 225, 228, 231, 116, 29, 119, 235,
|
||||
160, 14, 156, 205, 66, 1, 75, 204, 204, 220, 14, 150, 191, 203, 174, 88, 121, 173,
|
||||
83, 219, 188, 164, 194, 212, 238, 228, 4, 128, 48, 105, 224, 83, 17, 246, 233, 16,
|
||||
235, 223, 68, 87, 13, 40, 34, 186, 218, 204, 126, 145,
|
||||
];
|
||||
|
||||
let new_deserialized =
|
||||
Socks5ProviderRequest::try_from_bytes(&old_serialized_connect).unwrap();
|
||||
|
||||
match new_deserialized.content {
|
||||
RequestContent::ProviderData(req) => match req.content {
|
||||
Socks5RequestContent::Connect(connect_req) => {
|
||||
assert_eq!(connect_req.remote_addr, "80.249.99.148:80".to_string());
|
||||
assert_eq!(connect_req.conn_id, 215647648274976171);
|
||||
assert_eq!(connect_req.return_address, Some("3KRydEpanwjFhq5GAraVjRUF1Tno7w7oc4EwJYTGNo5J.RgZ7uMJHruBQqD5hC9Ghi3sqiTn6NycfM5qCfJz6yoM@9Byd9VAtyYMnbVAcqdoQxJnq76XEg2dbxbiF5Aa5Jj9J".parse().unwrap()));
|
||||
}
|
||||
_ => panic!("unexpected request"),
|
||||
},
|
||||
_ => panic!("unexpected request"),
|
||||
}
|
||||
|
||||
let old_serialized_send = vec![
|
||||
0, 1, 108, 102, 28, 19, 50, 178, 37, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 69, 84,
|
||||
32, 47, 49, 77, 66, 46, 122, 105, 112, 32, 72, 84, 84, 80, 47, 49, 46, 49, 13, 10,
|
||||
72, 111, 115, 116, 58, 32, 105, 112, 118, 52, 46, 100, 111, 119, 110, 108, 111, 97,
|
||||
100, 46, 116, 104, 105, 110, 107, 98, 114, 111, 97, 100, 98, 97, 110, 100, 46, 99,
|
||||
111, 109, 13, 10, 85, 115, 101, 114, 45, 65, 103, 101, 110, 116, 58, 32, 99, 117,
|
||||
114, 108, 47, 55, 46, 54, 56, 46, 48, 13, 10, 65, 99, 99, 101, 112, 116, 58, 32,
|
||||
42, 47, 42, 13, 10, 13, 10,
|
||||
];
|
||||
|
||||
let new_deserialized =
|
||||
Socks5ProviderRequest::try_from_bytes(&old_serialized_send).unwrap();
|
||||
|
||||
match new_deserialized.content {
|
||||
RequestContent::ProviderData(req) => match req.content {
|
||||
Socks5RequestContent::Send(send_req) => {
|
||||
assert_eq!(send_req.conn_id, 7810961472501196273);
|
||||
assert_eq!(send_req.data.len(), 111);
|
||||
assert!(!send_req.local_closed);
|
||||
}
|
||||
_ => panic!("unexpected request"),
|
||||
},
|
||||
_ => panic!("unexpected request"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_client_vs_old_service_provider() {
|
||||
let return_address = "3KRydEpanwjFhq5GAraVjRUF1Tno7w7oc4EwJYTGNo5J.RgZ7uMJHruBQqD5hC9Ghi3sqiTn6NycfM5qCfJz6yoM@9Byd9VAtyYMnbVAcqdoQxJnq76XEg2dbxbiF5Aa5Jj9J".parse().unwrap();
|
||||
|
||||
let new_connect = Socks5ProviderRequest::new_provider_data(
|
||||
ProviderInterfaceVersion::Legacy,
|
||||
Socks5Request::new_connect(
|
||||
Socks5ProtocolVersion::Legacy,
|
||||
215647648274976171,
|
||||
"80.249.99.148:80".to_string(),
|
||||
Some(return_address),
|
||||
),
|
||||
);
|
||||
|
||||
let legacy_serialised = new_connect.into_bytes();
|
||||
let old_serialized_connect = vec![
|
||||
0, 0, 2, 254, 34, 100, 192, 20, 13, 171, 0, 16, 56, 48, 46, 50, 52, 57, 46, 57, 57,
|
||||
46, 49, 52, 56, 58, 56, 48, 34, 112, 17, 182, 225, 6, 174, 216, 160, 41, 72, 236,
|
||||
160, 90, 156, 3, 250, 41, 243, 53, 191, 178, 218, 53, 170, 14, 185, 33, 94, 153,
|
||||
25, 41, 6, 82, 169, 187, 88, 246, 211, 57, 68, 225, 228, 231, 116, 29, 119, 235,
|
||||
160, 14, 156, 205, 66, 1, 75, 204, 204, 220, 14, 150, 191, 203, 174, 88, 121, 173,
|
||||
83, 219, 188, 164, 194, 212, 238, 228, 4, 128, 48, 105, 224, 83, 17, 246, 233, 16,
|
||||
235, 223, 68, 87, 13, 40, 34, 186, 218, 204, 126, 145,
|
||||
];
|
||||
|
||||
assert_eq!(legacy_serialised, old_serialized_connect);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use msg::*;
|
||||
pub use network_requester_response::*;
|
||||
pub use request::*;
|
||||
pub use response::*;
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::network_requester_response::{Error as NrError, NetworkRequesterResponse};
|
||||
use crate::request::{Request, RequestError};
|
||||
use crate::response::{Response, ResponseError};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum MessageError {
|
||||
#[error(transparent)]
|
||||
Request(RequestError),
|
||||
|
||||
#[error("{0:?}")]
|
||||
Response(ResponseError),
|
||||
|
||||
#[error(transparent)]
|
||||
NetworkRequesterResponseError(NrError),
|
||||
|
||||
#[error("no data")]
|
||||
NoData,
|
||||
|
||||
#[error("unknown message type received")]
|
||||
UnknownMessageType,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
Request(Request),
|
||||
Response(Response),
|
||||
NetworkRequesterResponse(NetworkRequesterResponse),
|
||||
}
|
||||
|
||||
impl Message {
|
||||
const REQUEST_FLAG: u8 = 0;
|
||||
const RESPONSE_FLAG: u8 = 1;
|
||||
const NR_RESPONSE_FLAG: u8 = 2;
|
||||
|
||||
pub fn conn_id(&self) -> u64 {
|
||||
match self {
|
||||
Message::Request(req) => match req {
|
||||
Request::Connect(c) => c.conn_id,
|
||||
Request::Send(conn_id, _, _) => *conn_id,
|
||||
},
|
||||
Message::Response(resp) => resp.connection_id,
|
||||
Message::NetworkRequesterResponse(resp) => resp.connection_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
match self {
|
||||
Message::Request(req) => match req {
|
||||
Request::Connect(_) => 0,
|
||||
Request::Send(_, data, _) => data.len(),
|
||||
},
|
||||
Message::Response(resp) => resp.data.len(),
|
||||
Message::NetworkRequesterResponse(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<Message, MessageError> {
|
||||
if b.is_empty() {
|
||||
return Err(MessageError::NoData);
|
||||
}
|
||||
|
||||
if b[0] == Self::REQUEST_FLAG {
|
||||
Request::try_from_bytes(&b[1..])
|
||||
.map(Message::Request)
|
||||
.map_err(MessageError::Request)
|
||||
} else if b[0] == Self::RESPONSE_FLAG {
|
||||
Response::try_from_bytes(&b[1..])
|
||||
.map(Message::Response)
|
||||
.map_err(MessageError::Response)
|
||||
} else if b[0] == Self::NR_RESPONSE_FLAG {
|
||||
NetworkRequesterResponse::try_from_bytes(&b[1..])
|
||||
.map(Message::NetworkRequesterResponse)
|
||||
.map_err(MessageError::NetworkRequesterResponseError)
|
||||
} else {
|
||||
Err(MessageError::UnknownMessageType)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
match self {
|
||||
Self::Request(r) => std::iter::once(Self::REQUEST_FLAG)
|
||||
.chain(r.into_bytes().iter().cloned())
|
||||
.collect(),
|
||||
Self::Response(r) => std::iter::once(Self::RESPONSE_FLAG)
|
||||
.chain(r.into_bytes().iter().cloned())
|
||||
.collect(),
|
||||
Self::NetworkRequesterResponse(r) => std::iter::once(Self::NR_RESPONSE_FLAG)
|
||||
.chain(r.into_bytes().iter().cloned())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::ConnectionId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NetworkRequesterResponse {
|
||||
pub connection_id: ConnectionId,
|
||||
pub network_requester_error: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
#[error("no data provided")]
|
||||
NoData,
|
||||
|
||||
#[error("not enough bytes to recover the connection id")]
|
||||
ConnectionIdTooShort,
|
||||
|
||||
#[error("message is not utf8 encoded")]
|
||||
MalformedErrorMessage(#[from] std::string::FromUtf8Error),
|
||||
}
|
||||
|
||||
impl NetworkRequesterResponse {
|
||||
pub fn new(connection_id: ConnectionId, network_requester_error: String) -> Self {
|
||||
NetworkRequesterResponse {
|
||||
connection_id,
|
||||
network_requester_error,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<NetworkRequesterResponse, Error> {
|
||||
if b.is_empty() {
|
||||
return Err(Error::NoData);
|
||||
}
|
||||
|
||||
if b.len() < 8 {
|
||||
return Err(Error::ConnectionIdTooShort);
|
||||
}
|
||||
|
||||
let mut connection_id_bytes = b.to_vec();
|
||||
let network_requester_error_bytes = connection_id_bytes.split_off(8);
|
||||
|
||||
let connection_id = u64::from_be_bytes([
|
||||
connection_id_bytes[0],
|
||||
connection_id_bytes[1],
|
||||
connection_id_bytes[2],
|
||||
connection_id_bytes[3],
|
||||
connection_id_bytes[4],
|
||||
connection_id_bytes[5],
|
||||
connection_id_bytes[6],
|
||||
connection_id_bytes[7],
|
||||
]);
|
||||
let network_requester_error = String::from_utf8(network_requester_error_bytes)?;
|
||||
|
||||
Ok(NetworkRequesterResponse {
|
||||
connection_id,
|
||||
network_requester_error,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.connection_id
|
||||
.to_be_bytes()
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(self.network_requester_error.into_bytes().into_iter())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod network_requester_response_serde_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn simple_serde() {
|
||||
let conn_id = 42;
|
||||
let network_requester_error = String::from("This is a test msg");
|
||||
let response = NetworkRequesterResponse::new(conn_id, network_requester_error.clone());
|
||||
let bytes = response.into_bytes();
|
||||
let deserialized_response = NetworkRequesterResponse::try_from_bytes(&bytes).unwrap();
|
||||
|
||||
assert_eq!(conn_id, deserialized_response.connection_id);
|
||||
assert_eq!(
|
||||
network_requester_error,
|
||||
deserialized_response.network_requester_error
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialization_errors() {
|
||||
let err = NetworkRequesterResponse::try_from_bytes(&[]).err().unwrap();
|
||||
assert_eq!(err, Error::NoData);
|
||||
|
||||
let bytes: [u8; 5] = [1, 2, 3, 4, 5];
|
||||
let err = NetworkRequesterResponse::try_from_bytes(&bytes)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(err, Error::ConnectionIdTooShort);
|
||||
|
||||
let bytes: Vec<u8> = 42u64
|
||||
.to_be_bytes()
|
||||
.into_iter()
|
||||
.chain([0, 159, 146, 150].into_iter())
|
||||
.collect();
|
||||
let err = NetworkRequesterResponse::try_from_bytes(&bytes)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(matches!(err, Error::MalformedErrorMessage(_)));
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{Socks5ProtocolVersion, Socks5RequestError, Socks5Response};
|
||||
use nymsphinx_addressing::clients::{Recipient, RecipientFormattingError};
|
||||
use service_providers_common::interface::{Serializable, ServiceProviderRequest};
|
||||
use std::convert::TryFrom;
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -18,19 +16,19 @@ pub enum RequestFlag {
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for RequestFlag {
|
||||
type Error = RequestDeserializationError;
|
||||
type Error = RequestError;
|
||||
|
||||
fn try_from(value: u8) -> Result<RequestFlag, RequestDeserializationError> {
|
||||
fn try_from(value: u8) -> Result<RequestFlag, RequestError> {
|
||||
match value {
|
||||
_ if value == (RequestFlag::Connect as u8) => Ok(Self::Connect),
|
||||
_ if value == (RequestFlag::Send as u8) => Ok(Self::Send),
|
||||
value => Err(RequestDeserializationError::UnknownRequestFlag { value }),
|
||||
_ => Err(RequestError::UnknownRequestFlag),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RequestDeserializationError {
|
||||
pub enum RequestError {
|
||||
#[error("not enough bytes to recover the length of the address")]
|
||||
AddressLengthTooShort,
|
||||
|
||||
@@ -43,8 +41,8 @@ pub enum RequestDeserializationError {
|
||||
#[error("no data provided")]
|
||||
NoData,
|
||||
|
||||
#[error("{value} is not a valid request flag")]
|
||||
UnknownRequestFlag { value: u8 },
|
||||
#[error("request of unknown type")]
|
||||
UnknownRequestFlag,
|
||||
|
||||
#[error("too short return address")]
|
||||
ReturnAddressTooShort,
|
||||
@@ -53,13 +51,13 @@ pub enum RequestDeserializationError {
|
||||
MalformedReturnAddress(RecipientFormattingError),
|
||||
}
|
||||
|
||||
impl RequestDeserializationError {
|
||||
impl RequestError {
|
||||
pub fn is_malformed_return(&self) -> bool {
|
||||
matches!(self, RequestDeserializationError::MalformedReturnAddress(_))
|
||||
matches!(self, RequestError::MalformedReturnAddress(_))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct ConnectRequest {
|
||||
// TODO: is connection_id redundant now?
|
||||
pub conn_id: ConnectionId,
|
||||
@@ -67,128 +65,27 @@ pub struct ConnectRequest {
|
||||
pub return_address: Option<Recipient>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SendRequest {
|
||||
pub conn_id: ConnectionId,
|
||||
pub data: Vec<u8>,
|
||||
pub local_closed: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Socks5Request {
|
||||
pub protocol_version: Socks5ProtocolVersion,
|
||||
pub content: Socks5RequestContent,
|
||||
}
|
||||
|
||||
impl Serializable for Socks5Request {
|
||||
type Error = Socks5RequestError;
|
||||
|
||||
// legacy requests had the format of
|
||||
// 0 (Message::REQUEST_FLAG) || 0 (RequestFlag::Connect) || <data> for connect requests
|
||||
// 0 (Message::REQUEST_FLAG) || 1 (RequestFlag::Send) || <data> for send requests
|
||||
// the updated formats use
|
||||
// 3 (Socks5ProtocolVersion) || 0 (RequestFlag::Connect) || <data> for connect requests
|
||||
// 3 (Socks5ProtocolVersion) || 1 (RequestFlag::Send) || <data> for send requests
|
||||
// in both cases, the actual data is serialized the same way, so the process is quite straight forward
|
||||
fn into_bytes(self) -> Vec<u8> {
|
||||
if let Some(version) = self.protocol_version.as_u8() {
|
||||
std::iter::once(version)
|
||||
.chain(self.content.into_bytes().into_iter())
|
||||
.collect()
|
||||
} else {
|
||||
std::iter::once(Self::LEGACY_TYPE_TAG)
|
||||
.chain(self.content.into_bytes())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn try_from_bytes(b: &[u8]) -> Result<Self, Self::Error> {
|
||||
if b.is_empty() {
|
||||
return Err(RequestDeserializationError::NoData.into());
|
||||
}
|
||||
|
||||
let protocol_version = Socks5ProtocolVersion::from(b[0]);
|
||||
Ok(Socks5Request {
|
||||
protocol_version,
|
||||
content: Socks5RequestContent::try_from_bytes(&b[1..])?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ServiceProviderRequest for Socks5Request {
|
||||
type ProtocolVersion = Socks5ProtocolVersion;
|
||||
type Response = Socks5Response;
|
||||
type Error = Socks5RequestError;
|
||||
|
||||
fn provider_specific_version(&self) -> Self::ProtocolVersion {
|
||||
self.protocol_version
|
||||
}
|
||||
|
||||
fn max_supported_version() -> Self::ProtocolVersion {
|
||||
Socks5ProtocolVersion::new_current()
|
||||
}
|
||||
}
|
||||
|
||||
impl Socks5Request {
|
||||
// type tag that used to be prepended to all request messages
|
||||
const LEGACY_TYPE_TAG: u8 = 0x00;
|
||||
|
||||
pub fn new(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
content: Socks5RequestContent,
|
||||
) -> Socks5Request {
|
||||
Socks5Request {
|
||||
protocol_version,
|
||||
content,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_connect(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
conn_id: ConnectionId,
|
||||
remote_addr: RemoteAddress,
|
||||
return_address: Option<Recipient>,
|
||||
) -> Socks5Request {
|
||||
Socks5Request {
|
||||
protocol_version,
|
||||
content: Socks5RequestContent::new_connect(conn_id, remote_addr, return_address),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_send(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
conn_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
local_closed: bool,
|
||||
) -> Socks5Request {
|
||||
Socks5Request {
|
||||
protocol_version,
|
||||
content: Socks5RequestContent::new_send(conn_id, data, local_closed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A request from a SOCKS5 client that a Nym Socks5 service provider should
|
||||
/// take an action for an application using a (probably local) Nym Socks5 proxy.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Socks5RequestContent {
|
||||
#[derive(Debug)]
|
||||
pub enum Request {
|
||||
/// Start a new TCP connection to the specified `RemoteAddress` and send
|
||||
/// the request data up the connection.
|
||||
/// All responses produced on this `ConnectionId` should come back to the specified `Recipient`
|
||||
Connect(Box<ConnectRequest>),
|
||||
|
||||
/// Re-use an existing TCP connection, sending more request data up it.
|
||||
Send(SendRequest),
|
||||
Send(ConnectionId, Vec<u8>, bool),
|
||||
}
|
||||
|
||||
impl Socks5RequestContent {
|
||||
impl Request {
|
||||
/// Construct a new Request::Connect instance
|
||||
pub fn new_connect(
|
||||
conn_id: ConnectionId,
|
||||
remote_addr: RemoteAddress,
|
||||
return_address: Option<Recipient>,
|
||||
) -> Socks5RequestContent {
|
||||
Socks5RequestContent::Connect(Box::new(ConnectRequest {
|
||||
) -> Request {
|
||||
Request::Connect(Box::new(ConnectRequest {
|
||||
conn_id,
|
||||
remote_addr,
|
||||
return_address,
|
||||
@@ -196,16 +93,8 @@ impl Socks5RequestContent {
|
||||
}
|
||||
|
||||
/// Construct a new Request::Send instance
|
||||
pub fn new_send(
|
||||
conn_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
local_closed: bool,
|
||||
) -> Socks5RequestContent {
|
||||
Socks5RequestContent::Send(SendRequest {
|
||||
conn_id,
|
||||
data,
|
||||
local_closed,
|
||||
})
|
||||
pub fn new_send(conn_id: ConnectionId, data: Vec<u8>, local_closed: bool) -> Request {
|
||||
Request::Send(conn_id, data, local_closed)
|
||||
}
|
||||
|
||||
/// Deserialize the request type, connection id, destination address and port,
|
||||
@@ -222,23 +111,23 @@ impl Socks5RequestContent {
|
||||
/// The request_flag tells us whether this is a new connection request (`new_connect`),
|
||||
/// an already-established connection we should send up (`new_send`), or
|
||||
/// a request to close an established connection (`new_close`).
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<Socks5RequestContent, RequestDeserializationError> {
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<Request, RequestError> {
|
||||
// each request needs to at least contain flag and ConnectionId
|
||||
if b.is_empty() {
|
||||
return Err(RequestDeserializationError::NoData);
|
||||
return Err(RequestError::NoData);
|
||||
}
|
||||
|
||||
if b.len() < 9 {
|
||||
return Err(RequestDeserializationError::ConnectionIdTooShort);
|
||||
return Err(RequestError::ConnectionIdTooShort);
|
||||
}
|
||||
let conn_id = u64::from_be_bytes([b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]]);
|
||||
let connection_id = u64::from_be_bytes([b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]]);
|
||||
match RequestFlag::try_from(b[0])? {
|
||||
RequestFlag::Connect => {
|
||||
let connect_request_bytes = &b[9..];
|
||||
|
||||
// we need to be able to read at least 2 bytes that specify address length
|
||||
if connect_request_bytes.len() < 2 {
|
||||
return Err(RequestDeserializationError::AddressLengthTooShort);
|
||||
return Err(RequestError::AddressLengthTooShort);
|
||||
}
|
||||
|
||||
let address_length =
|
||||
@@ -246,7 +135,7 @@ impl Socks5RequestContent {
|
||||
as usize;
|
||||
|
||||
if connect_request_bytes.len() < 2 + address_length {
|
||||
return Err(RequestDeserializationError::AddressTooShort);
|
||||
return Err(RequestError::AddressTooShort);
|
||||
}
|
||||
|
||||
let address_start = 2;
|
||||
@@ -261,19 +150,19 @@ impl Socks5RequestContent {
|
||||
None
|
||||
} else {
|
||||
if recipient_data_bytes.len() != Recipient::LEN {
|
||||
return Err(RequestDeserializationError::ReturnAddressTooShort);
|
||||
return Err(RequestError::ReturnAddressTooShort);
|
||||
}
|
||||
|
||||
let mut return_bytes = [0u8; Recipient::LEN];
|
||||
return_bytes.copy_from_slice(&recipient_data_bytes[..Recipient::LEN]);
|
||||
Some(
|
||||
Recipient::try_from_bytes(return_bytes)
|
||||
.map_err(RequestDeserializationError::MalformedReturnAddress)?,
|
||||
.map_err(RequestError::MalformedReturnAddress)?,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Socks5RequestContent::new_connect(
|
||||
conn_id,
|
||||
Ok(Request::new_connect(
|
||||
connection_id,
|
||||
remote_address,
|
||||
return_address,
|
||||
))
|
||||
@@ -282,11 +171,7 @@ impl Socks5RequestContent {
|
||||
let local_closed = b[9] != 0;
|
||||
let data = b[10..].to_vec();
|
||||
|
||||
Ok(Socks5RequestContent::Send(SendRequest {
|
||||
conn_id,
|
||||
data,
|
||||
local_closed,
|
||||
}))
|
||||
Ok(Request::Send(connection_id, data, local_closed))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,7 +182,7 @@ impl Socks5RequestContent {
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
match self {
|
||||
// connect is: CONN_FLAG || CONN_ID || REMOTE_LEN || REMOTE || RETURN
|
||||
Socks5RequestContent::Connect(req) => {
|
||||
Request::Connect(req) => {
|
||||
let remote_address_bytes = req.remote_addr.into_bytes();
|
||||
let remote_address_bytes_len = remote_address_bytes.len() as u16;
|
||||
|
||||
@@ -312,10 +197,10 @@ impl Socks5RequestContent {
|
||||
iter.collect()
|
||||
}
|
||||
}
|
||||
Socks5RequestContent::Send(req) => std::iter::once(RequestFlag::Send as u8)
|
||||
.chain(req.conn_id.to_be_bytes().into_iter())
|
||||
.chain(std::iter::once(req.local_closed as u8))
|
||||
.chain(req.data.into_iter())
|
||||
Request::Send(conn_id, data, local_closed) => std::iter::once(RequestFlag::Send as u8)
|
||||
.chain(conn_id.to_be_bytes().into_iter())
|
||||
.chain(std::iter::once(local_closed as u8))
|
||||
.chain(data.into_iter())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
@@ -331,8 +216,8 @@ mod request_deserialization_tests {
|
||||
#[test]
|
||||
fn returns_error_when_zero_bytes() {
|
||||
let request_bytes = Vec::new();
|
||||
match Socks5RequestContent::try_from_bytes(&request_bytes).unwrap_err() {
|
||||
RequestDeserializationError::NoData => {}
|
||||
match Request::try_from_bytes(&request_bytes).unwrap_err() {
|
||||
RequestError::NoData => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -340,8 +225,8 @@ mod request_deserialization_tests {
|
||||
#[test]
|
||||
fn returns_error_when_connection_id_too_short() {
|
||||
let request_bytes = [RequestFlag::Connect as u8, 1, 2, 3, 4, 5, 6, 7].to_vec(); // 7 bytes connection id
|
||||
match Socks5RequestContent::try_from_bytes(&request_bytes).unwrap_err() {
|
||||
RequestDeserializationError::ConnectionIdTooShort => {}
|
||||
match Request::try_from_bytes(&request_bytes).unwrap_err() {
|
||||
RequestError::ConnectionIdTooShort => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -356,13 +241,13 @@ mod request_deserialization_tests {
|
||||
let request_bytes1 = [RequestFlag::Connect as u8, 1, 2, 3, 4, 5, 6, 7, 8].to_vec(); // 8 bytes connection id, 0 bytes address length (2 were expected)
|
||||
let request_bytes2 = [RequestFlag::Connect as u8, 1, 2, 3, 4, 5, 6, 7, 8, 0].to_vec(); // 8 bytes connection id, 1 bytes address length (2 were expected)
|
||||
|
||||
match Socks5RequestContent::try_from_bytes(&request_bytes1).unwrap_err() {
|
||||
RequestDeserializationError::AddressLengthTooShort => {}
|
||||
match Request::try_from_bytes(&request_bytes1).unwrap_err() {
|
||||
RequestError::AddressLengthTooShort => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
match Socks5RequestContent::try_from_bytes(&request_bytes2).unwrap_err() {
|
||||
RequestDeserializationError::AddressLengthTooShort => {}
|
||||
match Request::try_from_bytes(&request_bytes2).unwrap_err() {
|
||||
RequestError::AddressLengthTooShort => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -370,8 +255,8 @@ mod request_deserialization_tests {
|
||||
#[test]
|
||||
fn returns_error_when_address_too_short_for_given_address_length() {
|
||||
let request_bytes = [RequestFlag::Connect as u8, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1].to_vec(); // 8 bytes connection id, 2 bytes address length, missing address
|
||||
match Socks5RequestContent::try_from_bytes(&request_bytes).unwrap_err() {
|
||||
RequestDeserializationError::AddressTooShort => {}
|
||||
match Request::try_from_bytes(&request_bytes).unwrap_err() {
|
||||
RequestError::AddressTooShort => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -410,8 +295,8 @@ mod request_deserialization_tests {
|
||||
.chain(recipient_bytes.iter().take(40).cloned())
|
||||
.collect();
|
||||
|
||||
match Socks5RequestContent::try_from_bytes(&request_bytes).unwrap_err() {
|
||||
RequestDeserializationError::ReturnAddressTooShort => {}
|
||||
match Request::try_from_bytes(&request_bytes).unwrap_err() {
|
||||
RequestError::ReturnAddressTooShort => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -453,7 +338,7 @@ mod request_deserialization_tests {
|
||||
.cloned()
|
||||
.chain(recipient_bytes.into_iter())
|
||||
.collect();
|
||||
assert!(Socks5RequestContent::try_from_bytes(&request_bytes)
|
||||
assert!(Request::try_from_bytes(&request_bytes)
|
||||
.unwrap_err()
|
||||
.is_malformed_return());
|
||||
}
|
||||
@@ -491,9 +376,9 @@ mod request_deserialization_tests {
|
||||
.chain(recipient_bytes.into_iter())
|
||||
.collect();
|
||||
|
||||
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
|
||||
let request = Request::try_from_bytes(&request_bytes).unwrap();
|
||||
match request {
|
||||
Socks5RequestContent::Connect(req) => {
|
||||
Request::Connect(req) => {
|
||||
assert_eq!("foo.com".to_string(), req.remote_addr);
|
||||
assert_eq!(u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), req.conn_id);
|
||||
assert_eq!(
|
||||
@@ -538,9 +423,9 @@ mod request_deserialization_tests {
|
||||
.chain(recipient_bytes.into_iter())
|
||||
.collect();
|
||||
|
||||
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
|
||||
let request = Request::try_from_bytes(&request_bytes).unwrap();
|
||||
match request {
|
||||
Socks5RequestContent::Connect(req) => {
|
||||
Request::Connect(req) => {
|
||||
assert_eq!("foo.com".to_string(), req.remote_addr);
|
||||
assert_eq!(u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), req.conn_id);
|
||||
assert_eq!(
|
||||
@@ -561,13 +446,9 @@ mod request_deserialization_tests {
|
||||
fn works_when_request_is_sized_properly_even_without_data() {
|
||||
// correct 8 bytes of connection_id, 1 byte of local_closed and 0 bytes request data
|
||||
let request_bytes = [RequestFlag::Send as u8, 1, 2, 3, 4, 5, 6, 7, 8, 0].to_vec();
|
||||
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
|
||||
let request = Request::try_from_bytes(&request_bytes).unwrap();
|
||||
match request {
|
||||
Socks5RequestContent::Send(SendRequest {
|
||||
conn_id,
|
||||
data,
|
||||
local_closed,
|
||||
}) => {
|
||||
Request::Send(conn_id, data, local_closed) => {
|
||||
assert_eq!(u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), conn_id);
|
||||
assert_eq!(Vec::<u8>::new(), data);
|
||||
assert!(!local_closed)
|
||||
@@ -596,13 +477,9 @@ mod request_deserialization_tests {
|
||||
]
|
||||
.to_vec();
|
||||
|
||||
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
|
||||
let request = Request::try_from_bytes(&request_bytes).unwrap();
|
||||
match request {
|
||||
Socks5RequestContent::Send(SendRequest {
|
||||
conn_id,
|
||||
data,
|
||||
local_closed,
|
||||
}) => {
|
||||
Request::Send(conn_id, data, local_closed) => {
|
||||
assert_eq!(u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]), conn_id);
|
||||
assert_eq!(vec![255, 255, 255], data);
|
||||
assert!(!local_closed)
|
||||
|
||||
@@ -1,239 +1,46 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{ConnectionId, Socks5ProtocolVersion, Socks5RequestError};
|
||||
use service_providers_common::interface::{Serializable, ServiceProviderResponse};
|
||||
use thiserror::Error;
|
||||
|
||||
// don't start tags from 0 for easier backwards compatibility since `NetworkData`
|
||||
// used to be a `Response` with tag 1
|
||||
// and `ConnectionError` used to be `NetworkRequesterResponse` with tag 2
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ResponseFlag {
|
||||
NetworkData = 1,
|
||||
ConnectionError = 2,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for ResponseFlag {
|
||||
type Error = ResponseDeserializationError;
|
||||
|
||||
fn try_from(value: u8) -> Result<ResponseFlag, ResponseDeserializationError> {
|
||||
match value {
|
||||
_ if value == (ResponseFlag::NetworkData as u8) => Ok(Self::NetworkData),
|
||||
_ if value == (ResponseFlag::ConnectionError as u8) => Ok(Self::ConnectionError),
|
||||
value => Err(ResponseDeserializationError::UnknownResponseFlag { value }),
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::ConnectionId;
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
pub enum ResponseDeserializationError {
|
||||
pub enum ResponseError {
|
||||
#[error("not enough bytes to recover the connection id")]
|
||||
ConnectionIdTooShort,
|
||||
|
||||
#[error("{value} is not a valid response flag")]
|
||||
UnknownResponseFlag { value: u8 },
|
||||
|
||||
#[error("no data provided")]
|
||||
NoData,
|
||||
|
||||
#[error("message is not utf8 encoded: {source}")]
|
||||
MalformedErrorMessage {
|
||||
#[from]
|
||||
source: std::string::FromUtf8Error,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Socks5Response {
|
||||
pub protocol_version: Socks5ProtocolVersion,
|
||||
pub content: Socks5ResponseContent,
|
||||
}
|
||||
|
||||
impl Serializable for Socks5Response {
|
||||
type Error = Socks5RequestError;
|
||||
|
||||
// legacy responses had the format of
|
||||
// 1 (Message::RESPONSE_FLAG) || <data> for data responses
|
||||
// 2 (Message::NR_RESPONSE_FLAG) || <data> for error responses
|
||||
// the updated formats use
|
||||
// 3 (Socks5ProtocolVersion) || 0 (ResponseFlag::NetworkData) || <data> for data responses
|
||||
// 3 (Socks5ProtocolVersion) || 1 (ResponseFlag::ConnectionError) || <data> for error responses
|
||||
// so for serialization an optional version tag is prepended
|
||||
// and in deserialization it's just the case of shifting the buffer in case of non-legacy response payload
|
||||
fn into_bytes(self) -> Vec<u8> {
|
||||
if let Some(version) = self.protocol_version.as_u8() {
|
||||
std::iter::once(version)
|
||||
.chain(self.content.into_bytes().into_iter())
|
||||
.collect()
|
||||
} else {
|
||||
self.content.into_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
fn try_from_bytes(b: &[u8]) -> Result<Self, Self::Error> {
|
||||
if b.is_empty() {
|
||||
return Err(ResponseDeserializationError::NoData.into());
|
||||
}
|
||||
|
||||
let protocol_version = Socks5ProtocolVersion::from(b[0]);
|
||||
let content = if protocol_version.is_legacy() {
|
||||
Socks5ResponseContent::try_from_bytes(b)
|
||||
} else {
|
||||
Socks5ResponseContent::try_from_bytes(&b[1..])
|
||||
}?;
|
||||
Ok(Socks5Response {
|
||||
protocol_version,
|
||||
content,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ServiceProviderResponse for Socks5Response {}
|
||||
|
||||
impl Socks5Response {
|
||||
pub fn new(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
content: Socks5ResponseContent,
|
||||
) -> Socks5Response {
|
||||
Socks5Response {
|
||||
protocol_version,
|
||||
content,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_network_data(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
connection_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
is_closed: bool,
|
||||
) -> Socks5Response {
|
||||
Socks5Response {
|
||||
protocol_version,
|
||||
content: Socks5ResponseContent::new_network_data(connection_id, data, is_closed),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_closed_empty(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
connection_id: ConnectionId,
|
||||
) -> Socks5Response {
|
||||
Socks5Response {
|
||||
protocol_version,
|
||||
content: Socks5ResponseContent::new_closed_empty(connection_id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_connection_error(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
connection_id: ConnectionId,
|
||||
error_message: String,
|
||||
) -> Socks5Response {
|
||||
Socks5Response {
|
||||
protocol_version,
|
||||
content: Socks5ResponseContent::new_connection_error(connection_id, error_message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Socks5ResponseContent {
|
||||
NetworkData(NetworkData),
|
||||
ConnectionError(ConnectionError),
|
||||
}
|
||||
|
||||
impl Socks5ResponseContent {
|
||||
pub fn new_network_data(
|
||||
connection_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
is_closed: bool,
|
||||
) -> Socks5ResponseContent {
|
||||
Socks5ResponseContent::NetworkData(NetworkData::new(connection_id, data, is_closed))
|
||||
}
|
||||
|
||||
pub fn new_closed_empty(connection_id: ConnectionId) -> Socks5ResponseContent {
|
||||
Socks5ResponseContent::NetworkData(NetworkData::new_closed_empty(connection_id))
|
||||
}
|
||||
|
||||
pub fn new_connection_error(
|
||||
connection_id: ConnectionId,
|
||||
error_message: String,
|
||||
) -> Socks5ResponseContent {
|
||||
Socks5ResponseContent::ConnectionError(ConnectionError::new(connection_id, error_message))
|
||||
}
|
||||
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
match self {
|
||||
Socks5ResponseContent::NetworkData(res) => {
|
||||
std::iter::once(ResponseFlag::NetworkData as u8)
|
||||
.chain(res.into_bytes().into_iter())
|
||||
.collect()
|
||||
}
|
||||
Socks5ResponseContent::ConnectionError(res) => {
|
||||
std::iter::once(ResponseFlag::ConnectionError as u8)
|
||||
.chain(res.into_bytes().into_iter())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<Socks5ResponseContent, ResponseDeserializationError> {
|
||||
if b.is_empty() {
|
||||
// TODO: bad error type since this branch could be reached in the 'versioned' case
|
||||
// after reading 1 byte already
|
||||
return Err(ResponseDeserializationError::NoData);
|
||||
}
|
||||
|
||||
let response_flag = ResponseFlag::try_from(b[0])?;
|
||||
match response_flag {
|
||||
ResponseFlag::NetworkData => Ok(Socks5ResponseContent::NetworkData(
|
||||
NetworkData::try_from_bytes(&b[1..])?,
|
||||
)),
|
||||
ResponseFlag::ConnectionError => Ok(Socks5ResponseContent::ConnectionError(
|
||||
ConnectionError::try_from_bytes(&b[1..])?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A remote network network data response retrieved by the Socks5 service provider. This
|
||||
/// A remote network response retrieved by the Socks5 service provider. This
|
||||
/// can be serialized and sent back through the mixnet to the requesting
|
||||
/// application.
|
||||
#[derive(Debug)]
|
||||
pub struct NetworkData {
|
||||
pub struct Response {
|
||||
pub data: Vec<u8>,
|
||||
pub connection_id: ConnectionId,
|
||||
pub is_closed: bool,
|
||||
}
|
||||
|
||||
impl NetworkData {
|
||||
impl Response {
|
||||
/// Constructor for responses
|
||||
pub fn new(connection_id: ConnectionId, data: Vec<u8>, is_closed: bool) -> Self {
|
||||
NetworkData {
|
||||
Response {
|
||||
data,
|
||||
connection_id,
|
||||
is_closed,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_closed_empty(connection_id: ConnectionId) -> Self {
|
||||
NetworkData {
|
||||
data: vec![],
|
||||
connection_id,
|
||||
is_closed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<NetworkData, ResponseDeserializationError> {
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<Response, ResponseError> {
|
||||
if b.is_empty() {
|
||||
return Err(ResponseDeserializationError::NoData);
|
||||
return Err(ResponseError::NoData);
|
||||
}
|
||||
|
||||
let is_closed = b[0] != 0;
|
||||
|
||||
if b.len() < 9 {
|
||||
return Err(ResponseDeserializationError::ConnectionIdTooShort);
|
||||
return Err(ResponseError::ConnectionIdTooShort);
|
||||
}
|
||||
|
||||
let mut connection_id_bytes = b.to_vec();
|
||||
@@ -250,7 +57,7 @@ impl NetworkData {
|
||||
connection_id_bytes[8],
|
||||
]);
|
||||
|
||||
let response = NetworkData::new(connection_id, data, is_closed);
|
||||
let response = Response::new(connection_id, data, is_closed);
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
@@ -264,154 +71,54 @@ impl NetworkData {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConnectionError {
|
||||
pub connection_id: ConnectionId,
|
||||
pub network_requester_error: String,
|
||||
}
|
||||
|
||||
impl ConnectionError {
|
||||
pub fn new(connection_id: ConnectionId, network_requester_error: String) -> Self {
|
||||
ConnectionError {
|
||||
connection_id,
|
||||
network_requester_error,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<ConnectionError, ResponseDeserializationError> {
|
||||
if b.is_empty() {
|
||||
return Err(ResponseDeserializationError::NoData);
|
||||
}
|
||||
|
||||
if b.len() < 8 {
|
||||
return Err(ResponseDeserializationError::ConnectionIdTooShort);
|
||||
}
|
||||
|
||||
let mut connection_id_bytes = b.to_vec();
|
||||
let network_requester_error_bytes = connection_id_bytes.split_off(8);
|
||||
|
||||
let connection_id = u64::from_be_bytes([
|
||||
connection_id_bytes[0],
|
||||
connection_id_bytes[1],
|
||||
connection_id_bytes[2],
|
||||
connection_id_bytes[3],
|
||||
connection_id_bytes[4],
|
||||
connection_id_bytes[5],
|
||||
connection_id_bytes[6],
|
||||
connection_id_bytes[7],
|
||||
]);
|
||||
let network_requester_error = String::from_utf8(network_requester_error_bytes)?;
|
||||
|
||||
Ok(ConnectionError {
|
||||
connection_id,
|
||||
network_requester_error,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.connection_id
|
||||
.to_be_bytes()
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(self.network_requester_error.into_bytes().into_iter())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
mod constructing_socks5_responses_from_bytes {
|
||||
use super::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod constructing_socks5_data_responses_from_bytes {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn fails_when_zero_bytes_are_supplied() {
|
||||
let response_bytes = Vec::new();
|
||||
|
||||
#[test]
|
||||
fn fails_when_zero_bytes_are_supplied() {
|
||||
let response_bytes = Vec::new();
|
||||
|
||||
assert_eq!(
|
||||
ResponseDeserializationError::NoData,
|
||||
NetworkData::try_from_bytes(&response_bytes).unwrap_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fails_when_connection_id_bytes_are_too_short() {
|
||||
let response_bytes = vec![0, 1, 2, 3, 4, 5, 6];
|
||||
assert_eq!(
|
||||
ResponseDeserializationError::ConnectionIdTooShort,
|
||||
NetworkData::try_from_bytes(&response_bytes).unwrap_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_when_there_is_no_data() {
|
||||
let response_bytes = vec![0, 0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let expected = NetworkData::new(
|
||||
u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
|
||||
Vec::new(),
|
||||
false,
|
||||
);
|
||||
let actual = NetworkData::try_from_bytes(&response_bytes).unwrap();
|
||||
assert_eq!(expected.connection_id, actual.connection_id);
|
||||
assert_eq!(expected.data, actual.data);
|
||||
assert_eq!(expected.is_closed, actual.is_closed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_when_there_is_data() {
|
||||
let response_bytes = vec![0, 0, 1, 2, 3, 4, 5, 6, 7, 255, 255, 255];
|
||||
let expected = NetworkData::new(
|
||||
u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
|
||||
vec![255, 255, 255],
|
||||
false,
|
||||
);
|
||||
let actual = NetworkData::try_from_bytes(&response_bytes).unwrap();
|
||||
assert_eq!(expected.connection_id, actual.connection_id);
|
||||
assert_eq!(expected.data, actual.data);
|
||||
assert_eq!(expected.is_closed, actual.is_closed);
|
||||
}
|
||||
assert_eq!(
|
||||
ResponseError::NoData,
|
||||
Response::try_from_bytes(&response_bytes).unwrap_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod connection_error_response_serde_tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn fails_when_connection_id_bytes_are_too_short() {
|
||||
let response_bytes = vec![0, 1, 2, 3, 4, 5, 6];
|
||||
assert_eq!(
|
||||
ResponseError::ConnectionIdTooShort,
|
||||
Response::try_from_bytes(&response_bytes).unwrap_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_serde() {
|
||||
let conn_id = 42;
|
||||
let network_requester_error = String::from("This is a test msg");
|
||||
let response = ConnectionError::new(conn_id, network_requester_error.clone());
|
||||
let bytes = response.into_bytes();
|
||||
let deserialized_response = ConnectionError::try_from_bytes(&bytes).unwrap();
|
||||
#[test]
|
||||
fn works_when_there_is_no_data() {
|
||||
let response_bytes = vec![0, 0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let expected = Response::new(
|
||||
u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
|
||||
Vec::new(),
|
||||
false,
|
||||
);
|
||||
let actual = Response::try_from_bytes(&response_bytes).unwrap();
|
||||
assert_eq!(expected.connection_id, actual.connection_id);
|
||||
assert_eq!(expected.data, actual.data);
|
||||
assert_eq!(expected.is_closed, actual.is_closed);
|
||||
}
|
||||
|
||||
assert_eq!(conn_id, deserialized_response.connection_id);
|
||||
assert_eq!(
|
||||
network_requester_error,
|
||||
deserialized_response.network_requester_error
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialization_errors() {
|
||||
let err = ConnectionError::try_from_bytes(&[]).err().unwrap();
|
||||
assert_eq!(err, ResponseDeserializationError::NoData);
|
||||
|
||||
let bytes: [u8; 5] = [1, 2, 3, 4, 5];
|
||||
let err = ConnectionError::try_from_bytes(&bytes).err().unwrap();
|
||||
assert_eq!(err, ResponseDeserializationError::ConnectionIdTooShort);
|
||||
|
||||
let bytes: Vec<u8> = 42u64
|
||||
.to_be_bytes()
|
||||
.into_iter()
|
||||
.chain([0, 159, 146, 150].into_iter())
|
||||
.collect();
|
||||
let err = ConnectionError::try_from_bytes(&bytes).err().unwrap();
|
||||
assert!(matches!(
|
||||
err,
|
||||
ResponseDeserializationError::MalformedErrorMessage { .. }
|
||||
));
|
||||
}
|
||||
#[test]
|
||||
fn works_when_there_is_data() {
|
||||
let response_bytes = vec![0, 0, 1, 2, 3, 4, 5, 6, 7, 255, 255, 255];
|
||||
let expected = Response::new(
|
||||
u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
|
||||
vec![255, 255, 255],
|
||||
false,
|
||||
);
|
||||
let actual = Response::try_from_bytes(&response_bytes).unwrap();
|
||||
assert_eq!(expected.connection_id, actual.connection_id);
|
||||
assert_eq!(expected.data, actual.data);
|
||||
assert_eq!(expected.is_closed, actual.is_closed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use service_providers_common::{define_simple_version, interface::Version};
|
||||
|
||||
/// Defines initial version of the communication interface between socks5 clients and
|
||||
/// network requesters (socks5).
|
||||
// note: we start from '3' so that we could distinguish cases where no version is provided
|
||||
// and legacy communication mode is used instead
|
||||
pub const INITIAL_INTERFACE_VERSION: u8 = 3;
|
||||
|
||||
/// Defines the current version of the communication interface between socks5 clients and
|
||||
/// network requesters (socks5).
|
||||
/// It has to be incremented for any breaking change.
|
||||
pub const INTERFACE_VERSION: u8 = 3;
|
||||
|
||||
define_simple_version!(
|
||||
Socks5ProtocolVersion,
|
||||
INITIAL_INTERFACE_VERSION,
|
||||
INTERFACE_VERSION
|
||||
);
|
||||
Vendored
BIN
Binary file not shown.
@@ -7,9 +7,7 @@ use crate::dealers::queries::{
|
||||
use crate::dealers::transactions::try_add_dealer;
|
||||
use crate::dealings::queries::query_dealings_paged;
|
||||
use crate::dealings::transactions::try_commit_dealings;
|
||||
use crate::epoch_state::queries::{
|
||||
query_current_epoch, query_current_epoch_threshold, query_initial_dealers,
|
||||
};
|
||||
use crate::epoch_state::queries::{query_current_epoch, query_current_epoch_threshold};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_surpassed_threshold};
|
||||
use crate::error::ContractError;
|
||||
@@ -77,17 +75,15 @@ pub fn execute(
|
||||
ExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof,
|
||||
announce_address,
|
||||
resharing,
|
||||
} => try_add_dealer(deps, info, bte_key_with_proof, announce_address, resharing),
|
||||
ExecuteMsg::CommitDealing {
|
||||
dealing_bytes,
|
||||
resharing,
|
||||
} => try_commit_dealings(deps, info, dealing_bytes, resharing),
|
||||
ExecuteMsg::CommitVerificationKeyShare { share, resharing } => {
|
||||
try_commit_verification_key_share(deps, env, info, share, resharing)
|
||||
} => try_add_dealer(deps, info, bte_key_with_proof, announce_address),
|
||||
ExecuteMsg::CommitDealing { dealing_bytes } => {
|
||||
try_commit_dealings(deps, info, dealing_bytes)
|
||||
}
|
||||
ExecuteMsg::VerifyVerificationKeyShare { owner, resharing } => {
|
||||
try_verify_verification_key_share(deps, info, owner, resharing)
|
||||
ExecuteMsg::CommitVerificationKeyShare { share } => {
|
||||
try_commit_verification_key_share(deps, env, info, share)
|
||||
}
|
||||
ExecuteMsg::VerifyVerificationKeyShare { owner } => {
|
||||
try_verify_verification_key_share(deps, info, owner)
|
||||
}
|
||||
ExecuteMsg::SurpassedThreshold {} => try_surpassed_threshold(deps, env),
|
||||
ExecuteMsg::AdvanceEpochState {} => advance_epoch_state(deps, env),
|
||||
@@ -101,7 +97,6 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse,
|
||||
QueryMsg::GetCurrentEpochThreshold {} => {
|
||||
to_binary(&query_current_epoch_threshold(deps.storage)?)?
|
||||
}
|
||||
QueryMsg::GetInitialDealers {} => to_binary(&query_initial_dealers(deps.storage)?)?,
|
||||
QueryMsg::GetDealerDetails { dealer_address } => {
|
||||
to_binary(&query_dealer_details(deps, dealer_address)?)?
|
||||
}
|
||||
@@ -243,7 +238,6 @@ mod tests {
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
resharing: false,
|
||||
},
|
||||
&vec![],
|
||||
)
|
||||
@@ -257,7 +251,6 @@ mod tests {
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
resharing: false,
|
||||
},
|
||||
&vec![],
|
||||
)
|
||||
@@ -273,7 +266,6 @@ mod tests {
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
resharing: false,
|
||||
},
|
||||
&vec![],
|
||||
)
|
||||
|
||||
@@ -2,33 +2,26 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealers::storage as dealers_storage;
|
||||
use crate::epoch_state::storage::INITIAL_REPLACEMENT_DATA;
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::STATE;
|
||||
use crate::state::{State, STATE};
|
||||
use coconut_dkg_common::types::{DealerDetails, EncodedBTEPublicKeyWithProof, EpochState};
|
||||
use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response};
|
||||
|
||||
// currently we only require that
|
||||
// a) it's part of the signer group
|
||||
// b) it isn't already a dealer
|
||||
fn verify_dealer(deps: DepsMut<'_>, dealer: &Addr, resharing: bool) -> Result<(), ContractError> {
|
||||
fn verify_dealer(deps: DepsMut<'_>, state: &State, dealer: &Addr) -> Result<(), ContractError> {
|
||||
if dealers_storage::current_dealers()
|
||||
.may_load(deps.storage, dealer)?
|
||||
.is_some()
|
||||
{
|
||||
return Err(ContractError::AlreadyADealer);
|
||||
}
|
||||
let state = STATE.load(deps.storage)?;
|
||||
|
||||
let height = if resharing {
|
||||
INITIAL_REPLACEMENT_DATA.load(deps.storage)?.initial_height
|
||||
} else {
|
||||
None
|
||||
};
|
||||
state
|
||||
.group_addr
|
||||
.is_voting_member(&deps.querier, dealer, height)?
|
||||
.is_voting_member(&deps.querier, dealer, None)?
|
||||
.ok_or(ContractError::Unauthorized {})?;
|
||||
|
||||
Ok(())
|
||||
@@ -39,11 +32,11 @@ pub fn try_add_dealer(
|
||||
info: MessageInfo,
|
||||
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
check_epoch_state(deps.storage, EpochState::PublicKeySubmission { resharing })?;
|
||||
check_epoch_state(deps.storage, EpochState::PublicKeySubmission)?;
|
||||
let state = STATE.load(deps.storage)?;
|
||||
|
||||
verify_dealer(deps.branch(), &info.sender, resharing)?;
|
||||
verify_dealer(deps.branch(), &state, &info.sender)?;
|
||||
|
||||
// if it was already a dealer in the past, assign the same node index
|
||||
let node_index = if let Some(prior_details) =
|
||||
@@ -76,63 +69,10 @@ pub fn try_add_dealer(
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::dealers::storage::current_dealers;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::support::tests::fixtures::dealer_details_fixture;
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::GROUP_MEMBERS;
|
||||
use coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
|
||||
use coconut_dkg_common::types::TimeConfiguration;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cw4::Member;
|
||||
use rusty_fork::rusty_fork_test;
|
||||
|
||||
rusty_fork_test! {
|
||||
#[test]
|
||||
fn verification() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let new_dealer = Addr::unchecked("new_dealer");
|
||||
let details1 = dealer_details_fixture(1);
|
||||
let details2 = dealer_details_fixture(2);
|
||||
let details3 = dealer_details_fixture(3);
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details1.address, &details1)
|
||||
.unwrap();
|
||||
let err = verify_dealer(deps.as_mut(), &details1.address, false).unwrap_err();
|
||||
assert_eq!(err, ContractError::AlreadyADealer);
|
||||
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&InitialReplacementData {
|
||||
initial_dealers: vec![details1.address, details2.address, details3.address],
|
||||
initial_height: Some(1),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let err = verify_dealer(deps.as_mut(), &new_dealer, false).unwrap_err();
|
||||
assert_eq!(err, ContractError::Unauthorized);
|
||||
|
||||
GROUP_MEMBERS.lock().unwrap().push((
|
||||
Member {
|
||||
addr: new_dealer.to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
2,
|
||||
));
|
||||
verify_dealer(deps.as_mut(), &new_dealer, false).unwrap();
|
||||
|
||||
let err = verify_dealer(deps.as_mut(), &new_dealer, true).unwrap_err();
|
||||
assert_eq!(err, ContractError::Unauthorized);
|
||||
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut data| {
|
||||
data.initial_height = Some(2);
|
||||
Ok(data)
|
||||
})
|
||||
.unwrap();
|
||||
verify_dealer(deps.as_mut(), &new_dealer, true).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_state() {
|
||||
@@ -154,13 +94,12 @@ pub(crate) mod tests {
|
||||
info.clone(),
|
||||
bte_key_with_proof.clone(),
|
||||
announce_address.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::DealingExchange { resharing: false }.to_string(),
|
||||
current_state: EpochState::DealingExchange.to_string(),
|
||||
expected_state: EpochState::default().to_string(),
|
||||
}
|
||||
);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use crate::dealers::storage as dealers_storage;
|
||||
use crate::dealings::storage::DEALINGS_BYTES;
|
||||
use crate::epoch_state::storage::INITIAL_REPLACEMENT_DATA;
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use coconut_dkg_common::types::{ContractSafeBytes, EpochState};
|
||||
@@ -13,9 +12,8 @@ pub fn try_commit_dealings(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
check_epoch_state(deps.storage, EpochState::DealingExchange { resharing })?;
|
||||
check_epoch_state(deps.storage, EpochState::DealingExchange)?;
|
||||
// ensure the sender is a dealer
|
||||
if dealers_storage::current_dealers()
|
||||
.may_load(deps.storage, &info.sender)?
|
||||
@@ -23,14 +21,6 @@ pub fn try_commit_dealings(
|
||||
{
|
||||
return Err(ContractError::NotADealer);
|
||||
}
|
||||
if resharing
|
||||
&& !INITIAL_REPLACEMENT_DATA
|
||||
.load(deps.storage)?
|
||||
.initial_dealers
|
||||
.contains(&info.sender)
|
||||
{
|
||||
return Err(ContractError::NotAnInitialDealer);
|
||||
}
|
||||
|
||||
// check if this dealer has already committed to all dealings
|
||||
// (we don't want to allow overwriting anything)
|
||||
@@ -49,30 +39,29 @@ pub fn try_commit_dealings(
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, dealing_bytes_fixture};
|
||||
use crate::support::tests::fixtures::dealing_bytes_fixture;
|
||||
use crate::support::tests::helpers;
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
|
||||
use coconut_dkg_common::types::TimeConfiguration;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
#[test]
|
||||
fn invalid_commit_dealing() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let owner = Addr::unchecked("owner1");
|
||||
let owner = Addr::unchecked("owner");
|
||||
let mut env = mock_env();
|
||||
let info = mock_info(owner.as_str(), &[]);
|
||||
let dealing_bytes = dealing_bytes_fixture();
|
||||
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), false)
|
||||
.unwrap_err();
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone()).unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
expected_state: EpochState::DealingExchange { resharing: false }.to_string()
|
||||
expected_state: EpochState::DealingExchange.to_string()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -82,8 +71,8 @@ pub(crate) mod tests {
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), false)
|
||||
.unwrap_err();
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone()).unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotADealer);
|
||||
|
||||
let dealer_details = DealerDetails {
|
||||
@@ -96,41 +85,14 @@ pub(crate) mod tests {
|
||||
.save(deps.as_mut().storage, &owner, &dealer_details)
|
||||
.unwrap();
|
||||
|
||||
// assume we're in resharing mode
|
||||
CURRENT_EPOCH
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
epoch.state = EpochState::DealingExchange { resharing: true };
|
||||
Ok(epoch)
|
||||
})
|
||||
.unwrap();
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&InitialReplacementData {
|
||||
initial_dealers: vec![],
|
||||
initial_height: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), true)
|
||||
.unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotAnInitialDealer);
|
||||
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut data| {
|
||||
data.initial_dealers = vec![dealer_details_fixture(1).address];
|
||||
Ok(data)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
for dealings in DEALINGS_BYTES {
|
||||
assert!(!dealings.has(deps.as_mut().storage, &owner));
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), true);
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone());
|
||||
assert!(ret.is_ok());
|
||||
assert!(dealings.has(deps.as_mut().storage, &owner));
|
||||
}
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), true)
|
||||
.unwrap_err();
|
||||
let ret =
|
||||
try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone()).unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::AlreadyCommitted {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA, THRESHOLD};
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, THRESHOLD};
|
||||
use crate::error::ContractError;
|
||||
use coconut_dkg_common::types::{Epoch, InitialReplacementData};
|
||||
use coconut_dkg_common::types::Epoch;
|
||||
use cosmwasm_std::Storage;
|
||||
|
||||
pub(crate) fn query_current_epoch(storage: &dyn Storage) -> Result<Epoch, ContractError> {
|
||||
@@ -18,12 +18,6 @@ pub(crate) fn query_current_epoch_threshold(
|
||||
Ok(THRESHOLD.may_load(storage)?)
|
||||
}
|
||||
|
||||
pub(crate) fn query_initial_dealers(
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Option<InitialReplacementData>, ContractError> {
|
||||
Ok(INITIAL_REPLACEMENT_DATA.may_load(storage)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
@@ -35,10 +29,7 @@ pub(crate) mod test {
|
||||
fn query_state() {
|
||||
let mut deps = init_contract();
|
||||
let epoch = query_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(epoch.state, EpochState::PublicKeySubmission);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
mock_env()
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use coconut_dkg_common::types::{Epoch, InitialReplacementData};
|
||||
use coconut_dkg_common::types::Epoch;
|
||||
use cw_storage_plus::Item;
|
||||
|
||||
pub(crate) const CURRENT_EPOCH: Item<'_, Epoch> = Item::new("current_epoch");
|
||||
pub const THRESHOLD: Item<u64> = Item::new("threshold");
|
||||
pub const INITIAL_REPLACEMENT_DATA: Item<InitialReplacementData> =
|
||||
Item::new("initial_replacement_data");
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
use crate::dealers::storage::{current_dealers, past_dealers};
|
||||
use crate::dealings::storage::DEALINGS_BYTES;
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA, THRESHOLD};
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, THRESHOLD};
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::STATE;
|
||||
use coconut_dkg_common::types::{Epoch, EpochState, InitialReplacementData};
|
||||
use cosmwasm_std::{Addr, Deps, DepsMut, Env, Order, Response, Storage};
|
||||
use coconut_dkg_common::types::{Epoch, EpochState};
|
||||
use cosmwasm_std::{DepsMut, Env, Order, Response, Storage};
|
||||
|
||||
fn reset_epoch_state(storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
THRESHOLD.remove(storage);
|
||||
@@ -27,13 +27,13 @@ fn reset_epoch_state(storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dealers_still_active(
|
||||
deps: &Deps<'_>,
|
||||
dealers: impl Iterator<Item = Addr>,
|
||||
) -> Result<usize, ContractError> {
|
||||
fn dealers_still_active(deps: &DepsMut<'_>) -> Result<usize, ContractError> {
|
||||
let state = STATE.load(deps.storage)?;
|
||||
let mut still_active = 0;
|
||||
for dealer_addr in dealers {
|
||||
for dealer_addr in current_dealers()
|
||||
.keys(deps.storage, None, None, Order::Ascending)
|
||||
.flatten()
|
||||
{
|
||||
if state
|
||||
.group_addr
|
||||
.is_voting_member(&deps.querier, &dealer_addr, None)?
|
||||
@@ -45,36 +45,6 @@ fn dealers_still_active(
|
||||
Ok(still_active)
|
||||
}
|
||||
|
||||
fn dealers_eq_members(deps: &DepsMut<'_>) -> Result<bool, ContractError> {
|
||||
let dealers_still_active = dealers_still_active(
|
||||
&deps.as_ref(),
|
||||
current_dealers()
|
||||
.keys(deps.storage, None, None, Order::Ascending)
|
||||
.flatten(),
|
||||
)?;
|
||||
let all_dealers = current_dealers()
|
||||
.keys(deps.storage, None, None, Order::Ascending)
|
||||
.count();
|
||||
let group_members = STATE
|
||||
.load(deps.storage)?
|
||||
.group_addr
|
||||
.list_members(&deps.querier, None, None)?
|
||||
.len();
|
||||
|
||||
Ok(dealers_still_active == all_dealers && all_dealers == group_members)
|
||||
}
|
||||
|
||||
fn replacement_threshold_surpassed(deps: &DepsMut<'_>) -> Result<bool, ContractError> {
|
||||
let threshold = THRESHOLD.load(deps.storage)? as usize;
|
||||
let initial_dealers = INITIAL_REPLACEMENT_DATA.load(deps.storage)?.initial_dealers;
|
||||
let initial_dealer_count = initial_dealers.len();
|
||||
let replacement_threshold = threshold - (initial_dealers.len() + 2 - 1) / 2 + 1;
|
||||
let removed_dealer_count =
|
||||
initial_dealer_count - dealers_still_active(&deps.as_ref(), initial_dealers.into_iter())?;
|
||||
|
||||
Ok(removed_dealer_count >= replacement_threshold)
|
||||
}
|
||||
|
||||
pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Response, ContractError> {
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
if epoch.finish_timestamp > env.block.time {
|
||||
@@ -89,20 +59,13 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
|
||||
let current_epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
let next_epoch = if let Some(state) = current_epoch.state.next() {
|
||||
// We are during DKG process
|
||||
if let EpochState::DealingExchange { resharing } = state {
|
||||
let current_dealers = current_dealers()
|
||||
if state == EpochState::DealingExchange {
|
||||
let current_dealer_count = current_dealers()
|
||||
.keys(deps.storage, None, None, Order::Ascending)
|
||||
.collect::<Result<Vec<Addr>, _>>()?;
|
||||
.count();
|
||||
// note: ceiling in integer division can be achieved via q = (x + y - 1) / y;
|
||||
let threshold = (2 * current_dealers.len() as u64 + 3 - 1) / 3;
|
||||
let threshold = (2 * current_dealer_count as u64 + 3 - 1) / 3;
|
||||
THRESHOLD.save(deps.storage, &threshold)?;
|
||||
if !resharing {
|
||||
let replacement_data = InitialReplacementData {
|
||||
initial_dealers: current_dealers,
|
||||
initial_height: None,
|
||||
};
|
||||
INITIAL_REPLACEMENT_DATA.save(deps.storage, &replacement_data)?;
|
||||
}
|
||||
}
|
||||
Epoch::new(
|
||||
state,
|
||||
@@ -110,9 +73,14 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
|
||||
current_epoch.time_configuration,
|
||||
env.block.time,
|
||||
)
|
||||
} else if dealers_eq_members(&deps)? {
|
||||
} else if dealers_still_active(&deps)?
|
||||
== STATE
|
||||
.load(deps.storage)?
|
||||
.group_addr
|
||||
.list_members(&deps.querier, None, None)?
|
||||
.len()
|
||||
{
|
||||
// The dealer set hasn't changed, so we only extend the finish timestamp
|
||||
// The epoch remains the same, as we use it as key for storing VKs
|
||||
Epoch::new(
|
||||
current_epoch.state,
|
||||
current_epoch.epoch_id,
|
||||
@@ -120,21 +88,10 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
|
||||
env.block.time,
|
||||
)
|
||||
} else {
|
||||
// Dealer set changed, we need to redo DKG...
|
||||
let state = if replacement_threshold_surpassed(&deps)? {
|
||||
// ... in reset mode
|
||||
EpochState::default()
|
||||
} else {
|
||||
// ... in reshare mode
|
||||
INITIAL_REPLACEMENT_DATA.update::<_, ContractError>(deps.storage, |mut data| {
|
||||
data.initial_height = Some(env.block.height);
|
||||
Ok(data)
|
||||
})?;
|
||||
EpochState::PublicKeySubmission { resharing: true }
|
||||
};
|
||||
// Dealer set changed, we need to redo DKG from scratch
|
||||
reset_epoch_state(deps.storage)?;
|
||||
Epoch::new(
|
||||
state,
|
||||
EpochState::default(),
|
||||
current_epoch.epoch_id + 1,
|
||||
current_epoch.time_configuration,
|
||||
env.block.time,
|
||||
@@ -152,10 +109,7 @@ pub(crate) fn try_surpassed_threshold(
|
||||
check_epoch_state(deps.storage, EpochState::InProgress)?;
|
||||
|
||||
let threshold = THRESHOLD.load(deps.storage)?;
|
||||
let dealers = current_dealers()
|
||||
.keys(deps.storage, None, None, Order::Ascending)
|
||||
.flatten();
|
||||
if dealers_still_active(&deps.as_ref(), dealers)? < threshold as usize {
|
||||
if dealers_still_active(&deps)? < threshold as usize {
|
||||
reset_epoch_state(deps.storage)?;
|
||||
CURRENT_EPOCH.update::<_, ContractError>(deps.storage, |epoch| {
|
||||
Ok(Epoch::new(
|
||||
@@ -185,137 +139,27 @@ pub(crate) mod tests {
|
||||
use rusty_fork::rusty_fork_test;
|
||||
|
||||
// Because of the global variable handling group, we need individual process for each test
|
||||
|
||||
rusty_fork_test! {
|
||||
// Using values from the DKG document
|
||||
#[test]
|
||||
fn threshold_surpassed() {
|
||||
let mut deps = init_contract();
|
||||
let two_thirds = |n: u64| (2 * n + 3 - 1) / 3;
|
||||
let three_fourths = |n: u64| (3 * n + 4 - 1) / 4;
|
||||
let ninty_pc = |n: u64| (9 * n + 10 - 2) / 10;
|
||||
let mut limits = [3, 4, 5, 5, 7, 11, 10, 14, 21, 18, 26, 41].iter();
|
||||
|
||||
for n in [10, 25, 50, 100] {
|
||||
let dealers: Vec<_> = (0..n).map(dealer_details_fixture).collect();
|
||||
let initial_dealers = dealers.iter().map(|d| d.address.clone()).collect();
|
||||
let data = InitialReplacementData {
|
||||
initial_dealers,
|
||||
initial_height: None,
|
||||
};
|
||||
for f in [two_thirds, three_fourths, ninty_pc] {
|
||||
let threshold = f(n);
|
||||
THRESHOLD.save(deps.as_mut().storage, &threshold).unwrap();
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
.save(deps.as_mut().storage, &data)
|
||||
.unwrap();
|
||||
|
||||
let limit = *limits.next().unwrap();
|
||||
{
|
||||
let mut group_members = GROUP_MEMBERS.lock().unwrap();
|
||||
for i in 0..n as usize {
|
||||
group_members.push((
|
||||
Member {
|
||||
addr: dealers[i].address.to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
}
|
||||
for _ in 1..limit {
|
||||
group_members.pop();
|
||||
}
|
||||
}
|
||||
assert!(!replacement_threshold_surpassed(&deps.as_mut()).unwrap());
|
||||
GROUP_MEMBERS.lock().unwrap().pop();
|
||||
assert!(replacement_threshold_surpassed(&deps.as_mut()).unwrap());
|
||||
|
||||
*GROUP_MEMBERS.lock().unwrap() = vec![];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealers_and_members() {
|
||||
let mut deps = init_contract();
|
||||
|
||||
assert!(dealers_eq_members(&deps.as_mut()).unwrap());
|
||||
|
||||
let details = dealer_details_fixture(1);
|
||||
let different_details = dealer_details_fixture(2);
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, &details)
|
||||
.unwrap();
|
||||
assert!(!dealers_eq_members(&deps.as_mut()).unwrap());
|
||||
|
||||
current_dealers()
|
||||
.remove(deps.as_mut().storage, &details.address)
|
||||
.unwrap();
|
||||
GROUP_MEMBERS.lock().unwrap().push((
|
||||
Member {
|
||||
addr: "owner1".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
assert!(!dealers_eq_members(&deps.as_mut()).unwrap());
|
||||
|
||||
current_dealers()
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&different_details.address,
|
||||
&different_details,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!dealers_eq_members(&deps.as_mut()).unwrap());
|
||||
|
||||
current_dealers()
|
||||
.remove(deps.as_mut().storage, &different_details.address)
|
||||
.unwrap();
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, &details)
|
||||
.unwrap();
|
||||
assert!(dealers_eq_members(&deps.as_mut()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn still_active() {
|
||||
let mut deps = init_contract();
|
||||
{
|
||||
let mut group = GROUP_MEMBERS.lock().unwrap();
|
||||
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner1".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner2".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner3".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push(Member {
|
||||
addr: "owner1".to_string(),
|
||||
weight: 10,
|
||||
});
|
||||
group.push(Member {
|
||||
addr: "owner2".to_string(),
|
||||
weight: 10,
|
||||
});
|
||||
group.push(Member {
|
||||
addr: "owner3".to_string(),
|
||||
weight: 10,
|
||||
});
|
||||
}
|
||||
assert_eq!(
|
||||
0,
|
||||
dealers_still_active(
|
||||
&deps.as_ref(),
|
||||
current_dealers()
|
||||
.keys(&deps.storage, None, None, Order::Ascending)
|
||||
.flatten()
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(0, dealers_still_active(&deps.as_mut()).unwrap());
|
||||
for i in 0..3 as u64 {
|
||||
let details = dealer_details_fixture(i + 1);
|
||||
current_dealers()
|
||||
@@ -323,13 +167,7 @@ pub(crate) mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
i as usize + 1,
|
||||
dealers_still_active(
|
||||
&deps.as_ref(),
|
||||
current_dealers()
|
||||
.keys(&deps.storage, None, None, Order::Ascending)
|
||||
.flatten()
|
||||
)
|
||||
.unwrap()
|
||||
dealers_still_active(&deps.as_mut()).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -341,41 +179,22 @@ pub(crate) mod tests {
|
||||
{
|
||||
let mut group = GROUP_MEMBERS.lock().unwrap();
|
||||
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner1".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner2".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner3".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner4".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push(Member {
|
||||
addr: "owner1".to_string(),
|
||||
weight: 10,
|
||||
});
|
||||
group.push(Member {
|
||||
addr: "owner2".to_string(),
|
||||
weight: 10,
|
||||
});
|
||||
group.push(Member {
|
||||
addr: "owner3".to_string(),
|
||||
weight: 10,
|
||||
});
|
||||
}
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(epoch.state, EpochState::PublicKeySubmission);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block
|
||||
@@ -392,37 +211,16 @@ pub(crate) mod tests {
|
||||
EarlyEpochStateAdvancement(1)
|
||||
);
|
||||
|
||||
// setup dealer details
|
||||
let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1));
|
||||
for details in all_details.iter() {
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
assert!(INITIAL_REPLACEMENT_DATA
|
||||
.may_load(&deps.storage)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
env.block.time = env.block.time.plus_seconds(1);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::DealingExchange { resharing: false }
|
||||
);
|
||||
assert_eq!(epoch.state, EpochState::DealingExchange);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.dealing_exchange_time_secs)
|
||||
);
|
||||
let replacement_data = INITIAL_REPLACEMENT_DATA.load(&deps.storage).unwrap();
|
||||
let expected_replacement_data = InitialReplacementData {
|
||||
initial_dealers: all_details.iter().map(|d| d.address.clone()).collect(),
|
||||
initial_height: None,
|
||||
};
|
||||
assert_eq!(replacement_data, expected_replacement_data);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
@@ -436,10 +234,7 @@ pub(crate) mod tests {
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(epoch.state, EpochState::VerificationKeySubmission);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block.time.plus_seconds(
|
||||
@@ -463,10 +258,7 @@ pub(crate) mod tests {
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeyValidation { resharing: false }
|
||||
);
|
||||
assert_eq!(epoch.state, EpochState::VerificationKeyValidation);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block.time.plus_seconds(
|
||||
@@ -490,10 +282,7 @@ pub(crate) mod tests {
|
||||
env.block.time = env.block.time.plus_seconds(3);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::VerificationKeyFinalization { resharing: false }
|
||||
);
|
||||
assert_eq!(epoch.state, EpochState::VerificationKeyFinalization);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block.time.plus_seconds(
|
||||
@@ -538,6 +327,14 @@ pub(crate) mod tests {
|
||||
EarlyEpochStateAdvancement(50)
|
||||
);
|
||||
|
||||
// setup dealer details
|
||||
let all_details: [_; 3] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1));
|
||||
for details in all_details.iter() {
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Group hasn't changed, so we remain in the same epoch, with updated finish timestamp
|
||||
env.block.time = env.block.time.plus_seconds(100);
|
||||
let prev_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
@@ -551,14 +348,11 @@ pub(crate) mod tests {
|
||||
);
|
||||
assert_eq!(curr_epoch, expected_epoch);
|
||||
|
||||
// Group changed slightly, so re-run dkg in reshare mode
|
||||
*GROUP_MEMBERS.lock().unwrap().first_mut().unwrap() = (
|
||||
Member {
|
||||
addr: "owner5".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
);
|
||||
// Group changed, slightly, so reset dkg state
|
||||
*GROUP_MEMBERS.lock().unwrap().first_mut().unwrap() = Member {
|
||||
addr: "owner4".to_string(),
|
||||
weight: 10,
|
||||
};
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
@@ -567,154 +361,98 @@ pub(crate) mod tests {
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let expected_epoch = Epoch::new(
|
||||
EpochState::PublicKeySubmission { resharing: true },
|
||||
EpochState::default(),
|
||||
prev_epoch.epoch_id + 1,
|
||||
prev_epoch.time_configuration,
|
||||
env.block.time,
|
||||
);
|
||||
assert_eq!(curr_epoch, expected_epoch);
|
||||
assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none());
|
||||
}
|
||||
|
||||
let all_details: [_; 2] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 2));
|
||||
for details in all_details.iter() {
|
||||
past_dealers().remove(deps.as_mut().storage, &details.address).unwrap();
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
}
|
||||
for times in [
|
||||
epoch.time_configuration.public_key_submission_time_secs,
|
||||
epoch.time_configuration.dealing_exchange_time_secs,
|
||||
epoch.time_configuration.verification_key_submission_time_secs,
|
||||
epoch.time_configuration.verification_key_validation_time_secs,
|
||||
epoch.time_configuration.verification_key_finalization_time_secs,
|
||||
] {
|
||||
env.block.time = env.block.time.plus_seconds(times);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
}
|
||||
|
||||
// Group changed even more, surpassing threshold, so re-run dkg in reset mode
|
||||
*GROUP_MEMBERS.lock().unwrap().last_mut().unwrap() = (
|
||||
Member {
|
||||
addr: "owner6".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs);
|
||||
let prev_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
#[test]
|
||||
fn surpass_threshold() {
|
||||
let mut deps = init_contract();
|
||||
let mut env = mock_env();
|
||||
let time_configuration = TimeConfiguration::default();
|
||||
{
|
||||
let mut group = GROUP_MEMBERS.lock().unwrap();
|
||||
|
||||
group.push(Member {
|
||||
addr: "owner1".to_string(),
|
||||
weight: 10,
|
||||
});
|
||||
group.push(Member {
|
||||
addr: "owner2".to_string(),
|
||||
weight: 10,
|
||||
});
|
||||
group.push(Member {
|
||||
addr: "owner3".to_string(),
|
||||
weight: 10,
|
||||
});
|
||||
}
|
||||
|
||||
let ret = try_surpassed_threshold(deps.as_mut(), env.clone()).unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
expected_state: EpochState::InProgress.to_string()
|
||||
}
|
||||
);
|
||||
|
||||
let all_details: [_; 3] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1));
|
||||
for details in all_details.iter() {
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for times in [
|
||||
time_configuration.public_key_submission_time_secs,
|
||||
time_configuration.dealing_exchange_time_secs,
|
||||
time_configuration.verification_key_submission_time_secs,
|
||||
time_configuration.verification_key_validation_time_secs,
|
||||
time_configuration.verification_key_finalization_time_secs,
|
||||
] {
|
||||
env.block.time = env.block.time.plus_seconds(times);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let curr_epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
let expected_epoch = Epoch::new(
|
||||
EpochState::PublicKeySubmission { resharing: false },
|
||||
prev_epoch.epoch_id + 1,
|
||||
prev_epoch.time_configuration,
|
||||
}
|
||||
let curr_epoch = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
assert_eq!(THRESHOLD.load(&deps.storage).unwrap(), 2);
|
||||
|
||||
// epoch hasn't advanced as we are still in the threshold range
|
||||
try_surpassed_threshold(deps.as_mut(), env.clone()).unwrap();
|
||||
assert_eq!(THRESHOLD.load(&deps.storage).unwrap(), 2);
|
||||
assert_eq!(CURRENT_EPOCH.load(&deps.storage).unwrap(), curr_epoch);
|
||||
|
||||
*GROUP_MEMBERS.lock().unwrap().first_mut().unwrap() = Member {
|
||||
addr: "owner4".to_string(),
|
||||
weight: 10,
|
||||
};
|
||||
// epoch hasn't advanced as we are still in the threshold range
|
||||
try_surpassed_threshold(deps.as_mut(), env.clone()).unwrap();
|
||||
assert_eq!(THRESHOLD.load(&deps.storage).unwrap(), 2);
|
||||
assert_eq!(CURRENT_EPOCH.load(&deps.storage).unwrap(), curr_epoch);
|
||||
|
||||
*GROUP_MEMBERS.lock().unwrap().last_mut().unwrap() = Member {
|
||||
addr: "owner5".to_string(),
|
||||
weight: 10,
|
||||
};
|
||||
try_surpassed_threshold(deps.as_mut(), env.clone()).unwrap();
|
||||
assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none());
|
||||
let next_epoch = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
assert_eq!(
|
||||
next_epoch,
|
||||
Epoch::new(
|
||||
EpochState::default(),
|
||||
curr_epoch.epoch_id + 1,
|
||||
curr_epoch.time_configuration,
|
||||
env.block.time,
|
||||
);
|
||||
assert_eq!(curr_epoch, expected_epoch);
|
||||
assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn surpass_threshold() {
|
||||
let mut deps = init_contract();
|
||||
let mut env = mock_env();
|
||||
let time_configuration = TimeConfiguration::default();
|
||||
{
|
||||
let mut group = GROUP_MEMBERS.lock().unwrap();
|
||||
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner1".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner2".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
group.push((
|
||||
Member {
|
||||
addr: "owner3".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
));
|
||||
}
|
||||
|
||||
let ret = try_surpassed_threshold(deps.as_mut(), env.clone()).unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
expected_state: EpochState::InProgress.to_string()
|
||||
}
|
||||
);
|
||||
|
||||
let all_details: [_; 3] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 1));
|
||||
for details in all_details.iter() {
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for times in [
|
||||
time_configuration.public_key_submission_time_secs,
|
||||
time_configuration.dealing_exchange_time_secs,
|
||||
time_configuration.verification_key_submission_time_secs,
|
||||
time_configuration.verification_key_validation_time_secs,
|
||||
time_configuration.verification_key_finalization_time_secs,
|
||||
] {
|
||||
env.block.time = env.block.time.plus_seconds(times);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
}
|
||||
let curr_epoch = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
assert_eq!(THRESHOLD.load(&deps.storage).unwrap(), 2);
|
||||
|
||||
// epoch hasn't advanced as we are still in the threshold range
|
||||
try_surpassed_threshold(deps.as_mut(), env.clone()).unwrap();
|
||||
assert_eq!(THRESHOLD.load(&deps.storage).unwrap(), 2);
|
||||
assert_eq!(CURRENT_EPOCH.load(&deps.storage).unwrap(), curr_epoch);
|
||||
|
||||
*GROUP_MEMBERS.lock().unwrap().first_mut().unwrap() = (
|
||||
Member {
|
||||
addr: "owner4".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
);
|
||||
// epoch hasn't advanced as we are still in the threshold range
|
||||
try_surpassed_threshold(deps.as_mut(), env.clone()).unwrap();
|
||||
assert_eq!(THRESHOLD.load(&deps.storage).unwrap(), 2);
|
||||
assert_eq!(CURRENT_EPOCH.load(&deps.storage).unwrap(), curr_epoch);
|
||||
|
||||
*GROUP_MEMBERS.lock().unwrap().last_mut().unwrap() = (
|
||||
Member {
|
||||
addr: "owner5".to_string(),
|
||||
weight: 10,
|
||||
},
|
||||
1,
|
||||
);
|
||||
try_surpassed_threshold(deps.as_mut(), env.clone()).unwrap();
|
||||
assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none());
|
||||
let next_epoch = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
assert_eq!(
|
||||
next_epoch,
|
||||
Epoch::new(
|
||||
EpochState::default(),
|
||||
curr_epoch.epoch_id + 1,
|
||||
curr_epoch.time_configuration,
|
||||
env.block.time,
|
||||
)
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -40,9 +40,6 @@ pub enum ContractError {
|
||||
#[error("This sender is not a dealer for the current epoch")]
|
||||
NotADealer,
|
||||
|
||||
#[error("This sender is not a dealer for the current resharing epoch")]
|
||||
NotAnInitialDealer,
|
||||
|
||||
#[error("This dealer has already committed {commitment}")]
|
||||
AlreadyCommitted { commitment: String },
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ pub const GROUP_CONTRACT: &str = "group contract address";
|
||||
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
|
||||
|
||||
lazy_static! {
|
||||
pub static ref GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(vec![]);
|
||||
pub static ref GROUP_MEMBERS: Mutex<Vec<Member>> = Mutex::new(vec![]);
|
||||
}
|
||||
|
||||
fn querier_handler(query: &WasmQuery) -> QuerierResult {
|
||||
@@ -29,14 +29,9 @@ fn querier_handler(query: &WasmQuery) -> QuerierResult {
|
||||
panic!("Not supported");
|
||||
}
|
||||
match from_binary(msg) {
|
||||
Ok(Cw4QueryMsg::Member { addr, at_height }) => {
|
||||
let weight = GROUP_MEMBERS.lock().unwrap().iter().find_map(|(m, h)| {
|
||||
Ok(Cw4QueryMsg::Member { addr, .. }) => {
|
||||
let weight = GROUP_MEMBERS.lock().unwrap().iter().find_map(|m| {
|
||||
if m.addr == addr {
|
||||
if let Some(height) = at_height {
|
||||
if height != *h {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(m.weight)
|
||||
} else {
|
||||
None
|
||||
@@ -45,12 +40,7 @@ fn querier_handler(query: &WasmQuery) -> QuerierResult {
|
||||
to_binary(&MemberResponse { weight }).unwrap()
|
||||
}
|
||||
Ok(Cw4QueryMsg::ListMembers { .. }) => {
|
||||
let members = GROUP_MEMBERS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|m| m.0.clone())
|
||||
.collect();
|
||||
let members = GROUP_MEMBERS.lock().unwrap().to_vec();
|
||||
to_binary(&MemberListResponse { members }).unwrap()
|
||||
}
|
||||
_ => panic!("Not supported"),
|
||||
|
||||
@@ -17,12 +17,8 @@ pub fn try_commit_verification_key_share(
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
share: VerificationKeyShare,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
check_epoch_state(
|
||||
deps.storage,
|
||||
EpochState::VerificationKeySubmission { resharing },
|
||||
)?;
|
||||
check_epoch_state(deps.storage, EpochState::VerificationKeySubmission)?;
|
||||
// ensure the sender is a dealer
|
||||
let details = dealers_storage::current_dealers()
|
||||
.load(deps.storage, &info.sender)
|
||||
@@ -49,7 +45,6 @@ pub fn try_commit_verification_key_share(
|
||||
|
||||
let msg = to_cosmos_msg(
|
||||
info.sender,
|
||||
resharing,
|
||||
env.contract.address.to_string(),
|
||||
STATE.load(deps.storage)?.multisig_addr.to_string(),
|
||||
env.block
|
||||
@@ -64,12 +59,8 @@ pub fn try_verify_verification_key_share(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
owner: Addr,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
check_epoch_state(
|
||||
deps.storage,
|
||||
EpochState::VerificationKeyFinalization { resharing },
|
||||
)?;
|
||||
check_epoch_state(deps.storage, EpochState::VerificationKeyFinalization)?;
|
||||
let epoch_id = CURRENT_EPOCH.load(deps.storage)?.epoch_id;
|
||||
MULTISIG.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
vk_shares().update(deps.storage, (&owner, epoch_id), |vk_share| {
|
||||
@@ -126,14 +117,8 @@ mod tests {
|
||||
.save(deps.as_mut().storage, &dealer, &dealer_details)
|
||||
.unwrap();
|
||||
|
||||
try_commit_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
share.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
try_commit_verification_key_share(deps.as_mut(), env.clone(), info.clone(), share.clone())
|
||||
.unwrap();
|
||||
let vk_share = vk_shares().load(&deps.storage, (&info.sender, 0)).unwrap();
|
||||
assert_eq!(
|
||||
vk_share,
|
||||
@@ -160,15 +145,13 @@ mod tests {
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
share.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
expected_state: EpochState::VerificationKeySubmission { resharing: false }
|
||||
.to_string()
|
||||
expected_state: EpochState::VerificationKeySubmission.to_string()
|
||||
}
|
||||
);
|
||||
env.block.time = env
|
||||
@@ -186,7 +169,6 @@ mod tests {
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
share.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotADealer);
|
||||
@@ -202,21 +184,14 @@ mod tests {
|
||||
.save(deps.as_mut().storage, &dealer, &dealer_details)
|
||||
.unwrap();
|
||||
|
||||
try_commit_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
share.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
try_commit_verification_key_share(deps.as_mut(), env.clone(), info.clone(), share.clone())
|
||||
.unwrap();
|
||||
|
||||
let ret = try_commit_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
share.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
@@ -235,15 +210,13 @@ mod tests {
|
||||
let owner = Addr::unchecked("owner");
|
||||
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
|
||||
|
||||
let ret =
|
||||
try_verify_verification_key_share(deps.as_mut(), info.clone(), owner.clone(), false)
|
||||
.unwrap_err();
|
||||
let ret = try_verify_verification_key_share(deps.as_mut(), info.clone(), owner.clone())
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
expected_state: EpochState::VerificationKeyFinalization { resharing: false }
|
||||
.to_string()
|
||||
expected_state: EpochState::VerificationKeyFinalization.to_string()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -268,13 +241,12 @@ mod tests {
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_validation_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret = try_verify_verification_key_share(deps.as_mut(), info, owner.clone(), false)
|
||||
.unwrap_err();
|
||||
let ret =
|
||||
try_verify_verification_key_share(deps.as_mut(), info, owner.clone()).unwrap_err();
|
||||
assert_eq!(ret, ContractError::Admin(AdminError::NotAdmin {}));
|
||||
|
||||
let ret =
|
||||
try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.clone(), false)
|
||||
.unwrap_err();
|
||||
let ret = try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.clone())
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::NoCommitForOwner {
|
||||
@@ -312,14 +284,8 @@ mod tests {
|
||||
dealers_storage::current_dealers()
|
||||
.save(deps.as_mut().storage, &owner, &dealer_details)
|
||||
.unwrap();
|
||||
try_commit_verification_key_share(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
share.clone(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
try_commit_verification_key_share(deps.as_mut(), env.clone(), info.clone(), share.clone())
|
||||
.unwrap();
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
@@ -332,7 +298,6 @@ mod tests {
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_validation_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.clone(), false)
|
||||
.unwrap();
|
||||
try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.clone()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,6 @@ fn dkg_proposal() {
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
resharing: false,
|
||||
},
|
||||
&vec![],
|
||||
)
|
||||
@@ -125,7 +124,6 @@ fn dkg_proposal() {
|
||||
|
||||
let msg = CommitVerificationKeyShare {
|
||||
share: "share".to_string(),
|
||||
resharing: false,
|
||||
};
|
||||
let res = app
|
||||
.execute_contract(
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
## UNRELEASED
|
||||
|
||||
- nothing yet
|
||||
|
||||
## [nym-explorer-v1.0.5](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.5) (2023-02-07)
|
||||
|
||||
- NE - link `Owner` field on the node detail page to the account details on NG explorer ([#2923])
|
||||
- NE - Upgrade Sandbox and make below changes: ([#2332])
|
||||
|
||||
[#2923]: https://github.com/nymtech/nym/issues/2923
|
||||
[#2332]: https://github.com/nymtech/nym/issues/2332
|
||||
|
||||
## [nym-explorer-v1.0.4](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.4) (2023-01-31)
|
||||
|
||||
- Add routing score on gateway list ([#2913])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { Link, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material';
|
||||
import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { Tooltip } from '@nymproject/react/tooltip/Tooltip';
|
||||
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard';
|
||||
@@ -37,19 +37,9 @@ function formatCellValues(val: string | number, field: string) {
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (field === 'bond') {
|
||||
return unymToNym(val, 6);
|
||||
}
|
||||
|
||||
if (field === 'owner') {
|
||||
return (
|
||||
<Link underline="none" color="inherit" target="_blank" href={`https://mixnet.explorers.guru/account/${val}`}>
|
||||
{val}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,7 @@ pub struct Init {
|
||||
long,
|
||||
alias = "validators",
|
||||
alias = "nymd_validators",
|
||||
value_delimiter = ',',
|
||||
hide = true
|
||||
value_delimiter = ','
|
||||
)]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
@@ -67,7 +66,7 @@ pub struct Init {
|
||||
|
||||
/// Set this gateway to work only with coconut credentials; that would disallow clients to
|
||||
/// bypass bandwidth credential requirement
|
||||
#[clap(long, hide = true)]
|
||||
#[clap(long)]
|
||||
only_coconut_credentials: Option<bool>,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
|
||||
@@ -53,8 +53,7 @@ pub struct Run {
|
||||
long,
|
||||
alias = "validators",
|
||||
alias = "nymd_validators",
|
||||
value_delimiter = ',',
|
||||
hide = true
|
||||
value_delimiter = ','
|
||||
)]
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
@@ -65,7 +64,7 @@ pub struct Run {
|
||||
|
||||
/// Set this gateway to work only with coconut credentials; that would disallow clients to
|
||||
/// bypass bandwidth credential requirement
|
||||
#[clap(long, hide = true)]
|
||||
#[clap(long)]
|
||||
only_coconut_credentials: Option<bool>,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
|
||||
@@ -181,7 +181,6 @@ impl<St: Storage> ConnectionHandler<St> {
|
||||
mut shutdown: TaskClient,
|
||||
) {
|
||||
debug!("Starting connection handler for {:?}", remote);
|
||||
shutdown.mark_as_success();
|
||||
let mut framed_conn = Framed::new(conn, SphinxCodec);
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
|
||||
+2
-3
@@ -4,8 +4,7 @@
|
||||
"nym-wallet",
|
||||
"nym-connect",
|
||||
"nym-connect-android",
|
||||
"sdk/typescript/examples/docs",
|
||||
"sdk/typescript/packages/**"
|
||||
"sdk/typescript/**"
|
||||
],
|
||||
"version": "0.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -77,7 +77,6 @@ impl ConnectionHandler {
|
||||
mut shutdown: TaskClient,
|
||||
) {
|
||||
debug!("Starting connection handler for {:?}", remote);
|
||||
shutdown.mark_as_success();
|
||||
let mut framed_conn = Framed::new(conn, SphinxCodec);
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-api"
|
||||
version = "1.1.9"
|
||||
version = "1.1.8"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
|
||||
@@ -15,11 +15,7 @@ pub(crate) mod routes;
|
||||
|
||||
/// Merges the routes with http information and returns it to Rocket for serving
|
||||
pub(crate) fn circulating_supply_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![
|
||||
settings: routes::get_full_circulating_supply,
|
||||
routes::get_total_supply,
|
||||
routes::get_circulating_supply
|
||||
]
|
||||
openapi_get_routes_spec![settings: routes::get_circulating_supply]
|
||||
}
|
||||
|
||||
/// Spawn the circulating supply cache refresher.
|
||||
|
||||
@@ -1,30 +1,15 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
use rocket::http::Status;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
|
||||
use crate::circulating_supply_api::cache::CirculatingSupplyCache;
|
||||
use crate::node_status_api::models::ErrorResponse;
|
||||
use nym_api_requests::models::CirculatingSupplyResponse;
|
||||
use rocket::http::Status;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::State;
|
||||
use rocket_okapi::openapi;
|
||||
use validator_client::nyxd::Coin;
|
||||
|
||||
// TODO: this is not the best place to put it, it should be more centralised,
|
||||
// but for a quick fix, that's good enough for now...
|
||||
// (for proper solution we should be managing `NymNetworkDetails` via rocket and grabbing display exponent
|
||||
// value from the mix denom here.
|
||||
const UNYM_RATIO: f64 = 1000000.;
|
||||
|
||||
fn unym_coin_to_float_unym(coin: Coin) -> f64 {
|
||||
// our total supply can't exceed 1B so an overflow here is impossible
|
||||
// (if it happened, then we SHOULD crash)
|
||||
coin.amount as f64 / UNYM_RATIO
|
||||
}
|
||||
|
||||
#[openapi(tag = "circulating-supply")]
|
||||
#[get("/circulating-supply")]
|
||||
pub(crate) async fn get_full_circulating_supply(
|
||||
pub(crate) async fn get_circulating_supply(
|
||||
cache: &State<CirculatingSupplyCache>,
|
||||
) -> Result<Json<CirculatingSupplyResponse>, ErrorResponse> {
|
||||
match cache.get_circulating_supply().await {
|
||||
@@ -35,43 +20,3 @@ pub(crate) async fn get_full_circulating_supply(
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[openapi(tag = "circulating-supply")]
|
||||
#[get("/circulating-supply/total-supply-value")]
|
||||
pub(crate) async fn get_total_supply(
|
||||
cache: &State<CirculatingSupplyCache>,
|
||||
) -> Result<Json<f64>, ErrorResponse> {
|
||||
let full_circulating_supply = match cache.get_circulating_supply().await {
|
||||
Some(res) => res,
|
||||
None => {
|
||||
return Err(ErrorResponse::new(
|
||||
"unavailable",
|
||||
Status::InternalServerError,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(unym_coin_to_float_unym(
|
||||
full_circulating_supply.total_supply.into(),
|
||||
)))
|
||||
}
|
||||
|
||||
#[openapi(tag = "circulating-supply")]
|
||||
#[get("/circulating-supply/circulating-supply-value")]
|
||||
pub(crate) async fn get_circulating_supply(
|
||||
cache: &State<CirculatingSupplyCache>,
|
||||
) -> Result<Json<f64>, ErrorResponse> {
|
||||
let full_circulating_supply = match cache.get_circulating_supply().await {
|
||||
Some(res) => res,
|
||||
None => {
|
||||
return Err(ErrorResponse::new(
|
||||
"unavailable",
|
||||
Status::InternalServerError,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(unym_coin_to_float_unym(
|
||||
full_circulating_supply.circulating_supply.into(),
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
use crate::coconut::error::Result;
|
||||
use coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse;
|
||||
use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse};
|
||||
use coconut_dkg_common::types::{
|
||||
EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData,
|
||||
};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch, EpochId};
|
||||
use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use cw3::ProposalResponse;
|
||||
@@ -28,7 +26,6 @@ pub trait Client {
|
||||
async fn get_current_epoch(&self) -> Result<Epoch>;
|
||||
async fn group_member(&self, addr: String) -> Result<MemberResponse>;
|
||||
async fn get_current_epoch_threshold(&self) -> Result<Option<Threshold>>;
|
||||
async fn get_initial_dealers(&self) -> Result<Option<InitialReplacementData>>;
|
||||
async fn get_self_registered_dealer_details(&self) -> Result<DealerDetailsResponse>;
|
||||
async fn get_current_dealers(&self) -> Result<Vec<DealerDetails>>;
|
||||
async fn get_dealings(&self, idx: usize) -> Result<Vec<ContractDealing>>;
|
||||
@@ -41,16 +38,10 @@ pub trait Client {
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult>;
|
||||
async fn submit_dealing(
|
||||
&self,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult>;
|
||||
async fn submit_dealing(&self, dealing_bytes: ContractSafeBytes) -> Result<ExecuteResult>;
|
||||
async fn submit_verification_key_share(
|
||||
&self,
|
||||
share: VerificationKeyShare,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult>;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
use crate::coconut::client::Client;
|
||||
use crate::coconut::error::CoconutError;
|
||||
use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse};
|
||||
use coconut_dkg_common::types::{
|
||||
EncodedBTEPublicKeyWithProof, Epoch, EpochId, InitialReplacementData, NodeIndex,
|
||||
};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, Epoch, EpochId, NodeIndex};
|
||||
use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use cw3::ProposalResponse;
|
||||
@@ -61,12 +59,6 @@ impl DkgClient {
|
||||
self.inner.get_current_epoch_threshold().await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_initial_dealers(
|
||||
&self,
|
||||
) -> Result<Option<InitialReplacementData>, CoconutError> {
|
||||
self.inner.get_initial_dealers().await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_self_registered_dealer_details(
|
||||
&self,
|
||||
) -> Result<DealerDetailsResponse, CoconutError> {
|
||||
@@ -110,11 +102,10 @@ impl DkgClient {
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
) -> Result<NodeIndex, CoconutError> {
|
||||
let res = self
|
||||
.inner
|
||||
.register_dealer(bte_key, announce_address, resharing)
|
||||
.register_dealer(bte_key, announce_address)
|
||||
.await?;
|
||||
let node_index = find_attribute(&res.logs, "wasm", NODE_INDEX)
|
||||
.ok_or(CoconutError::NodeIndexRecoveryError {
|
||||
@@ -132,20 +123,18 @@ impl DkgClient {
|
||||
pub(crate) async fn submit_dealing(
|
||||
&self,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
self.inner.submit_dealing(dealing_bytes, resharing).await?;
|
||||
self.inner.submit_dealing(dealing_bytes).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn submit_verification_key_share(
|
||||
&self,
|
||||
share: VerificationKeyShare,
|
||||
resharing: bool,
|
||||
) -> Result<ExecuteResult, CoconutError> {
|
||||
let mut ret = self
|
||||
.inner
|
||||
.submit_verification_key_share(share.clone(), resharing)
|
||||
.submit_verification_key_share(share.clone())
|
||||
.await;
|
||||
for _ in 0..Self::RETRIES {
|
||||
if let Ok(res) = ret {
|
||||
@@ -153,7 +142,7 @@ impl DkgClient {
|
||||
}
|
||||
ret = self
|
||||
.inner
|
||||
.submit_verification_key_share(share.clone(), resharing)
|
||||
.submit_verification_key_share(share.clone())
|
||||
.await;
|
||||
}
|
||||
ret
|
||||
|
||||
@@ -101,19 +101,13 @@ impl<R: RngCore + Clone> DkgController<R> {
|
||||
return;
|
||||
}
|
||||
let ret = match epoch.state {
|
||||
EpochState::PublicKeySubmission { resharing } => {
|
||||
public_key_submission(&self.dkg_client, &mut self.state, resharing).await
|
||||
EpochState::PublicKeySubmission => {
|
||||
public_key_submission(&self.dkg_client, &mut self.state).await
|
||||
}
|
||||
EpochState::DealingExchange { resharing } => {
|
||||
dealing_exchange(
|
||||
&self.dkg_client,
|
||||
&mut self.state,
|
||||
self.rng.clone(),
|
||||
resharing,
|
||||
)
|
||||
.await
|
||||
EpochState::DealingExchange => {
|
||||
dealing_exchange(&self.dkg_client, &mut self.state, self.rng.clone()).await
|
||||
}
|
||||
EpochState::VerificationKeySubmission { resharing } => {
|
||||
EpochState::VerificationKeySubmission => {
|
||||
let keypair_path = pemstore::KeyPairPath::new(
|
||||
self.secret_key_path.clone(),
|
||||
self.verification_key_path.clone(),
|
||||
@@ -122,17 +116,14 @@ impl<R: RngCore + Clone> DkgController<R> {
|
||||
&self.dkg_client,
|
||||
&mut self.state,
|
||||
&keypair_path,
|
||||
resharing,
|
||||
)
|
||||
.await
|
||||
}
|
||||
EpochState::VerificationKeyValidation { resharing } => {
|
||||
verification_key_validation(&self.dkg_client, &mut self.state, resharing)
|
||||
.await
|
||||
EpochState::VerificationKeyValidation => {
|
||||
verification_key_validation(&self.dkg_client, &mut self.state).await
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { resharing } => {
|
||||
verification_key_finalization(&self.dkg_client, &mut self.state, resharing)
|
||||
.await
|
||||
EpochState::VerificationKeyFinalization => {
|
||||
verification_key_finalization(&self.dkg_client, &mut self.state).await
|
||||
}
|
||||
// Just wait, in case we need to redo dkg at some point
|
||||
EpochState::InProgress => {
|
||||
|
||||
@@ -9,13 +9,11 @@ use contracts_common::dealings::ContractSafeBytes;
|
||||
use dkg::bte::setup;
|
||||
use dkg::Dealing;
|
||||
use rand::RngCore;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub(crate) async fn dealing_exchange(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
rng: impl RngCore + Clone,
|
||||
resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
if state.receiver_index().is_some() {
|
||||
return Ok(());
|
||||
@@ -23,54 +21,26 @@ pub(crate) async fn dealing_exchange(
|
||||
|
||||
let dealers = dkg_client.get_current_dealers().await?;
|
||||
let threshold = dkg_client.get_current_epoch_threshold().await?;
|
||||
let initial_dealers = dkg_client
|
||||
.get_initial_dealers()
|
||||
.await?
|
||||
.map(|d| d.initial_dealers)
|
||||
.unwrap_or_default();
|
||||
let own_address = dkg_client.get_address().await.as_ref().to_string();
|
||||
state.set_dealers(dealers);
|
||||
state.set_threshold(threshold);
|
||||
let receivers = state.current_dealers_by_idx();
|
||||
let params = setup();
|
||||
let dealer_index = state.node_index_value()?;
|
||||
let receiver_index = receivers
|
||||
.keys()
|
||||
.position(|node_index| *node_index == dealer_index);
|
||||
|
||||
let prior_resharing_secrets = if let Some(sk) = state.coconut_secret_key().await {
|
||||
// Double check that we are in resharing mode
|
||||
if resharing {
|
||||
let (x, mut scalars) = sk.into_raw();
|
||||
if scalars.len() + 1 != TOTAL_DEALINGS {
|
||||
return Err(CoconutError::CorruptedCoconutKeyPair);
|
||||
}
|
||||
// We can now erase the keypair from memory
|
||||
state.set_coconut_keypair(None).await;
|
||||
scalars.push(x);
|
||||
scalars
|
||||
} else {
|
||||
log::warn!("Coconut key hasn't been reset in memory. The state might be corrupt");
|
||||
vec![]
|
||||
}
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let mut prior_resharing_secrets = VecDeque::from(prior_resharing_secrets);
|
||||
if !resharing || initial_dealers.iter().any(|d| *d == own_address) {
|
||||
let params = setup();
|
||||
for _ in 0..TOTAL_DEALINGS {
|
||||
let (dealing, _) = Dealing::create(
|
||||
rng.clone(),
|
||||
¶ms,
|
||||
dealer_index,
|
||||
state.threshold()?,
|
||||
&receivers,
|
||||
prior_resharing_secrets.pop_front(),
|
||||
);
|
||||
dkg_client
|
||||
.submit_dealing(ContractSafeBytes::from(&dealing), resharing)
|
||||
.await?;
|
||||
}
|
||||
for _ in 0..TOTAL_DEALINGS {
|
||||
let (dealing, _) = Dealing::create(
|
||||
rng.clone(),
|
||||
¶ms,
|
||||
dealer_index,
|
||||
state.threshold()?,
|
||||
&receivers,
|
||||
None,
|
||||
);
|
||||
dkg_client
|
||||
.submit_dealing(ContractSafeBytes::from(&dealing))
|
||||
.await?;
|
||||
}
|
||||
|
||||
info!("DKG: Finished submitting dealing");
|
||||
@@ -87,11 +57,9 @@ pub(crate) mod tests {
|
||||
use crate::coconut::tests::DummyClient;
|
||||
use crate::coconut::KeyPair;
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::types::InitialReplacementData;
|
||||
use cosmwasm_std::Addr;
|
||||
use dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use dkg::bte::{Params, PublicKeyWithProof};
|
||||
use nymcoconut::{ttp_keygen, Parameters};
|
||||
use dkg::bte::Params;
|
||||
use rand::rngs::OsRng;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
@@ -100,11 +68,10 @@ pub(crate) mod tests {
|
||||
use url::Url;
|
||||
use validator_client::nyxd::AccountId;
|
||||
|
||||
const TEST_VALIDATORS_ADDRESS: [&str; 4] = [
|
||||
const TEST_VALIDATORS_ADDRESS: [&str; 3] = [
|
||||
"n1aq9kakfgwqcufr23lsv644apavcntrsqsk4yus",
|
||||
"n1s9l3xr4g0rglvk4yctktmck3h4eq0gp6z2e20v",
|
||||
"n19kl4py32vsk297dm93ezem992cdyzdy4zuc2x6",
|
||||
"n1jfrs6cmw9t7dv0x8cgny6geunzjh56n2s89fkv",
|
||||
];
|
||||
|
||||
fn insert_dealers(
|
||||
@@ -154,7 +121,7 @@ pub(crate) mod tests {
|
||||
state.set_node_index(Some(self_index));
|
||||
let keypairs = insert_dealers(¶ms, &dealer_details_db);
|
||||
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng, false)
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -175,7 +142,7 @@ pub(crate) mod tests {
|
||||
.clone();
|
||||
assert_eq!(dealings.len(), TOTAL_DEALINGS);
|
||||
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng, false)
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng)
|
||||
.await
|
||||
.unwrap();
|
||||
let new_dealings = dealings_db
|
||||
@@ -219,22 +186,12 @@ pub(crate) mod tests {
|
||||
let mut bytes = bs58::decode(details.bte_public_key_with_proof.clone())
|
||||
.into_vec()
|
||||
.unwrap();
|
||||
// Find another value for last byte that still deserializes to a public key with proof
|
||||
let initial_byte = *bytes.last_mut().unwrap();
|
||||
loop {
|
||||
let last_byte = bytes.last_mut().unwrap();
|
||||
let (ret, _) = last_byte.overflowing_add(1);
|
||||
*last_byte = ret;
|
||||
// stop when we find that value, or if we do a full round trip of u8 values
|
||||
// and can't find one, in which case this test is invalid
|
||||
if PublicKeyWithProof::try_from_bytes(&bytes).is_ok() || ret == initial_byte {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let last_byte = bytes.last_mut().unwrap();
|
||||
*last_byte += 1;
|
||||
details.bte_public_key_with_proof = bs58::encode(&bytes).into_string();
|
||||
});
|
||||
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng, false)
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -247,85 +204,4 @@ pub(crate) mod tests {
|
||||
ComplaintReason::InvalidBTEPublicKey
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // expensive test
|
||||
async fn resharing_exchange_dealing() {
|
||||
let self_index = 2;
|
||||
let dealer_details_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let dealings_db = Arc::new(RwLock::new(HashMap::new()));
|
||||
let threshold_db = Arc::new(RwLock::new(Some(3)));
|
||||
let initial_dealers_db = Arc::new(RwLock::new(Some(InitialReplacementData {
|
||||
initial_dealers: vec![Addr::unchecked(TEST_VALIDATORS_ADDRESS[0])],
|
||||
initial_height: Some(100),
|
||||
})));
|
||||
let dkg_client = DkgClient::new(
|
||||
DummyClient::new(
|
||||
AccountId::from_str("n1vxkywf9g4cg0k2dehanzwzz64jw782qm0kuynf").unwrap(),
|
||||
)
|
||||
.with_dealer_details(&dealer_details_db)
|
||||
.with_dealings(&dealings_db)
|
||||
.with_threshold(&threshold_db)
|
||||
.with_initial_dealers_db(&initial_dealers_db),
|
||||
);
|
||||
let params = setup();
|
||||
let mut keys = ttp_keygen(&Parameters::new(4).unwrap(), 3, 4).unwrap();
|
||||
let coconut_keypair = KeyPair::new();
|
||||
coconut_keypair.set(Some(keys.pop().unwrap())).await;
|
||||
|
||||
let mut state = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
DkgKeyPair::new(¶ms, OsRng),
|
||||
coconut_keypair.clone(),
|
||||
);
|
||||
state.set_node_index(Some(self_index));
|
||||
let keypairs = insert_dealers(¶ms, &dealer_details_db);
|
||||
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
state.current_dealers_by_idx().values().collect::<Vec<_>>(),
|
||||
keypairs
|
||||
.iter()
|
||||
.map(|k| k.public_key().public_key())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
assert_eq!(state.threshold().unwrap(), 3);
|
||||
assert_eq!(state.receiver_index().unwrap(), 1);
|
||||
let addr = dkg_client.get_address().await;
|
||||
assert!(dealings_db.read().unwrap().get(addr.as_ref()).is_none());
|
||||
|
||||
let mut state = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
DkgKeyPair::new(¶ms, OsRng),
|
||||
coconut_keypair,
|
||||
);
|
||||
state.set_node_index(Some(self_index));
|
||||
// Use a client that is in the initial dealers set
|
||||
let dkg_client = DkgClient::new(
|
||||
DummyClient::new(AccountId::from_str(TEST_VALIDATORS_ADDRESS[0]).unwrap())
|
||||
.with_dealer_details(&dealer_details_db)
|
||||
.with_dealings(&dealings_db)
|
||||
.with_threshold(&threshold_db)
|
||||
.with_initial_dealers_db(&initial_dealers_db),
|
||||
);
|
||||
|
||||
dealing_exchange(&dkg_client, &mut state, OsRng, true)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let dealings = dealings_db
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(TEST_VALIDATORS_ADDRESS[0])
|
||||
.unwrap()
|
||||
.clone();
|
||||
assert_eq!(dealings.len(), TOTAL_DEALINGS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,9 @@ use coconut_dkg_common::dealer::DealerType;
|
||||
pub(crate) async fn public_key_submission(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
if state.was_in_progress() {
|
||||
state.reset_persistent(resharing).await;
|
||||
state.reset_persistent().await;
|
||||
}
|
||||
if state.node_index().is_some() {
|
||||
return Ok(());
|
||||
@@ -24,14 +23,14 @@ pub(crate) async fn public_key_submission(
|
||||
if dealer_details.dealer_type == DealerType::Past {
|
||||
// If it was a dealer in a previous epoch, re-register it for this epoch
|
||||
dkg_client
|
||||
.register_dealer(bte_key, state.announce_address().to_string(), resharing)
|
||||
.register_dealer(bte_key, state.announce_address().to_string())
|
||||
.await?;
|
||||
}
|
||||
details.assigned_index
|
||||
} else {
|
||||
// First time registration
|
||||
dkg_client
|
||||
.register_dealer(bte_key, state.announce_address().to_string(), resharing)
|
||||
.register_dealer(bte_key, state.announce_address().to_string())
|
||||
.await?
|
||||
};
|
||||
state.set_node_index(Some(index));
|
||||
@@ -75,7 +74,7 @@ pub(crate) mod tests {
|
||||
.unwrap()
|
||||
.details
|
||||
.is_none());
|
||||
public_key_submission(&dkg_client, &mut state, false)
|
||||
public_key_submission(&dkg_client, &mut state)
|
||||
.await
|
||||
.unwrap();
|
||||
let client_idx = dkg_client
|
||||
@@ -89,7 +88,7 @@ pub(crate) mod tests {
|
||||
|
||||
// keeps the same index from chain, not calling register_dealer again
|
||||
state.set_node_index(None);
|
||||
public_key_submission(&dkg_client, &mut state, false)
|
||||
public_key_submission(&dkg_client, &mut state)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(state.node_index().unwrap(), client_idx);
|
||||
|
||||
@@ -9,7 +9,6 @@ use coconut_dkg_common::types::EpochState;
|
||||
use cosmwasm_std::Addr;
|
||||
use dkg::bte::{keys::KeyPair as DkgKeyPair, PublicKey, PublicKeyWithProof};
|
||||
use dkg::{NodeIndex, RecoveredVerificationKeys, Threshold};
|
||||
use nymcoconut::SecretKey;
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::collections::BTreeMap;
|
||||
@@ -72,18 +71,18 @@ pub(crate) trait ConsistentState {
|
||||
fn proposal_id_value(&self) -> Result<u64, CoconutError>;
|
||||
async fn is_consistent(&self, epoch_state: EpochState) -> Result<(), CoconutError> {
|
||||
match epoch_state {
|
||||
EpochState::PublicKeySubmission { .. } => {}
|
||||
EpochState::DealingExchange { .. } => {
|
||||
EpochState::PublicKeySubmission => {}
|
||||
EpochState::DealingExchange => {
|
||||
self.node_index_value()?;
|
||||
}
|
||||
EpochState::VerificationKeySubmission { .. } => {
|
||||
EpochState::VerificationKeySubmission => {
|
||||
self.receiver_index_value()?;
|
||||
self.threshold()?;
|
||||
}
|
||||
EpochState::VerificationKeyValidation { .. } => {
|
||||
EpochState::VerificationKeyValidation => {
|
||||
self.coconut_keypair_is_some().await?;
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { .. } => {
|
||||
EpochState::VerificationKeyFinalization => {
|
||||
self.proposal_id_value()?;
|
||||
}
|
||||
EpochState::InProgress => {}
|
||||
@@ -242,10 +241,8 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn reset_persistent(&mut self, resharing: bool) {
|
||||
if !resharing {
|
||||
self.coconut_keypair.set(None).await;
|
||||
}
|
||||
pub async fn reset_persistent(&mut self) {
|
||||
self.coconut_keypair.set(None).await;
|
||||
self.node_index = Default::default();
|
||||
self.dealers = Default::default();
|
||||
self.receiver_index = Default::default();
|
||||
@@ -273,14 +270,6 @@ impl State {
|
||||
self.coconut_keypair.get().await.is_some()
|
||||
}
|
||||
|
||||
pub async fn coconut_secret_key(&self) -> Option<SecretKey> {
|
||||
self.coconut_keypair
|
||||
.get()
|
||||
.await
|
||||
.as_ref()
|
||||
.map(|kp| kp.secret_key())
|
||||
}
|
||||
|
||||
pub fn node_index(&self) -> Option<NodeIndex> {
|
||||
self.node_index
|
||||
}
|
||||
@@ -335,11 +324,8 @@ impl State {
|
||||
self.recovered_vks = recovered_vks;
|
||||
}
|
||||
|
||||
pub async fn set_coconut_keypair(
|
||||
&mut self,
|
||||
coconut_keypair: Option<coconut_interface::KeyPair>,
|
||||
) {
|
||||
self.coconut_keypair.set(coconut_keypair).await
|
||||
pub async fn set_coconut_keypair(&mut self, coconut_keypair: coconut_interface::KeyPair) {
|
||||
self.coconut_keypair.set(Some(coconut_keypair)).await
|
||||
}
|
||||
|
||||
pub fn set_node_index(&mut self, node_index: Option<NodeIndex>) {
|
||||
|
||||
@@ -14,7 +14,6 @@ use cosmwasm_std::Addr;
|
||||
use credentials::coconut::bandwidth::{PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES};
|
||||
use cw3::{ProposalResponse, Status};
|
||||
use dkg::bte::{decrypt_share, setup};
|
||||
use dkg::error::DkgError;
|
||||
use dkg::{combine_shares, try_recover_verification_keys, Dealing, Threshold};
|
||||
use nymcoconut::tests::helpers::transpose_matrix;
|
||||
use nymcoconut::{check_vk_pairing, Base58, KeyPair, Parameters, SecretKey, VerificationKey};
|
||||
@@ -27,21 +26,10 @@ async fn deterministic_filter_dealers(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
threshold: Threshold,
|
||||
resharing: bool,
|
||||
) -> Result<Vec<BTreeMap<NodeIndex, (Addr, Dealing)>>, CoconutError> {
|
||||
let mut dealings_maps = vec![];
|
||||
let initial_dealers_by_addr = state.current_dealers_by_addr();
|
||||
let initial_receivers = state.current_dealers_by_idx();
|
||||
let initial_resharing_dealers = if resharing {
|
||||
dkg_client
|
||||
.get_initial_dealers()
|
||||
.await?
|
||||
.map(|d| d.initial_dealers)
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let params = setup();
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS {
|
||||
@@ -78,15 +66,11 @@ async fn deterministic_filter_dealers(
|
||||
}));
|
||||
dealings_maps.push(dealings_map);
|
||||
}
|
||||
|
||||
for (addr, _) in initial_dealers_by_addr.iter() {
|
||||
// in resharing mode, we don't commit dealings from dealers outside the initial set
|
||||
if !resharing || initial_resharing_dealers.contains(addr) {
|
||||
for dealings_map in dealings_maps.iter() {
|
||||
if !dealings_map.iter().any(|(_, (address, _))| address == addr) {
|
||||
state.mark_bad_dealer(addr, ComplaintReason::MissingDealing);
|
||||
break;
|
||||
}
|
||||
for dealings_map in dealings_maps.iter() {
|
||||
if !dealings_map.iter().any(|(_, (address, _))| address == addr) {
|
||||
state.mark_bad_dealer(addr, ComplaintReason::MissingDealing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,16 +90,16 @@ fn derive_partial_keypair(
|
||||
let mut scalars = vec![];
|
||||
let mut recovered_vks = vec![];
|
||||
for dealings_map in dealings_maps.into_iter() {
|
||||
let (filtered_dealers, filtered_dealings): (Vec<_>, Vec<_>) = dealings_map
|
||||
let filtered_dealings: Vec<_> = dealings_map
|
||||
.into_iter()
|
||||
.filter_map(|(idx, (addr, dealing))| {
|
||||
.filter_map(|(_, (addr, dealing))| {
|
||||
if filtered_dealers_by_addr.keys().any(|a| addr == *a) {
|
||||
Some((idx, dealing))
|
||||
Some(dealing)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unzip();
|
||||
.collect();
|
||||
let recovered = try_recover_verification_keys(
|
||||
&filtered_dealings,
|
||||
threshold,
|
||||
@@ -127,18 +111,19 @@ fn derive_partial_keypair(
|
||||
.iter()
|
||||
.map(|dealing| decrypt_share(dk, node_index_value, &dealing.ciphertexts, None))
|
||||
.collect::<Result<_, _>>()?;
|
||||
let scalar = combine_shares(shares, &filtered_dealers)?;
|
||||
let scalar = combine_shares(
|
||||
shares,
|
||||
&filtered_receivers_by_idx
|
||||
.keys()
|
||||
.copied()
|
||||
.collect::<Vec<_>>(),
|
||||
)?;
|
||||
scalars.push(scalar);
|
||||
}
|
||||
state.set_recovered_vks(recovered_vks);
|
||||
|
||||
let params = Parameters::new(PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES)?;
|
||||
let x = scalars.pop().ok_or(CoconutError::DkgError(
|
||||
DkgError::NotEnoughDealingsAvailable {
|
||||
available: 0,
|
||||
required: 1,
|
||||
},
|
||||
))?;
|
||||
let x = scalars.pop().unwrap();
|
||||
let sk = SecretKey::create_from_raw(x, scalars);
|
||||
let vk = sk.verification_key(¶ms);
|
||||
|
||||
@@ -149,21 +134,17 @@ pub(crate) async fn verification_key_submission(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
keypair_path: &KeyPairPath,
|
||||
resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
if state.coconut_keypair_is_some().await {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let threshold = state.threshold()?;
|
||||
let dealings_maps =
|
||||
deterministic_filter_dealers(dkg_client, state, threshold, resharing).await?;
|
||||
let dealings_maps = deterministic_filter_dealers(dkg_client, state, threshold).await?;
|
||||
let coconut_keypair = derive_partial_keypair(state, threshold, dealings_maps)?;
|
||||
let vk_share = coconut_keypair.verification_key().to_bs58();
|
||||
pemstore::store_keypair(&coconut_keypair, keypair_path)?;
|
||||
let res = dkg_client
|
||||
.submit_verification_key_share(vk_share, resharing)
|
||||
.await?;
|
||||
let res = dkg_client.submit_verification_key_share(vk_share).await?;
|
||||
let proposal_id = find_attribute(&res.logs, "wasm", DKG_PROPOSAL_ID)
|
||||
.ok_or(CoconutError::ProposalIdError {
|
||||
reason: String::from("proposal id not found"),
|
||||
@@ -174,7 +155,7 @@ pub(crate) async fn verification_key_submission(
|
||||
reason: String::from("proposal id could not be parsed to u64"),
|
||||
})?;
|
||||
state.set_proposal_id(proposal_id);
|
||||
state.set_coconut_keypair(Some(coconut_keypair)).await;
|
||||
state.set_coconut_keypair(coconut_keypair).await;
|
||||
info!("DKG: Submitted own verification key");
|
||||
|
||||
Ok(())
|
||||
@@ -192,7 +173,6 @@ fn validate_proposal(proposal: &ProposalResponse) -> Option<(Addr, u64)> {
|
||||
pub(crate) async fn verification_key_validation(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
_resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
if state.voted_vks() {
|
||||
return Ok(());
|
||||
@@ -253,7 +233,6 @@ pub(crate) async fn verification_key_validation(
|
||||
pub(crate) async fn verification_key_finalization(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
_resharing: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
if state.executed_proposal() {
|
||||
return Ok(());
|
||||
@@ -278,11 +257,9 @@ pub(crate) mod tests {
|
||||
use crate::coconut::tests::DummyClient;
|
||||
use crate::coconut::KeyPair;
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::types::InitialReplacementData;
|
||||
use coconut_dkg_common::verification_key::ContractVKShare;
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use nymcoconut::aggregate_verification_keys;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::Rng;
|
||||
use std::collections::HashMap;
|
||||
@@ -299,7 +276,6 @@ pub(crate) mod tests {
|
||||
proposal_db: Arc<RwLock<HashMap<u64, ProposalResponse>>>,
|
||||
verification_share_db: Arc<RwLock<HashMap<String, ContractVKShare>>>,
|
||||
threshold_db: Arc<RwLock<Option<Threshold>>>,
|
||||
initial_dealers_db: Arc<RwLock<Option<InitialReplacementData>>>,
|
||||
}
|
||||
|
||||
impl MockContractDb {
|
||||
@@ -310,7 +286,6 @@ pub(crate) mod tests {
|
||||
proposal_db: Arc::new(Default::default()),
|
||||
verification_share_db: Arc::new(Default::default()),
|
||||
threshold_db: Arc::new(RwLock::new(Some(2))),
|
||||
initial_dealers_db: Arc::new(RwLock::new(Default::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -332,8 +307,7 @@ pub(crate) mod tests {
|
||||
.with_dealings(&db.dealings_db)
|
||||
.with_proposal_db(&db.proposal_db)
|
||||
.with_verification_share(&db.verification_share_db)
|
||||
.with_threshold(&db.threshold_db)
|
||||
.with_initial_dealers_db(&db.initial_dealers_db),
|
||||
.with_threshold(&db.threshold_db),
|
||||
);
|
||||
let keypair = DkgKeyPair::new(¶ms, OsRng);
|
||||
let state = State::new(
|
||||
@@ -346,21 +320,10 @@ pub(crate) mod tests {
|
||||
clients_and_states.push((dkg_client, state));
|
||||
}
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
public_key_submission(dkg_client, state, false)
|
||||
.await
|
||||
.unwrap();
|
||||
public_key_submission(dkg_client, state).await.unwrap();
|
||||
}
|
||||
clients_and_states
|
||||
}
|
||||
|
||||
async fn prepare_clients_and_states_with_dealing(
|
||||
db: &MockContractDb,
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
let mut clients_and_states = prepare_clients_and_states(db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
dealing_exchange(dkg_client, state, OsRng, false)
|
||||
.await
|
||||
.unwrap();
|
||||
dealing_exchange(dkg_client, state, OsRng).await.unwrap();
|
||||
}
|
||||
clients_and_states
|
||||
}
|
||||
@@ -368,13 +331,13 @@ pub(crate) mod tests {
|
||||
async fn prepare_clients_and_states_with_submission(
|
||||
db: &MockContractDb,
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(db).await;
|
||||
let mut clients_and_states = prepare_clients_and_states(db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
let random_file: usize = OsRng.gen();
|
||||
let private_key_path = temp_dir().join(format!("private{}.pem", random_file));
|
||||
let public_key_path = temp_dir().join(format!("public{}.pem", random_file));
|
||||
let keypair_path = KeyPairPath::new(private_key_path.clone(), public_key_path.clone());
|
||||
verification_key_submission(dkg_client, state, &keypair_path, false)
|
||||
verification_key_submission(dkg_client, state, &keypair_path)
|
||||
.await
|
||||
.unwrap();
|
||||
std::fs::remove_file(private_key_path).unwrap();
|
||||
@@ -388,7 +351,7 @@ pub(crate) mod tests {
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
let mut clients_and_states = prepare_clients_and_states_with_submission(db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_validation(dkg_client, state, false)
|
||||
verification_key_validation(dkg_client, state)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -400,7 +363,7 @@ pub(crate) mod tests {
|
||||
) -> Vec<(DkgClient, State)> {
|
||||
let mut clients_and_states = prepare_clients_and_states_with_validation(db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_finalization(dkg_client, state, false)
|
||||
verification_key_finalization(dkg_client, state)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -411,9 +374,9 @@ pub(crate) mod tests {
|
||||
#[ignore] // expensive test
|
||||
async fn check_dealers_filter_all_good() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
@@ -427,7 +390,7 @@ pub(crate) mod tests {
|
||||
#[ignore] // expensive test
|
||||
async fn check_dealers_filter_one_bad_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
|
||||
// corrupt just one dealing
|
||||
db.dealings_db
|
||||
@@ -441,7 +404,7 @@ pub(crate) mod tests {
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
@@ -455,75 +418,11 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // expensive test
|
||||
async fn check_dealers_resharing_filter_one_missing_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
|
||||
// add all but the first dealing
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
dealing_exchange(dkg_client, state, OsRng, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
*db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData {
|
||||
initial_dealers: vec![Addr::unchecked(TEST_VALIDATORS_ADDRESS[0])],
|
||||
initial_height: None,
|
||||
});
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, true)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
let corrupted_status = state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(*corrupted_status, ComplaintReason::MissingDealing);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // expensive test
|
||||
async fn check_dealers_resharing_filter_one_noninitial_missing_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
|
||||
// add all but the first dealing
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
dealing_exchange(dkg_client, state, OsRng, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
*db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData {
|
||||
initial_dealers: vec![],
|
||||
initial_height: None,
|
||||
});
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, true)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
assert!(state
|
||||
.all_dealers()
|
||||
.get(&Addr::unchecked(TEST_VALIDATORS_ADDRESS[0]))
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.is_ok(),);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // expensive test
|
||||
async fn check_dealers_filter_all_bad_dealings() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
|
||||
// corrupt all dealings of one address
|
||||
db.dealings_db
|
||||
@@ -537,7 +436,7 @@ pub(crate) mod tests {
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
@@ -558,7 +457,7 @@ pub(crate) mod tests {
|
||||
#[ignore] // expensive test
|
||||
async fn check_dealers_filter_malformed_dealing() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
|
||||
// corrupt just one dealing
|
||||
db.dealings_db
|
||||
@@ -572,12 +471,12 @@ pub(crate) mod tests {
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
// second filter will leave behind the bad dealer and surface why it was left out
|
||||
// in the first place
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
@@ -595,7 +494,7 @@ pub(crate) mod tests {
|
||||
#[ignore] // expensive test
|
||||
async fn check_dealers_filter_dealing_verification_error() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
|
||||
// corrupt just one dealing
|
||||
db.dealings_db
|
||||
@@ -614,12 +513,12 @@ pub(crate) mod tests {
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
// second filter will leave behind the bad dealer and surface why it was left out
|
||||
// in the first place
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(filtered.len(), TOTAL_DEALINGS);
|
||||
@@ -637,9 +536,9 @@ pub(crate) mod tests {
|
||||
#[ignore] // expensive test
|
||||
async fn partial_keypair_derivation() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(derive_partial_keypair(state, 2, filtered).is_ok());
|
||||
@@ -650,7 +549,7 @@ pub(crate) mod tests {
|
||||
#[ignore] // expensive test
|
||||
async fn partial_keypair_derivation_with_threshold() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_dealing(&db).await;
|
||||
let mut clients_and_states = prepare_clients_and_states(&db).await;
|
||||
|
||||
// corrupt just one dealing
|
||||
db.dealings_db
|
||||
@@ -664,7 +563,7 @@ pub(crate) mod tests {
|
||||
});
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut().skip(1) {
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2, false)
|
||||
let filtered = deterministic_filter_dealers(dkg_client, state, 2)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(derive_partial_keypair(state, 2, filtered).is_ok());
|
||||
@@ -717,7 +616,7 @@ pub(crate) mod tests {
|
||||
.and_modify(|share| share.share.push('x'));
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_validation(dkg_client, state, false)
|
||||
verification_key_validation(dkg_client, state)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -759,7 +658,7 @@ pub(crate) mod tests {
|
||||
.and_modify(|share| share.share = second_share);
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
verification_key_validation(dkg_client, state, false)
|
||||
verification_key_validation(dkg_client, state)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -797,102 +696,4 @@ pub(crate) mod tests {
|
||||
assert_eq!(proposal.status, Status::Executed);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // expensive test
|
||||
async fn reshare_preserves_keys() {
|
||||
let db = MockContractDb::new();
|
||||
let mut clients_and_states = prepare_clients_and_states_with_finalization(&db).await;
|
||||
let params = Parameters::new(4).unwrap();
|
||||
|
||||
let mut vks = vec![];
|
||||
let mut indices = vec![];
|
||||
for (_, state) in clients_and_states.iter() {
|
||||
let vk = state
|
||||
.coconut_secret_key()
|
||||
.await
|
||||
.unwrap()
|
||||
.verification_key(¶ms);
|
||||
let index = state.node_index().unwrap();
|
||||
vks.push(vk);
|
||||
indices.push(index);
|
||||
}
|
||||
let initial_master_vk = aggregate_verification_keys(&vks, Some(&indices)).unwrap();
|
||||
|
||||
let new_dkg_client = DkgClient::new(
|
||||
DummyClient::new(
|
||||
AccountId::from_str("n1sqkxzh7nl6kgndr4ew9795t2nkwmd8tpql67q7").unwrap(),
|
||||
)
|
||||
.with_dealer_details(&db.dealer_details_db)
|
||||
.with_dealings(&db.dealings_db)
|
||||
.with_proposal_db(&db.proposal_db)
|
||||
.with_verification_share(&db.verification_share_db)
|
||||
.with_threshold(&db.threshold_db)
|
||||
.with_initial_dealers_db(&db.initial_dealers_db),
|
||||
);
|
||||
let keypair = DkgKeyPair::new(&setup(), OsRng);
|
||||
let state = State::new(
|
||||
PathBuf::default(),
|
||||
PersistentState::default(),
|
||||
Url::parse("localhost:8000").unwrap(),
|
||||
keypair,
|
||||
KeyPair::new(),
|
||||
);
|
||||
|
||||
let removed_dealer = clients_and_states.first().unwrap().0.get_address().await;
|
||||
db.dealer_details_db
|
||||
.write()
|
||||
.unwrap()
|
||||
.remove(removed_dealer.as_ref());
|
||||
*db.dealings_db.write().unwrap() = Default::default();
|
||||
*db.verification_share_db.write().unwrap() = Default::default();
|
||||
*db.initial_dealers_db.write().unwrap() = Some(InitialReplacementData {
|
||||
initial_dealers: vec![
|
||||
Addr::unchecked(clients_and_states[1].0.get_address().await.as_ref()),
|
||||
Addr::unchecked(clients_and_states[2].0.get_address().await.as_ref()),
|
||||
],
|
||||
initial_height: None,
|
||||
});
|
||||
*clients_and_states.first_mut().unwrap() = (new_dkg_client, state);
|
||||
clients_and_states[1].1.set_was_in_progress();
|
||||
clients_and_states[2].1.set_was_in_progress();
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
public_key_submission(dkg_client, state, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
dealing_exchange(dkg_client, state, OsRng, true)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for (dkg_client, state) in clients_and_states.iter_mut() {
|
||||
let random_file: usize = OsRng.gen();
|
||||
let private_key_path = temp_dir().join(format!("private{}.pem", random_file));
|
||||
let public_key_path = temp_dir().join(format!("public{}.pem", random_file));
|
||||
let keypair_path = KeyPairPath::new(private_key_path.clone(), public_key_path.clone());
|
||||
verification_key_submission(dkg_client, state, &keypair_path, true)
|
||||
.await
|
||||
.unwrap();
|
||||
std::fs::remove_file(private_key_path).unwrap();
|
||||
std::fs::remove_file(public_key_path).unwrap();
|
||||
}
|
||||
let mut vks = vec![];
|
||||
let mut indices = vec![];
|
||||
for (_, state) in clients_and_states.iter() {
|
||||
let vk = state
|
||||
.coconut_secret_key()
|
||||
.await
|
||||
.unwrap()
|
||||
.verification_key(¶ms);
|
||||
let index = state.node_index().unwrap();
|
||||
vks.push(vk);
|
||||
indices.push(index);
|
||||
}
|
||||
let reshared_master_vk = aggregate_verification_keys(&vks, Some(&indices)).unwrap();
|
||||
assert_eq!(initial_master_vk, reshared_master_vk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,9 @@ pub enum CoconutError {
|
||||
)]
|
||||
DifferentPublicAttributes(String, String),
|
||||
|
||||
#[error("No signature found")]
|
||||
NoSignature,
|
||||
|
||||
#[error("Error in coconut interface - {0}")]
|
||||
CoconutInterfaceError(#[from] coconut_interface::error::CoconutInterfaceError),
|
||||
|
||||
@@ -97,9 +100,6 @@ pub enum CoconutError {
|
||||
#[error("DKG has not finished yet in order to derive the coconut key")]
|
||||
KeyPairNotDerivedYet,
|
||||
|
||||
#[error("The coconut keypair is corrupted")]
|
||||
CorruptedCoconutKeyPair,
|
||||
|
||||
#[error("There was a problem with the proposal id: {reason}")]
|
||||
ProposalIdError { reason: String },
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user