Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f25f76c1df | |||
| 29f95febe9 |
@@ -63,7 +63,7 @@ jobs:
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.69.0
|
||||
toolchain: stable
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.69.0
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
@@ -107,8 +107,6 @@ jobs:
|
||||
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR
|
||||
cp contracts/target/wasm32-unknown-unknown/release/nym_name_service.wasm $OUTPUT_DIR
|
||||
|
||||
- name: Deploy branch to CI www
|
||||
continue-on-error: true
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
name: Nym Connect - Android APK Build
|
||||
|
||||
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 \
|
||||
build-essential \
|
||||
unzip \
|
||||
curl \
|
||||
wget \
|
||||
libssl-dev \
|
||||
squashfs-tools \
|
||||
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
|
||||
|
||||
# TODO this step takes a considerable amount of time
|
||||
# We could avoid to compile from source tauri-cli and use instead
|
||||
# pre-compiled binary provided by the node package `@tauri-apps/cli`
|
||||
# But when using the later the build fails for some reason
|
||||
# so keep installing and using tauri-cli
|
||||
- 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-mobile webpack:prod
|
||||
|
||||
- name: Build APK
|
||||
working-directory: nym-connect/mobile
|
||||
env:
|
||||
# NODE_TAURI_CLI=${{ github.workspace }}/nym-connect/mobile/node_modules/.bin/tauri
|
||||
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
|
||||
WRY_ANDROID_PACKAGE: net.nymtech.nym_connect
|
||||
WRY_ANDROID_LIBRARY: nym_connect
|
||||
# TODO build with release profile (--release), it will requires
|
||||
# to sign the APK. For now build with debug profile to avoid that
|
||||
# TODO build using `yarn tauri`, provide NODE_TAURI_CLI, see TODO notes above
|
||||
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/mobile/src-tauri/gen/android/nym_connect/app/build/outputs/apk/arm64/debug/app-arm64-debug.apk \
|
||||
apk/nym-connect-arm64-debug.apk
|
||||
mv nym-connect/mobile/src-tauri/gen/android/nym_connect/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: ???
|
||||
@@ -1,102 +0,0 @@
|
||||
name: Nyms5 Android
|
||||
# unsigned APKs only, supported archs:
|
||||
# - arm64-v8a (arm64)
|
||||
# - x86_64
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- nyms5-android-v*
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build APK
|
||||
runs-on: custom-runner-linux
|
||||
env:
|
||||
ANDROID_HOME: ${{ github.workspace }}/android-sdk
|
||||
NDK_VERSION: 25.2.9519653
|
||||
NDK_HOME: ${{ github.workspace }}/android-sdk/ndk/25.2.9519653
|
||||
SDK_PLATFORM_VERSION: android-33
|
||||
SDK_BUILDTOOLS_VERSION: 33.0.2
|
||||
|
||||
steps:
|
||||
- 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 rust android targets
|
||||
run: |
|
||||
rustup target add aarch64-linux-android \
|
||||
x86_64-linux-android
|
||||
|
||||
- name: Build lib nym-socks5-listener
|
||||
working-directory: sdk/lib/socks5-listener/
|
||||
env:
|
||||
RELEASE: true
|
||||
# build for arm64 and x86_64
|
||||
run: ./build-android.sh aarch64 x86_64
|
||||
|
||||
- name: Build APKs (unsigned)
|
||||
working-directory: nym-connect/native/android
|
||||
env:
|
||||
ANDROID_SDK_ROOT: ${{ env.ANDROID_HOME }}
|
||||
# build for arm64 and x86_64
|
||||
run: ./gradlew :app:assembleArch64Release
|
||||
|
||||
- name: Prepare APKs
|
||||
run: |
|
||||
mkdir apk
|
||||
mv nym-connect/native/android/app/build/outputs/apk/arch64/release/app-arch64-release-unsigned.apk \
|
||||
apk/nyms5-arch64-release.apk
|
||||
|
||||
- name: Upload APKs
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: nyms5-apk-arch64-release
|
||||
path: |
|
||||
apk/nyms5-arch64-release.apk
|
||||
|
||||
gh-release:
|
||||
name: Publish APK (GH release)
|
||||
needs: build
|
||||
runs-on: custom-runner-linux
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Download binary artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: nyms5-apk-arch64-release
|
||||
path: apk
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: apk/nyms5-arch64-release.apk
|
||||
|
||||
@@ -4,54 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v1.1.21] (2023-06-13)
|
||||
|
||||
- mixFetch: Change socks5 `SendRequest` to include OrderedMessage index as a field rather than making it serialized inside the `data` field
|
||||
([#3534])
|
||||
- Explorer - add more data columns to the Service Provider section: ([#3474])
|
||||
- network-requester: support report if they run an open proxy using `ControlRequest` API ([#3461])
|
||||
- Refactor client configs (London discussion) ([#3444])
|
||||
- Increase `DEFAULT_MAXIMUM_CONNECTION_BUFFER_SIZE` to 2000 to improve reliability ([#3433])
|
||||
- socks5: sender waits for lanes to clear even though the connection is closed ([#3366])
|
||||
- version bump for variables ([#3545])
|
||||
|
||||
[#3534]: https://github.com/nymtech/nym/issues/3534
|
||||
[#3474]: https://github.com/nymtech/nym/issues/3474
|
||||
[#3461]: https://github.com/nymtech/nym/issues/3461
|
||||
[#3444]: https://github.com/nymtech/nym/issues/3444
|
||||
[#3433]: https://github.com/nymtech/nym/issues/3433
|
||||
[#3366]: https://github.com/nymtech/nym/issues/3366
|
||||
[#3545]: https://github.com/nymtech/nym/pull/3545
|
||||
|
||||
## [v1.1.20] (2023-06-06)
|
||||
|
||||
- Explorer - Fix SP supported apps list ([#3458])
|
||||
- Investigate if we need to lower `SHUTDOWN_TIMEOUT` in socks5 to zero (or almost zero) ([#3438])
|
||||
- Explorer - show all gateways in the default view regardless of the version number ([#3427])
|
||||
- service-provider-directory: add signature check when announcing ([#3360])
|
||||
- Support functionality for nym-name-service (nym-api, nym-cli, etc) ([#3355])
|
||||
- Edit the nym-network-requester to support the enabled-credentials-mode flag ([#3101])
|
||||
- [BUG] network requester documentation update ([#3493])
|
||||
- removing hardcoded version numbers ([#3485])
|
||||
- [BUG] network requester documentation update ([#3481])
|
||||
- [BUG] network requester documentation update ([#3469])
|
||||
- Sign when announcing service providers to the directory contract ([#3459])
|
||||
- mixnode documentation update ([#3435])
|
||||
- updated readme with new developer chat links + new docs links ([#3141])
|
||||
|
||||
[#3458]: https://github.com/nymtech/nym/issues/3458
|
||||
[#3438]: https://github.com/nymtech/nym/issues/3438
|
||||
[#3427]: https://github.com/nymtech/nym/issues/3427
|
||||
[#3360]: https://github.com/nymtech/nym/issues/3360
|
||||
[#3355]: https://github.com/nymtech/nym/issues/3355
|
||||
[#3101]: https://github.com/nymtech/nym/issues/3101
|
||||
[#3493]: https://github.com/nymtech/nym/pull/3493
|
||||
[#3485]: https://github.com/nymtech/nym/pull/3485
|
||||
[#3481]: https://github.com/nymtech/nym/pull/3481
|
||||
[#3469]: https://github.com/nymtech/nym/pull/3469
|
||||
[#3459]: https://github.com/nymtech/nym/pull/3459
|
||||
[#3435]: https://github.com/nymtech/nym/pull/3435
|
||||
[#3141]: https://github.com/nymtech/nym/pull/3141
|
||||
## [v1.1.19] (2023-05-16)
|
||||
|
||||
- nym-name-service endpoint in nym-api ([#3403])
|
||||
|
||||
Generated
+15
-50
@@ -378,15 +378,6 @@ 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"
|
||||
@@ -1252,20 +1243,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-controllers"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-storage-plus"
|
||||
version = "0.13.4"
|
||||
@@ -1655,7 +1632,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.21"
|
||||
version = "1.1.19"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.2.7",
|
||||
@@ -3199,7 +3176,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.22"
|
||||
version = "1.1.20"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3333,7 +3310,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.21"
|
||||
version = "1.1.19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.13.1",
|
||||
@@ -3382,7 +3359,6 @@ dependencies = [
|
||||
"nym-name-service-common",
|
||||
"nym-network-defaults",
|
||||
"nym-service-provider-directory-common",
|
||||
"nym-sphinx",
|
||||
"nym-validator-client",
|
||||
"nym-vesting-contract-common",
|
||||
"rand 0.6.5",
|
||||
@@ -3397,7 +3373,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.21"
|
||||
version = "1.1.19"
|
||||
dependencies = [
|
||||
"clap 4.2.7",
|
||||
"dirs",
|
||||
@@ -3555,7 +3531,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.5.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -3611,7 +3587,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-crypto"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"aes 0.8.2",
|
||||
"blake3",
|
||||
@@ -3668,7 +3644,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.21"
|
||||
version = "1.1.19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3800,7 +3776,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.6.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -3819,7 +3795,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.22"
|
||||
version = "1.1.20"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58",
|
||||
@@ -3929,12 +3905,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.21"
|
||||
version = "1.1.19"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-file-watcher",
|
||||
"async-trait",
|
||||
"bs58",
|
||||
"clap 4.2.7",
|
||||
"dirs",
|
||||
"futures",
|
||||
@@ -3949,6 +3923,7 @@ dependencies = [
|
||||
"nym-credential-storage",
|
||||
"nym-crypto",
|
||||
"nym-network-defaults",
|
||||
"nym-ordered-buffer",
|
||||
"nym-sdk",
|
||||
"nym-service-providers-common",
|
||||
"nym-socks5-proxy-helpers",
|
||||
@@ -3956,13 +3931,11 @@ dependencies = [
|
||||
"nym-sphinx",
|
||||
"nym-statistics-common",
|
||||
"nym-task",
|
||||
"nym-types",
|
||||
"pretty_env_logger",
|
||||
"publicsuffix",
|
||||
"rand 0.7.3",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx 0.6.3",
|
||||
"tap",
|
||||
"tempfile",
|
||||
@@ -3974,7 +3947,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-statistics"
|
||||
version = "1.1.21"
|
||||
version = "1.1.19"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"log",
|
||||
@@ -4049,7 +4022,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-pemstore"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"pem",
|
||||
]
|
||||
@@ -4090,12 +4063,8 @@ name = "nym-service-provider-directory-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-utils",
|
||||
"nym-contracts-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4107,7 +4076,6 @@ dependencies = [
|
||||
"log",
|
||||
"nym-bin-common",
|
||||
"nym-sdk",
|
||||
"nym-socks5-requests",
|
||||
"nym-sphinx-anonymous-replies",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4117,7 +4085,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.21"
|
||||
version = "1.1.19"
|
||||
dependencies = [
|
||||
"clap 4.2.7",
|
||||
"lazy_static",
|
||||
@@ -4212,13 +4180,10 @@ dependencies = [
|
||||
name = "nym-socks5-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"log",
|
||||
"nym-service-providers-common",
|
||||
"nym-sphinx-addressing",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tap",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -4516,7 +4481,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.7.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"nym-contracts-common",
|
||||
|
||||
+13
-11
@@ -116,19 +116,21 @@ async-trait = "0.1.64"
|
||||
anyhow = "1.0.71"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
cfg-if = "1.0.0"
|
||||
cosmwasm-derive = "=1.0.0"
|
||||
cosmwasm-schema = "=1.0.0"
|
||||
cosmwasm-std = "=1.0.0"
|
||||
cosmwasm-storage = "=1.0.0"
|
||||
cw-controllers = "=0.13.4"
|
||||
cw-storage-plus = "=0.13.4"
|
||||
cw-utils = "=0.13.4"
|
||||
cw2 = { version = "=0.13.4" }
|
||||
cw3 = { version = "=0.13.4" }
|
||||
cw3-fixed-multisig = { version = "=0.13.4" }
|
||||
cw4 = { version = "=0.13.4" }
|
||||
cosmwasm-derive = "=1.2.5"
|
||||
cosmwasm-schema = "=1.2.5"
|
||||
cosmwasm-std = "=1.2.5"
|
||||
cosmwasm-storage = "=1.2.5"
|
||||
cosmrs = "=0.8.0"
|
||||
cw-utils = "=1.0.1"
|
||||
cw-storage-plus = "=1.0.1"
|
||||
cw2 = { version = "=1.0.1" }
|
||||
cw3 = { version = "=1.0.1" }
|
||||
cw3-fixed-multisig = { version = "=1.0.1" }
|
||||
cw4 = { version = "=1.0.1" }
|
||||
cw-controllers = { version = "=1.0.1" }
|
||||
dotenvy = "0.15.6"
|
||||
generic-array = "0.14.7"
|
||||
k256 = "0.11"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
once_cell = "1.7.2"
|
||||
|
||||
@@ -21,8 +21,8 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
||||
|
||||
### Building
|
||||
|
||||
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/build-nym.html).
|
||||
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/wallet/desktop-wallet.html).
|
||||
Platform build instructions are available on [our docs site](https://nymtech.net/docs/binaries/building-nym.html).
|
||||
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers).
|
||||
|
||||
### Developing
|
||||
|
||||
@@ -32,11 +32,7 @@ For Typescript components, please see [ts-packages](./ts-packages).
|
||||
|
||||
### Developer chat
|
||||
|
||||
> We used to use Keybase for developer chats, but we have since migrated to Matrix and Discord. We no longer check the old **nymtech.friends** Keybase team.
|
||||
|
||||
You can chat to us in two places:
|
||||
* The #dev channel on [Matrix](https://matrix.to/#/#dev:nymtech.chat)
|
||||
* The various developer channels on [Discord](https://discord.gg/nym)
|
||||
You can chat with us in [Keybase](https://keybase.io). Download their chat app, then click **Teams -> Join a team**. Type **nymtech.friends** into the team name and hit **continue**. For general chat, hang out in the **#general** channel. Our development takes place in the **#dev** channel. Node operators should be in the **#node-operators** channel.
|
||||
|
||||
### Rewards
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.21"
|
||||
version = "1.1.19"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.21"
|
||||
version = "1.1.19"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
Generated
+5
-23
@@ -865,20 +865,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-controllers"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-storage-plus"
|
||||
version = "0.13.4"
|
||||
@@ -2381,7 +2367,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.5.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -2416,7 +2402,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-crypto"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"aes 0.8.2",
|
||||
"blake3",
|
||||
@@ -2523,7 +2509,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.6.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -2623,7 +2609,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-pemstore"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"pem",
|
||||
]
|
||||
@@ -2633,12 +2619,8 @@ name = "nym-service-provider-directory-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-utils",
|
||||
"nym-contracts-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2891,7 +2873,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.7.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"nym-contracts-common",
|
||||
|
||||
@@ -565,7 +565,7 @@ where
|
||||
fragments: Vec<Fragment>,
|
||||
reply_surbs: Vec<ReplySurb>,
|
||||
) -> Result<Vec<PreparedFragment>, SurbWrappedPreparationError> {
|
||||
debug_assert_eq!(
|
||||
debug_assert_ne!(
|
||||
fragments.len(),
|
||||
reply_surbs.len(),
|
||||
"attempted to send {} fragments with {} reply surbs",
|
||||
|
||||
@@ -155,7 +155,7 @@ pub async fn get_registered_gateway<S>(
|
||||
key_store: &S::KeyStore,
|
||||
setup: GatewaySetup,
|
||||
overwrite_keys: bool,
|
||||
) -> Result<(GatewayEndpointConfig, ManagedKeys), ClientCoreError>
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError>
|
||||
where
|
||||
S: MixnetClientStorage,
|
||||
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
|
||||
@@ -164,11 +164,11 @@ where
|
||||
|
||||
// try load keys
|
||||
let mut managed_keys = match ManagedKeys::try_load(key_store).await {
|
||||
Ok(loaded_keys) => {
|
||||
Ok(_) => {
|
||||
// if we loaded something and we don't have full gateway details, check if we can overwrite the data
|
||||
if let GatewaySetup::Predefined { config } = setup {
|
||||
// we already have defined gateway details AND a shared key, so nothing more for us to do
|
||||
return Ok((config, loaded_keys));
|
||||
return Ok(config);
|
||||
} else if overwrite_keys {
|
||||
ManagedKeys::generate_new(&mut rng)
|
||||
} else {
|
||||
@@ -196,7 +196,7 @@ where
|
||||
|
||||
// TODO: here we should be probably persisting gateway details as opposed to returning them
|
||||
|
||||
Ok((gateway_details, managed_keys))
|
||||
Ok(gateway_details)
|
||||
}
|
||||
|
||||
/// Convenience function for setting up the gateway for a client given a `Config`. Depending on the
|
||||
@@ -299,15 +299,6 @@ pub fn get_client_address(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn load_identity_keys(
|
||||
pathfinder: &ClientKeyPathfinder,
|
||||
) -> Result<identity::KeyPair, ClientCoreError> {
|
||||
let identity_keypair: identity::KeyPair =
|
||||
nym_pemstore::load_keypair(&pathfinder.identity_key_pair_path())
|
||||
.tap_err(|_| log::error!("Failed to read stored identity key files"))?;
|
||||
Ok(identity_keypair)
|
||||
}
|
||||
|
||||
/// Get the client address by loading the keys from stored files.
|
||||
// TODO: rethink that sucker
|
||||
pub fn get_client_address_from_stored_ondisk_keys<T>(
|
||||
|
||||
@@ -40,7 +40,7 @@ nym-api-requests = { path = "../../../nym-api/nym-api-requests" }
|
||||
async-trait = { workspace = true, optional = true }
|
||||
bip39 = { workspace = true, features = ["rand"], optional = true }
|
||||
nym-config = { path = "../../config", optional = true }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true }
|
||||
cosmrs = { workspace = true, features = ["rpc", "bip32", "cosmwasm"], optional = true }
|
||||
# note that this has the same version as used by cosmrs
|
||||
eyre = { version = "0.6", optional = true }
|
||||
cw3 = { workspace = true, optional = true }
|
||||
@@ -54,7 +54,7 @@ cosmwasm-std = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
bip39 = { workspace = true }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32"] }
|
||||
cosmrs = { workspace = true, features = ["rpc", "bip32"] }
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
||||
ts-rs = "6.1.2"
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ impl GasAdjustable for Gas {
|
||||
mod sealed {
|
||||
use cosmrs::tx::{self, Gas};
|
||||
use cosmrs::Coin as CosmosCoin;
|
||||
use cosmrs::{AccountId, Decimal as CosmosDecimal, Denom as CosmosDenom};
|
||||
use cosmrs::{AccountId, Denom as CosmosDenom};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
fn cosmos_denom_inner_getter(val: &CosmosDenom) -> String {
|
||||
@@ -144,29 +144,11 @@ mod sealed {
|
||||
}
|
||||
}
|
||||
|
||||
fn cosmos_decimal_inner_getter(val: &CosmosDecimal) -> u64 {
|
||||
// haha, this code is so disgusting. I'll make a PR on cosmrs to slightly alleviate those issues...
|
||||
// note: unwrap here is fine as the to_string is just returning a stringified u64 which, well, is a valid u64
|
||||
val.to_string().parse().unwrap()
|
||||
}
|
||||
|
||||
// at the time of writing it the current cosmrs' Decimal is extremely limited...
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(remote = "CosmosDecimal")]
|
||||
struct Decimal(#[serde(getter = "cosmos_decimal_inner_getter")] u64);
|
||||
|
||||
impl From<Decimal> for CosmosDecimal {
|
||||
fn from(val: Decimal) -> Self {
|
||||
val.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
struct Coin {
|
||||
#[serde(with = "Denom")]
|
||||
denom: CosmosDenom,
|
||||
#[serde(with = "Decimal")]
|
||||
amount: CosmosDecimal,
|
||||
amount: u128,
|
||||
}
|
||||
|
||||
impl From<Coin> for CosmosCoin {
|
||||
|
||||
@@ -39,7 +39,7 @@ pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
|
||||
pub use cosmrs::tendermint::Time as TendermintTime;
|
||||
pub use cosmrs::tx::{self, Gas};
|
||||
pub use cosmrs::Coin as CosmosCoin;
|
||||
pub use cosmrs::{bip32, AccountId, Decimal, Denom};
|
||||
pub use cosmrs::{bip32, AccountId, Denom};
|
||||
use cosmwasm_std::Addr;
|
||||
pub use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
|
||||
use nym_contracts_common::ContractBuildInformation;
|
||||
use nym_service_provider_directory_common::{
|
||||
msg::QueryMsg as SpQueryMsg,
|
||||
response::{
|
||||
ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse,
|
||||
},
|
||||
NymAddress, Service, ServiceId,
|
||||
NymAddress, ServiceId, ServiceInfo,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -63,14 +63,17 @@ pub trait SpDirectoryQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_services(&self) -> Result<Vec<Service>, NyxdError> {
|
||||
async fn get_all_services(&self) -> Result<Vec<ServiceInfo>, NyxdError> {
|
||||
let mut services = Vec::new();
|
||||
let mut start_after = None;
|
||||
|
||||
loop {
|
||||
let mut paged_response = self.get_services_paged(start_after.take(), None).await?;
|
||||
|
||||
let last_id = paged_response.services.last().map(|serv| serv.service_id);
|
||||
services.append(&mut paged_response.services);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
if let Some(start_after_res) = last_id {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
@@ -79,13 +82,6 @@ pub trait SpDirectoryQueryClient {
|
||||
|
||||
Ok(services)
|
||||
}
|
||||
|
||||
async fn get_service_signing_nonce(&self, address: &AccountId) -> Result<Nonce, NyxdError> {
|
||||
self.query_service_provider_contract(SpQueryMsg::SigningNonce {
|
||||
address: address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use async_trait::async_trait;
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_service_provider_directory_common::{
|
||||
msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceDetails, ServiceId,
|
||||
msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceId, ServiceType,
|
||||
};
|
||||
|
||||
use crate::nyxd::{
|
||||
@@ -23,16 +22,16 @@ pub trait SpDirectorySigningClient {
|
||||
|
||||
async fn announce_service_provider(
|
||||
&self,
|
||||
service: ServiceDetails,
|
||||
owner_signature: MessageSignature,
|
||||
nym_address: NymAddress,
|
||||
service_type: ServiceType,
|
||||
deposit: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_service_provider_directory_contract(
|
||||
fee,
|
||||
SpExecuteMsg::Announce {
|
||||
service,
|
||||
owner_signature,
|
||||
nym_address,
|
||||
service_type,
|
||||
},
|
||||
vec![deposit],
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ clap = { version = "4.0", features = ["derive"] }
|
||||
cw-utils = { workspace = true }
|
||||
handlebars = "3.0.1"
|
||||
humantime-serde = "1.0"
|
||||
k256 = { version = "0.10", features = ["ecdsa", "sha256"] }
|
||||
k256 = { workspace = true, features = ["ecdsa", "sha256"] }
|
||||
log = { workspace = true }
|
||||
rand = {version = "0.6", features = ["std"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
@@ -25,7 +25,7 @@ toml = "0.5.6"
|
||||
url = "2.2"
|
||||
tap = "1"
|
||||
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
|
||||
cosmrs = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
|
||||
nym-validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
|
||||
@@ -40,4 +40,3 @@ nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
|
||||
nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
|
||||
nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" }
|
||||
nym-name-service-common = { path = "../cosmwasm-smart-contracts/name-service" }
|
||||
nym-sphinx = { path = "../../common/nymsphinx" }
|
||||
|
||||
@@ -56,6 +56,8 @@ pub async fn generate(args: Args) {
|
||||
.expect("threshold can't be converted to Decimal"),
|
||||
},
|
||||
max_voting_period: Duration::Time(args.max_voting_period),
|
||||
executor: None,
|
||||
proposal_deposit: None,
|
||||
coconut_bandwidth_contract_address: coconut_bandwidth_contract_address.to_string(),
|
||||
coconut_dkg_contract_address: coconut_dkg_contract_address.to_string(),
|
||||
};
|
||||
|
||||
@@ -14,7 +14,6 @@ pub struct Mixnet {
|
||||
pub command: MixnetCommands,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetCommands {
|
||||
/// Query the mixnet directory
|
||||
|
||||
@@ -15,7 +15,6 @@ pub struct MixnetOperators {
|
||||
pub command: MixnetOperatorsCommands,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsCommands {
|
||||
/// Manage your mixnode
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_service_provider_directory_common::{Coin, NymAddress, ServiceDetails, ServiceType};
|
||||
use nym_service_provider_directory_common::{Coin, NymAddress, ServiceType};
|
||||
use nym_validator_client::nyxd::traits::SpDirectorySigningClient;
|
||||
|
||||
use crate::context::SigningClient;
|
||||
@@ -11,15 +10,9 @@ pub struct Args {
|
||||
#[clap(long)]
|
||||
pub nym_address: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub signature: MessageSignature,
|
||||
|
||||
/// Deposit to be made to the service provider directory, in curent DENOMINATION (e.g. 'unym')
|
||||
#[clap(long)]
|
||||
pub deposit: u128,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
}
|
||||
|
||||
pub async fn announce(args: Args, client: SigningClient) {
|
||||
@@ -27,17 +20,12 @@ pub async fn announce(args: Args, client: SigningClient) {
|
||||
|
||||
let nym_address = NymAddress::Address(args.nym_address);
|
||||
let service_type = ServiceType::NetworkRequester;
|
||||
let service = ServiceDetails {
|
||||
nym_address,
|
||||
service_type,
|
||||
identity_key: args.identity_key,
|
||||
};
|
||||
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
let deposit = Coin::new(args.deposit, denom);
|
||||
|
||||
let res = client
|
||||
.announce_service_provider(service, args.signature, deposit.into(), None)
|
||||
.announce_service_provider(nym_address, service_type, deposit.into(), None)
|
||||
.await
|
||||
.expect("Failed to announce service provider");
|
||||
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{
|
||||
context::SigningClient,
|
||||
utils::{account_id_to_cw_addr, DataWrapper},
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use cosmwasm_std::Coin;
|
||||
|
||||
use nym_bin_common::output_format::OutputFormat;
|
||||
use nym_service_provider_directory_common::{
|
||||
signing_types::construct_service_provider_announce_sign_payload, NymAddress,
|
||||
ServiceType::NetworkRequester,
|
||||
};
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_validator_client::nyxd::traits::SpDirectoryQueryClient;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub nym_address: Recipient,
|
||||
|
||||
#[clap(long)]
|
||||
pub amount: u128,
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: String,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
output: OutputFormat,
|
||||
}
|
||||
|
||||
pub async fn create_payload(args: Args, client: SigningClient) {
|
||||
let service = nym_service_provider_directory_common::ServiceDetails {
|
||||
nym_address: NymAddress::new(&args.nym_address.to_string()),
|
||||
service_type: NetworkRequester,
|
||||
identity_key: args.identity_key,
|
||||
};
|
||||
|
||||
let denom = client.current_chain_details().mix_denom.base.as_str();
|
||||
let deposit = Coin::new(args.amount, denom);
|
||||
|
||||
let nonce = match client.get_service_signing_nonce(client.address()).await {
|
||||
Ok(nonce) => nonce,
|
||||
Err(err) => {
|
||||
eprint!(
|
||||
"failed to query for the signing nonce of {}: {err}",
|
||||
client.address()
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let address = account_id_to_cw_addr(client.address());
|
||||
let payload =
|
||||
construct_service_provider_announce_sign_payload(nonce, address, deposit, service);
|
||||
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
|
||||
println!("{}", args.output.format(&wrapper))
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod announce;
|
||||
pub mod announce_sign_payload;
|
||||
pub mod delete;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
@@ -11,13 +10,10 @@ pub struct MixnetOperatorsService {
|
||||
pub command: MixnetOperatorsServiceCommands,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsServiceCommands {
|
||||
/// Announce service provider to the world
|
||||
Announce(announce::Args),
|
||||
/// Delete entry for service provider from the directory
|
||||
Delete(delete::Args),
|
||||
/// Create base58-encoded payload required for producing valid announce signature.
|
||||
CreateServiceAnnounceSignPayload(announce_sign_payload::Args),
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ pub async fn query(args: Args, client: &QueryClientWithNyxd) {
|
||||
for service in res.services {
|
||||
table.add_row(vec![
|
||||
service.service_id.to_string(),
|
||||
service.announcer.to_string(),
|
||||
service.service.announcer.to_string(),
|
||||
service.service.service_type.to_string(),
|
||||
service.service.nym_address.to_string(),
|
||||
]);
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct InstantiateMsg {
|
||||
pub mix_denom: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
DepositFunds { data: DepositData },
|
||||
|
||||
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::msg::ExecuteMsg;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct SpendCredentialData {
|
||||
funds: Coin,
|
||||
blinded_serial_number: String,
|
||||
@@ -43,7 +43,7 @@ pub enum SpendCredentialStatus {
|
||||
Spent,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct SpendCredential {
|
||||
funds: Coin,
|
||||
blinded_serial_number: String,
|
||||
@@ -74,7 +74,7 @@ impl SpendCredential {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PagedSpendCredentialResponse {
|
||||
pub spend_credentials: Vec<SpendCredential>,
|
||||
pub per_page: usize,
|
||||
@@ -95,7 +95,7 @@ impl PagedSpendCredentialResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct SpendCredentialResponse {
|
||||
pub spend_credential: Option<SpendCredential>,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.5.0"
|
||||
version = "0.4.0"
|
||||
description = "Common library for Nym cosmwasm contracts"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
|
||||
@@ -15,7 +15,7 @@ pub type Nonce = u32;
|
||||
|
||||
// define this type explicitly for [hopefully] better usability
|
||||
// (so you wouldn't need to worry about whether you should use bytes, bs58, etc.)
|
||||
#[derive(Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct MessageSignature(Vec<u8>);
|
||||
|
||||
impl MessageSignature {
|
||||
|
||||
@@ -11,9 +11,6 @@ use std::ops::Mul;
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
|
||||
pub type IdentityKey = String;
|
||||
pub type IdentityKeyRef<'a> = &'a str;
|
||||
|
||||
pub fn truncate_decimal(amount: Decimal) -> Uint128 {
|
||||
amount * Uint128::new(1)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use cosmwasm_schema::{cw_serde, QueryResponses};
|
||||
use cw4::Member;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[cw_serde]
|
||||
pub struct InstantiateMsg {
|
||||
/// The admin is the only account that can update the group state.
|
||||
/// Omit it to make the group immutable.
|
||||
@@ -15,8 +9,7 @@ pub struct InstantiateMsg {
|
||||
pub members: Vec<Member>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[cw_serde]
|
||||
pub enum ExecuteMsg {
|
||||
/// Change the admin
|
||||
UpdateAdmin { admin: Option<String> },
|
||||
@@ -32,23 +25,24 @@ pub enum ExecuteMsg {
|
||||
RemoveHook { addr: String },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[cw_serde]
|
||||
#[derive(QueryResponses)]
|
||||
pub enum QueryMsg {
|
||||
/// Return AdminResponse
|
||||
#[returns(cw_controllers::AdminResponse)]
|
||||
Admin {},
|
||||
/// Return TotalWeightResponse
|
||||
TotalWeight {},
|
||||
/// Returns MembersListResponse
|
||||
#[returns(cw4::TotalWeightResponse)]
|
||||
TotalWeight { at_height: Option<u64> },
|
||||
#[returns(cw4::MemberListResponse)]
|
||||
ListMembers {
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
/// Returns MemberResponse
|
||||
#[returns(cw4::MemberResponse)]
|
||||
Member {
|
||||
addr: String,
|
||||
at_height: Option<u64>,
|
||||
},
|
||||
/// Shows all registered hooks. Returns HooksResponse.
|
||||
/// Shows all registered hooks.
|
||||
#[returns(cw_controllers::HooksResponse)]
|
||||
Hooks {},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.6.0"
|
||||
version = "0.5.0"
|
||||
description = "Common library for the Nym mixnet contract"
|
||||
rust-version = "1.62"
|
||||
edition = { workspace = true }
|
||||
@@ -15,7 +15,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_repr = "0.1"
|
||||
schemars = "0.8"
|
||||
thiserror = "1.0"
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.4.0" }
|
||||
# use 0.4.1 as that's the version used by cosmwasm-std 1.0.0
|
||||
# (and ideally we don't want to pull the same dependency twice)
|
||||
serde-json-wasm = "=0.4.1"
|
||||
|
||||
@@ -37,7 +37,7 @@ pub fn generate_owner_storage_subkey(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)]
|
||||
pub struct Delegation {
|
||||
/// Address of the owner of this delegation.
|
||||
pub owner: Addr,
|
||||
@@ -114,7 +114,7 @@ impl Delegation {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PagedMixNodeDelegationsResponse {
|
||||
pub delegations: Vec<Delegation>,
|
||||
pub start_next_after: Option<OwnerProxySubKey>,
|
||||
@@ -129,7 +129,7 @@ impl PagedMixNodeDelegationsResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PagedDelegatorDelegationsResponse {
|
||||
pub delegations: Vec<Delegation>,
|
||||
pub start_next_after: Option<(MixId, OwnerProxySubKey)>,
|
||||
@@ -147,7 +147,7 @@ impl PagedDelegatorDelegationsResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct MixNodeDelegationResponse {
|
||||
pub delegation: Option<Delegation>,
|
||||
pub mixnode_still_bonded: bool,
|
||||
@@ -162,7 +162,7 @@ impl MixNodeDelegationResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PagedAllDelegationsResponse {
|
||||
pub delegations: Vec<Delegation>,
|
||||
pub start_next_after: Option<StorageKey>,
|
||||
|
||||
@@ -23,7 +23,7 @@ pub struct Gateway {
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct GatewayBond {
|
||||
pub pledge_amount: Coin,
|
||||
pub owner: Addr,
|
||||
@@ -132,7 +132,7 @@ impl GatewayConfigUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PagedGatewayResponse {
|
||||
pub nodes: Vec<GatewayBond>,
|
||||
pub per_page: usize,
|
||||
@@ -153,13 +153,13 @@ impl PagedGatewayResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct GatewayOwnershipResponse {
|
||||
pub address: Addr,
|
||||
pub gateway: Option<GatewayBond>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct GatewayBondResponse {
|
||||
pub identity: IdentityKey,
|
||||
pub gateway: Option<GatewayBond>,
|
||||
|
||||
@@ -489,7 +489,7 @@ impl CurrentIntervalResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct PendingEpochEventsResponse {
|
||||
pub seconds_until_executable: i64,
|
||||
pub events: Vec<PendingEpochEvent>,
|
||||
@@ -510,7 +510,7 @@ impl PendingEpochEventsResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct PendingIntervalEventsResponse {
|
||||
pub seconds_until_executable: i64,
|
||||
pub events: Vec<PendingIntervalEvent>,
|
||||
|
||||
@@ -33,7 +33,7 @@ impl RewardedSetNodeStatus {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct MixNodeDetails {
|
||||
pub bond_information: MixNodeBond,
|
||||
pub rewarding_details: MixNodeRewarding,
|
||||
@@ -86,7 +86,7 @@ impl MixNodeDetails {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct MixNodeRewarding {
|
||||
/// Information provided by the operator that influence the cost function.
|
||||
pub cost_params: MixNodeCostParams,
|
||||
@@ -465,7 +465,7 @@ impl MixNodeRewarding {
|
||||
}
|
||||
|
||||
// operator information + data assigned by the contract(s)
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct MixNodeBond {
|
||||
/// Unique id assigned to the bonded mixnode.
|
||||
pub mix_id: MixId,
|
||||
@@ -559,7 +559,7 @@ pub struct MixNode {
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct MixNodeCostParams {
|
||||
pub profit_margin_percent: Percent,
|
||||
|
||||
@@ -686,7 +686,7 @@ impl MixNodeConfigUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PagedMixnodeBondsResponse {
|
||||
pub nodes: Vec<MixNodeBond>,
|
||||
pub per_page: usize,
|
||||
@@ -703,7 +703,7 @@ impl PagedMixnodeBondsResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct PagedMixnodesDetailsResponse {
|
||||
pub nodes: Vec<MixNodeDetails>,
|
||||
pub per_page: usize,
|
||||
@@ -745,19 +745,19 @@ impl PagedUnbondedMixnodesResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct MixOwnershipResponse {
|
||||
pub address: Addr,
|
||||
pub mixnode_details: Option<MixNodeDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct MixnodeDetailsResponse {
|
||||
pub mix_id: MixId,
|
||||
pub mixnode_details: Option<MixNodeDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub struct MixnodeRewardingDetailsResponse {
|
||||
pub mix_id: MixId,
|
||||
pub rewarding_details: Option<MixNodeRewarding>,
|
||||
|
||||
@@ -7,19 +7,19 @@ use crate::{BlockHeight, EpochEventId, IntervalEventId, MixId};
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct PendingEpochEvent {
|
||||
pub id: EpochEventId,
|
||||
pub event: PendingEpochEventData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct PendingEpochEventData {
|
||||
pub created_at: BlockHeight,
|
||||
pub kind: PendingEpochEventKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum PendingEpochEventKind {
|
||||
// can't just pass the `Delegation` struct here as it's impossible to determine
|
||||
// `cumulative_reward_ratio` ahead of time
|
||||
@@ -68,19 +68,19 @@ impl From<(EpochEventId, PendingEpochEventData)> for PendingEpochEvent {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct PendingIntervalEvent {
|
||||
pub id: IntervalEventId,
|
||||
pub event: PendingIntervalEventData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct PendingIntervalEventData {
|
||||
pub created_at: BlockHeight,
|
||||
pub kind: PendingIntervalEventKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum PendingIntervalEventKind {
|
||||
ChangeMixCostParams {
|
||||
mix_id: MixId,
|
||||
|
||||
@@ -35,7 +35,7 @@ pub struct RewardDistribution {
|
||||
pub delegates: Decimal,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)]
|
||||
pub struct PendingRewardResponse {
|
||||
pub amount_staked: Option<Coin>,
|
||||
pub amount_earned: Option<Coin>,
|
||||
@@ -46,7 +46,7 @@ pub struct PendingRewardResponse {
|
||||
pub mixnode_still_fully_bonded: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)]
|
||||
pub struct EstimatedCurrentEpochRewardResponse {
|
||||
pub original_stake: Option<Coin>,
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
use crate::error::MixnetContractError;
|
||||
use crate::families::{Family, FamilyHead};
|
||||
use crate::{Layer, RewardedSetNodeStatus};
|
||||
use contracts_common::IdentityKey;
|
||||
use cosmwasm_std::Addr;
|
||||
use cosmwasm_std::Coin;
|
||||
use schemars::JsonSchema;
|
||||
@@ -12,6 +11,8 @@ use serde::{Deserialize, Serialize};
|
||||
use std::ops::Index;
|
||||
|
||||
// type aliases for better reasoning about available data
|
||||
pub type IdentityKey = String;
|
||||
pub type IdentityKeyRef<'a> = &'a str;
|
||||
pub type SphinxKey = String;
|
||||
pub type SphinxKeyRef<'a> = &'a str;
|
||||
pub type EpochId = u32;
|
||||
@@ -22,7 +23,7 @@ pub type EpochEventId = u32;
|
||||
pub type IntervalEventId = u32;
|
||||
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, PartialEq, Eq)]
|
||||
pub struct LayerAssignment {
|
||||
mix_id: MixId,
|
||||
layer: Layer,
|
||||
@@ -118,7 +119,7 @@ impl Index<Layer> for LayerDistribution {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct ContractState {
|
||||
pub owner: Addr, // only the owner account can update state
|
||||
pub rewarding_validator_address: Addr,
|
||||
@@ -130,7 +131,7 @@ pub struct ContractState {
|
||||
pub params: ContractStateParams,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct ContractStateParams {
|
||||
/// Minimum amount a delegator must stake in orders for his delegation to get accepted.
|
||||
pub minimum_mixnode_delegation: Option<Coin>,
|
||||
|
||||
@@ -9,6 +9,8 @@ edition = "2021"
|
||||
cw-utils = { workspace = true }
|
||||
cw3 = { workspace = true }
|
||||
cw4 = { workspace= true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::StdError;
|
||||
use cw_utils::ThresholdError;
|
||||
use cw3::DepositError;
|
||||
use cw_utils::{PaymentError, ThresholdError};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -17,9 +18,6 @@ pub enum ContractError {
|
||||
#[error("Group contract invalid address '{addr}'")]
|
||||
InvalidGroup { addr: String },
|
||||
|
||||
#[error("Coconut bandwidth contract address not found")]
|
||||
InvalidCoconutBandwidth {},
|
||||
|
||||
#[error("Unauthorized")]
|
||||
Unauthorized {},
|
||||
|
||||
@@ -43,4 +41,10 @@ pub enum ContractError {
|
||||
|
||||
#[error("Cannot close completed or passed proposals")]
|
||||
WrongCloseStatus {},
|
||||
|
||||
#[error("{0}")]
|
||||
Payment(#[from] PaymentError),
|
||||
|
||||
#[error("{0}")]
|
||||
Deposit(#[from] DepositError),
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod error;
|
||||
pub mod msg;
|
||||
pub mod state;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use cosmwasm_schema::{cw_serde, QueryResponses};
|
||||
use cosmwasm_std::{CosmosMsg, Empty};
|
||||
use cw3::Vote;
|
||||
use cw3::{UncheckedDepositInfo, Vote};
|
||||
use cw4::MemberChangedHookMsg;
|
||||
use cw_utils::{Duration, Expiration, Threshold};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
|
||||
use crate::state::Executor;
|
||||
|
||||
#[cw_serde]
|
||||
pub struct InstantiateMsg {
|
||||
// this is the group contract that contains the member list
|
||||
pub group_addr: String,
|
||||
@@ -17,11 +17,15 @@ pub struct InstantiateMsg {
|
||||
pub coconut_dkg_contract_address: String,
|
||||
pub threshold: Threshold,
|
||||
pub max_voting_period: Duration,
|
||||
// who is able to execute passed proposals
|
||||
// None means that anyone can execute
|
||||
pub executor: Option<Executor>,
|
||||
/// The cost of creating a proposal (if any).
|
||||
pub proposal_deposit: Option<UncheckedDepositInfo>,
|
||||
}
|
||||
|
||||
// TODO: add some T variants? Maybe good enough as fixed Empty for now
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[cw_serde]
|
||||
pub enum ExecuteMsg {
|
||||
Propose {
|
||||
title: String,
|
||||
@@ -45,41 +49,44 @@ pub enum ExecuteMsg {
|
||||
}
|
||||
|
||||
// We can also add this as a cw3 extension
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[cw_serde]
|
||||
#[derive(QueryResponses)]
|
||||
pub enum QueryMsg {
|
||||
/// Return ThresholdResponse
|
||||
#[returns(cw_utils::ThresholdResponse)]
|
||||
Threshold {},
|
||||
/// Returns ProposalResponse
|
||||
#[returns(cw3::ProposalResponse)]
|
||||
Proposal { proposal_id: u64 },
|
||||
/// Returns ProposalListResponse
|
||||
#[returns(cw3::ProposalListResponse)]
|
||||
ListProposals {
|
||||
start_after: Option<u64>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
/// Returns ProposalListResponse
|
||||
#[returns(cw3::ProposalListResponse)]
|
||||
ReverseProposals {
|
||||
start_before: Option<u64>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
/// Returns VoteResponse
|
||||
#[returns(cw3::VoteResponse)]
|
||||
Vote { proposal_id: u64, voter: String },
|
||||
/// Returns VoteListResponse
|
||||
#[returns(cw3::VoteListResponse)]
|
||||
ListVotes {
|
||||
proposal_id: u64,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
/// Returns VoterInfo
|
||||
#[returns(cw3::VoterResponse)]
|
||||
Voter { address: String },
|
||||
/// Returns VoterListResponse
|
||||
#[returns(cw3::VoterListResponse)]
|
||||
ListVoters {
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
/// Gets the current configuration.
|
||||
#[returns(crate::state::Config)]
|
||||
Config {},
|
||||
}
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
|
||||
#[cw_serde]
|
||||
pub struct MigrateMsg {
|
||||
pub coconut_bandwidth_address: String,
|
||||
pub coconut_dkg_address: String,
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{Addr, QuerierWrapper};
|
||||
use cw3::DepositInfo;
|
||||
use cw4::Cw4Contract;
|
||||
use cw_storage_plus::Item;
|
||||
use cw_utils::{Duration, Threshold};
|
||||
|
||||
use crate::error::ContractError;
|
||||
|
||||
/// Defines who is able to execute proposals once passed
|
||||
#[cw_serde]
|
||||
pub enum Executor {
|
||||
/// Any member of the voting group, even with 0 points
|
||||
Member,
|
||||
/// Only the given address
|
||||
Only(Addr),
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct Config {
|
||||
pub threshold: Threshold,
|
||||
pub max_voting_period: Duration,
|
||||
// Total weight and voters are queried from this contract
|
||||
pub group_addr: Cw4Contract,
|
||||
pub coconut_bandwidth_addr: Addr,
|
||||
pub coconut_dkg_addr: Addr,
|
||||
// who is able to execute passed proposals
|
||||
// None means that anyone can execute
|
||||
pub executor: Option<Executor>,
|
||||
/// The price, if any, of creating a new proposal.
|
||||
pub proposal_deposit: Option<DepositInfo>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
// Executor can be set in 3 ways:
|
||||
// - Member: any member of the voting group is authorized
|
||||
// - Only: only passed address is authorized
|
||||
// - None: Everyone are authorized
|
||||
pub fn authorize(&self, querier: &QuerierWrapper, sender: &Addr) -> Result<(), ContractError> {
|
||||
if let Some(executor) = &self.executor {
|
||||
match executor {
|
||||
Executor::Member => {
|
||||
self.group_addr
|
||||
.is_member(querier, sender, None)?
|
||||
.ok_or(ContractError::Unauthorized {})?;
|
||||
}
|
||||
Executor::Only(addr) => {
|
||||
if addr != sender {
|
||||
return Err(ContractError::Unauthorized {});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// unique items
|
||||
pub const CONFIG: Item<Config> = Item::new("config");
|
||||
@@ -7,9 +7,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { workspace = true }
|
||||
nym-contracts-common = { path = "../contracts-common", version = "0.5.0" }
|
||||
schemars = "0.8"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
|
||||
@@ -39,16 +39,16 @@ pub fn new_announce_event(service_id: ServiceId, service: Service) -> Event {
|
||||
Event::new(ServiceProviderEventType::Announce)
|
||||
.add_attribute(ACTION, ServiceProviderEventType::Announce)
|
||||
.add_attribute(SERVICE_ID, service_id.to_string())
|
||||
.add_attribute(SERVICE_TYPE, service.service.service_type.to_string())
|
||||
.add_attribute(NYM_ADDRESS, service.service.nym_address.to_string())
|
||||
.add_attribute(SERVICE_TYPE, service.service_type.to_string())
|
||||
.add_attribute(NYM_ADDRESS, service.nym_address.to_string())
|
||||
.add_attribute(OWNER, service.announcer.to_string())
|
||||
}
|
||||
|
||||
pub fn new_delete_id_event(service: Service) -> Event {
|
||||
pub fn new_delete_id_event(service_id: ServiceId, service: Service) -> Event {
|
||||
Event::new(ServiceProviderEventType::DeleteId)
|
||||
.add_attribute(ACTION, ServiceProviderEventType::DeleteId)
|
||||
.add_attribute(SERVICE_ID, service.service_id.to_string())
|
||||
.add_attribute(NYM_ADDRESS, service.service.nym_address.to_string())
|
||||
.add_attribute(SERVICE_ID, service_id.to_string())
|
||||
.add_attribute(NYM_ADDRESS, service.nym_address.to_string())
|
||||
}
|
||||
|
||||
pub fn new_update_deposit_required_event(deposit_required: Coin) -> Event {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
pub mod msg;
|
||||
pub mod response;
|
||||
pub mod signing_types;
|
||||
pub mod types;
|
||||
|
||||
// Re-export all types at the top-level
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{NymAddress, ServiceDetails, ServiceId};
|
||||
use crate::{NymAddress, ServiceId, ServiceType};
|
||||
use cosmwasm_std::Coin;
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
@@ -23,8 +22,8 @@ pub struct MigrateMsg {}
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
Announce {
|
||||
service: ServiceDetails,
|
||||
owner_signature: MessageSignature,
|
||||
nym_address: NymAddress,
|
||||
service_type: ServiceType,
|
||||
},
|
||||
DeleteId {
|
||||
service_id: ServiceId,
|
||||
@@ -45,12 +44,9 @@ impl ExecuteMsg {
|
||||
pub fn default_memo(&self) -> String {
|
||||
match self {
|
||||
ExecuteMsg::Announce {
|
||||
service,
|
||||
owner_signature: _,
|
||||
} => format!(
|
||||
"announcing {} as type {}",
|
||||
service.nym_address, service.service_type
|
||||
),
|
||||
nym_address,
|
||||
service_type,
|
||||
} => format!("announcing {nym_address} as type {service_type}"),
|
||||
ExecuteMsg::DeleteId { service_id } => {
|
||||
format!("deleting service with service id {service_id}")
|
||||
}
|
||||
@@ -80,9 +76,6 @@ pub enum QueryMsg {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<ServiceId>,
|
||||
},
|
||||
SigningNonce {
|
||||
address: String,
|
||||
},
|
||||
Config {},
|
||||
GetContractVersion {},
|
||||
#[serde(rename = "get_cw2_contract_version")]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{Service, ServiceId};
|
||||
use crate::{msg::ExecuteMsg, Service, ServiceId, ServiceInfo};
|
||||
use cosmwasm_std::Coin;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -13,17 +13,22 @@ pub struct ServiceInfoResponse {
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct ServicesListResponse {
|
||||
pub services: Vec<Service>,
|
||||
pub services: Vec<ServiceInfo>,
|
||||
}
|
||||
|
||||
impl ServicesListResponse {
|
||||
pub fn new(services: Vec<Service>) -> ServicesListResponse {
|
||||
ServicesListResponse { services }
|
||||
pub fn new(services: Vec<(ServiceId, Service)>) -> ServicesListResponse {
|
||||
ServicesListResponse {
|
||||
services: services
|
||||
.into_iter()
|
||||
.map(|(service_id, service)| ServiceInfo::new(service_id, service))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[Service]> for ServicesListResponse {
|
||||
fn from(services: &[Service]) -> Self {
|
||||
impl From<&[ServiceInfo]> for ServicesListResponse {
|
||||
fn from(services: &[ServiceInfo]) -> Self {
|
||||
Self {
|
||||
services: services.to_vec(),
|
||||
}
|
||||
@@ -33,17 +38,21 @@ impl From<&[Service]> for ServicesListResponse {
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct PagedServicesListResponse {
|
||||
pub services: Vec<Service>,
|
||||
pub services: Vec<ServiceInfo>,
|
||||
pub per_page: usize,
|
||||
pub start_next_after: Option<ServiceId>,
|
||||
}
|
||||
|
||||
impl PagedServicesListResponse {
|
||||
pub fn new(
|
||||
services: Vec<Service>,
|
||||
services: Vec<(ServiceId, Service)>,
|
||||
per_page: usize,
|
||||
start_next_after: Option<ServiceId>,
|
||||
) -> PagedServicesListResponse {
|
||||
let services = services
|
||||
.into_iter()
|
||||
.map(|(service_id, service)| ServiceInfo::new(service_id, service))
|
||||
.collect();
|
||||
PagedServicesListResponse {
|
||||
services,
|
||||
per_page,
|
||||
@@ -57,3 +66,12 @@ impl PagedServicesListResponse {
|
||||
pub struct ConfigResponse {
|
||||
pub deposit_required: Coin,
|
||||
}
|
||||
|
||||
impl From<Service> for ExecuteMsg {
|
||||
fn from(service: Service) -> Self {
|
||||
ExecuteMsg::Announce {
|
||||
nym_address: service.nym_address,
|
||||
service_type: service.service_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
use nym_contracts_common::signing::{
|
||||
ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose,
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::ServiceDetails;
|
||||
|
||||
pub type SignableServiceProviderAnnounceMsg =
|
||||
SignableMessage<ContractMessageContent<ServiceProviderAnnounce>>;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ServiceProviderAnnounce {
|
||||
service: ServiceDetails,
|
||||
}
|
||||
|
||||
impl SigningPurpose for ServiceProviderAnnounce {
|
||||
fn message_type() -> MessageType {
|
||||
MessageType::new("service-provider-announce")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn construct_service_provider_announce_sign_payload(
|
||||
nonce: Nonce,
|
||||
sender: Addr,
|
||||
deposit: Coin,
|
||||
service: ServiceDetails,
|
||||
) -> SignableServiceProviderAnnounceMsg {
|
||||
let payload = ServiceProviderAnnounce { service };
|
||||
let proxy = None;
|
||||
let content = ContractMessageContent::new(sender, proxy, vec![deposit], payload);
|
||||
SignableMessage::new(nonce, content)
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
use nym_contracts_common::IdentityKey;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -10,11 +9,11 @@ pub type ServiceId = u32;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)]
|
||||
pub struct Service {
|
||||
/// Unique id assigned to the anounced service.
|
||||
pub service_id: ServiceId,
|
||||
/// The announced service.
|
||||
pub service: ServiceDetails,
|
||||
/// Address of the service owner.
|
||||
/// The address of the service.
|
||||
pub nym_address: NymAddress,
|
||||
/// The service type.
|
||||
pub service_type: ServiceType,
|
||||
/// Service owner.
|
||||
pub announcer: Addr,
|
||||
/// Block height at which the service was added.
|
||||
pub block_height: u64,
|
||||
@@ -22,16 +21,6 @@ pub struct Service {
|
||||
pub deposit: Coin,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)]
|
||||
pub struct ServiceDetails {
|
||||
/// The address of the service.
|
||||
pub nym_address: NymAddress,
|
||||
/// The service type.
|
||||
pub service_type: ServiceType,
|
||||
/// The identity key of the service.
|
||||
pub identity_key: IdentityKey,
|
||||
}
|
||||
|
||||
/// The types of addresses supported.
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -39,7 +28,7 @@ pub enum NymAddress {
|
||||
/// String representation of a nym address, which is of the form
|
||||
/// client_id.client_enc@gateway_id.
|
||||
Address(String),
|
||||
// String name that can looked up in the nym-name-service contract (once it exists)
|
||||
// For the future when we have a nym-dns contract
|
||||
//Name(String),
|
||||
}
|
||||
|
||||
@@ -52,7 +41,6 @@ impl NymAddress {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
NymAddress::Address(address) => address,
|
||||
//NymAddress::Name(name) => name,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,3 +66,19 @@ impl std::fmt::Display for ServiceType {
|
||||
write!(f, "{service_type}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct ServiceInfo {
|
||||
pub service_id: ServiceId,
|
||||
pub service: Service,
|
||||
}
|
||||
|
||||
impl ServiceInfo {
|
||||
pub fn new(service_id: ServiceId, service: Service) -> Self {
|
||||
Self {
|
||||
service_id,
|
||||
service,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.7.0"
|
||||
version = "0.6.0"
|
||||
description = "Common library for the Nym vesting contract"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
@@ -9,8 +9,8 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { workspace = true }
|
||||
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.6.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
|
||||
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.5.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.4.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
schemars = "0.8"
|
||||
ts-rs = {version = "6.1.2", optional = true}
|
||||
|
||||
@@ -28,7 +28,7 @@ pub enum Period {
|
||||
After,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct PledgeData {
|
||||
pub amount: Coin,
|
||||
pub block_time: Timestamp,
|
||||
@@ -49,7 +49,7 @@ impl PledgeData {
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub enum PledgeCap {
|
||||
Percent(Percent),
|
||||
Absolute(Uint128), // This has to be in unym
|
||||
@@ -77,7 +77,7 @@ impl Default for PledgeCap {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct OriginalVestingResponse {
|
||||
pub amount: Coin,
|
||||
pub number_of_periods: usize,
|
||||
|
||||
@@ -55,7 +55,7 @@ impl VestingSpecification {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
// Families
|
||||
|
||||
@@ -7,7 +7,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
|
||||
cosmrs = { workspace = true }
|
||||
thiserror = "1.0"
|
||||
|
||||
# I guess temporarily until we get serde support in coconut up and running
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-crypto"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
description = "Crypto library for the nym mixnet"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
@@ -28,7 +28,7 @@ zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] }
|
||||
|
||||
# internal
|
||||
nym-sphinx-types = { path = "../nymsphinx/types", version = "0.2.0" }
|
||||
nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" }
|
||||
nym-pemstore = { path = "../../common/pemstore", version = "0.2.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
rand_chacha = "0.2"
|
||||
|
||||
@@ -6,8 +6,8 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bip32 = "0.3.0"
|
||||
k256 = "0.10.4"
|
||||
bip32 = "0.4.0"
|
||||
k256 = { workspace = true }
|
||||
ledger-transport = "0.10.0"
|
||||
ledger-transport-hid = "0.10.0"
|
||||
thiserror = "1"
|
||||
@@ -25,12 +25,12 @@ nym-sphinx-types = { path = "types" }
|
||||
|
||||
# those dependencies are due to intriducing preparer and receiver. Perpaphs that indicates they should be moved
|
||||
# to separate crate?
|
||||
nym-crypto = { path = "../crypto", version = "0.4.0" }
|
||||
nym-crypto = { path = "../crypto", version = "0.3.0" }
|
||||
nym-topology = { path = "../topology" }
|
||||
|
||||
[dev-dependencies]
|
||||
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
|
||||
nym-crypto = { path = "../crypto", version = "0.4.0", features = ["asymmetric"] }
|
||||
nym-crypto = { path = "../crypto", version = "0.3.0", features = ["asymmetric"] }
|
||||
|
||||
# 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nym-pemstore"
|
||||
description = "Store private-public keypairs in PEM format"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
license = { workspace = true }
|
||||
|
||||
@@ -428,14 +428,18 @@ impl SocksClient {
|
||||
Some(self.lane_queue_lengths.clone()),
|
||||
self.shutdown_listener.clone(),
|
||||
)
|
||||
.run(move |socket_data| {
|
||||
let lane = TransmissionLane::ConnectionId(socket_data.header.connection_id);
|
||||
let provider_request =
|
||||
Socks5Request::new_send(request_version.provider_protocol, socket_data);
|
||||
.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 lane = TransmissionLane::ConnectionId(conn_id);
|
||||
if anonymous {
|
||||
InputMessage::new_anonymous(
|
||||
recipient,
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::Socks5ClientCoreError;
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
|
||||
use nym_client_core::client::received_buffer::ReconstructedMessagesReceiver;
|
||||
use nym_client_core::client::received_buffer::{
|
||||
ReceivedBufferMessage, ReceivedBufferRequestSender,
|
||||
};
|
||||
use nym_service_providers_common::interface::{ControlResponse, ResponseContent};
|
||||
use nym_socks5_proxy_helpers::connection_controller::{ControllerCommand, ControllerSender};
|
||||
use nym_socks5_proxy_helpers::connection_controller::ControllerSender;
|
||||
use nym_socks5_requests::{Socks5ProviderResponse, Socks5Response, Socks5ResponseContent};
|
||||
use nym_sphinx::receiver::ReconstructedMessage;
|
||||
use nym_task::TaskClient;
|
||||
|
||||
use crate::error::Socks5ClientCoreError;
|
||||
|
||||
pub(crate) struct MixnetResponseListener {
|
||||
buffer_requester: ReceivedBufferRequestSender,
|
||||
mix_response_receiver: ReconstructedMessagesReceiver,
|
||||
@@ -80,20 +79,12 @@ impl MixnetResponseListener {
|
||||
);
|
||||
Err(err_response.into())
|
||||
}
|
||||
Socks5ResponseContent::NetworkData { content } => {
|
||||
Socks5ResponseContent::NetworkData(response) => {
|
||||
self.controller_sender
|
||||
.unbounded_send(ControllerCommand::new_send(content))
|
||||
.unbounded_send(response.into())
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
Socks5ResponseContent::Query(response) => {
|
||||
error!("received a query response which we don't know how to handle yet!");
|
||||
error!("got: {:?}", response);
|
||||
|
||||
// I guess we'd need another channel here to forward those to where they need to go
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,6 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::message::OrderedMessage;
|
||||
use log::*;
|
||||
use std::collections::BTreeMap;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
pub enum OrderedMessageError {
|
||||
#[error("received message with sequence number {received}, which is way higher than our current {current}")]
|
||||
MessageSequenceTooLarge { current: u64, received: u64 },
|
||||
|
||||
#[error("received message with sequence number {received}, while we're already at {current}!")]
|
||||
MessageAlreadyReconstructed { current: u64, received: u64 },
|
||||
|
||||
#[error("attempted to overwrite message at sequence {received}")]
|
||||
AttemptedToOverwriteSequence { received: u64 },
|
||||
}
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Stores messages and emits them in order.
|
||||
///
|
||||
@@ -24,58 +9,36 @@ pub enum OrderedMessageError {
|
||||
/// to fill up with the full sequence.
|
||||
#[derive(Debug)]
|
||||
pub struct OrderedMessageBuffer {
|
||||
next_sequence: u64,
|
||||
messages: BTreeMap<u64, Vec<u8>>,
|
||||
next_index: u64,
|
||||
messages: HashMap<u64, OrderedMessage>,
|
||||
}
|
||||
|
||||
/// Data returned from `OrderedMessageBuffer` on a successful read of gapless ordered data.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ReadContiguousData {
|
||||
pub data: Vec<u8>,
|
||||
pub last_sequence: u64,
|
||||
pub last_index: u64,
|
||||
}
|
||||
|
||||
const MAX_REASONABLE_OFFSET: u64 = 1000;
|
||||
|
||||
impl OrderedMessageBuffer {
|
||||
pub fn new() -> OrderedMessageBuffer {
|
||||
OrderedMessageBuffer {
|
||||
next_sequence: 0,
|
||||
messages: BTreeMap::new(),
|
||||
next_index: 0,
|
||||
messages: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a message to the buffer. messages are sort on insertion, so
|
||||
/// that later on multiple reads for incomplete sequences don't result in
|
||||
/// useless sort work.
|
||||
pub fn write(&mut self, sequence: u64, data: Vec<u8>) -> Result<(), OrderedMessageError> {
|
||||
// reject messages that have clearly malformed sequence
|
||||
if sequence > self.next_sequence + MAX_REASONABLE_OFFSET {
|
||||
return Err(OrderedMessageError::MessageSequenceTooLarge {
|
||||
current: self.next_sequence,
|
||||
received: sequence,
|
||||
});
|
||||
}
|
||||
|
||||
if self.messages.contains_key(&sequence) {
|
||||
return Err(OrderedMessageError::AttemptedToOverwriteSequence { received: sequence });
|
||||
}
|
||||
|
||||
if sequence < self.next_sequence {
|
||||
return Err(OrderedMessageError::MessageAlreadyReconstructed {
|
||||
current: self.next_sequence,
|
||||
received: sequence,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn write(&mut self, message: OrderedMessage) {
|
||||
trace!(
|
||||
"Writing message index: {} length {} to OrderedMessageBuffer.",
|
||||
sequence,
|
||||
data.len()
|
||||
"Writing message index: {} length {:?} to OrderedMessageBuffer.",
|
||||
message.index,
|
||||
message.data.len()
|
||||
);
|
||||
|
||||
self.messages.insert(sequence, data);
|
||||
Ok(())
|
||||
self.messages.insert(message.index, message);
|
||||
}
|
||||
|
||||
/// Returns `Option<Vec<u8>>` where it's `Some(bytes)` if there is gapless
|
||||
@@ -86,31 +49,33 @@ impl OrderedMessageBuffer {
|
||||
/// a read will return the bytes of messages 0, 1, 2. Subsequent reads will
|
||||
/// return `None` until message 3 comes in, at which point 3, 4, and any
|
||||
/// further contiguous messages which have arrived will be returned.
|
||||
#[must_use]
|
||||
pub fn read(&mut self) -> Option<ReadContiguousData> {
|
||||
if !self.messages.contains_key(&self.next_sequence) {
|
||||
if !self.messages.contains_key(&self.next_index) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut contiguous_messages = Vec::new();
|
||||
let mut seq = self.next_sequence;
|
||||
let mut index = self.next_index;
|
||||
|
||||
while let Some(mut data) = self.messages.remove(&seq) {
|
||||
contiguous_messages.append(&mut data);
|
||||
seq += 1;
|
||||
while let Some(ordered_message) = self.messages.remove(&index) {
|
||||
contiguous_messages.push(ordered_message);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
let high_water = seq;
|
||||
self.next_sequence = high_water;
|
||||
trace!("Next high water mark is: {high_water}");
|
||||
let high_water = index;
|
||||
self.next_index = high_water;
|
||||
trace!("Next high water mark is: {}", high_water);
|
||||
|
||||
trace!(
|
||||
"Returning {} bytes from ordered message buffer",
|
||||
contiguous_messages.len()
|
||||
);
|
||||
// dig out the bytes from inside the struct
|
||||
let data: Vec<u8> = contiguous_messages
|
||||
.into_iter()
|
||||
.flat_map(|message| message.data)
|
||||
.collect();
|
||||
|
||||
trace!("Returning {} bytes from ordered message buffer", data.len());
|
||||
Some(ReadContiguousData {
|
||||
data: contiguous_messages,
|
||||
last_sequence: seq,
|
||||
data,
|
||||
last_index: index,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -125,64 +90,6 @@ impl Default for OrderedMessageBuffer {
|
||||
mod test_chunking_and_reassembling {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn trying_to_write_unreasonable_high_sequence() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
let first_message = vec![1, 2, 3, 4];
|
||||
let second_message = vec![5, 6, 7, 8];
|
||||
|
||||
buffer.write(0, first_message).unwrap();
|
||||
buffer.write(1, second_message).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Err(OrderedMessageError::MessageSequenceTooLarge {
|
||||
current: 0,
|
||||
received: 12345678
|
||||
}),
|
||||
buffer.write(12345678, b"foomp".to_vec())
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trying_to_overwrite_sequence() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
let message = vec![1, 2, 3, 4];
|
||||
|
||||
buffer.write(0, message.clone()).unwrap();
|
||||
buffer.write(1, message.clone()).unwrap();
|
||||
buffer.write(2, message.clone()).unwrap();
|
||||
buffer.write(3, message.clone()).unwrap();
|
||||
|
||||
for seq in 0..=3 {
|
||||
assert_eq!(
|
||||
Err(OrderedMessageError::AttemptedToOverwriteSequence { received: seq }),
|
||||
buffer.write(seq, message.clone())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn writing_past_data() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
let message = vec![1, 2, 3, 4];
|
||||
|
||||
buffer.write(0, message.clone()).unwrap();
|
||||
buffer.write(1, message.clone()).unwrap();
|
||||
buffer.write(2, message.clone()).unwrap();
|
||||
buffer.write(3, message.clone()).unwrap();
|
||||
let _ = buffer.read().unwrap();
|
||||
|
||||
for seq in 0..=3 {
|
||||
assert_eq!(
|
||||
Err(OrderedMessageError::MessageAlreadyReconstructed {
|
||||
current: 4,
|
||||
received: seq
|
||||
}),
|
||||
buffer.write(seq, message.clone())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod reading_from_and_writing_to_the_buffer {
|
||||
use super::*;
|
||||
@@ -195,14 +102,20 @@ mod test_chunking_and_reassembling {
|
||||
fn read_returns_ordered_bytes_and_resets_buffer() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
|
||||
let first_message = vec![1, 2, 3, 4];
|
||||
let second_message = vec![5, 6, 7, 8];
|
||||
let first_message = OrderedMessage {
|
||||
data: vec![1, 2, 3, 4],
|
||||
index: 0,
|
||||
};
|
||||
let second_message = OrderedMessage {
|
||||
data: vec![5, 6, 7, 8],
|
||||
index: 1,
|
||||
};
|
||||
|
||||
buffer.write(0, first_message).unwrap();
|
||||
buffer.write(first_message);
|
||||
let first_read = buffer.read().unwrap().data;
|
||||
assert_eq!(vec![1, 2, 3, 4], first_read);
|
||||
|
||||
buffer.write(1, second_message).unwrap();
|
||||
buffer.write(second_message);
|
||||
let second_read = buffer.read().unwrap().data;
|
||||
assert_eq!(vec![5, 6, 7, 8], second_read);
|
||||
|
||||
@@ -213,11 +126,17 @@ mod test_chunking_and_reassembling {
|
||||
fn test_multiple_adds_stacks_up_bytes_in_the_buffer() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
|
||||
let first_message = vec![1, 2, 3, 4];
|
||||
let second_message = vec![5, 6, 7, 8];
|
||||
let first_message = OrderedMessage {
|
||||
data: vec![1, 2, 3, 4],
|
||||
index: 0,
|
||||
};
|
||||
let second_message = OrderedMessage {
|
||||
data: vec![5, 6, 7, 8],
|
||||
index: 1,
|
||||
};
|
||||
|
||||
buffer.write(0, first_message).unwrap();
|
||||
buffer.write(1, second_message).unwrap();
|
||||
buffer.write(first_message);
|
||||
buffer.write(second_message);
|
||||
let second_read = buffer.read();
|
||||
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], second_read.unwrap().data);
|
||||
assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty
|
||||
@@ -227,11 +146,17 @@ mod test_chunking_and_reassembling {
|
||||
fn out_of_order_adds_results_in_ordered_byte_vector() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
|
||||
let first_message = vec![1, 2, 3, 4];
|
||||
let second_message = vec![5, 6, 7, 8];
|
||||
let first_message = OrderedMessage {
|
||||
data: vec![1, 2, 3, 4],
|
||||
index: 0,
|
||||
};
|
||||
let second_message = OrderedMessage {
|
||||
data: vec![5, 6, 7, 8],
|
||||
index: 1,
|
||||
};
|
||||
|
||||
buffer.write(1, second_message).unwrap();
|
||||
buffer.write(0, first_message).unwrap();
|
||||
buffer.write(second_message);
|
||||
buffer.write(first_message);
|
||||
let read = buffer.read().unwrap().data;
|
||||
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8], read);
|
||||
assert_eq!(None, buffer.read()); // second read on fully ordered result set is empty
|
||||
@@ -245,13 +170,23 @@ mod test_chunking_and_reassembling {
|
||||
fn setup() -> OrderedMessageBuffer {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
|
||||
let zero_message = vec![0, 0, 0, 0];
|
||||
let one_message = vec![1, 1, 1, 1];
|
||||
let three_message = vec![3, 3, 3, 3];
|
||||
let zero_message = OrderedMessage {
|
||||
data: vec![0, 0, 0, 0],
|
||||
index: 0,
|
||||
};
|
||||
let one_message = OrderedMessage {
|
||||
data: vec![1, 1, 1, 1],
|
||||
index: 1,
|
||||
};
|
||||
|
||||
buffer.write(0, zero_message).unwrap();
|
||||
buffer.write(1, one_message).unwrap();
|
||||
buffer.write(3, three_message).unwrap();
|
||||
let three_message = OrderedMessage {
|
||||
data: vec![3, 3, 3, 3],
|
||||
index: 3,
|
||||
};
|
||||
|
||||
buffer.write(zero_message);
|
||||
buffer.write(one_message);
|
||||
buffer.write(three_message);
|
||||
buffer
|
||||
}
|
||||
#[test]
|
||||
@@ -264,31 +199,43 @@ mod test_chunking_and_reassembling {
|
||||
assert_eq!(None, buffer.read());
|
||||
|
||||
// let's add another message, leaving a gap in place at index 2
|
||||
let five_message = vec![5, 5, 5, 5];
|
||||
buffer.write(5, five_message).unwrap();
|
||||
let five_message = OrderedMessage {
|
||||
data: vec![5, 5, 5, 5],
|
||||
index: 5,
|
||||
};
|
||||
buffer.write(five_message);
|
||||
assert_eq!(None, buffer.read());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filling_the_gap_allows_us_to_get_everything() {
|
||||
let mut buffer = setup();
|
||||
let _ = buffer.read(); // that burns the first two. We still have a gap before the 3s.
|
||||
buffer.read(); // that burns the first two. We still have a gap before the 3s.
|
||||
|
||||
let two_message = vec![2, 2, 2, 2];
|
||||
buffer.write(2, two_message).unwrap();
|
||||
let two_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 2,
|
||||
};
|
||||
buffer.write(two_message);
|
||||
|
||||
let more_ordered_bytes = buffer.read().unwrap().data;
|
||||
assert_eq!([2, 2, 2, 2, 3, 3, 3, 3].to_vec(), more_ordered_bytes);
|
||||
|
||||
// let's add another message
|
||||
let five_message = vec![5, 5, 5, 5];
|
||||
buffer.write(5, five_message).unwrap();
|
||||
let five_message = OrderedMessage {
|
||||
data: vec![5, 5, 5, 5],
|
||||
index: 5,
|
||||
};
|
||||
buffer.write(five_message);
|
||||
|
||||
assert_eq!(None, buffer.read());
|
||||
|
||||
// let's fill in the gap of 4s now and read again
|
||||
let four_message = vec![4, 4, 4, 4];
|
||||
buffer.write(4, four_message).unwrap();
|
||||
let four_message = OrderedMessage {
|
||||
data: vec![4, 4, 4, 4],
|
||||
index: 4,
|
||||
};
|
||||
buffer.write(four_message);
|
||||
|
||||
assert_eq!(
|
||||
[4, 4, 4, 4, 5, 5, 5, 5].to_vec(),
|
||||
@@ -302,47 +249,70 @@ mod test_chunking_and_reassembling {
|
||||
#[test]
|
||||
fn filling_the_gap_allows_us_to_get_everything_when_last_element_is_empty() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
let zero_message = vec![0, 0, 0, 0];
|
||||
let one_message = vec![2, 2, 2, 2];
|
||||
let two_message = vec![];
|
||||
let zero_message = OrderedMessage {
|
||||
data: vec![0, 0, 0, 0],
|
||||
index: 0,
|
||||
};
|
||||
let one_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 1,
|
||||
};
|
||||
let two_message = OrderedMessage {
|
||||
data: vec![],
|
||||
index: 2,
|
||||
};
|
||||
|
||||
buffer.write(0, zero_message).unwrap();
|
||||
buffer.write(zero_message);
|
||||
assert!(buffer.read().is_some()); // burn the buffer
|
||||
|
||||
buffer.write(2, two_message).unwrap();
|
||||
buffer.write(1, one_message).unwrap();
|
||||
buffer.write(two_message);
|
||||
buffer.write(one_message);
|
||||
assert!(buffer.read().is_some());
|
||||
assert_eq!(buffer.next_sequence, 3);
|
||||
assert_eq!(buffer.next_index, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_with_gaps_bigger_than_one() {
|
||||
let mut buffer = OrderedMessageBuffer::new();
|
||||
let zero_message = vec![0, 0, 0, 0];
|
||||
let one_message = vec![2, 2, 2, 2];
|
||||
let two_message = vec![2, 2, 2, 2];
|
||||
let three_message = vec![2, 2, 2, 2];
|
||||
let four_message = vec![2, 2, 2, 2];
|
||||
|
||||
buffer.write(0, zero_message).unwrap();
|
||||
let zero_message = OrderedMessage {
|
||||
data: vec![0, 0, 0, 0],
|
||||
index: 0,
|
||||
};
|
||||
let one_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 1,
|
||||
};
|
||||
let two_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 2,
|
||||
};
|
||||
let three_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 3,
|
||||
};
|
||||
let four_message = OrderedMessage {
|
||||
data: vec![2, 2, 2, 2],
|
||||
index: 4,
|
||||
};
|
||||
buffer.write(zero_message);
|
||||
assert!(buffer.read().is_some());
|
||||
assert_eq!(buffer.next_sequence, 1);
|
||||
assert_eq!(buffer.next_index, 1);
|
||||
|
||||
buffer.write(4, four_message).unwrap();
|
||||
buffer.write(four_message);
|
||||
assert!(buffer.read().is_none());
|
||||
assert_eq!(buffer.next_sequence, 1);
|
||||
assert_eq!(buffer.next_index, 1);
|
||||
|
||||
buffer.write(3, three_message).unwrap();
|
||||
buffer.write(three_message);
|
||||
assert!(buffer.read().is_none());
|
||||
assert_eq!(buffer.next_sequence, 1);
|
||||
assert_eq!(buffer.next_index, 1);
|
||||
|
||||
buffer.write(2, two_message).unwrap();
|
||||
buffer.write(two_message);
|
||||
assert!(buffer.read().is_none());
|
||||
assert_eq!(buffer.next_sequence, 1);
|
||||
assert_eq!(buffer.next_index, 1);
|
||||
|
||||
buffer.write(1, one_message).unwrap();
|
||||
buffer.write(one_message);
|
||||
assert!(buffer.read().is_some());
|
||||
assert_eq!(buffer.next_sequence, 5)
|
||||
assert_eq!(buffer.next_index, 5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
mod buffer;
|
||||
mod message;
|
||||
mod sender;
|
||||
|
||||
pub use buffer::{OrderedMessageBuffer, OrderedMessageError, ReadContiguousData};
|
||||
pub use buffer::{OrderedMessageBuffer, ReadContiguousData};
|
||||
pub use message::MessageError;
|
||||
pub use message::OrderedMessage;
|
||||
pub use sender::OrderedMessageSender;
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright 2020-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, PartialEq, Eq)]
|
||||
pub enum MessageError {
|
||||
#[error("the received message was empty")]
|
||||
NoData,
|
||||
|
||||
#[error("could not extract message index. Received {received} bytes, but expected {expected}")]
|
||||
IndexTooShort { received: usize, expected: usize },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct OrderedMessage {
|
||||
pub data: Vec<u8>,
|
||||
pub index: u64,
|
||||
}
|
||||
|
||||
impl OrderedMessage {
|
||||
/// Serializes an `OrderedMessage` into bytes.
|
||||
/// The output format is:
|
||||
/// | 8 bytes index | data... |
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.index
|
||||
.to_be_bytes()
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(self.data.into_iter())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Attempts to deserialize an `OrderedMessage` from bytes.
|
||||
pub fn try_from_bytes(data: Vec<u8>) -> Result<OrderedMessage, MessageError> {
|
||||
if data.is_empty() {
|
||||
return Err(MessageError::NoData);
|
||||
}
|
||||
|
||||
if data.len() < 8 {
|
||||
return Err(MessageError::IndexTooShort {
|
||||
received: data.len(),
|
||||
expected: 8,
|
||||
});
|
||||
}
|
||||
let index = u64::from_be_bytes([
|
||||
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
|
||||
]);
|
||||
Ok(OrderedMessage {
|
||||
data: data[8..].to_vec(),
|
||||
index,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Order messages by their index only, ignoring their data
|
||||
impl PartialOrd for OrderedMessage {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some((self.index).cmp(&(other.index)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod ordered_message_to_bytes {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn works() {
|
||||
let message = OrderedMessage {
|
||||
data: vec![123],
|
||||
index: 1,
|
||||
};
|
||||
let bytes = message.into_bytes();
|
||||
|
||||
let expected = vec![0, 0, 0, 0, 0, 0, 0, 1, 123];
|
||||
assert_eq!(expected, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod ordered_message_from_bytes {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fails_when_there_is_no_data() {
|
||||
let result = OrderedMessage::try_from_bytes(Vec::new());
|
||||
assert_eq!(Err(MessageError::NoData), result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fails_when_data_is_too_short() {
|
||||
let result = OrderedMessage::try_from_bytes(vec![1, 2, 3]);
|
||||
assert_eq!(
|
||||
Err(MessageError::IndexTooShort {
|
||||
received: 3,
|
||||
expected: 8
|
||||
}),
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_when_there_is_enough_to_make_a_sequence_number_but_no_message_data() {
|
||||
let expected = OrderedMessage {
|
||||
data: Vec::new(),
|
||||
index: 1,
|
||||
};
|
||||
let result = OrderedMessage::try_from_bytes(vec![0, 0, 0, 0, 0, 0, 0, 1]).unwrap();
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_when_there_is_seq_number_and_data() {
|
||||
let expected = OrderedMessage {
|
||||
data: vec![255, 255, 255],
|
||||
index: 1,
|
||||
};
|
||||
let result =
|
||||
OrderedMessage::try_from_bytes(vec![0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255]).unwrap();
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_message_does_not_affect_ordering() {
|
||||
let mut msg1 = OrderedMessage {
|
||||
data: vec![255, 255, 255],
|
||||
index: 1,
|
||||
};
|
||||
|
||||
let mut msg2 = OrderedMessage {
|
||||
data: vec![],
|
||||
index: 2,
|
||||
};
|
||||
|
||||
assert!(msg1 < msg2);
|
||||
|
||||
msg1.index = 2;
|
||||
msg2.index = 1;
|
||||
|
||||
assert!(msg1 > msg2);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
use crate::message::OrderedMessage;
|
||||
|
||||
/// Assigns sequence numbers to outbound byte vectors. These messages can then
|
||||
/// be reassembled into an ordered sequence by the `OrderedMessageSender`.
|
||||
#[derive(Debug)]
|
||||
pub struct OrderedMessageSender {
|
||||
next_index: u64,
|
||||
}
|
||||
|
||||
impl OrderedMessageSender {
|
||||
pub fn new() -> OrderedMessageSender {
|
||||
OrderedMessageSender { next_index: 0 }
|
||||
}
|
||||
|
||||
/// Turns raw bytes into an OrderedMessage containing the original bytes
|
||||
/// and a sequence number;
|
||||
pub fn wrap_message(&mut self, input: Vec<u8>) -> OrderedMessage {
|
||||
let message = OrderedMessage {
|
||||
data: input.to_vec(),
|
||||
index: self.next_index,
|
||||
};
|
||||
self.next_index += 1;
|
||||
message
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OrderedMessageSender {
|
||||
fn default() -> Self {
|
||||
OrderedMessageSender::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod ordered_message_sender {
|
||||
use super::*;
|
||||
|
||||
mod when_input_bytes_are_empty {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod sequence_index_numbers {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn increase_as_messages_are_sent() {
|
||||
let mut sender = OrderedMessageSender::new();
|
||||
let first_bytes = vec![1, 2, 3, 4];
|
||||
let second_bytes = vec![5, 6, 7, 8];
|
||||
|
||||
let first_message = sender.wrap_message(first_bytes);
|
||||
|
||||
assert_eq!(first_message.index, 0);
|
||||
|
||||
let second_message = sender.wrap_message(second_bytes);
|
||||
assert_eq!(second_message.index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use nym_ordered_buffer::{OrderedMessageBuffer, ReadContiguousData};
|
||||
use nym_socks5_requests::{ConnectionId, SocketData};
|
||||
use nym_ordered_buffer::{OrderedMessage, OrderedMessageBuffer, ReadContiguousData};
|
||||
use nym_socks5_requests::{ConnectionId, NetworkData, SendRequest};
|
||||
use nym_task::connections::{ConnectionCommand, ConnectionCommandSender};
|
||||
use nym_task::TaskClient;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@@ -40,13 +40,29 @@ pub enum ControllerCommand {
|
||||
connection_id: ConnectionId,
|
||||
},
|
||||
Send {
|
||||
data: SocketData,
|
||||
connection_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
is_closed: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl ControllerCommand {
|
||||
pub fn new_send(data: SocketData) -> Self {
|
||||
ControllerCommand::Send { data }
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,13 +74,18 @@ struct ActiveConnection {
|
||||
}
|
||||
|
||||
impl ActiveConnection {
|
||||
fn write_to_buf(&mut self, seq: u64, payload: Vec<u8>, is_closed: bool) {
|
||||
fn write_to_buf(&mut self, payload: Vec<u8>, is_closed: bool) {
|
||||
let ordered_message = match OrderedMessage::try_from_bytes(payload) {
|
||||
Ok(msg) => msg,
|
||||
Err(err) => {
|
||||
error!("Malformed ordered message - {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
if is_closed {
|
||||
self.closed_at_index = Some(seq);
|
||||
}
|
||||
if let Err(err) = self.ordered_buffer.write(seq, payload) {
|
||||
error!("failed to write to the buffer: {err}")
|
||||
self.closed_at_index = Some(ordered_message.index);
|
||||
}
|
||||
self.ordered_buffer.write(ordered_message);
|
||||
}
|
||||
|
||||
fn read_from_buf(&mut self) -> Option<ReadContiguousData> {
|
||||
@@ -96,7 +117,7 @@ pub struct Controller {
|
||||
|
||||
// buffer for messages received before connection was established due to mixnet being able to
|
||||
// un-order messages. Note we don't ever expect to have more than 1-2 messages per connection here
|
||||
pending_messages: HashMap<ConnectionId, Vec<SocketData>>,
|
||||
pending_messages: HashMap<ConnectionId, Vec<(Vec<u8>, bool)>>,
|
||||
|
||||
shutdown: TaskClient,
|
||||
}
|
||||
@@ -133,8 +154,8 @@ impl Controller {
|
||||
// check if there were any pending messages
|
||||
if let Some(pending) = self.pending_messages.remove(&conn_id) {
|
||||
debug!("There were some pending messages for {}", conn_id);
|
||||
for data in pending {
|
||||
self.send_to_connection(data)
|
||||
for (payload, is_closed) in pending {
|
||||
self.send_to_connection(conn_id, payload, is_closed)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,25 +184,20 @@ impl Controller {
|
||||
}
|
||||
}
|
||||
|
||||
fn send_to_connection(&mut self, message: SocketData) {
|
||||
let hdr = message.header;
|
||||
if let Some(active_connection) = self.active_connections.get_mut(&hdr.connection_id) {
|
||||
// always write to the buffer even if payload is empty (because it could have been the keep-alive message)
|
||||
active_connection.write_to_buf(hdr.seq, message.data, hdr.local_socket_closed);
|
||||
fn send_to_connection(&mut self, conn_id: ConnectionId, payload: Vec<u8>, is_closed: bool) {
|
||||
if let Some(active_connection) = self.active_connections.get_mut(&conn_id) {
|
||||
if !payload.is_empty() {
|
||||
active_connection.write_to_buf(payload, is_closed);
|
||||
} else if !is_closed {
|
||||
error!("Tried to write an empty message to a not-closing connection. Please let us know if you see this message");
|
||||
}
|
||||
|
||||
if let Some(payload) = active_connection.read_from_buf() {
|
||||
if let Some(closed_at_index) = active_connection.closed_at_index {
|
||||
if payload.last_sequence > closed_at_index {
|
||||
if payload.last_index > closed_at_index {
|
||||
active_connection.is_closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// however, don't send empty payload to the actual connection if it's not a close message
|
||||
// TODO: or should we?
|
||||
if payload.data.is_empty() && !active_connection.is_closed {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = active_connection
|
||||
.connection_sender
|
||||
.as_mut()
|
||||
@@ -191,26 +207,34 @@ impl Controller {
|
||||
socket_closed: active_connection.is_closed,
|
||||
})
|
||||
{
|
||||
error!("failed to send on the active connection channel: {err}");
|
||||
error!("WTF IS THIS: {err}");
|
||||
}
|
||||
|
||||
// TODO: ABOVE UNWRAP CAUSED A CRASH IN A NORMAL USE!!!!
|
||||
// TODO:
|
||||
// TODO: surprisingly it only happened on socks client, never on nSP
|
||||
// TODO:
|
||||
// TODO:
|
||||
// TODO:
|
||||
// TODO:
|
||||
}
|
||||
} else if !self.recently_closed.contains(&hdr.connection_id) {
|
||||
} else if !self.recently_closed.contains(&conn_id) {
|
||||
debug!("Received a 'Send' before 'Connect' - going to buffer the data");
|
||||
let pending = self
|
||||
.pending_messages
|
||||
.entry(hdr.connection_id)
|
||||
.entry(conn_id)
|
||||
.or_insert_with(Vec::new);
|
||||
pending.push(message);
|
||||
} else if !hdr.local_socket_closed {
|
||||
pending.push((payload, is_closed));
|
||||
} else if !is_closed {
|
||||
error!(
|
||||
"Tried to write to closed connection {} ({} bytes were 'lost)",
|
||||
hdr.connection_id,
|
||||
message.data.len()
|
||||
conn_id,
|
||||
payload.len()
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
"Tried to write to closed connection {}, but remote is already closed",
|
||||
hdr.connection_id
|
||||
conn_id
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -219,8 +243,8 @@ impl Controller {
|
||||
loop {
|
||||
tokio::select! {
|
||||
command = self.receiver.next() => match command {
|
||||
Some(ControllerCommand::Send{data}) => {
|
||||
self.send_to_connection(data)
|
||||
Some(ControllerCommand::Send{connection_id, data, is_closed}) => {
|
||||
self.send_to_connection(connection_id, data, is_closed)
|
||||
}
|
||||
Some(ControllerCommand::Insert{connection_id, connection_sender}) => {
|
||||
self.insert_connection(connection_id, connection_sender)
|
||||
|
||||
@@ -3,5 +3,4 @@
|
||||
|
||||
pub mod available_reader;
|
||||
pub mod connection_controller;
|
||||
pub mod ordered_sender;
|
||||
pub mod proxy_runner;
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::proxy_runner::MixProxySender;
|
||||
use bytes::Bytes;
|
||||
use log::{debug, error};
|
||||
use nym_socks5_requests::{ConnectionId, SocketData};
|
||||
use std::io;
|
||||
|
||||
pub(crate) struct OrderedMessageSender<F, S> {
|
||||
connection_id: ConnectionId,
|
||||
// addresses are provided for better logging
|
||||
local_destination_address: String,
|
||||
remote_source_address: String,
|
||||
mixnet_sender: MixProxySender<S>,
|
||||
|
||||
next_message_seq: u64,
|
||||
mix_message_adapter: F,
|
||||
}
|
||||
|
||||
impl<F, S> OrderedMessageSender<F, S>
|
||||
where
|
||||
F: Fn(SocketData) -> S,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
local_destination_address: String,
|
||||
remote_source_address: String,
|
||||
connection_id: ConnectionId,
|
||||
mixnet_sender: MixProxySender<S>,
|
||||
mix_message_adapter: F,
|
||||
) -> Self {
|
||||
OrderedMessageSender {
|
||||
local_destination_address,
|
||||
remote_source_address,
|
||||
connection_id,
|
||||
mixnet_sender,
|
||||
next_message_seq: 0,
|
||||
mix_message_adapter,
|
||||
}
|
||||
}
|
||||
|
||||
fn sequence(&mut self) -> u64 {
|
||||
let next = self.next_message_seq;
|
||||
self.next_message_seq += 1;
|
||||
next
|
||||
}
|
||||
|
||||
fn construct_message(&mut self, data: Vec<u8>, local_socket_closed: bool) -> S {
|
||||
let data = SocketData::new(
|
||||
self.sequence(),
|
||||
self.connection_id,
|
||||
local_socket_closed,
|
||||
data,
|
||||
);
|
||||
(self.mix_message_adapter)(data)
|
||||
}
|
||||
|
||||
async fn send_message(&self, message: S) {
|
||||
if self.mixnet_sender.send(message).await.is_err() {
|
||||
panic!("BatchRealMessageReceiver has stopped receiving!")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn send_empty_close(&mut self) {
|
||||
let message = self.construct_message(Vec::new(), true);
|
||||
self.send_message(message).await
|
||||
}
|
||||
|
||||
pub(crate) async fn send_empty_keepalive(&mut self) {
|
||||
log::trace!("Sending keepalive for connection: {}", self.connection_id);
|
||||
let message = self.construct_message(Vec::new(), false);
|
||||
self.send_message(message).await
|
||||
}
|
||||
|
||||
pub(crate) fn process_data(&self, read_data: Option<io::Result<Bytes>>) -> ProcessedData {
|
||||
let (read_data, is_finished) = match read_data {
|
||||
Some(data) => match data {
|
||||
Ok(data) => (data, false),
|
||||
Err(err) => {
|
||||
error!(target: &*format!("({}) socks5 inbound", self.connection_id), "failed to read request from the socket - {err}");
|
||||
(Default::default(), true)
|
||||
}
|
||||
},
|
||||
None => (Default::default(), true),
|
||||
};
|
||||
|
||||
ProcessedData {
|
||||
data: read_data,
|
||||
is_done: is_finished,
|
||||
}
|
||||
}
|
||||
|
||||
fn log_sent_message(&self, data: &ProcessedData) {
|
||||
debug!(
|
||||
target: &*format!("({}) socks5 inbound", self.connection_id),
|
||||
"[{} bytes]\t{} → local → mixnet → remote → {}. Local closed: {}",
|
||||
data.data.len(),
|
||||
self.local_destination_address,
|
||||
self.remote_source_address,
|
||||
data.is_done
|
||||
);
|
||||
}
|
||||
|
||||
/// Send data read from local socket into the mixnet
|
||||
pub(crate) async fn send_data(&mut self, data: ProcessedData) {
|
||||
self.log_sent_message(&data);
|
||||
let message = self.construct_message(data.data.into(), data.is_done);
|
||||
self.send_message(message).await;
|
||||
}
|
||||
}
|
||||
|
||||
// helper wrapper to keep track of field meanings
|
||||
pub(crate) struct ProcessedData {
|
||||
data: Bytes,
|
||||
pub(crate) is_done: bool,
|
||||
}
|
||||
@@ -1,22 +1,106 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::MixProxySender;
|
||||
use super::SHUTDOWN_TIMEOUT;
|
||||
use crate::available_reader::AvailableReader;
|
||||
use crate::ordered_sender::OrderedMessageSender;
|
||||
use crate::proxy_runner::KEEPALIVE_INTERVAL;
|
||||
use bytes::Bytes;
|
||||
use futures::FutureExt;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use nym_socks5_requests::{ConnectionId, SocketData};
|
||||
use nym_ordered_buffer::OrderedMessageSender;
|
||||
use nym_socks5_requests::ConnectionId;
|
||||
use nym_task::connections::LaneQueueLengths;
|
||||
use nym_task::connections::TransmissionLane;
|
||||
use nym_task::TaskClient;
|
||||
use std::sync::Arc;
|
||||
use std::fmt::Debug;
|
||||
use std::time::Duration;
|
||||
use std::{io, sync::Arc};
|
||||
use tokio::select;
|
||||
use tokio::{net::tcp::OwnedReadHalf, sync::Notify, time::sleep};
|
||||
|
||||
async fn send_empty_close<F, S>(
|
||||
connection_id: ConnectionId,
|
||||
message_sender: &mut OrderedMessageSender,
|
||||
mix_sender: &MixProxySender<S>,
|
||||
adapter_fn: F,
|
||||
) where
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
|
||||
S: Debug,
|
||||
{
|
||||
let ordered_msg = message_sender.wrap_message(Vec::new()).into_bytes();
|
||||
mix_sender
|
||||
.send(adapter_fn(connection_id, ordered_msg, true))
|
||||
.await
|
||||
.expect("BatchRealMessageReceiver has stopped receiving!");
|
||||
}
|
||||
|
||||
async fn send_empty_keepalive<F, S>(
|
||||
connection_id: ConnectionId,
|
||||
message_sender: &mut OrderedMessageSender,
|
||||
mix_sender: &MixProxySender<S>,
|
||||
adapter_fn: F,
|
||||
) where
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
|
||||
S: Debug,
|
||||
{
|
||||
log::trace!("Sending keepalive for connection: {connection_id}");
|
||||
let ordered_msg = message_sender.wrap_message(Vec::new()).into_bytes();
|
||||
mix_sender
|
||||
.send(adapter_fn(connection_id, ordered_msg, false))
|
||||
.await
|
||||
.expect("BatchRealMessageReceiver has stopped receiving!");
|
||||
}
|
||||
|
||||
async fn deal_with_data<F, S>(
|
||||
read_data: Option<io::Result<Bytes>>,
|
||||
local_destination_address: &str,
|
||||
remote_source_address: &str,
|
||||
connection_id: ConnectionId,
|
||||
message_sender: &mut OrderedMessageSender,
|
||||
mix_sender: &MixProxySender<S>,
|
||||
adapter_fn: F,
|
||||
) -> bool
|
||||
where
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S,
|
||||
S: Debug,
|
||||
{
|
||||
let (read_data, is_finished) = match read_data {
|
||||
Some(data) => match data {
|
||||
Ok(data) => (data, false),
|
||||
Err(err) => {
|
||||
error!(target: &*format!("({connection_id}) socks5 inbound"), "failed to read request from the socket - {err}");
|
||||
(Default::default(), true)
|
||||
}
|
||||
},
|
||||
None => (Default::default(), true),
|
||||
};
|
||||
|
||||
debug!(
|
||||
target: &*format!("({connection_id}) socks5 inbound"),
|
||||
"[{} bytes]\t{} → local → mixnet → remote → {}. Local closed: {}",
|
||||
read_data.len(),
|
||||
local_destination_address,
|
||||
remote_source_address,
|
||||
is_finished
|
||||
);
|
||||
|
||||
// if we're sending through the mixnet increase the sequence number...
|
||||
let ordered_msg = message_sender.wrap_message(read_data.to_vec()).into_bytes();
|
||||
log::trace!(
|
||||
"pushing data down the input sender: size: {}",
|
||||
ordered_msg.len()
|
||||
);
|
||||
|
||||
mix_sender
|
||||
.send(adapter_fn(connection_id, ordered_msg, is_finished))
|
||||
.await
|
||||
.expect("InputMessageReceiver has stopped receiving!");
|
||||
|
||||
is_finished
|
||||
}
|
||||
|
||||
async fn wait_until_lane_empty(lane_queue_lengths: &Option<LaneQueueLengths>, connection_id: u64) {
|
||||
if let Some(lane_queue_lengths) = lane_queue_lengths {
|
||||
if tokio::time::timeout(
|
||||
@@ -74,21 +158,27 @@ async fn wait_for_lane(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) async fn run_inbound<F, S>(
|
||||
mut reader: OwnedReadHalf,
|
||||
mut message_sender: OrderedMessageSender<F, S>,
|
||||
local_destination_address: String, // addresses are provided for better logging
|
||||
remote_source_address: String,
|
||||
connection_id: ConnectionId,
|
||||
mix_sender: MixProxySender<S>,
|
||||
available_plaintext_per_mix_packet: usize,
|
||||
adapter_fn: F,
|
||||
shutdown_notify: Arc<Notify>,
|
||||
lane_queue_lengths: Option<LaneQueueLengths>,
|
||||
mut shutdown_listener: TaskClient,
|
||||
) -> OwnedReadHalf
|
||||
where
|
||||
F: Fn(SocketData) -> S + Send + 'static,
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S + Send + 'static,
|
||||
S: Debug,
|
||||
{
|
||||
// TODO: this multiplication by 4 is completely arbitrary here
|
||||
let mut available_reader =
|
||||
AvailableReader::new(&mut reader, Some(available_plaintext_per_mix_packet * 4));
|
||||
let mut message_sender = OrderedMessageSender::new();
|
||||
|
||||
// Shutdown if outbound signled to shutdown
|
||||
let shutdown_future = shutdown_notify.notified().then(|_| sleep(SHUTDOWN_TIMEOUT));
|
||||
@@ -127,7 +217,7 @@ where
|
||||
);
|
||||
// inform remote just in case it was closed because of lack of heartbeat.
|
||||
// worst case the remote will just have couple of false negatives
|
||||
message_sender.send_empty_close().await;
|
||||
send_empty_close(connection_id, &mut message_sender, &mix_sender, &adapter_fn).await;
|
||||
break;
|
||||
}
|
||||
_ = shutdown_listener.recv() => {
|
||||
@@ -143,7 +233,7 @@ where
|
||||
break;
|
||||
}
|
||||
_ = keepalive_timer.tick() => {
|
||||
message_sender.send_empty_keepalive().await;
|
||||
send_empty_keepalive(connection_id, &mut message_sender, &mix_sender, &adapter_fn).await;
|
||||
}
|
||||
// Read the next data when there is space in the lane.
|
||||
// The purpose of chaining the wait here is that it makes sure we can cancel the
|
||||
@@ -151,12 +241,15 @@ where
|
||||
read_data = wait_until_lane_almost_empty(&lane_queue_lengths, connection_id)
|
||||
.then(|_| available_reader.next()), if !we_are_closed =>
|
||||
{
|
||||
let processed = message_sender.process_data(read_data);
|
||||
let is_done = processed.is_done;
|
||||
|
||||
message_sender.send_data(processed).await;
|
||||
|
||||
if is_done {
|
||||
if deal_with_data(
|
||||
read_data,
|
||||
&local_destination_address,
|
||||
&remote_source_address,
|
||||
connection_id,
|
||||
&mut message_sender,
|
||||
&mix_sender,
|
||||
&adapter_fn,
|
||||
).await {
|
||||
// After reading the last data, notify the closing_future to wait until the
|
||||
// lane is clear before exiting.
|
||||
// We don't wait here since we want to be able to cancel the wait on close or
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::connection_controller::ConnectionReceiver;
|
||||
use crate::ordered_sender::OrderedMessageSender;
|
||||
use nym_socks5_requests::{ConnectionId, SocketData};
|
||||
use nym_socks5_requests::ConnectionId;
|
||||
use nym_task::connections::LaneQueueLengths;
|
||||
use nym_task::TaskClient;
|
||||
use std::fmt::Debug;
|
||||
@@ -14,7 +13,7 @@ mod inbound;
|
||||
mod outbound;
|
||||
|
||||
// TODO: make this configurable
|
||||
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(3);
|
||||
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
// Send empty keepalive messages regurarly to keep the connection alive. This should be smaller
|
||||
// than [`MIX_TTL`].
|
||||
@@ -93,24 +92,20 @@ where
|
||||
// request/response as required by entity running particular side of the proxy.
|
||||
pub async fn run<F>(mut self, adapter_fn: F) -> Self
|
||||
where
|
||||
F: Fn(SocketData) -> S + Send + Sync + 'static,
|
||||
F: Fn(ConnectionId, Vec<u8>, bool) -> S + Send + Sync + 'static,
|
||||
{
|
||||
let (read_half, write_half) = self.socket.take().unwrap().into_split();
|
||||
let shutdown_notify = Arc::new(Notify::new());
|
||||
|
||||
// should run until either inbound closes or is notified from outbound
|
||||
let ordered_sender = OrderedMessageSender::new(
|
||||
let inbound_future = inbound::run_inbound(
|
||||
read_half,
|
||||
self.local_destination_address.clone(),
|
||||
self.remote_source_address.clone(),
|
||||
self.connection_id,
|
||||
self.mix_sender.clone(),
|
||||
adapter_fn,
|
||||
);
|
||||
let inbound_future = inbound::run_inbound(
|
||||
read_half,
|
||||
ordered_sender,
|
||||
self.connection_id,
|
||||
self.available_plaintext_per_mix_packet,
|
||||
adapter_fn,
|
||||
Arc::clone(&shutdown_notify),
|
||||
self.lane_queue_lengths.clone(),
|
||||
self.shutdown_listener.clone(),
|
||||
|
||||
@@ -7,11 +7,9 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.3"
|
||||
log = { workspace = true }
|
||||
nym-service-providers-common = { path = "../../../service-providers/common" }
|
||||
nym-sphinx-addressing = { path = "../../../common/nymsphinx/addressing" }
|
||||
thiserror = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
tap = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-sphinx-addressing = { path = "../../../common/nymsphinx/addressing" }
|
||||
nym-service-providers-common = { path = "../../../service-providers/common" }
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
use nym_service_providers_common::interface;
|
||||
use nym_service_providers_common::interface::ServiceProviderMessagingError;
|
||||
use std::mem;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use request::*;
|
||||
@@ -17,185 +16,6 @@ pub mod version;
|
||||
pub type Socks5ProviderRequest = interface::Request<Socks5Request>;
|
||||
pub type Socks5ProviderResponse = interface::Response<Socks5Request>;
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
#[error(
|
||||
"didn't receive enough data to recover socket data. got {received}, but expected at least {expected}"
|
||||
)]
|
||||
pub struct InsufficientSocketDataError {
|
||||
received: usize,
|
||||
expected: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct SocketDataHeader {
|
||||
pub seq: u64,
|
||||
pub connection_id: ConnectionId,
|
||||
pub local_socket_closed: bool,
|
||||
}
|
||||
|
||||
impl SocketDataHeader {
|
||||
const SERIALIZED_LEN: usize = mem::size_of::<ConnectionId>() + 1 + mem::size_of::<u64>();
|
||||
|
||||
// we need to have two serialization methods for backwards compatibility,
|
||||
// since we serialized those fields differently depending on whether it was ingress vs egress...
|
||||
|
||||
pub fn try_from_request_bytes(
|
||||
b: &[u8],
|
||||
) -> Result<SocketDataHeader, InsufficientSocketDataError> {
|
||||
if b.len() != Self::SERIALIZED_LEN {
|
||||
return Err(InsufficientSocketDataError {
|
||||
received: b.len(),
|
||||
expected: Self::SERIALIZED_LEN,
|
||||
});
|
||||
}
|
||||
|
||||
// the unwraps here are fine as we just ensured we have the exact amount of bytes we need
|
||||
let connection_id = ConnectionId::from_be_bytes(b[0..8].try_into().unwrap());
|
||||
let local_socket_closed = b[8] != 0;
|
||||
let seq = u64::from_be_bytes(b[9..].try_into().unwrap());
|
||||
|
||||
Ok(SocketDataHeader {
|
||||
seq,
|
||||
connection_id,
|
||||
local_socket_closed,
|
||||
})
|
||||
}
|
||||
|
||||
// the serialization of the header looks as follows:
|
||||
// (it's vital it's not modified as we need this exact structure for backwards compatibility)
|
||||
// CONNECTION_ID (8B) || SOCKET_CLOSED (1B) || SEQUENCE (8B)
|
||||
pub fn into_request_bytes(self) -> Vec<u8> {
|
||||
self.into_request_bytes_iter().collect()
|
||||
}
|
||||
|
||||
pub fn into_request_bytes_iter(self) -> impl Iterator<Item = u8> {
|
||||
self.connection_id
|
||||
.to_be_bytes()
|
||||
.into_iter()
|
||||
.chain(std::iter::once(self.local_socket_closed as u8))
|
||||
.chain(self.seq.to_be_bytes().into_iter())
|
||||
}
|
||||
|
||||
pub fn try_from_response_bytes(
|
||||
b: &[u8],
|
||||
) -> Result<SocketDataHeader, InsufficientSocketDataError> {
|
||||
if b.len() != Self::SERIALIZED_LEN {
|
||||
return Err(InsufficientSocketDataError {
|
||||
received: b.len(),
|
||||
expected: Self::SERIALIZED_LEN,
|
||||
});
|
||||
}
|
||||
|
||||
// the unwraps here are fine as we just ensured we have the exact amount of bytes we need
|
||||
let local_socket_closed = b[0] != 0;
|
||||
let connection_id = ConnectionId::from_be_bytes(b[1..9].try_into().unwrap());
|
||||
let seq = u64::from_be_bytes(b[9..].try_into().unwrap());
|
||||
|
||||
Ok(SocketDataHeader {
|
||||
seq,
|
||||
connection_id,
|
||||
local_socket_closed,
|
||||
})
|
||||
}
|
||||
|
||||
// the serialization of the header looks as follows:
|
||||
// (it's vital it's not modified as we need this exact structure for backwards compatibility)
|
||||
// SOCKET_CLOSED (1B) || CONNECTION_ID (8B) || SEQUENCE (8B)
|
||||
pub fn into_response_bytes(self) -> Vec<u8> {
|
||||
self.into_response_bytes_iter().collect()
|
||||
}
|
||||
|
||||
pub fn into_response_bytes_iter(self) -> impl Iterator<Item = u8> {
|
||||
std::iter::once(self.local_socket_closed as u8)
|
||||
.chain(self.connection_id.to_be_bytes().into_iter())
|
||||
.chain(self.seq.to_be_bytes().into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SocketData {
|
||||
pub header: SocketDataHeader,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl SocketData {
|
||||
pub fn new(
|
||||
seq: u64,
|
||||
connection_id: ConnectionId,
|
||||
local_socket_closed: bool,
|
||||
data: Vec<u8>,
|
||||
) -> Self {
|
||||
SocketData {
|
||||
header: SocketDataHeader {
|
||||
seq,
|
||||
connection_id,
|
||||
local_socket_closed,
|
||||
},
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_deserialization_len(b: &[u8]) -> Result<(), InsufficientSocketDataError> {
|
||||
if b.is_empty() {
|
||||
return Err(InsufficientSocketDataError {
|
||||
received: 0,
|
||||
expected: SocketDataHeader::SERIALIZED_LEN,
|
||||
});
|
||||
}
|
||||
|
||||
if b.len() < SocketDataHeader::SERIALIZED_LEN {
|
||||
return Err(InsufficientSocketDataError {
|
||||
received: b.len(),
|
||||
expected: SocketDataHeader::SERIALIZED_LEN,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// we need to have two serialization methods for backwards compatibility,
|
||||
// since we serialized those fields differently depending on whether it was ingress vs egress...
|
||||
pub fn try_from_request_bytes(b: &[u8]) -> Result<SocketData, InsufficientSocketDataError> {
|
||||
Self::verify_deserialization_len(b)?;
|
||||
let header =
|
||||
SocketDataHeader::try_from_request_bytes(&b[..SocketDataHeader::SERIALIZED_LEN])?;
|
||||
let data = b[SocketDataHeader::SERIALIZED_LEN..].to_vec();
|
||||
|
||||
Ok(SocketData { header, data })
|
||||
}
|
||||
|
||||
// the serialization of the socket data looks as follows:
|
||||
// HEADER || DATA
|
||||
pub fn into_request_bytes(self) -> Vec<u8> {
|
||||
self.into_request_bytes_iter().collect()
|
||||
}
|
||||
|
||||
pub fn into_request_bytes_iter(self) -> impl Iterator<Item = u8> {
|
||||
self.header
|
||||
.into_request_bytes_iter()
|
||||
.chain(self.data.into_iter())
|
||||
}
|
||||
|
||||
pub fn try_from_response_bytes(b: &[u8]) -> Result<SocketData, InsufficientSocketDataError> {
|
||||
Self::verify_deserialization_len(b)?;
|
||||
|
||||
let header =
|
||||
SocketDataHeader::try_from_response_bytes(&b[..SocketDataHeader::SERIALIZED_LEN])?;
|
||||
let data = b[SocketDataHeader::SERIALIZED_LEN..].to_vec();
|
||||
|
||||
Ok(SocketData { header, data })
|
||||
}
|
||||
|
||||
pub fn into_response_bytes(self) -> Vec<u8> {
|
||||
self.into_response_bytes_iter().collect()
|
||||
}
|
||||
|
||||
pub fn into_response_bytes_iter(self) -> impl Iterator<Item = u8> {
|
||||
self.header
|
||||
.into_response_bytes_iter()
|
||||
.chain(self.data.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Socks5RequestError {
|
||||
#[error("failed to deserialize received request: {source}")]
|
||||
@@ -212,18 +32,6 @@ pub enum Socks5RequestError {
|
||||
|
||||
#[error(transparent)]
|
||||
ProviderInterfaceError(#[from] ServiceProviderMessagingError),
|
||||
|
||||
#[error("received unsupported request protocol version: {protocol_version}")]
|
||||
UnsupportedProtocolVersion {
|
||||
protocol_version: <Socks5Request as interface::ServiceProviderRequest>::ProtocolVersion,
|
||||
},
|
||||
}
|
||||
|
||||
fn make_bincode_serializer() -> impl bincode::Options {
|
||||
use bincode::Options;
|
||||
bincode::DefaultOptions::new()
|
||||
.with_big_endian()
|
||||
.with_varint_encoding()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -231,103 +39,6 @@ mod tests {
|
||||
use super::*;
|
||||
use nym_service_providers_common::interface::RequestContent;
|
||||
|
||||
#[cfg(test)]
|
||||
mod socket_data_serialization {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn for_requests() {
|
||||
assert_eq!(
|
||||
InsufficientSocketDataError {
|
||||
received: 0,
|
||||
expected: SocketDataHeader::SERIALIZED_LEN
|
||||
},
|
||||
SocketData::try_from_request_bytes(&[]).unwrap_err()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
InsufficientSocketDataError {
|
||||
received: 10,
|
||||
expected: SocketDataHeader::SERIALIZED_LEN
|
||||
},
|
||||
SocketData::try_from_request_bytes(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).unwrap_err()
|
||||
);
|
||||
|
||||
let good_data = SocketData::new(42, 12345, false, vec![2, 3]);
|
||||
let serialized = good_data.clone().into_request_bytes();
|
||||
|
||||
assert_eq!(
|
||||
good_data,
|
||||
SocketData::try_from_request_bytes(&serialized).unwrap()
|
||||
);
|
||||
assert_ne!(
|
||||
good_data,
|
||||
SocketData::try_from_response_bytes(&serialized).unwrap()
|
||||
);
|
||||
|
||||
let raw_bytes = [
|
||||
6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 1, 2, 3, 4, 5, 6, 7, 255, 255, 255,
|
||||
];
|
||||
assert_eq!(
|
||||
SocketData {
|
||||
header: SocketDataHeader {
|
||||
seq: u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
|
||||
connection_id: ConnectionId::from_be_bytes([6, 6, 6, 6, 6, 6, 6, 6]),
|
||||
local_socket_closed: false,
|
||||
},
|
||||
data: vec![255, 255, 255],
|
||||
},
|
||||
SocketData::try_from_request_bytes(&raw_bytes).unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_responses() {
|
||||
assert_eq!(
|
||||
InsufficientSocketDataError {
|
||||
received: 0,
|
||||
expected: SocketDataHeader::SERIALIZED_LEN
|
||||
},
|
||||
SocketData::try_from_response_bytes(&[]).unwrap_err()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
InsufficientSocketDataError {
|
||||
received: 10,
|
||||
expected: SocketDataHeader::SERIALIZED_LEN
|
||||
},
|
||||
SocketData::try_from_response_bytes(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]).unwrap_err()
|
||||
);
|
||||
|
||||
let good_data = SocketData::new(42, 12345, false, vec![2, 3]);
|
||||
let serialized = good_data.clone().into_response_bytes();
|
||||
|
||||
assert_eq!(
|
||||
good_data,
|
||||
SocketData::try_from_response_bytes(&serialized).unwrap()
|
||||
);
|
||||
assert_ne!(
|
||||
good_data,
|
||||
SocketData::try_from_request_bytes(&serialized).unwrap()
|
||||
);
|
||||
|
||||
let raw_bytes = [
|
||||
0, 6, 6, 6, 6, 6, 6, 6, 6, 0, 1, 2, 3, 4, 5, 6, 7, 255, 255, 255,
|
||||
];
|
||||
assert_eq!(
|
||||
SocketData {
|
||||
header: SocketDataHeader {
|
||||
seq: u64::from_be_bytes([0, 1, 2, 3, 4, 5, 6, 7]),
|
||||
connection_id: ConnectionId::from_be_bytes([6, 6, 6, 6, 6, 6, 6, 6]),
|
||||
local_socket_closed: false,
|
||||
},
|
||||
data: vec![255, 255, 255],
|
||||
},
|
||||
SocketData::try_from_response_bytes(&raw_bytes).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod interface_backwards_compatibility {
|
||||
use super::*;
|
||||
@@ -376,10 +87,9 @@ mod tests {
|
||||
match new_deserialized.content {
|
||||
RequestContent::ProviderData(req) => match req.content {
|
||||
Socks5RequestContent::Send(send_req) => {
|
||||
assert_eq!(send_req.data.header.connection_id, 7810961472501196273);
|
||||
assert_eq!(send_req.data.header.seq, 0);
|
||||
assert_eq!(send_req.data.data.len(), 103);
|
||||
assert!(!send_req.data.header.local_socket_closed);
|
||||
assert_eq!(send_req.conn_id, 7810961472501196273);
|
||||
assert_eq!(send_req.data.len(), 111);
|
||||
assert!(!send_req.local_closed);
|
||||
}
|
||||
_ => panic!("unexpected request"),
|
||||
},
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{
|
||||
make_bincode_serializer, InsufficientSocketDataError, SocketData, Socks5ProtocolVersion,
|
||||
Socks5RequestError, Socks5Response,
|
||||
};
|
||||
use crate::{Socks5ProtocolVersion, Socks5RequestError, Socks5Response};
|
||||
use nym_service_providers_common::interface::{Serializable, ServiceProviderRequest};
|
||||
use nym_sphinx_addressing::clients::{Recipient, RecipientFormattingError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
use tap::TapFallible;
|
||||
use thiserror::Error;
|
||||
|
||||
pub type ConnectionId = u64;
|
||||
@@ -20,7 +15,6 @@ pub type RemoteAddress = String;
|
||||
pub enum RequestFlag {
|
||||
Connect = 0,
|
||||
Send = 1,
|
||||
Query = 2,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for RequestFlag {
|
||||
@@ -30,7 +24,6 @@ impl TryFrom<u8> for RequestFlag {
|
||||
match value {
|
||||
_ if value == (RequestFlag::Connect as u8) => Ok(Self::Connect),
|
||||
_ if value == (RequestFlag::Send as u8) => Ok(Self::Send),
|
||||
_ if value == (RequestFlag::Query as u8) => Ok(Self::Query),
|
||||
value => Err(RequestDeserializationError::UnknownRequestFlag { value }),
|
||||
}
|
||||
}
|
||||
@@ -58,15 +51,6 @@ pub enum RequestDeserializationError {
|
||||
|
||||
#[error("malformed return address - {0}")]
|
||||
MalformedReturnAddress(RecipientFormattingError),
|
||||
|
||||
#[error("failed to deserialize query request: {source}")]
|
||||
QueryDeserializationError {
|
||||
#[from]
|
||||
source: bincode::Error,
|
||||
},
|
||||
|
||||
#[error(transparent)]
|
||||
InvalidSocketData(#[from] InsufficientSocketDataError),
|
||||
}
|
||||
|
||||
impl RequestDeserializationError {
|
||||
@@ -75,7 +59,7 @@ impl RequestDeserializationError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConnectRequest {
|
||||
// TODO: is connection_id redundant now?
|
||||
pub conn_id: ConnectionId,
|
||||
@@ -83,15 +67,11 @@ pub struct ConnectRequest {
|
||||
pub return_address: Option<Recipient>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SendRequest {
|
||||
pub data: SocketData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum QueryRequest {
|
||||
OpenProxy,
|
||||
Description,
|
||||
pub conn_id: ConnectionId,
|
||||
pub data: Vec<u8>,
|
||||
pub local_closed: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -128,13 +108,6 @@ impl Serializable for Socks5Request {
|
||||
}
|
||||
|
||||
let protocol_version = Socks5ProtocolVersion::from(b[0]);
|
||||
if protocol_version > Self::max_supported_version() {
|
||||
return Err(Socks5RequestError::UnsupportedProtocolVersion { protocol_version });
|
||||
}
|
||||
|
||||
// TODO: handle the case then protocol version if less then the current one. Then we should
|
||||
// make sure to only respond with the same version
|
||||
|
||||
Ok(Socks5Request {
|
||||
protocol_version,
|
||||
content: Socks5RequestContent::try_from_bytes(&b[1..])?,
|
||||
@@ -182,27 +155,22 @@ impl Socks5Request {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_send(protocol_version: Socks5ProtocolVersion, data: SocketData) -> Socks5Request {
|
||||
Socks5Request {
|
||||
protocol_version,
|
||||
content: Socks5RequestContent::new_send(data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_query(
|
||||
pub fn new_send(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
query: QueryRequest,
|
||||
conn_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
local_closed: bool,
|
||||
) -> Socks5Request {
|
||||
Socks5Request {
|
||||
protocol_version,
|
||||
content: Socks5RequestContent::Query(query),
|
||||
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, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Socks5RequestContent {
|
||||
/// Start a new TCP connection to the specified `RemoteAddress` and send
|
||||
/// the request data up the connection.
|
||||
@@ -211,8 +179,6 @@ pub enum Socks5RequestContent {
|
||||
|
||||
/// Re-use an existing TCP connection, sending more request data up it.
|
||||
Send(SendRequest),
|
||||
|
||||
Query(QueryRequest),
|
||||
}
|
||||
|
||||
impl Socks5RequestContent {
|
||||
@@ -230,8 +196,16 @@ impl Socks5RequestContent {
|
||||
}
|
||||
|
||||
/// Construct a new Request::Send instance
|
||||
pub fn new_send(data: SocketData) -> Socks5RequestContent {
|
||||
Socks5RequestContent::Send(SendRequest { data })
|
||||
pub fn new_send(
|
||||
conn_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
local_closed: bool,
|
||||
) -> Socks5RequestContent {
|
||||
Socks5RequestContent::Send(SendRequest {
|
||||
conn_id,
|
||||
data,
|
||||
local_closed,
|
||||
})
|
||||
}
|
||||
|
||||
/// Deserialize the request type, connection id, destination address and port,
|
||||
@@ -248,27 +222,18 @@ 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`).
|
||||
|
||||
// connect:
|
||||
// RequestFlag::Connect || CONN_ID || ADDR_LEN || ADDR || <RETURN_ADDR>
|
||||
//
|
||||
// send:
|
||||
// RequestFlag::Send || CONN_ID || LOCAL_CLOSED || DATA
|
||||
// where DATA: SEQ || TRUE_DATA
|
||||
|
||||
pub fn try_from_bytes(b: &[u8]) -> Result<Socks5RequestContent, RequestDeserializationError> {
|
||||
// each request needs to at least contain flag and ConnectionId
|
||||
if b.is_empty() {
|
||||
return Err(RequestDeserializationError::NoData);
|
||||
}
|
||||
|
||||
if b.len() < 9 {
|
||||
return Err(RequestDeserializationError::ConnectionIdTooShort);
|
||||
}
|
||||
let conn_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 => {
|
||||
if b.len() < 9 {
|
||||
return Err(RequestDeserializationError::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 connect_request_bytes = &b[9..];
|
||||
|
||||
// we need to be able to read at least 2 bytes that specify address length
|
||||
@@ -313,13 +278,15 @@ impl Socks5RequestContent {
|
||||
return_address,
|
||||
))
|
||||
}
|
||||
RequestFlag::Send => Ok(Socks5RequestContent::Send(SendRequest {
|
||||
data: SocketData::try_from_request_bytes(&b[1..])?,
|
||||
})),
|
||||
RequestFlag::Query => {
|
||||
use bincode::Options;
|
||||
let query = make_bincode_serializer().deserialize(&b[1..])?;
|
||||
Ok(Socks5RequestContent::Query(query))
|
||||
RequestFlag::Send => {
|
||||
let local_closed = b[9] != 0;
|
||||
let data = b[10..].to_vec();
|
||||
|
||||
Ok(Socks5RequestContent::Send(SendRequest {
|
||||
conn_id,
|
||||
data,
|
||||
local_closed,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -346,21 +313,10 @@ impl Socks5RequestContent {
|
||||
}
|
||||
}
|
||||
Socks5RequestContent::Send(req) => std::iter::once(RequestFlag::Send as u8)
|
||||
.chain(req.data.into_request_bytes_iter())
|
||||
.chain(req.conn_id.to_be_bytes().into_iter())
|
||||
.chain(std::iter::once(req.local_closed as u8))
|
||||
.chain(req.data.into_iter())
|
||||
.collect(),
|
||||
|
||||
Socks5RequestContent::Query(query) => {
|
||||
use bincode::Options;
|
||||
let query_bytes: Vec<u8> = make_bincode_serializer()
|
||||
.serialize(&query)
|
||||
.tap_err(|err| {
|
||||
log::error!("Failed to serialize query request: {:?}: {err}", query);
|
||||
})
|
||||
.unwrap_or_default();
|
||||
std::iter::once(RequestFlag::Query as u8)
|
||||
.chain(query_bytes.into_iter())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -603,38 +559,18 @@ mod request_deserialization_tests {
|
||||
|
||||
#[test]
|
||||
fn works_when_request_is_sized_properly_even_without_data() {
|
||||
// correct 8 bytes of connection_id, 1 byte of local_closed, 8 bytes of sequence and 0 bytes request data
|
||||
let request_bytes = [
|
||||
RequestFlag::Send as u8,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
]
|
||||
.to_vec();
|
||||
// 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();
|
||||
match request {
|
||||
Socks5RequestContent::Send(SendRequest { data }) => {
|
||||
assert_eq!(
|
||||
u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
|
||||
data.header.connection_id
|
||||
);
|
||||
assert!(!data.header.local_socket_closed);
|
||||
assert_eq!(1, data.header.seq);
|
||||
assert!(data.data.is_empty());
|
||||
Socks5RequestContent::Send(SendRequest {
|
||||
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)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -642,7 +578,7 @@ mod request_deserialization_tests {
|
||||
|
||||
#[test]
|
||||
fn works_when_request_is_sized_properly_and_has_data() {
|
||||
// correct 8 bytes of connection_id, 1 byte of local_closed, 8 bytes of sequence and 3 bytes request data (all 255)
|
||||
// correct 8 bytes of connection_id, 1 byte of local_closed and 3 bytes request data (all 255)
|
||||
let request_bytes = [
|
||||
RequestFlag::Send as u8,
|
||||
1,
|
||||
@@ -653,15 +589,7 @@ mod request_deserialization_tests {
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
@@ -670,39 +598,17 @@ mod request_deserialization_tests {
|
||||
|
||||
let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap();
|
||||
match request {
|
||||
Socks5RequestContent::Send(SendRequest { data }) => {
|
||||
assert_eq!(
|
||||
u64::from_be_bytes([1, 2, 3, 4, 5, 6, 7, 8]),
|
||||
data.header.connection_id
|
||||
);
|
||||
assert!(data.header.local_socket_closed);
|
||||
assert_eq!(1, data.header.seq);
|
||||
assert_eq!(vec![255, 255, 255], data.data);
|
||||
Socks5RequestContent::Send(SendRequest {
|
||||
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)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod serialize_query_request {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serialize_there_and_back() {
|
||||
let open_proxy = Socks5RequestContent::Query(QueryRequest::OpenProxy);
|
||||
let bytes_open_proxy = open_proxy.clone().into_bytes();
|
||||
assert_eq!(bytes_open_proxy, vec![2, 0]);
|
||||
|
||||
let description = Socks5RequestContent::Query(QueryRequest::Description);
|
||||
let bytes_description = description.clone().into_bytes();
|
||||
assert_eq!(bytes_description, vec![2, 1]);
|
||||
|
||||
let open_proxy2 = Socks5RequestContent::try_from_bytes(&bytes_open_proxy).unwrap();
|
||||
let description2 = Socks5RequestContent::try_from_bytes(&bytes_description).unwrap();
|
||||
|
||||
assert_eq!(open_proxy, open_proxy2);
|
||||
assert_eq!(description, description2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{
|
||||
make_bincode_serializer, ConnectionId, InsufficientSocketDataError, SocketData,
|
||||
Socks5ProtocolVersion, Socks5RequestError,
|
||||
};
|
||||
use crate::{ConnectionId, Socks5ProtocolVersion, Socks5RequestError};
|
||||
use nym_service_providers_common::interface::{Serializable, ServiceProviderResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tap::TapFallible;
|
||||
use thiserror::Error;
|
||||
|
||||
// don't start tags from 0 for easier backwards compatibility since `NetworkData`
|
||||
@@ -18,7 +13,6 @@ use thiserror::Error;
|
||||
pub enum ResponseFlag {
|
||||
NetworkData = 1,
|
||||
ConnectionError = 2,
|
||||
Query = 3,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for ResponseFlag {
|
||||
@@ -28,20 +22,13 @@ impl TryFrom<u8> for ResponseFlag {
|
||||
match value {
|
||||
_ if value == (ResponseFlag::NetworkData as u8) => Ok(Self::NetworkData),
|
||||
_ if value == (ResponseFlag::ConnectionError as u8) => Ok(Self::ConnectionError),
|
||||
_ if value == (ResponseFlag::Query as u8) => Ok(Self::Query),
|
||||
value => Err(ResponseDeserializationError::UnknownResponseFlag { value }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
pub enum ResponseDeserializationError {
|
||||
#[error("the network data was malformed: {source}")]
|
||||
MalformedNetworkData {
|
||||
#[from]
|
||||
source: InsufficientSocketDataError,
|
||||
},
|
||||
|
||||
#[error("not enough bytes to recover the connection id")]
|
||||
ConnectionIdTooShort,
|
||||
|
||||
@@ -56,12 +43,6 @@ pub enum ResponseDeserializationError {
|
||||
#[from]
|
||||
source: std::string::FromUtf8Error,
|
||||
},
|
||||
|
||||
#[error("failed to deserialize query response: {source}")]
|
||||
QueryDeserializationError {
|
||||
#[from]
|
||||
source: bincode::Error,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -124,14 +105,23 @@ impl Socks5Response {
|
||||
|
||||
pub fn new_network_data(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
seq: u64,
|
||||
connection_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
is_closed: bool,
|
||||
) -> Socks5Response {
|
||||
Socks5Response {
|
||||
protocol_version,
|
||||
content: Socks5ResponseContent::new_network_data(seq, connection_id, data, is_closed),
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,35 +135,25 @@ impl Socks5Response {
|
||||
content: Socks5ResponseContent::new_connection_error(connection_id, error_message),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_query(
|
||||
protocol_version: Socks5ProtocolVersion,
|
||||
query_response: QueryResponse,
|
||||
) -> Socks5Response {
|
||||
Socks5Response {
|
||||
protocol_version,
|
||||
content: Socks5ResponseContent::Query(query_response),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Debug)]
|
||||
pub enum Socks5ResponseContent {
|
||||
NetworkData { content: SocketData },
|
||||
NetworkData(NetworkData),
|
||||
ConnectionError(ConnectionError),
|
||||
Query(QueryResponse),
|
||||
}
|
||||
|
||||
impl Socks5ResponseContent {
|
||||
pub fn new_network_data(
|
||||
seq: u64,
|
||||
connection_id: ConnectionId,
|
||||
data: Vec<u8>,
|
||||
is_closed: bool,
|
||||
) -> Socks5ResponseContent {
|
||||
Socks5ResponseContent::NetworkData {
|
||||
content: SocketData::new(seq, connection_id, is_closed, data),
|
||||
}
|
||||
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(
|
||||
@@ -185,9 +165,9 @@ impl Socks5ResponseContent {
|
||||
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
match self {
|
||||
Socks5ResponseContent::NetworkData { content } => {
|
||||
Socks5ResponseContent::NetworkData(res) => {
|
||||
std::iter::once(ResponseFlag::NetworkData as u8)
|
||||
.chain(content.into_response_bytes_iter())
|
||||
.chain(res.into_bytes().into_iter())
|
||||
.collect()
|
||||
}
|
||||
Socks5ResponseContent::ConnectionError(res) => {
|
||||
@@ -195,18 +175,6 @@ impl Socks5ResponseContent {
|
||||
.chain(res.into_bytes().into_iter())
|
||||
.collect()
|
||||
}
|
||||
Socks5ResponseContent::Query(query) => {
|
||||
use bincode::Options;
|
||||
let query_bytes: Vec<u8> = make_bincode_serializer()
|
||||
.serialize(&query)
|
||||
.tap_err(|err| {
|
||||
log::error!("Failed to serialize query response: {:?}: {err}", query);
|
||||
})
|
||||
.unwrap_or_default();
|
||||
std::iter::once(ResponseFlag::Query as u8)
|
||||
.chain(query_bytes.into_iter())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,22 +187,84 @@ impl Socks5ResponseContent {
|
||||
|
||||
let response_flag = ResponseFlag::try_from(b[0])?;
|
||||
match response_flag {
|
||||
ResponseFlag::NetworkData => Ok(Socks5ResponseContent::NetworkData {
|
||||
content: SocketData::try_from_response_bytes(&b[1..])?,
|
||||
}),
|
||||
ResponseFlag::NetworkData => Ok(Socks5ResponseContent::NetworkData(
|
||||
NetworkData::try_from_bytes(&b[1..])?,
|
||||
)),
|
||||
ResponseFlag::ConnectionError => Ok(Socks5ResponseContent::ConnectionError(
|
||||
ConnectionError::try_from_bytes(&b[1..])?,
|
||||
)),
|
||||
ResponseFlag::Query => {
|
||||
use bincode::Options;
|
||||
let query = make_bincode_serializer().deserialize(&b[1..])?;
|
||||
Ok(Socks5ResponseContent::Query(query))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
/// A remote network network data 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 data: Vec<u8>,
|
||||
pub connection_id: ConnectionId,
|
||||
pub is_closed: bool,
|
||||
}
|
||||
|
||||
impl NetworkData {
|
||||
/// Constructor for responses
|
||||
pub fn new(connection_id: ConnectionId, data: Vec<u8>, is_closed: bool) -> Self {
|
||||
NetworkData {
|
||||
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> {
|
||||
if b.is_empty() {
|
||||
return Err(ResponseDeserializationError::NoData);
|
||||
}
|
||||
|
||||
let is_closed = b[0] != 0;
|
||||
|
||||
if b.len() < 9 {
|
||||
return Err(ResponseDeserializationError::ConnectionIdTooShort);
|
||||
}
|
||||
|
||||
let mut connection_id_bytes = b.to_vec();
|
||||
let data = connection_id_bytes.split_off(9);
|
||||
|
||||
let connection_id = u64::from_be_bytes([
|
||||
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],
|
||||
connection_id_bytes[8],
|
||||
]);
|
||||
|
||||
let response = NetworkData::new(connection_id, data, is_closed);
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// Serializes the response into bytes so that it can be sent back through
|
||||
/// the mixnet to the requesting application.
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
std::iter::once(self.is_closed as u8)
|
||||
.chain(self.connection_id.to_be_bytes().iter().cloned())
|
||||
.chain(self.data.into_iter())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConnectionError {
|
||||
pub connection_id: ConnectionId,
|
||||
pub network_requester_error: String,
|
||||
@@ -288,16 +318,62 @@ impl ConnectionError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum QueryResponse {
|
||||
OpenProxy(bool),
|
||||
Description(String),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod connection_error_response_serde_tests {
|
||||
use super::*;
|
||||
@@ -320,14 +396,11 @@ mod tests {
|
||||
#[test]
|
||||
fn deserialization_errors() {
|
||||
let err = ConnectionError::try_from_bytes(&[]).err().unwrap();
|
||||
assert!(matches!(err, ResponseDeserializationError::NoData));
|
||||
assert_eq!(err, ResponseDeserializationError::NoData);
|
||||
|
||||
let bytes: [u8; 5] = [1, 2, 3, 4, 5];
|
||||
let err = ConnectionError::try_from_bytes(&bytes).err().unwrap();
|
||||
assert!(matches!(
|
||||
err,
|
||||
ResponseDeserializationError::ConnectionIdTooShort
|
||||
));
|
||||
assert_eq!(err, ResponseDeserializationError::ConnectionIdTooShort);
|
||||
|
||||
let bytes: Vec<u8> = 42u64
|
||||
.to_be_bytes()
|
||||
@@ -341,27 +414,4 @@ mod tests {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod serialize_query_response {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn serialize_there_and_back() {
|
||||
let open_proxy = Socks5ResponseContent::Query(QueryResponse::OpenProxy(true));
|
||||
let bytes_open_proxy = open_proxy.clone().into_bytes();
|
||||
assert_eq!(bytes_open_proxy, vec![3, 0, 1]);
|
||||
|
||||
let description =
|
||||
Socks5ResponseContent::Query(QueryResponse::Description("foo".to_string()));
|
||||
let bytes_description = description.clone().into_bytes();
|
||||
assert_eq!(bytes_description, vec![3, 1, 3, 102, 111, 111]);
|
||||
|
||||
let open_proxy2 = Socks5ResponseContent::try_from_bytes(&bytes_open_proxy).unwrap();
|
||||
let description2 = Socks5ResponseContent::try_from_bytes(&bytes_description).unwrap();
|
||||
|
||||
assert_eq!(open_proxy, open_proxy2);
|
||||
assert_eq!(description, description2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ url = "2.2"
|
||||
ts-rs = "6.1.2"
|
||||
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
|
||||
cosmrs = { workspace = true }
|
||||
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client", features = [
|
||||
"nyxd-client",
|
||||
|
||||
Generated
+240
-116
@@ -241,8 +241,8 @@ dependencies = [
|
||||
"cosmwasm-storage",
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw3",
|
||||
"cw3-flex-multisig",
|
||||
"cw4",
|
||||
@@ -260,9 +260,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.7.1"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
|
||||
checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
@@ -272,11 +272,11 @@ checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b"
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-crypto"
|
||||
version = "1.0.0"
|
||||
version = "1.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd"
|
||||
checksum = "75836a10cb9654c54e77ee56da94d592923092a10b369cdb0dbd56acefc16340"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"digest 0.10.7",
|
||||
"ed25519-zebra",
|
||||
"k256",
|
||||
"rand_core 0.6.4",
|
||||
@@ -285,45 +285,62 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-derive"
|
||||
version = "1.0.0"
|
||||
version = "1.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4"
|
||||
checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773"
|
||||
dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-schema"
|
||||
version = "1.0.0"
|
||||
version = "1.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "772e80bbad231a47a2068812b723a1ff81dd4a0d56c9391ac748177bea3a61da"
|
||||
checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc"
|
||||
dependencies = [
|
||||
"cosmwasm-schema-derive",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-schema-derive"
|
||||
version = "1.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-std"
|
||||
version = "1.0.0"
|
||||
version = "1.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195"
|
||||
checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"cosmwasm-crypto",
|
||||
"cosmwasm-derive",
|
||||
"derivative",
|
||||
"forward_ref",
|
||||
"hex",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde-json-wasm",
|
||||
"serde-json-wasm 0.5.1",
|
||||
"sha2 0.10.6",
|
||||
"thiserror",
|
||||
"uint",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-storage"
|
||||
version = "1.0.0"
|
||||
version = "1.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d18403b07304d15d304dad11040d45bbcaf78d603b4be3fb5e2685c16f9229b5"
|
||||
checksum = "a3737a3aac48f5ed883b5b73bfb731e77feebd8fc6b43419844ec2971072164d"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"serde",
|
||||
@@ -389,9 +406,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.3.2"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21"
|
||||
checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef"
|
||||
dependencies = [
|
||||
"generic-array 0.14.6",
|
||||
"rand_core 0.6.4",
|
||||
@@ -454,13 +471,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw-controllers"
|
||||
version = "0.13.4"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa"
|
||||
checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
@@ -468,17 +486,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw-multi-test"
|
||||
version = "0.13.4"
|
||||
version = "0.16.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f9a8ab7c3c29ec93cb7a39ce4b14a05e053153b4a17ef7cf2246af1b7c087e"
|
||||
checksum = "2a18afd2e201221c6d72a57f0886ef2a22151bbc9e6db7af276fde8a91081042"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"derivative",
|
||||
"itertools",
|
||||
"k256",
|
||||
"prost",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -487,9 +505,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw-storage-plus"
|
||||
version = "0.13.4"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a"
|
||||
checksum = "d9b6f91c0b94481a3e9ef1ceb183c37d00764f8751e39b45fc09f4d9b970d469"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-storage-plus"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"schemars",
|
||||
@@ -498,50 +527,117 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw-utils"
|
||||
version = "0.13.4"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b"
|
||||
checksum = "d6a84c6c1c0acc3616398eba50783934bd6c964bad6974241eaee3460c8f5b26"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw2 0.16.0",
|
||||
"schemars",
|
||||
"semver",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-utils"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw2 1.0.1",
|
||||
"schemars",
|
||||
"semver",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw2"
|
||||
version = "0.13.4"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1"
|
||||
checksum = "91398113b806f4d2a8d5f8d05684704a20ffd5968bf87e3473e1973710b884ad"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw-storage-plus 0.16.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw2"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw20"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-utils 1.0.1",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw20-base"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79314264ffc46b7658ee30caccc1540f14b9119568264bc02817f79c6f989a9"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 0.16.0",
|
||||
"cw-utils 0.16.0",
|
||||
"cw2 1.0.1",
|
||||
"cw20",
|
||||
"schemars",
|
||||
"semver",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw3"
|
||||
version = "0.13.4"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b"
|
||||
checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw-utils 1.0.1",
|
||||
"cw20",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw3-fixed-multisig"
|
||||
version = "0.13.4"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df54aa54c13f405ec4ab36b6217538bc957d439eee58f89312db05a79caf6706"
|
||||
checksum = "e9e2415adb201e5e89dab34edf59d7dc166bc558526de009a49ae66276c9119a"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"cw3",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -550,14 +646,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw3-flex-multisig"
|
||||
version = "0.13.1"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"cw20",
|
||||
"cw20-base",
|
||||
"cw3",
|
||||
"cw3-fixed-multisig",
|
||||
"cw4",
|
||||
@@ -565,31 +662,31 @@ dependencies = [
|
||||
"nym-group-contract-common",
|
||||
"nym-multisig-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw4"
|
||||
version = "0.13.4"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd"
|
||||
checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw4-group"
|
||||
version = "0.13.4"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"cw4",
|
||||
"nym-group-contract-common",
|
||||
"schemars",
|
||||
@@ -599,11 +696,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.5.1"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c"
|
||||
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -654,9 +752,9 @@ checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.13.4"
|
||||
version = "0.14.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9"
|
||||
checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c"
|
||||
dependencies = [
|
||||
"der",
|
||||
"elliptic-curve",
|
||||
@@ -683,7 +781,7 @@ dependencies = [
|
||||
"ed25519",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -698,7 +796,7 @@ dependencies = [
|
||||
"hex",
|
||||
"rand_core 0.6.4",
|
||||
"serde",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -710,16 +808,18 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.11.12"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6"
|
||||
checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"crypto-bigint",
|
||||
"der",
|
||||
"digest 0.10.7",
|
||||
"ff",
|
||||
"generic-array 0.14.6",
|
||||
"group",
|
||||
"pkcs8",
|
||||
"rand_core 0.6.4",
|
||||
"sec1",
|
||||
"subtle 2.4.1",
|
||||
@@ -778,9 +878,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.11.1"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924"
|
||||
checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"subtle 2.4.1",
|
||||
@@ -974,9 +1074,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.11.0"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89"
|
||||
checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"rand_core 0.6.4",
|
||||
@@ -1020,7 +1120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"hmac",
|
||||
"hmac 0.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1033,6 +1133,15 @@ dependencies = [
|
||||
"digest 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
@@ -1123,15 +1232,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "k256"
|
||||
version = "0.10.4"
|
||||
version = "0.11.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d"
|
||||
checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"sec1",
|
||||
"sha2",
|
||||
"sha2 0.10.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1267,7 +1375,7 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cw-controllers",
|
||||
"cw-storage-plus",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"nym-coconut-bandwidth-contract-common",
|
||||
"nym-multisig-contract-common",
|
||||
"schemars",
|
||||
@@ -1293,7 +1401,7 @@ dependencies = [
|
||||
"cosmwasm-storage",
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw4",
|
||||
"cw4-group",
|
||||
"lazy_static",
|
||||
@@ -1310,7 +1418,7 @@ name = "nym-coconut-dkg-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw-utils 1.0.1",
|
||||
"nym-contracts-common",
|
||||
"nym-multisig-contract-common",
|
||||
"schemars",
|
||||
@@ -1319,7 +1427,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.5.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -1330,7 +1438,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-crypto"
|
||||
version = "0.4.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"ed25519-dalek",
|
||||
@@ -1347,6 +1455,8 @@ dependencies = [
|
||||
name = "nym-group-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cw-controllers",
|
||||
"cw4",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -1361,8 +1471,8 @@ dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cw-storage-plus",
|
||||
"cw2",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"nym-contracts-common",
|
||||
"nym-crypto",
|
||||
"nym-mixnet-contract-common",
|
||||
@@ -1378,7 +1488,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.6.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -1387,7 +1497,7 @@ dependencies = [
|
||||
"nym-contracts-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde-json-wasm",
|
||||
"serde-json-wasm 0.4.1",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"time",
|
||||
@@ -1397,8 +1507,10 @@ dependencies = [
|
||||
name = "nym-multisig-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw3",
|
||||
"cw4",
|
||||
"schemars",
|
||||
@@ -1414,9 +1526,9 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"nym-contracts-common",
|
||||
"nym-name-service-common",
|
||||
"rand 0.8.5",
|
||||
@@ -1455,7 +1567,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-pemstore"
|
||||
version = "0.3.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"pem",
|
||||
]
|
||||
@@ -1465,18 +1577,14 @@ name = "nym-service-provider-directory"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"nym-contracts-common",
|
||||
"nym-crypto",
|
||||
"nym-service-provider-directory-common",
|
||||
"rand_chacha 0.2.2",
|
||||
"rstest",
|
||||
"semver",
|
||||
"serde",
|
||||
"thiserror",
|
||||
@@ -1488,12 +1596,8 @@ name = "nym-service-provider-directory-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-utils",
|
||||
"nym-contracts-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1513,8 +1617,8 @@ dependencies = [
|
||||
"cosmwasm-crypto",
|
||||
"cosmwasm-derive",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw2",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"hex",
|
||||
"nym-contracts-common",
|
||||
"nym-mixnet-contract-common",
|
||||
@@ -1530,7 +1634,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.7.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"nym-contracts-common",
|
||||
@@ -1588,13 +1692,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0"
|
||||
checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1820,12 +1923,12 @@ checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.1.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525"
|
||||
checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb"
|
||||
dependencies = [
|
||||
"crypto-bigint",
|
||||
"hmac",
|
||||
"hmac 0.12.1",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -1934,10 +2037,11 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.2.1"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1"
|
||||
checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"der",
|
||||
"generic-array 0.14.6",
|
||||
"pkcs8",
|
||||
@@ -1969,6 +2073,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-json-wasm"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.160"
|
||||
@@ -2027,12 +2140,23 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "1.4.0"
|
||||
name = "sha2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788"
|
||||
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "1.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
@@ -2060,20 +2184,20 @@ dependencies = [
|
||||
"curve25519-dalek",
|
||||
"digest 0.9.0",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"hmac 0.11.0",
|
||||
"lioness",
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"rand_distr",
|
||||
"sha2",
|
||||
"sha2 0.9.9",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.5.4"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27"
|
||||
checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
|
||||
+14
-13
@@ -32,16 +32,17 @@ incremental = false
|
||||
overflow-checks = true
|
||||
|
||||
[workspace.dependencies]
|
||||
cosmwasm-crypto = "=1.0.0"
|
||||
cosmwasm-derive = "=1.0.0"
|
||||
cosmwasm-schema = "=1.0.0"
|
||||
cosmwasm-std = "=1.0.0"
|
||||
cosmwasm-storage = "=1.0.0"
|
||||
cw-controllers = "=0.13.4"
|
||||
cw-multi-test = "=0.13.4"
|
||||
cw-storage-plus = "=0.13.4"
|
||||
cw-utils = "=0.13.4"
|
||||
cw2 = "=0.13.4"
|
||||
cw3 = "=0.13.4"
|
||||
cw3-fixed-multisig = "=0.13.4"
|
||||
cw4 = "=0.13.4"
|
||||
cosmwasm-crypto = "=1.2.5"
|
||||
cosmwasm-derive = "=1.2.5"
|
||||
cosmwasm-schema = "=1.2.5"
|
||||
cosmwasm-std = "=1.2.5"
|
||||
cosmwasm-storage = "=1.2.5"
|
||||
cw-controllers = "=1.0.1"
|
||||
cw-multi-test = "=0.16.4"
|
||||
cw-storage-plus = "=1.0.1"
|
||||
cw-utils = "=1.0.1"
|
||||
cw2 = "=1.0.1"
|
||||
cw3 = "=1.0.1"
|
||||
cw3-fixed-multisig = "=1.0.1"
|
||||
cw4 = "=1.0.1"
|
||||
cw20 = "=1.0.1"
|
||||
|
||||
@@ -30,7 +30,7 @@ impl<'a> IndexList<ContractVKShare> for VkShareIndex<'a> {
|
||||
pub(crate) fn vk_shares<'a>() -> IndexedMap<'a, VKShareKey<'a>, ContractVKShare, VkShareIndex<'a>> {
|
||||
let indexes = VkShareIndex {
|
||||
epoch_id: MultiIndex::new(
|
||||
|d| d.epoch_id,
|
||||
|_pk, d| d.epoch_id,
|
||||
VK_SHARES_PK_NAMESPACE,
|
||||
VK_SHARES_EPOCH_ID_IDX_NAMESPACE,
|
||||
),
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{entry_point, Addr, Coin, DepsMut, Empty, Env, Response};
|
||||
use cw3_flex_multisig::state::CONFIG;
|
||||
use cw_multi_test::{App, AppBuilder, Contract, ContractWrapper};
|
||||
use nym_multisig_contract_common::error::ContractError;
|
||||
use nym_multisig_contract_common::state::CONFIG;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ fn spend_credential_creates_proposal() {
|
||||
threshold: Threshold::AbsolutePercentage {
|
||||
percentage: Decimal::from_ratio(2u128, 3u128),
|
||||
},
|
||||
executor: None,
|
||||
proposal_deposit: None,
|
||||
max_voting_period: Duration::Height(1000),
|
||||
coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(),
|
||||
coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(),
|
||||
|
||||
@@ -52,6 +52,8 @@ fn dkg_proposal() {
|
||||
threshold: Threshold::AbsolutePercentage {
|
||||
percentage: Decimal::from_ratio(1u128, 1u128),
|
||||
},
|
||||
executor: None,
|
||||
proposal_deposit: None,
|
||||
max_voting_period: Duration::Time(1000),
|
||||
coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(),
|
||||
coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(),
|
||||
|
||||
@@ -22,9 +22,9 @@ name = "mixnet_contract"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.6.0" }
|
||||
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.7.0" }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.5.0" }
|
||||
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", package = "nym-mixnet-contract-common", version = "0.5.0" }
|
||||
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.6.0" }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.4.0" }
|
||||
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmwasm-storage = { workspace = true }
|
||||
|
||||
@@ -520,7 +520,7 @@ mod tests {
|
||||
.delegations
|
||||
.iter()
|
||||
.filter(|d| d.proxy.is_some())
|
||||
.all(|d| d.proxy.as_ref().unwrap() == &vesting_contract));
|
||||
.all(|d| d.proxy.as_ref().unwrap() == vesting_contract));
|
||||
|
||||
// now make sure that if we do it in paged manner, we'll get exactly the same result
|
||||
let per_page = Some(15);
|
||||
|
||||
@@ -27,12 +27,12 @@ impl<'a> IndexList<Delegation> for DelegationIndex<'a> {
|
||||
pub(crate) fn delegations<'a>() -> IndexedMap<'a, PrimaryKey, Delegation, DelegationIndex<'a>> {
|
||||
let indexes = DelegationIndex {
|
||||
owner: MultiIndex::new(
|
||||
|d| d.owner.clone(),
|
||||
|_pk, d| d.owner.clone(),
|
||||
DELEGATION_PK_NAMESPACE,
|
||||
DELEGATION_OWNER_IDX_NAMESPACE,
|
||||
),
|
||||
mixnode: MultiIndex::new(
|
||||
|d| d.mix_id,
|
||||
|_pk, d| d.mix_id,
|
||||
DELEGATION_PK_NAMESPACE,
|
||||
DELEGATION_MIXNODE_IDX_NAMESPACE,
|
||||
),
|
||||
|
||||
@@ -14,7 +14,7 @@ mod mixnet_contract_settings;
|
||||
mod mixnodes;
|
||||
mod queued_migrations;
|
||||
mod rewards;
|
||||
pub mod signing;
|
||||
mod signing;
|
||||
mod support;
|
||||
|
||||
#[cfg(feature = "contract-testing")]
|
||||
|
||||
@@ -39,12 +39,12 @@ pub(crate) fn unbonded_mixnodes<'a>(
|
||||
) -> IndexedMap<'a, MixId, UnbondedMixnode, UnbondedMixnodeIndex<'a>> {
|
||||
let indexes = UnbondedMixnodeIndex {
|
||||
owner: MultiIndex::new(
|
||||
|d| d.owner.clone(),
|
||||
|_pk, d| d.owner.clone(),
|
||||
UNBONDED_MIXNODES_PK_NAMESPACE,
|
||||
UNBONDED_MIXNODES_OWNER_IDX_NAMESPACE,
|
||||
),
|
||||
identity_key: MultiIndex::new(
|
||||
|d| d.identity_key.clone(),
|
||||
|_pk, d| d.identity_key.clone(),
|
||||
UNBONDED_MIXNODES_PK_NAMESPACE,
|
||||
UNBONDED_MIXNODES_IDENTITY_IDX_NAMESPACE,
|
||||
),
|
||||
|
||||
@@ -185,7 +185,7 @@ pub(crate) fn _try_withdraw_operator_reward(
|
||||
// we can only attempt to send the message to the vesting contract if the proxy IS the vesting contract
|
||||
// otherwise, we don't care
|
||||
let vesting_contract = mixnet_params_storage::vesting_contract_address(deps.storage)?;
|
||||
if proxy == &vesting_contract {
|
||||
if proxy == vesting_contract {
|
||||
let msg = VestingContractExecuteMsg::TrackReward {
|
||||
amount: reward.clone(),
|
||||
address: owner.clone().into_string(),
|
||||
@@ -271,7 +271,7 @@ pub(crate) fn _try_withdraw_delegator_reward(
|
||||
// we can only attempt to send the message to the vesting contract if the proxy IS the vesting contract
|
||||
// otherwise, we don't care
|
||||
let vesting_contract = mixnet_params_storage::vesting_contract_address(deps.storage)?;
|
||||
if proxy == &vesting_contract {
|
||||
if proxy == vesting_contract {
|
||||
let msg = VestingContractExecuteMsg::TrackReward {
|
||||
amount: reward.clone(),
|
||||
address: owner.clone().into_string(),
|
||||
|
||||
@@ -298,7 +298,7 @@ pub(crate) fn ensure_is_authorized(
|
||||
sender: &Addr,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<(), MixnetContractError> {
|
||||
if sender != &crate::mixnet_contract_settings::storage::rewarding_validator_address(storage)? {
|
||||
if sender != crate::mixnet_contract_settings::storage::rewarding_validator_address(storage)? {
|
||||
return Err(MixnetContractError::Unauthorized);
|
||||
}
|
||||
Ok(())
|
||||
@@ -309,7 +309,7 @@ pub(crate) fn ensure_can_advance_epoch(
|
||||
storage: &dyn Storage,
|
||||
) -> Result<EpochStatus, MixnetContractError> {
|
||||
let epoch_status = crate::interval::storage::current_epoch_status(storage)?;
|
||||
if sender != &epoch_status.being_advanced_by {
|
||||
if sender != epoch_status.being_advanced_by {
|
||||
// well, we know we're going to throw an error now,
|
||||
// but we might as well also check if we're even a validator
|
||||
// to return a possibly better error message
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "cw3-flex-multisig"
|
||||
version = "0.13.1"
|
||||
version = "1.0.0"
|
||||
authors = ["Ethan Frey <ethanfrey@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
description = "Implementing cw3 with multiple voting patterns and dynamic groups"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/CosmWasm/cw-plus"
|
||||
@@ -13,6 +13,7 @@ documentation = "https://docs.cosmwasm.com"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
backtraces = ["cosmwasm-std/backtraces"]
|
||||
# use library feature to disable all instantiate/execute/query exports
|
||||
library = []
|
||||
|
||||
@@ -22,15 +23,15 @@ cw2 = { workspace = true }
|
||||
cw3 = { workspace = true }
|
||||
cw3-fixed-multisig = { workspace = true, features = ["library"] }
|
||||
cw4 = { workspace = true }
|
||||
cw20 = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8.1"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
nym-group-contract-common = { path = "../../../common/cosmwasm-smart-contracts/group-contract" }
|
||||
nym-multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts/multisig-contract" }
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = { version = "1.0.0" }
|
||||
cw4-group = { path = "../cw4-group", version = "0.13.4" }
|
||||
cw4-group = { path = "../cw4-group", version = "1.0.0" }
|
||||
cw-multi-test = { workspace = true }
|
||||
cw20-base = "1.0.0"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,25 @@
|
||||
/*!
|
||||
This builds on [`cw3_fixed_multisig`] with a more
|
||||
powerful implementation of the [cw3 spec](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw3/README.md).
|
||||
It is a multisig contract that is backed by a
|
||||
[cw4 (group)](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw4/README.md) contract, which independently
|
||||
maintains the voter set.
|
||||
|
||||
This provides 2 main advantages:
|
||||
|
||||
* You can create two different multisigs with different voting thresholds
|
||||
backed by the same group. Thus, you can have a 50% vote, and a 67% vote
|
||||
that always use the same voter set, but can take other actions.
|
||||
* TODO: It allows dynamic multisig groups.
|
||||
|
||||
|
||||
In addition to the dynamic voting set, the main difference with the native
|
||||
Cosmos SDK multisig, is that it aggregates the signatures on chain, with
|
||||
visible proposals (like `x/gov` in the Cosmos SDK), rather than requiring
|
||||
signers to share signatures off chain.
|
||||
|
||||
For more information on this contract, please check out the
|
||||
[README](https://github.com/CosmWasm/cw-plus/blob/main/contracts/cw3-flex-multisig/README.md).
|
||||
*/
|
||||
|
||||
pub mod contract;
|
||||
pub mod state;
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
use cosmwasm_std::Addr;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use cw4::Cw4Contract;
|
||||
use cw_storage_plus::Item;
|
||||
use cw_utils::{Duration, Threshold};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
|
||||
pub struct Config {
|
||||
pub threshold: Threshold,
|
||||
pub max_voting_period: Duration,
|
||||
// Total weight and voters are queried from this contract
|
||||
pub group_addr: Cw4Contract,
|
||||
pub coconut_bandwidth_addr: Addr,
|
||||
pub coconut_dkg_addr: Addr,
|
||||
}
|
||||
|
||||
// unique items
|
||||
pub const CONFIG: Item<Config> = Item::new("config");
|
||||
@@ -1,5 +1,5 @@
|
||||
[alias]
|
||||
wasm = "build --release --target wasm32-unknown-unknown"
|
||||
wasm-debug = "build --target wasm32-unknown-unknown"
|
||||
wasm = "build --release --lib --target wasm32-unknown-unknown"
|
||||
wasm-debug = "build --lib --target wasm32-unknown-unknown"
|
||||
unit-test = "test --lib"
|
||||
schema = "run --example schema"
|
||||
schema = "run --bin schema"
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "cw4-group"
|
||||
version = "0.13.4"
|
||||
version = "1.0.0"
|
||||
authors = ["Ethan Frey <ethanfrey@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
description = "Simple cw4 implementation of group membership controlled by admin "
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/CosmWasm/cw-plus"
|
||||
@@ -20,6 +20,8 @@ exclude = [
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
# for more explicit tests, cargo test --features=backtraces
|
||||
backtraces = ["cosmwasm-std/backtraces"]
|
||||
# use library feature to disable all instantiate/execute/query exports
|
||||
library = []
|
||||
|
||||
@@ -31,10 +33,8 @@ cw2 = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
schemars = "0.8.1"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.23" }
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = { workspace = true }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use cosmwasm_std::entry_point;
|
||||
use cosmwasm_std::{
|
||||
attr, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, StdResult,
|
||||
SubMsg,
|
||||
SubMsg, Uint64,
|
||||
};
|
||||
use cw2::set_contract_version;
|
||||
use cw4::{
|
||||
@@ -13,6 +13,7 @@ use cw_storage_plus::Bound;
|
||||
use cw_utils::maybe_addr;
|
||||
|
||||
use crate::error::ContractError;
|
||||
use crate::helpers::validate_unique_members;
|
||||
use crate::state::{ADMIN, HOOKS, MEMBERS, TOTAL};
|
||||
use nym_group_contract_common::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
|
||||
|
||||
@@ -39,21 +40,25 @@ pub fn instantiate(
|
||||
pub fn create(
|
||||
mut deps: DepsMut,
|
||||
admin: Option<String>,
|
||||
members: Vec<Member>,
|
||||
mut members: Vec<Member>,
|
||||
height: u64,
|
||||
) -> Result<(), ContractError> {
|
||||
validate_unique_members(&mut members)?;
|
||||
let members = members; // let go of mutability
|
||||
|
||||
let admin_addr = admin
|
||||
.map(|admin| deps.api.addr_validate(&admin))
|
||||
.transpose()?;
|
||||
ADMIN.set(deps.branch(), admin_addr)?;
|
||||
|
||||
let mut total = 0u64;
|
||||
let mut total = Uint64::zero();
|
||||
for member in members.into_iter() {
|
||||
total += member.weight;
|
||||
let member_weight = Uint64::from(member.weight);
|
||||
total = total.checked_add(member_weight)?;
|
||||
let member_addr = deps.api.addr_validate(&member.addr)?;
|
||||
MEMBERS.save(deps.storage, &member_addr, &member.weight, height)?;
|
||||
MEMBERS.save(deps.storage, &member_addr, &member_weight.u64(), height)?;
|
||||
}
|
||||
TOTAL.save(deps.storage, &total)?;
|
||||
TOTAL.save(deps.storage, &total.u64(), height)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -115,20 +120,23 @@ pub fn update_members(
|
||||
deps: DepsMut,
|
||||
height: u64,
|
||||
sender: Addr,
|
||||
to_add: Vec<Member>,
|
||||
mut to_add: Vec<Member>,
|
||||
to_remove: Vec<String>,
|
||||
) -> Result<MemberChangedHookMsg, ContractError> {
|
||||
validate_unique_members(&mut to_add)?;
|
||||
let to_add = to_add; // let go of mutability
|
||||
|
||||
ADMIN.assert_admin(deps.as_ref(), &sender)?;
|
||||
|
||||
let mut total = TOTAL.load(deps.storage)?;
|
||||
let mut total = Uint64::from(TOTAL.load(deps.storage)?);
|
||||
let mut diffs: Vec<MemberDiff> = vec![];
|
||||
|
||||
// add all new members and update total
|
||||
for add in to_add.into_iter() {
|
||||
let add_addr = deps.api.addr_validate(&add.addr)?;
|
||||
MEMBERS.update(deps.storage, &add_addr, height, |old| -> StdResult<_> {
|
||||
total -= old.unwrap_or_default();
|
||||
total += add.weight;
|
||||
total = total.checked_sub(Uint64::from(old.unwrap_or_default()))?;
|
||||
total = total.checked_add(Uint64::from(add.weight))?;
|
||||
diffs.push(MemberDiff::new(add.addr, old, Some(add.weight)));
|
||||
Ok(add.weight)
|
||||
})?;
|
||||
@@ -140,12 +148,12 @@ pub fn update_members(
|
||||
// Only process this if they were actually in the list before
|
||||
if let Some(weight) = old {
|
||||
diffs.push(MemberDiff::new(remove, Some(weight), None));
|
||||
total -= weight;
|
||||
total = total.checked_sub(Uint64::from(weight))?;
|
||||
MEMBERS.remove(deps.storage, &remove_addr, height)?;
|
||||
}
|
||||
}
|
||||
|
||||
TOTAL.save(deps.storage, &total)?;
|
||||
TOTAL.save(deps.storage, &total.u64(), height)?;
|
||||
Ok(MemberChangedHookMsg { diffs })
|
||||
}
|
||||
|
||||
@@ -157,20 +165,26 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
|
||||
at_height: height,
|
||||
} => to_binary(&query_member(deps, addr, height)?),
|
||||
QueryMsg::ListMembers { start_after, limit } => {
|
||||
to_binary(&list_members(deps, start_after, limit)?)
|
||||
to_binary(&query_list_members(deps, start_after, limit)?)
|
||||
}
|
||||
QueryMsg::TotalWeight { at_height: height } => {
|
||||
to_binary(&query_total_weight(deps, height)?)
|
||||
}
|
||||
QueryMsg::TotalWeight {} => to_binary(&query_total_weight(deps)?),
|
||||
QueryMsg::Admin {} => to_binary(&ADMIN.query_admin(deps)?),
|
||||
QueryMsg::Hooks {} => to_binary(&HOOKS.query_hooks(deps)?),
|
||||
}
|
||||
}
|
||||
|
||||
fn query_total_weight(deps: Deps) -> StdResult<TotalWeightResponse> {
|
||||
let weight = TOTAL.load(deps.storage)?;
|
||||
pub fn query_total_weight(deps: Deps, height: Option<u64>) -> StdResult<TotalWeightResponse> {
|
||||
let weight = match height {
|
||||
Some(h) => TOTAL.may_load_at_height(deps.storage, h),
|
||||
None => TOTAL.may_load(deps.storage),
|
||||
}?
|
||||
.unwrap_or_default();
|
||||
Ok(TotalWeightResponse { weight })
|
||||
}
|
||||
|
||||
fn query_member(deps: Deps, addr: String, height: Option<u64>) -> StdResult<MemberResponse> {
|
||||
pub fn query_member(deps: Deps, addr: String, height: Option<u64>) -> StdResult<MemberResponse> {
|
||||
let addr = deps.api.addr_validate(&addr)?;
|
||||
let weight = match height {
|
||||
Some(h) => MEMBERS.may_load_at_height(deps.storage, &addr, h),
|
||||
@@ -183,7 +197,7 @@ fn query_member(deps: Deps, addr: String, height: Option<u64>) -> StdResult<Memb
|
||||
const MAX_LIMIT: u32 = 30;
|
||||
const DEFAULT_LIMIT: u32 = 10;
|
||||
|
||||
fn list_members(
|
||||
pub fn query_list_members(
|
||||
deps: Deps,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
@@ -205,352 +219,3 @@ fn list_members(
|
||||
|
||||
Ok(MemberListResponse { members })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
|
||||
use cosmwasm_std::{from_slice, Api, OwnedDeps, Querier, Storage};
|
||||
use cw4::{member_key, TOTAL_KEY};
|
||||
use cw_controllers::{AdminError, HookError};
|
||||
|
||||
const INIT_ADMIN: &str = "juan";
|
||||
const USER1: &str = "somebody";
|
||||
const USER2: &str = "else";
|
||||
const USER3: &str = "funny";
|
||||
|
||||
fn do_instantiate(deps: DepsMut) {
|
||||
let msg = InstantiateMsg {
|
||||
admin: Some(INIT_ADMIN.into()),
|
||||
members: vec![
|
||||
Member {
|
||||
addr: USER1.into(),
|
||||
weight: 11,
|
||||
},
|
||||
Member {
|
||||
addr: USER2.into(),
|
||||
weight: 6,
|
||||
},
|
||||
],
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
instantiate(deps, mock_env(), info, msg).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proper_instantiation() {
|
||||
let mut deps = mock_dependencies();
|
||||
do_instantiate(deps.as_mut());
|
||||
|
||||
// it worked, let's query the state
|
||||
let res = ADMIN.query_admin(deps.as_ref()).unwrap();
|
||||
assert_eq!(Some(INIT_ADMIN.into()), res.admin);
|
||||
|
||||
let res = query_total_weight(deps.as_ref()).unwrap();
|
||||
assert_eq!(17, res.weight);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_member_queries() {
|
||||
let mut deps = mock_dependencies();
|
||||
do_instantiate(deps.as_mut());
|
||||
|
||||
let member1 = query_member(deps.as_ref(), USER1.into(), None).unwrap();
|
||||
assert_eq!(member1.weight, Some(11));
|
||||
|
||||
let member2 = query_member(deps.as_ref(), USER2.into(), None).unwrap();
|
||||
assert_eq!(member2.weight, Some(6));
|
||||
|
||||
let member3 = query_member(deps.as_ref(), USER3.into(), None).unwrap();
|
||||
assert_eq!(member3.weight, None);
|
||||
|
||||
let members = list_members(deps.as_ref(), None, None).unwrap();
|
||||
assert_eq!(members.members.len(), 2);
|
||||
// TODO: assert the set is proper
|
||||
}
|
||||
|
||||
fn assert_users<S: Storage, A: Api, Q: Querier>(
|
||||
deps: &OwnedDeps<S, A, Q>,
|
||||
user1_weight: Option<u64>,
|
||||
user2_weight: Option<u64>,
|
||||
user3_weight: Option<u64>,
|
||||
height: Option<u64>,
|
||||
) {
|
||||
let member1 = query_member(deps.as_ref(), USER1.into(), height).unwrap();
|
||||
assert_eq!(member1.weight, user1_weight);
|
||||
|
||||
let member2 = query_member(deps.as_ref(), USER2.into(), height).unwrap();
|
||||
assert_eq!(member2.weight, user2_weight);
|
||||
|
||||
let member3 = query_member(deps.as_ref(), USER3.into(), height).unwrap();
|
||||
assert_eq!(member3.weight, user3_weight);
|
||||
|
||||
// this is only valid if we are not doing a historical query
|
||||
if height.is_none() {
|
||||
// compute expected metrics
|
||||
let weights = vec![user1_weight, user2_weight, user3_weight];
|
||||
let sum: u64 = weights.iter().map(|x| x.unwrap_or_default()).sum();
|
||||
let count = weights.iter().filter(|x| x.is_some()).count();
|
||||
|
||||
// TODO: more detailed compare?
|
||||
let members = list_members(deps.as_ref(), None, None).unwrap();
|
||||
assert_eq!(count, members.members.len());
|
||||
|
||||
let total = query_total_weight(deps.as_ref()).unwrap();
|
||||
assert_eq!(sum, total.weight); // 17 - 11 + 15 = 21
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_new_remove_old_member() {
|
||||
let mut deps = mock_dependencies();
|
||||
do_instantiate(deps.as_mut());
|
||||
|
||||
// add a new one and remove existing one
|
||||
let add = vec![Member {
|
||||
addr: USER3.into(),
|
||||
weight: 15,
|
||||
}];
|
||||
let remove = vec![USER1.into()];
|
||||
|
||||
// non-admin cannot update
|
||||
let height = mock_env().block.height;
|
||||
let err = update_members(
|
||||
deps.as_mut(),
|
||||
height + 5,
|
||||
Addr::unchecked(USER1),
|
||||
add.clone(),
|
||||
remove.clone(),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(err, AdminError::NotAdmin {}.into());
|
||||
|
||||
// Test the values from instantiate
|
||||
assert_users(&deps, Some(11), Some(6), None, None);
|
||||
// Note all values were set at height, the beginning of that block was all None
|
||||
assert_users(&deps, None, None, None, Some(height));
|
||||
// This will get us the values at the start of the block after instantiate (expected initial values)
|
||||
assert_users(&deps, Some(11), Some(6), None, Some(height + 1));
|
||||
|
||||
// admin updates properly
|
||||
update_members(
|
||||
deps.as_mut(),
|
||||
height + 10,
|
||||
Addr::unchecked(INIT_ADMIN),
|
||||
add,
|
||||
remove,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// updated properly
|
||||
assert_users(&deps, None, Some(6), Some(15), None);
|
||||
|
||||
// snapshot still shows old value
|
||||
assert_users(&deps, Some(11), Some(6), None, Some(height + 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_old_remove_new_member() {
|
||||
// add will over-write and remove have no effect
|
||||
let mut deps = mock_dependencies();
|
||||
do_instantiate(deps.as_mut());
|
||||
|
||||
// add a new one and remove existing one
|
||||
let add = vec![Member {
|
||||
addr: USER1.into(),
|
||||
weight: 4,
|
||||
}];
|
||||
let remove = vec![USER3.into()];
|
||||
|
||||
// admin updates properly
|
||||
let height = mock_env().block.height;
|
||||
update_members(
|
||||
deps.as_mut(),
|
||||
height,
|
||||
Addr::unchecked(INIT_ADMIN),
|
||||
add,
|
||||
remove,
|
||||
)
|
||||
.unwrap();
|
||||
assert_users(&deps, Some(4), Some(6), None, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_and_remove_same_member() {
|
||||
// add will over-write and remove have no effect
|
||||
let mut deps = mock_dependencies();
|
||||
do_instantiate(deps.as_mut());
|
||||
|
||||
// USER1 is updated and remove in the same call, we should remove this an add member3
|
||||
let add = vec![
|
||||
Member {
|
||||
addr: USER1.into(),
|
||||
weight: 20,
|
||||
},
|
||||
Member {
|
||||
addr: USER3.into(),
|
||||
weight: 5,
|
||||
},
|
||||
];
|
||||
let remove = vec![USER1.into()];
|
||||
|
||||
// admin updates properly
|
||||
let height = mock_env().block.height;
|
||||
update_members(
|
||||
deps.as_mut(),
|
||||
height,
|
||||
Addr::unchecked(INIT_ADMIN),
|
||||
add,
|
||||
remove,
|
||||
)
|
||||
.unwrap();
|
||||
assert_users(&deps, None, Some(6), Some(5), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_remove_hooks() {
|
||||
// add will over-write and remove have no effect
|
||||
let mut deps = mock_dependencies();
|
||||
do_instantiate(deps.as_mut());
|
||||
|
||||
let hooks = HOOKS.query_hooks(deps.as_ref()).unwrap();
|
||||
assert!(hooks.hooks.is_empty());
|
||||
|
||||
let contract1 = String::from("hook1");
|
||||
let contract2 = String::from("hook2");
|
||||
|
||||
let add_msg = ExecuteMsg::AddHook {
|
||||
addr: contract1.clone(),
|
||||
};
|
||||
|
||||
// non-admin cannot add hook
|
||||
let user_info = mock_info(USER1, &[]);
|
||||
let err = execute(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
user_info.clone(),
|
||||
add_msg.clone(),
|
||||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(err, HookError::Admin(AdminError::NotAdmin {}).into());
|
||||
|
||||
// admin can add it, and it appears in the query
|
||||
let admin_info = mock_info(INIT_ADMIN, &[]);
|
||||
let _ = execute(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
admin_info.clone(),
|
||||
add_msg.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
let hooks = HOOKS.query_hooks(deps.as_ref()).unwrap();
|
||||
assert_eq!(hooks.hooks, vec![contract1.clone()]);
|
||||
|
||||
// cannot remove a non-registered contract
|
||||
let remove_msg = ExecuteMsg::RemoveHook {
|
||||
addr: contract2.clone(),
|
||||
};
|
||||
let err = execute(deps.as_mut(), mock_env(), admin_info.clone(), remove_msg).unwrap_err();
|
||||
assert_eq!(err, HookError::HookNotRegistered {}.into());
|
||||
|
||||
// add second contract
|
||||
let add_msg2 = ExecuteMsg::AddHook {
|
||||
addr: contract2.clone(),
|
||||
};
|
||||
let _ = execute(deps.as_mut(), mock_env(), admin_info.clone(), add_msg2).unwrap();
|
||||
let hooks = HOOKS.query_hooks(deps.as_ref()).unwrap();
|
||||
assert_eq!(hooks.hooks, vec![contract1.clone(), contract2.clone()]);
|
||||
|
||||
// cannot re-add an existing contract
|
||||
let err = execute(deps.as_mut(), mock_env(), admin_info.clone(), add_msg).unwrap_err();
|
||||
assert_eq!(err, HookError::HookAlreadyRegistered {}.into());
|
||||
|
||||
// non-admin cannot remove
|
||||
let remove_msg = ExecuteMsg::RemoveHook { addr: contract1 };
|
||||
let err = execute(deps.as_mut(), mock_env(), user_info, remove_msg.clone()).unwrap_err();
|
||||
assert_eq!(err, HookError::Admin(AdminError::NotAdmin {}).into());
|
||||
|
||||
// remove the original
|
||||
let _ = execute(deps.as_mut(), mock_env(), admin_info, remove_msg).unwrap();
|
||||
let hooks = HOOKS.query_hooks(deps.as_ref()).unwrap();
|
||||
assert_eq!(hooks.hooks, vec![contract2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hooks_fire() {
|
||||
let mut deps = mock_dependencies();
|
||||
do_instantiate(deps.as_mut());
|
||||
|
||||
let hooks = HOOKS.query_hooks(deps.as_ref()).unwrap();
|
||||
assert!(hooks.hooks.is_empty());
|
||||
|
||||
let contract1 = String::from("hook1");
|
||||
let contract2 = String::from("hook2");
|
||||
|
||||
// register 2 hooks
|
||||
let admin_info = mock_info(INIT_ADMIN, &[]);
|
||||
let add_msg = ExecuteMsg::AddHook {
|
||||
addr: contract1.clone(),
|
||||
};
|
||||
let add_msg2 = ExecuteMsg::AddHook {
|
||||
addr: contract2.clone(),
|
||||
};
|
||||
for msg in vec![add_msg, add_msg2] {
|
||||
let _ = execute(deps.as_mut(), mock_env(), admin_info.clone(), msg).unwrap();
|
||||
}
|
||||
|
||||
// make some changes - add 3, remove 2, and update 1
|
||||
// USER1 is updated and remove in the same call, we should remove this an add member3
|
||||
let add = vec![
|
||||
Member {
|
||||
addr: USER1.into(),
|
||||
weight: 20,
|
||||
},
|
||||
Member {
|
||||
addr: USER3.into(),
|
||||
weight: 5,
|
||||
},
|
||||
];
|
||||
let remove = vec![USER2.into()];
|
||||
let msg = ExecuteMsg::UpdateMembers { remove, add };
|
||||
|
||||
// admin updates properly
|
||||
assert_users(&deps, Some(11), Some(6), None, None);
|
||||
let res = execute(deps.as_mut(), mock_env(), admin_info, msg).unwrap();
|
||||
assert_users(&deps, Some(20), None, Some(5), None);
|
||||
|
||||
// ensure 2 messages for the 2 hooks
|
||||
assert_eq!(res.messages.len(), 2);
|
||||
// same order as in the message (adds first, then remove)
|
||||
let diffs = vec![
|
||||
MemberDiff::new(USER1, Some(11), Some(20)),
|
||||
MemberDiff::new(USER3, None, Some(5)),
|
||||
MemberDiff::new(USER2, Some(6), None),
|
||||
];
|
||||
let hook_msg = MemberChangedHookMsg { diffs };
|
||||
let msg1 = SubMsg::new(hook_msg.clone().into_cosmos_msg(contract1).unwrap());
|
||||
let msg2 = SubMsg::new(hook_msg.into_cosmos_msg(contract2).unwrap());
|
||||
assert_eq!(res.messages, vec![msg1, msg2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raw_queries_work() {
|
||||
// add will over-write and remove have no effect
|
||||
let mut deps = mock_dependencies();
|
||||
do_instantiate(deps.as_mut());
|
||||
|
||||
// get total from raw key
|
||||
let total_raw = deps.storage.get(TOTAL_KEY.as_bytes()).unwrap();
|
||||
let total: u64 = from_slice(&total_raw).unwrap();
|
||||
assert_eq!(17, total);
|
||||
|
||||
// get member votes from raw key
|
||||
let member2_raw = deps.storage.get(&member_key(USER2)).unwrap();
|
||||
let member2: u64 = from_slice(&member2_raw).unwrap();
|
||||
assert_eq!(6, member2);
|
||||
|
||||
// and execute misses
|
||||
let member3_raw = deps.storage.get(&member_key(USER3));
|
||||
assert_eq!(None, member3_raw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
use cosmwasm_std::StdError;
|
||||
use cosmwasm_std::{OverflowError, StdError};
|
||||
use thiserror::Error;
|
||||
|
||||
use cw_controllers::{AdminError, HookError};
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum ContractError {
|
||||
#[error(transparent)]
|
||||
#[error("{0}")]
|
||||
Std(#[from] StdError),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("{0}")]
|
||||
Hook(#[from] HookError),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("{0}")]
|
||||
Admin(#[from] AdminError),
|
||||
|
||||
#[error("{0}")]
|
||||
Overflow(#[from] OverflowError),
|
||||
|
||||
#[error("Unauthorized")]
|
||||
Unauthorized {},
|
||||
|
||||
#[error("Message contained duplicate member: {member}")]
|
||||
DuplicateMember { member: String },
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::Deref;
|
||||
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg};
|
||||
use cw4::{Cw4Contract, Member};
|
||||
|
||||
use crate::ContractError;
|
||||
use nym_group_contract_common::msg::ExecuteMsg;
|
||||
|
||||
/// Cw4GroupContract is a wrapper around Cw4Contract that provides a lot of helpers
|
||||
/// for working with cw4-group contracts.
|
||||
///
|
||||
/// It extends Cw4Contract to add the extra calls from cw4-group.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[cw_serde]
|
||||
pub struct Cw4GroupContract(pub Cw4Contract);
|
||||
|
||||
impl Deref for Cw4GroupContract {
|
||||
@@ -41,3 +41,17 @@ impl Cw4GroupContract {
|
||||
self.encode_msg(msg)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sorts the slice and verifies all member addresses are unique.
|
||||
pub fn validate_unique_members(members: &mut [Member]) -> Result<(), ContractError> {
|
||||
members.sort_by(|a, b| a.addr.cmp(&b.addr));
|
||||
for (a, b) in members.iter().zip(members.iter().skip(1)) {
|
||||
if a.addr == b.addr {
|
||||
return Err(ContractError::DuplicateMember {
|
||||
member: a.addr.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
/*!
|
||||
This is a basic implementation of the [cw4 spec](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw4/README.md).
|
||||
It fulfills all elements of the spec, including the raw query lookups,
|
||||
and it designed to be used as a backing storage for
|
||||
[cw3 compliant contracts](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw3/README.md).
|
||||
|
||||
It stores a set of members along with an admin, and allows the admin to
|
||||
update the state. Raw queries (intended for cross-contract queries)
|
||||
can check a given member address and the total weight. Smart queries (designed
|
||||
for client API) can do the same, and also query the admin address as well as
|
||||
paginate over all members.
|
||||
|
||||
For more information on this contract, please check out the
|
||||
[README](https://github.com/CosmWasm/cw-plus/blob/main/contracts/cw4-group/README.md).
|
||||
*/
|
||||
|
||||
pub mod contract;
|
||||
pub mod error;
|
||||
pub mod helpers;
|
||||
pub mod state;
|
||||
|
||||
pub use crate::error::ContractError;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
use cosmwasm_std::Addr;
|
||||
use cw4::TOTAL_KEY;
|
||||
use cw4::{
|
||||
MEMBERS_CHANGELOG, MEMBERS_CHECKPOINTS, MEMBERS_KEY, TOTAL_KEY, TOTAL_KEY_CHANGELOG,
|
||||
TOTAL_KEY_CHECKPOINTS,
|
||||
};
|
||||
use cw_controllers::{Admin, Hooks};
|
||||
use cw_storage_plus::{Item, SnapshotMap, Strategy};
|
||||
use cw_storage_plus::{SnapshotItem, SnapshotMap, Strategy};
|
||||
|
||||
pub const ADMIN: Admin = Admin::new("admin");
|
||||
pub const HOOKS: Hooks = Hooks::new("cw4-hooks");
|
||||
|
||||
pub const TOTAL: Item<u64> = Item::new(TOTAL_KEY);
|
||||
|
||||
pub const MEMBERS: SnapshotMap<&Addr, u64> = SnapshotMap::new(
|
||||
cw4::MEMBERS_KEY,
|
||||
cw4::MEMBERS_CHECKPOINTS,
|
||||
cw4::MEMBERS_CHANGELOG,
|
||||
pub const TOTAL: SnapshotItem<u64> = SnapshotItem::new(
|
||||
TOTAL_KEY,
|
||||
TOTAL_KEY_CHECKPOINTS,
|
||||
TOTAL_KEY_CHANGELOG,
|
||||
Strategy::EveryBlock,
|
||||
);
|
||||
|
||||
pub const MEMBERS: SnapshotMap<&Addr, u64> = SnapshotMap::new(
|
||||
MEMBERS_KEY,
|
||||
MEMBERS_CHECKPOINTS,
|
||||
MEMBERS_CHANGELOG,
|
||||
Strategy::EveryBlock,
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user