Compare commits
43 Commits
base
..
fix/doclink
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bc7448b08 | |||
| 42acbfe806 | |||
| 7c55483585 | |||
| c04cc9a4cf | |||
| 17258d1445 | |||
| 8f3d7606f5 | |||
| fd97f0e8ca | |||
| d4ce1635a8 | |||
| 2bc564ad01 | |||
| 5910bcbc02 | |||
| 8c63fe9d0d | |||
| 6e5a1973da | |||
| 1aa11887aa | |||
| 07740cbf08 | |||
| 87cb8a6b20 | |||
| 2977b8f25f | |||
| 9ae4fd04ac | |||
| 4470969bec | |||
| 1a4c3a7709 | |||
| 99b31920d5 | |||
| 019b3299f2 | |||
| d684957423 | |||
| cb4eda4c62 | |||
| ac5f380ee2 | |||
| 4e278ca07d | |||
| cd6a725875 | |||
| 62ccb6b4cd | |||
| 365e0134b4 | |||
| d5514a060c | |||
| 8432c30f6c | |||
| c2764f90b3 | |||
| 958b6d37ee | |||
| 5e36bb014c | |||
| 8d821881ae | |||
| fca9761145 | |||
| 11481e4d13 | |||
| a6a39d1234 | |||
| 5f35d54fcb | |||
| c8b82a9553 | |||
| 00c2f5359c | |||
| 1a4e0f4e08 | |||
| 69230a10cb | |||
| 3a0c8f3f4e |
@@ -63,7 +63,7 @@ jobs:
|
||||
- name: Install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: 1.69.0
|
||||
|
||||
- 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: stable
|
||||
toolchain: 1.69.0
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
@@ -107,6 +107,8 @@ 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
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
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: ???
|
||||
@@ -0,0 +1,102 @@
|
||||
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,6 +4,36 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [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
+36
-14
@@ -1243,6 +1243,20 @@ 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"
|
||||
@@ -1632,7 +1646,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "explorer-api"
|
||||
version = "1.1.19"
|
||||
version = "1.1.20"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 4.2.7",
|
||||
@@ -3176,7 +3190,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.20"
|
||||
version = "1.1.21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3310,7 +3324,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-cli"
|
||||
version = "1.1.19"
|
||||
version = "1.1.20"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.13.1",
|
||||
@@ -3359,6 +3373,7 @@ 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",
|
||||
@@ -3373,7 +3388,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client"
|
||||
version = "1.1.19"
|
||||
version = "1.1.20"
|
||||
dependencies = [
|
||||
"clap 4.2.7",
|
||||
"dirs",
|
||||
@@ -3531,7 +3546,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -3587,7 +3602,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-crypto"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"aes 0.8.2",
|
||||
"blake3",
|
||||
@@ -3644,7 +3659,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.19"
|
||||
version = "1.1.20"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3776,7 +3791,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -3795,7 +3810,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnode"
|
||||
version = "1.1.20"
|
||||
version = "1.1.21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58",
|
||||
@@ -3905,10 +3920,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-requester"
|
||||
version = "1.1.19"
|
||||
version = "1.1.20"
|
||||
dependencies = [
|
||||
"async-file-watcher",
|
||||
"async-trait",
|
||||
"bs58",
|
||||
"clap 4.2.7",
|
||||
"dirs",
|
||||
"futures",
|
||||
@@ -3931,11 +3947,13 @@ 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",
|
||||
@@ -3947,7 +3965,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-network-statistics"
|
||||
version = "1.1.19"
|
||||
version = "1.1.20"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"log",
|
||||
@@ -4022,7 +4040,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-pemstore"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"pem",
|
||||
]
|
||||
@@ -4063,8 +4081,12 @@ name = "nym-service-provider-directory-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-utils",
|
||||
"nym-contracts-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4085,7 +4107,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.19"
|
||||
version = "1.1.20"
|
||||
dependencies = [
|
||||
"clap 4.2.7",
|
||||
"lazy_static",
|
||||
@@ -4481,7 +4503,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"nym-contracts-common",
|
||||
|
||||
+11
-13
@@ -116,21 +116,19 @@ async-trait = "0.1.64"
|
||||
anyhow = "1.0.71"
|
||||
bip39 = { version = "2.0.0", features = ["zeroize"] }
|
||||
cfg-if = "1.0.0"
|
||||
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" }
|
||||
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" }
|
||||
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"
|
||||
|
||||
@@ -22,7 +22,7 @@ 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/building-nym.html).
|
||||
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers).
|
||||
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/wallet/desktop-wallet.html).
|
||||
|
||||
### Developing
|
||||
|
||||
@@ -32,7 +32,11 @@ For Typescript components, please see [ts-packages](./ts-packages).
|
||||
|
||||
### Developer chat
|
||||
|
||||
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.
|
||||
> 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)
|
||||
|
||||
### Rewards
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.19"
|
||||
version = "1.1.20"
|
||||
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.19"
|
||||
version = "1.1.20"
|
||||
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
+23
-5
@@ -865,6 +865,20 @@ 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"
|
||||
@@ -2367,7 +2381,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -2402,7 +2416,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-crypto"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"aes 0.8.2",
|
||||
"blake3",
|
||||
@@ -2509,7 +2523,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -2609,7 +2623,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-pemstore"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"pem",
|
||||
]
|
||||
@@ -2619,8 +2633,12 @@ name = "nym-service-provider-directory-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-utils",
|
||||
"nym-contracts-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2873,7 +2891,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.6.0"
|
||||
version = "0.7.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_ne!(
|
||||
debug_assert_eq!(
|
||||
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, ClientCoreError>
|
||||
) -> Result<(GatewayEndpointConfig, ManagedKeys), 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(_) => {
|
||||
Ok(loaded_keys) => {
|
||||
// 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);
|
||||
return Ok((config, loaded_keys));
|
||||
} 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)
|
||||
Ok((gateway_details, managed_keys))
|
||||
}
|
||||
|
||||
/// Convenience function for setting up the gateway for a client given a `Config`. Depending on the
|
||||
@@ -299,6 +299,15 @@ 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 = { workspace = true, features = ["rpc", "bip32", "cosmwasm"], optional = true }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", 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 = { workspace = true, features = ["rpc", "bip32"] }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", 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, Denom as CosmosDenom};
|
||||
use cosmrs::{AccountId, Decimal as CosmosDecimal, Denom as CosmosDenom};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
fn cosmos_denom_inner_getter(val: &CosmosDenom) -> String {
|
||||
@@ -144,11 +144,29 @@ 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,
|
||||
amount: u128,
|
||||
#[serde(with = "Decimal")]
|
||||
amount: CosmosDecimal,
|
||||
}
|
||||
|
||||
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, Denom};
|
||||
pub use cosmrs::{bip32, AccountId, Decimal, 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::ContractBuildInformation;
|
||||
use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
|
||||
use nym_service_provider_directory_common::{
|
||||
msg::QueryMsg as SpQueryMsg,
|
||||
response::{
|
||||
ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse,
|
||||
},
|
||||
NymAddress, ServiceId, ServiceInfo,
|
||||
NymAddress, Service, ServiceId,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -63,17 +63,14 @@ pub trait SpDirectoryQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_services(&self) -> Result<Vec<ServiceInfo>, NyxdError> {
|
||||
async fn get_all_services(&self) -> Result<Vec<Service>, 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) = last_id {
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
@@ -82,6 +79,13 @@ 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,8 +2,9 @@
|
||||
// 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, ServiceId, ServiceType,
|
||||
msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceDetails, ServiceId,
|
||||
};
|
||||
|
||||
use crate::nyxd::{
|
||||
@@ -22,16 +23,16 @@ pub trait SpDirectorySigningClient {
|
||||
|
||||
async fn announce_service_provider(
|
||||
&self,
|
||||
nym_address: NymAddress,
|
||||
service_type: ServiceType,
|
||||
service: ServiceDetails,
|
||||
owner_signature: MessageSignature,
|
||||
deposit: Coin,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_service_provider_directory_contract(
|
||||
fee,
|
||||
SpExecuteMsg::Announce {
|
||||
nym_address,
|
||||
service_type,
|
||||
service,
|
||||
owner_signature,
|
||||
},
|
||||
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 = { workspace = true, features = ["ecdsa", "sha256"] }
|
||||
k256 = { version = "0.10", 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 = { workspace = true }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
|
||||
cosmwasm-std = { workspace = true }
|
||||
|
||||
nym-validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] }
|
||||
@@ -40,3 +40,4 @@ 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,8 +56,6 @@ 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,6 +14,7 @@ pub struct Mixnet {
|
||||
pub command: MixnetCommands,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetCommands {
|
||||
/// Query the mixnet directory
|
||||
|
||||
@@ -15,6 +15,7 @@ pub struct MixnetOperators {
|
||||
pub command: MixnetOperatorsCommands,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MixnetOperatorsCommands {
|
||||
/// Manage your mixnode
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use nym_service_provider_directory_common::{Coin, NymAddress, ServiceType};
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use nym_service_provider_directory_common::{Coin, NymAddress, ServiceDetails, ServiceType};
|
||||
use nym_validator_client::nyxd::traits::SpDirectorySigningClient;
|
||||
|
||||
use crate::context::SigningClient;
|
||||
@@ -10,9 +11,15 @@ 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) {
|
||||
@@ -20,12 +27,17 @@ 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(nym_address, service_type, deposit.into(), None)
|
||||
.announce_service_provider(service, args.signature, deposit.into(), None)
|
||||
.await
|
||||
.expect("Failed to announce service provider");
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// 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,6 +1,7 @@
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod announce;
|
||||
pub mod announce_sign_payload;
|
||||
pub mod delete;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
@@ -10,10 +11,13 @@ 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.service.announcer.to_string(),
|
||||
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, Eq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, 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, Eq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct SpendCredentialData {
|
||||
funds: Coin,
|
||||
blinded_serial_number: String,
|
||||
@@ -43,7 +43,7 @@ pub enum SpendCredentialStatus {
|
||||
Spent,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct SpendCredential {
|
||||
funds: Coin,
|
||||
blinded_serial_number: String,
|
||||
@@ -74,7 +74,7 @@ impl SpendCredential {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, 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, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct SpendCredentialResponse {
|
||||
pub spend_credential: Option<SpendCredential>,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.4.0"
|
||||
version = "0.5.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, Eq, JsonSchema)]
|
||||
#[derive(Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct MessageSignature(Vec<u8>);
|
||||
|
||||
impl MessageSignature {
|
||||
|
||||
@@ -11,6 +11,9 @@ 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,8 +6,6 @@ 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,7 +1,13 @@
|
||||
use cosmwasm_schema::{cw_serde, QueryResponses};
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use cw4::Member;
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct InstantiateMsg {
|
||||
/// The admin is the only account that can update the group state.
|
||||
/// Omit it to make the group immutable.
|
||||
@@ -9,7 +15,8 @@ pub struct InstantiateMsg {
|
||||
pub members: Vec<Member>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
/// Change the admin
|
||||
UpdateAdmin { admin: Option<String> },
|
||||
@@ -25,24 +32,23 @@ pub enum ExecuteMsg {
|
||||
RemoveHook { addr: String },
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(QueryResponses)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum QueryMsg {
|
||||
#[returns(cw_controllers::AdminResponse)]
|
||||
/// Return AdminResponse
|
||||
Admin {},
|
||||
#[returns(cw4::TotalWeightResponse)]
|
||||
TotalWeight { at_height: Option<u64> },
|
||||
#[returns(cw4::MemberListResponse)]
|
||||
/// Return TotalWeightResponse
|
||||
TotalWeight {},
|
||||
/// Returns MembersListResponse
|
||||
ListMembers {
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
#[returns(cw4::MemberResponse)]
|
||||
/// Returns MemberResponse
|
||||
Member {
|
||||
addr: String,
|
||||
at_height: Option<u64>,
|
||||
},
|
||||
/// Shows all registered hooks.
|
||||
#[returns(cw_controllers::HooksResponse)]
|
||||
/// Shows all registered hooks. Returns HooksResponse.
|
||||
Hooks {},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.5.0"
|
||||
version = "0.6.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.4.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.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, Eq, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, 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, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, 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, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, 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, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, 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, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, 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, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct GatewayBond {
|
||||
pub pledge_amount: Coin,
|
||||
pub owner: Addr,
|
||||
@@ -132,7 +132,7 @@ impl GatewayConfigUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct PagedGatewayResponse {
|
||||
pub nodes: Vec<GatewayBond>,
|
||||
pub per_page: usize,
|
||||
@@ -153,13 +153,13 @@ impl PagedGatewayResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct GatewayOwnershipResponse {
|
||||
pub address: Addr,
|
||||
pub gateway: Option<GatewayBond>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct GatewayBondResponse {
|
||||
pub identity: IdentityKey,
|
||||
pub gateway: Option<GatewayBond>,
|
||||
|
||||
@@ -489,7 +489,7 @@ impl CurrentIntervalResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct PendingEpochEventsResponse {
|
||||
pub seconds_until_executable: i64,
|
||||
pub events: Vec<PendingEpochEvent>,
|
||||
@@ -510,7 +510,7 @@ impl PendingEpochEventsResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, 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, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct MixNodeDetails {
|
||||
pub bond_information: MixNodeBond,
|
||||
pub rewarding_details: MixNodeRewarding,
|
||||
@@ -86,7 +86,7 @@ impl MixNodeDetails {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, 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, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, 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, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct MixNodeCostParams {
|
||||
pub profit_margin_percent: Percent,
|
||||
|
||||
@@ -686,7 +686,7 @@ impl MixNodeConfigUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct PagedMixnodeBondsResponse {
|
||||
pub nodes: Vec<MixNodeBond>,
|
||||
pub per_page: usize,
|
||||
@@ -703,7 +703,7 @@ impl PagedMixnodeBondsResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct PagedMixnodesDetailsResponse {
|
||||
pub nodes: Vec<MixNodeDetails>,
|
||||
pub per_page: usize,
|
||||
@@ -745,19 +745,19 @@ impl PagedUnbondedMixnodesResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct MixOwnershipResponse {
|
||||
pub address: Addr,
|
||||
pub mixnode_details: Option<MixNodeDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct MixnodeDetailsResponse {
|
||||
pub mix_id: MixId,
|
||||
pub mixnode_details: Option<MixNodeDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, 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, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingEpochEvent {
|
||||
pub id: EpochEventId,
|
||||
pub event: PendingEpochEventData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingEpochEventData {
|
||||
pub created_at: BlockHeight,
|
||||
pub kind: PendingEpochEventKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
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, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingIntervalEvent {
|
||||
pub id: IntervalEventId,
|
||||
pub event: PendingIntervalEventData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PendingIntervalEventData {
|
||||
pub created_at: BlockHeight,
|
||||
pub kind: PendingIntervalEventKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
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, Eq)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq)]
|
||||
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, Eq)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq)]
|
||||
pub struct EstimatedCurrentEpochRewardResponse {
|
||||
pub original_stake: Option<Coin>,
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
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;
|
||||
@@ -11,8 +12,6 @@ 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;
|
||||
@@ -23,7 +22,7 @@ pub type EpochEventId = u32;
|
||||
pub type IntervalEventId = u32;
|
||||
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, PartialEq)]
|
||||
pub struct LayerAssignment {
|
||||
mix_id: MixId,
|
||||
layer: Layer,
|
||||
@@ -119,7 +118,7 @@ impl Index<Layer> for LayerDistribution {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct ContractState {
|
||||
pub owner: Addr, // only the owner account can update state
|
||||
pub rewarding_validator_address: Addr,
|
||||
@@ -131,7 +130,7 @@ pub struct ContractState {
|
||||
pub params: ContractStateParams,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, 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,8 +9,6 @@ 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,8 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::StdError;
|
||||
use cw3::DepositError;
|
||||
use cw_utils::{PaymentError, ThresholdError};
|
||||
use cw_utils::ThresholdError;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -18,6 +17,9 @@ pub enum ContractError {
|
||||
#[error("Group contract invalid address '{addr}'")]
|
||||
InvalidGroup { addr: String },
|
||||
|
||||
#[error("Coconut bandwidth contract address not found")]
|
||||
InvalidCoconutBandwidth {},
|
||||
|
||||
#[error("Unauthorized")]
|
||||
Unauthorized {},
|
||||
|
||||
@@ -41,10 +43,4 @@ pub enum ContractError {
|
||||
|
||||
#[error("Cannot close completed or passed proposals")]
|
||||
WrongCloseStatus {},
|
||||
|
||||
#[error("{0}")]
|
||||
Payment(#[from] PaymentError),
|
||||
|
||||
#[error("{0}")]
|
||||
Deposit(#[from] DepositError),
|
||||
}
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
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 cosmwasm_schema::{cw_serde, QueryResponses};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use cosmwasm_std::{CosmosMsg, Empty};
|
||||
use cw3::{UncheckedDepositInfo, Vote};
|
||||
use cw3::Vote;
|
||||
use cw4::MemberChangedHookMsg;
|
||||
use cw_utils::{Duration, Expiration, Threshold};
|
||||
|
||||
use crate::state::Executor;
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
|
||||
pub struct InstantiateMsg {
|
||||
// this is the group contract that contains the member list
|
||||
pub group_addr: String,
|
||||
@@ -17,15 +17,11 @@ 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
|
||||
#[cw_serde]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
Propose {
|
||||
title: String,
|
||||
@@ -49,44 +45,41 @@ pub enum ExecuteMsg {
|
||||
}
|
||||
|
||||
// We can also add this as a cw3 extension
|
||||
#[cw_serde]
|
||||
#[derive(QueryResponses)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum QueryMsg {
|
||||
#[returns(cw_utils::ThresholdResponse)]
|
||||
/// Return ThresholdResponse
|
||||
Threshold {},
|
||||
#[returns(cw3::ProposalResponse)]
|
||||
/// Returns ProposalResponse
|
||||
Proposal { proposal_id: u64 },
|
||||
#[returns(cw3::ProposalListResponse)]
|
||||
/// Returns ProposalListResponse
|
||||
ListProposals {
|
||||
start_after: Option<u64>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
#[returns(cw3::ProposalListResponse)]
|
||||
/// Returns ProposalListResponse
|
||||
ReverseProposals {
|
||||
start_before: Option<u64>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
#[returns(cw3::VoteResponse)]
|
||||
/// Returns VoteResponse
|
||||
Vote { proposal_id: u64, voter: String },
|
||||
#[returns(cw3::VoteListResponse)]
|
||||
/// Returns VoteListResponse
|
||||
ListVotes {
|
||||
proposal_id: u64,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
#[returns(cw3::VoterResponse)]
|
||||
/// Returns VoterInfo
|
||||
Voter { address: String },
|
||||
#[returns(cw3::VoterListResponse)]
|
||||
/// Returns VoterListResponse
|
||||
ListVoters {
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
/// Gets the current configuration.
|
||||
#[returns(crate::state::Config)]
|
||||
Config {},
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {
|
||||
pub coconut_bandwidth_address: String,
|
||||
pub coconut_dkg_address: String,
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
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,5 +7,9 @@ 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 }
|
||||
|
||||
+20
-3
@@ -1,10 +1,12 @@
|
||||
use cosmwasm_std::{Addr, StdError};
|
||||
use cw_controllers::AdminError;
|
||||
use nym_service_provider_directory_common::{NymAddress, ServiceId};
|
||||
use nym_contracts_common::signing::verifier::ApiVerifierError;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{NymAddress, ServiceId};
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum ContractError {
|
||||
pub enum SpContractError {
|
||||
#[error("{0}")]
|
||||
Std(#[from] StdError),
|
||||
|
||||
@@ -46,6 +48,21 @@ pub enum ContractError {
|
||||
value: String,
|
||||
error_message: String,
|
||||
},
|
||||
|
||||
#[error("Failed to recover ed25519 public key from its base58 representation - {0}")]
|
||||
MalformedEd25519IdentityKey(String),
|
||||
|
||||
#[error("Failed to recover ed25519 signature from its base58 representation - {0}")]
|
||||
MalformedEd25519Signature(String),
|
||||
|
||||
#[error("Provided ed25519 signature did not verify correctly")]
|
||||
InvalidEd25519Signature,
|
||||
|
||||
#[error("failed to verify message signature: {source}")]
|
||||
SignatureVerificationFailure {
|
||||
#[from]
|
||||
source: ApiVerifierError,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) type Result<T, E = ContractError> = std::result::Result<T, E>;
|
||||
pub type Result<T, E = SpContractError> = std::result::Result<T, E>;
|
||||
@@ -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_type.to_string())
|
||||
.add_attribute(NYM_ADDRESS, service.nym_address.to_string())
|
||||
.add_attribute(SERVICE_TYPE, service.service.service_type.to_string())
|
||||
.add_attribute(NYM_ADDRESS, service.service.nym_address.to_string())
|
||||
.add_attribute(OWNER, service.announcer.to_string())
|
||||
}
|
||||
|
||||
pub fn new_delete_id_event(service_id: ServiceId, service: Service) -> Event {
|
||||
pub fn new_delete_id_event(service: Service) -> Event {
|
||||
Event::new(ServiceProviderEventType::DeleteId)
|
||||
.add_attribute(ACTION, ServiceProviderEventType::DeleteId)
|
||||
.add_attribute(SERVICE_ID, service_id.to_string())
|
||||
.add_attribute(NYM_ADDRESS, service.nym_address.to_string())
|
||||
.add_attribute(SERVICE_ID, service.service_id.to_string())
|
||||
.add_attribute(NYM_ADDRESS, service.service.nym_address.to_string())
|
||||
}
|
||||
|
||||
pub fn new_update_deposit_required_event(deposit_required: Coin) -> Event {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
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,5 +1,6 @@
|
||||
use crate::{NymAddress, ServiceId, ServiceType};
|
||||
use crate::{NymAddress, ServiceDetails, ServiceId};
|
||||
use cosmwasm_std::Coin;
|
||||
use nym_contracts_common::signing::MessageSignature;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
@@ -22,8 +23,8 @@ pub struct MigrateMsg {}
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
Announce {
|
||||
nym_address: NymAddress,
|
||||
service_type: ServiceType,
|
||||
service: ServiceDetails,
|
||||
owner_signature: MessageSignature,
|
||||
},
|
||||
DeleteId {
|
||||
service_id: ServiceId,
|
||||
@@ -44,9 +45,12 @@ impl ExecuteMsg {
|
||||
pub fn default_memo(&self) -> String {
|
||||
match self {
|
||||
ExecuteMsg::Announce {
|
||||
nym_address,
|
||||
service_type,
|
||||
} => format!("announcing {nym_address} as type {service_type}"),
|
||||
service,
|
||||
owner_signature: _,
|
||||
} => format!(
|
||||
"announcing {} as type {}",
|
||||
service.nym_address, service.service_type
|
||||
),
|
||||
ExecuteMsg::DeleteId { service_id } => {
|
||||
format!("deleting service with service id {service_id}")
|
||||
}
|
||||
@@ -76,6 +80,9 @@ 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::{msg::ExecuteMsg, Service, ServiceId, ServiceInfo};
|
||||
use crate::{Service, ServiceId};
|
||||
use cosmwasm_std::Coin;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -13,22 +13,17 @@ pub struct ServiceInfoResponse {
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct ServicesListResponse {
|
||||
pub services: Vec<ServiceInfo>,
|
||||
pub services: Vec<Service>,
|
||||
}
|
||||
|
||||
impl ServicesListResponse {
|
||||
pub fn new(services: Vec<(ServiceId, Service)>) -> ServicesListResponse {
|
||||
ServicesListResponse {
|
||||
services: services
|
||||
.into_iter()
|
||||
.map(|(service_id, service)| ServiceInfo::new(service_id, service))
|
||||
.collect(),
|
||||
}
|
||||
pub fn new(services: Vec<Service>) -> ServicesListResponse {
|
||||
ServicesListResponse { services }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[ServiceInfo]> for ServicesListResponse {
|
||||
fn from(services: &[ServiceInfo]) -> Self {
|
||||
impl From<&[Service]> for ServicesListResponse {
|
||||
fn from(services: &[Service]) -> Self {
|
||||
Self {
|
||||
services: services.to_vec(),
|
||||
}
|
||||
@@ -38,21 +33,17 @@ impl From<&[ServiceInfo]> for ServicesListResponse {
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct PagedServicesListResponse {
|
||||
pub services: Vec<ServiceInfo>,
|
||||
pub services: Vec<Service>,
|
||||
pub per_page: usize,
|
||||
pub start_next_after: Option<ServiceId>,
|
||||
}
|
||||
|
||||
impl PagedServicesListResponse {
|
||||
pub fn new(
|
||||
services: Vec<(ServiceId, Service)>,
|
||||
services: Vec<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,
|
||||
@@ -66,12 +57,3 @@ 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
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,6 +1,7 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use cosmwasm_std::{Addr, Coin};
|
||||
use nym_contracts_common::IdentityKey;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -9,11 +10,11 @@ pub type ServiceId = u32;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)]
|
||||
pub struct Service {
|
||||
/// The address of the service.
|
||||
pub nym_address: NymAddress,
|
||||
/// The service type.
|
||||
pub service_type: ServiceType,
|
||||
/// Service owner.
|
||||
/// Unique id assigned to the anounced service.
|
||||
pub service_id: ServiceId,
|
||||
/// The announced service.
|
||||
pub service: ServiceDetails,
|
||||
/// Address of the service owner.
|
||||
pub announcer: Addr,
|
||||
/// Block height at which the service was added.
|
||||
pub block_height: u64,
|
||||
@@ -21,6 +22,16 @@ 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")]
|
||||
@@ -28,7 +39,7 @@ pub enum NymAddress {
|
||||
/// String representation of a nym address, which is of the form
|
||||
/// client_id.client_enc@gateway_id.
|
||||
Address(String),
|
||||
// For the future when we have a nym-dns contract
|
||||
// String name that can looked up in the nym-name-service contract (once it exists)
|
||||
//Name(String),
|
||||
}
|
||||
|
||||
@@ -41,6 +52,7 @@ impl NymAddress {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
NymAddress::Address(address) => address,
|
||||
//NymAddress::Name(name) => name,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,19 +78,3 @@ 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.6.0"
|
||||
version = "0.7.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.5.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.4.0" }
|
||||
mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.6.0" }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
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, Eq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, 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, Eq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, 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, Eq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct OriginalVestingResponse {
|
||||
pub amount: Coin,
|
||||
pub number_of_periods: usize,
|
||||
|
||||
@@ -55,7 +55,7 @@ impl VestingSpecification {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, 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 = { workspace = true }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
|
||||
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.3.0"
|
||||
version = "0.4.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.2.0" }
|
||||
nym-pemstore = { path = "../../common/pemstore", version = "0.3.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.4.0"
|
||||
k256 = { workspace = true }
|
||||
bip32 = "0.3.0"
|
||||
k256 = "0.10.4"
|
||||
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.3.0" }
|
||||
nym-crypto = { path = "../crypto", version = "0.4.0" }
|
||||
nym-topology = { path = "../topology" }
|
||||
|
||||
[dev-dependencies]
|
||||
nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
|
||||
nym-crypto = { path = "../crypto", version = "0.3.0", features = ["asymmetric"] }
|
||||
nym-crypto = { path = "../crypto", version = "0.4.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.2.0"
|
||||
version = "0.3.0"
|
||||
edition = { workspace = true }
|
||||
authors = { workspace = true }
|
||||
license = { workspace = true }
|
||||
|
||||
@@ -13,7 +13,7 @@ mod inbound;
|
||||
mod outbound;
|
||||
|
||||
// TODO: make this configurable
|
||||
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(3);
|
||||
|
||||
// Send empty keepalive messages regurarly to keep the connection alive. This should be smaller
|
||||
// than [`MIX_TTL`].
|
||||
|
||||
@@ -20,7 +20,7 @@ url = "2.2"
|
||||
ts-rs = "6.1.2"
|
||||
|
||||
cosmwasm-std = { workspace = true }
|
||||
cosmrs = { workspace = true }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
|
||||
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client", features = [
|
||||
"nyxd-client",
|
||||
|
||||
Generated
+115
-239
@@ -241,8 +241,8 @@ dependencies = [
|
||||
"cosmwasm-storage",
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw3",
|
||||
"cw3-flex-multisig",
|
||||
"cw4",
|
||||
@@ -260,9 +260,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.2"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913"
|
||||
checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
@@ -272,11 +272,11 @@ checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b"
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-crypto"
|
||||
version = "1.2.5"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75836a10cb9654c54e77ee56da94d592923092a10b369cdb0dbd56acefc16340"
|
||||
checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
"digest 0.9.0",
|
||||
"ed25519-zebra",
|
||||
"k256",
|
||||
"rand_core 0.6.4",
|
||||
@@ -285,62 +285,45 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-derive"
|
||||
version = "1.2.5"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773"
|
||||
checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4"
|
||||
dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-schema"
|
||||
version = "1.2.5"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc"
|
||||
checksum = "772e80bbad231a47a2068812b723a1ff81dd4a0d56c9391ac748177bea3a61da"
|
||||
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.2.5"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb"
|
||||
checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"cosmwasm-crypto",
|
||||
"cosmwasm-derive",
|
||||
"derivative",
|
||||
"forward_ref",
|
||||
"hex",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde-json-wasm 0.5.1",
|
||||
"sha2 0.10.6",
|
||||
"serde-json-wasm",
|
||||
"thiserror",
|
||||
"uint",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-storage"
|
||||
version = "1.2.5"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3737a3aac48f5ed883b5b73bfb731e77feebd8fc6b43419844ec2971072164d"
|
||||
checksum = "d18403b07304d15d304dad11040d45bbcaf78d603b4be3fb5e2685c16f9229b5"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"serde",
|
||||
@@ -406,9 +389,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.4.9"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef"
|
||||
checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21"
|
||||
dependencies = [
|
||||
"generic-array 0.14.6",
|
||||
"rand_core 0.6.4",
|
||||
@@ -471,14 +454,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw-controllers"
|
||||
version = "1.0.1"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071"
|
||||
checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
@@ -486,17 +468,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw-multi-test"
|
||||
version = "0.16.4"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a18afd2e201221c6d72a57f0886ef2a22151bbc9e6db7af276fde8a91081042"
|
||||
checksum = "a3f9a8ab7c3c29ec93cb7a39ce4b14a05e053153b4a17ef7cf2246af1b7c087e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cosmwasm-storage",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"derivative",
|
||||
"itertools",
|
||||
"k256",
|
||||
"prost",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -505,20 +487,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw-storage-plus"
|
||||
version = "0.16.0"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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"
|
||||
checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"schemars",
|
||||
@@ -527,117 +498,50 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw-utils"
|
||||
version = "0.16.0"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6a84c6c1c0acc3616398eba50783934bd6c964bad6974241eaee3460c8f5b26"
|
||||
checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b"
|
||||
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.16.0"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91398113b806f4d2a8d5f8d05684704a20ffd5968bf87e3473e1973710b884ad"
|
||||
checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 0.16.0",
|
||||
"cw-storage-plus",
|
||||
"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 = "1.0.1"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa"
|
||||
checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-utils 1.0.1",
|
||||
"cw20",
|
||||
"cw-utils",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw3-fixed-multisig"
|
||||
version = "1.0.1"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9e2415adb201e5e89dab34edf59d7dc166bc558526de009a49ae66276c9119a"
|
||||
checksum = "df54aa54c13f405ec4ab36b6217538bc957d439eee58f89312db05a79caf6706"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw3",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -646,15 +550,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw3-flex-multisig"
|
||||
version = "1.0.0"
|
||||
version = "0.13.1"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"cw20",
|
||||
"cw20-base",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw3",
|
||||
"cw3-fixed-multisig",
|
||||
"cw4",
|
||||
@@ -662,31 +565,31 @@ dependencies = [
|
||||
"nym-group-contract-common",
|
||||
"nym-multisig-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw4"
|
||||
version = "1.0.1"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b"
|
||||
checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-storage-plus",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw4-group"
|
||||
version = "1.0.0"
|
||||
version = "0.13.4"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw4",
|
||||
"nym-group-contract-common",
|
||||
"schemars",
|
||||
@@ -696,12 +599,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.6.1"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
|
||||
checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -752,9 +654,9 @@ checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.14.8"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c"
|
||||
checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9"
|
||||
dependencies = [
|
||||
"der",
|
||||
"elliptic-curve",
|
||||
@@ -781,7 +683,7 @@ dependencies = [
|
||||
"ed25519",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"sha2 0.9.9",
|
||||
"sha2",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -796,7 +698,7 @@ dependencies = [
|
||||
"hex",
|
||||
"rand_core 0.6.4",
|
||||
"serde",
|
||||
"sha2 0.9.9",
|
||||
"sha2",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -808,18 +710,16 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.12.3"
|
||||
version = "0.11.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3"
|
||||
checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6"
|
||||
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",
|
||||
@@ -878,9 +778,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.12.1"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160"
|
||||
checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"subtle 2.4.1",
|
||||
@@ -1074,9 +974,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.12.1"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7"
|
||||
checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"rand_core 0.6.4",
|
||||
@@ -1120,7 +1020,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"hmac 0.11.0",
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1133,15 +1033,6 @@ 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"
|
||||
@@ -1232,14 +1123,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "k256"
|
||||
version = "0.11.6"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b"
|
||||
checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"sha2 0.10.6",
|
||||
"sec1",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1375,7 +1267,7 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cw-controllers",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-storage-plus",
|
||||
"nym-coconut-bandwidth-contract-common",
|
||||
"nym-multisig-contract-common",
|
||||
"schemars",
|
||||
@@ -1401,7 +1293,7 @@ dependencies = [
|
||||
"cosmwasm-storage",
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-storage-plus",
|
||||
"cw4",
|
||||
"cw4-group",
|
||||
"lazy_static",
|
||||
@@ -1418,7 +1310,7 @@ name = "nym-coconut-dkg-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-utils 1.0.1",
|
||||
"cw-utils",
|
||||
"nym-contracts-common",
|
||||
"nym-multisig-contract-common",
|
||||
"schemars",
|
||||
@@ -1427,7 +1319,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-contracts-common"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -1438,7 +1330,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-crypto"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"ed25519-dalek",
|
||||
@@ -1455,8 +1347,6 @@ dependencies = [
|
||||
name = "nym-group-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cw-controllers",
|
||||
"cw4",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -1471,8 +1361,8 @@ dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"cw-storage-plus",
|
||||
"cw2",
|
||||
"nym-contracts-common",
|
||||
"nym-crypto",
|
||||
"nym-mixnet-contract-common",
|
||||
@@ -1488,7 +1378,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-mixnet-contract-common"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
@@ -1497,7 +1387,7 @@ dependencies = [
|
||||
"nym-contracts-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde-json-wasm 0.4.1",
|
||||
"serde-json-wasm",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"time",
|
||||
@@ -1507,10 +1397,8 @@ dependencies = [
|
||||
name = "nym-multisig-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw-utils",
|
||||
"cw3",
|
||||
"cw4",
|
||||
"schemars",
|
||||
@@ -1526,9 +1414,9 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"nym-contracts-common",
|
||||
"nym-name-service-common",
|
||||
"rand 0.8.5",
|
||||
@@ -1567,7 +1455,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-pemstore"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"pem",
|
||||
]
|
||||
@@ -1577,14 +1465,18 @@ name = "nym-service-provider-directory"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58",
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw-utils 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"nym-contracts-common",
|
||||
"nym-crypto",
|
||||
"nym-service-provider-directory-common",
|
||||
"rand_chacha 0.2.2",
|
||||
"rstest",
|
||||
"semver",
|
||||
"serde",
|
||||
"thiserror",
|
||||
@@ -1596,8 +1488,12 @@ name = "nym-service-provider-directory-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-utils",
|
||||
"nym-contracts-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1617,8 +1513,8 @@ dependencies = [
|
||||
"cosmwasm-crypto",
|
||||
"cosmwasm-derive",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 1.0.1",
|
||||
"cw2 1.0.1",
|
||||
"cw-storage-plus",
|
||||
"cw2",
|
||||
"hex",
|
||||
"nym-contracts-common",
|
||||
"nym-mixnet-contract-common",
|
||||
@@ -1634,7 +1530,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"nym-contracts-common",
|
||||
@@ -1692,12 +1588,13 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.9.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
|
||||
checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1923,12 +1820,12 @@ checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.3.1"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb"
|
||||
checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525"
|
||||
dependencies = [
|
||||
"crypto-bigint",
|
||||
"hmac 0.12.1",
|
||||
"hmac",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -2037,11 +1934,10 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.3.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928"
|
||||
checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"der",
|
||||
"generic-array 0.14.6",
|
||||
"pkcs8",
|
||||
@@ -2073,15 +1969,6 @@ 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"
|
||||
@@ -2139,24 +2026,13 @@ dependencies = [
|
||||
"opaque-debug 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "1.6.4"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
|
||||
checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
"digest 0.9.0",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
@@ -2184,20 +2060,20 @@ dependencies = [
|
||||
"curve25519-dalek",
|
||||
"digest 0.9.0",
|
||||
"hkdf",
|
||||
"hmac 0.11.0",
|
||||
"hmac",
|
||||
"lioness",
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"rand_distr",
|
||||
"sha2 0.9.9",
|
||||
"sha2",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.6.0"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
|
||||
checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
|
||||
+13
-14
@@ -32,17 +32,16 @@ incremental = false
|
||||
overflow-checks = true
|
||||
|
||||
[workspace.dependencies]
|
||||
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"
|
||||
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"
|
||||
|
||||
@@ -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(
|
||||
|_pk, d| d.epoch_id,
|
||||
|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,8 +45,6 @@ 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,8 +52,6 @@ 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.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" }
|
||||
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" }
|
||||
|
||||
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(
|
||||
|_pk, d| d.owner.clone(),
|
||||
|d| d.owner.clone(),
|
||||
DELEGATION_PK_NAMESPACE,
|
||||
DELEGATION_OWNER_IDX_NAMESPACE,
|
||||
),
|
||||
mixnode: MultiIndex::new(
|
||||
|_pk, d| d.mix_id,
|
||||
|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;
|
||||
mod signing;
|
||||
pub 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(
|
||||
|_pk, d| d.owner.clone(),
|
||||
|d| d.owner.clone(),
|
||||
UNBONDED_MIXNODES_PK_NAMESPACE,
|
||||
UNBONDED_MIXNODES_OWNER_IDX_NAMESPACE,
|
||||
),
|
||||
identity_key: MultiIndex::new(
|
||||
|_pk, d| d.identity_key.clone(),
|
||||
|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 = "1.0.0"
|
||||
version = "0.13.1"
|
||||
authors = ["Ethan Frey <ethanfrey@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
description = "Implementing cw3 with multiple voting patterns and dynamic groups"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/CosmWasm/cw-plus"
|
||||
@@ -13,7 +13,6 @@ 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 = []
|
||||
|
||||
@@ -23,15 +22,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]
|
||||
cw4-group = { path = "../cw4-group", version = "1.0.0" }
|
||||
cosmwasm-schema = { version = "1.0.0" }
|
||||
cw4-group = { path = "../cw4-group", version = "0.13.4" }
|
||||
cw-multi-test = { workspace = true }
|
||||
cw20-base = "1.0.0"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,25 +1,2 @@
|
||||
/*!
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
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 --lib --target wasm32-unknown-unknown"
|
||||
wasm-debug = "build --lib --target wasm32-unknown-unknown"
|
||||
wasm = "build --release --target wasm32-unknown-unknown"
|
||||
wasm-debug = "build --target wasm32-unknown-unknown"
|
||||
unit-test = "test --lib"
|
||||
schema = "run --bin schema"
|
||||
schema = "run --example schema"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "cw4-group"
|
||||
version = "1.0.0"
|
||||
version = "0.13.4"
|
||||
authors = ["Ethan Frey <ethanfrey@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
description = "Simple cw4 implementation of group membership controlled by admin "
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/CosmWasm/cw-plus"
|
||||
@@ -20,8 +20,6 @@ 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 = []
|
||||
|
||||
@@ -33,8 +31,10 @@ 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, Uint64,
|
||||
SubMsg,
|
||||
};
|
||||
use cw2::set_contract_version;
|
||||
use cw4::{
|
||||
@@ -13,7 +13,6 @@ 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};
|
||||
|
||||
@@ -40,25 +39,21 @@ pub fn instantiate(
|
||||
pub fn create(
|
||||
mut deps: DepsMut,
|
||||
admin: Option<String>,
|
||||
mut members: Vec<Member>,
|
||||
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 = Uint64::zero();
|
||||
let mut total = 0u64;
|
||||
for member in members.into_iter() {
|
||||
let member_weight = Uint64::from(member.weight);
|
||||
total = total.checked_add(member_weight)?;
|
||||
total += member.weight;
|
||||
let member_addr = deps.api.addr_validate(&member.addr)?;
|
||||
MEMBERS.save(deps.storage, &member_addr, &member_weight.u64(), height)?;
|
||||
MEMBERS.save(deps.storage, &member_addr, &member.weight, height)?;
|
||||
}
|
||||
TOTAL.save(deps.storage, &total.u64(), height)?;
|
||||
TOTAL.save(deps.storage, &total)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -120,23 +115,20 @@ pub fn update_members(
|
||||
deps: DepsMut,
|
||||
height: u64,
|
||||
sender: Addr,
|
||||
mut to_add: Vec<Member>,
|
||||
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 = Uint64::from(TOTAL.load(deps.storage)?);
|
||||
let mut total = 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 = total.checked_sub(Uint64::from(old.unwrap_or_default()))?;
|
||||
total = total.checked_add(Uint64::from(add.weight))?;
|
||||
total -= old.unwrap_or_default();
|
||||
total += add.weight;
|
||||
diffs.push(MemberDiff::new(add.addr, old, Some(add.weight)));
|
||||
Ok(add.weight)
|
||||
})?;
|
||||
@@ -148,12 +140,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 = total.checked_sub(Uint64::from(weight))?;
|
||||
total -= weight;
|
||||
MEMBERS.remove(deps.storage, &remove_addr, height)?;
|
||||
}
|
||||
}
|
||||
|
||||
TOTAL.save(deps.storage, &total.u64(), height)?;
|
||||
TOTAL.save(deps.storage, &total)?;
|
||||
Ok(MemberChangedHookMsg { diffs })
|
||||
}
|
||||
|
||||
@@ -165,26 +157,20 @@ 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(&query_list_members(deps, start_after, limit)?)
|
||||
}
|
||||
QueryMsg::TotalWeight { at_height: height } => {
|
||||
to_binary(&query_total_weight(deps, height)?)
|
||||
to_binary(&list_members(deps, start_after, limit)?)
|
||||
}
|
||||
QueryMsg::TotalWeight {} => to_binary(&query_total_weight(deps)?),
|
||||
QueryMsg::Admin {} => to_binary(&ADMIN.query_admin(deps)?),
|
||||
QueryMsg::Hooks {} => to_binary(&HOOKS.query_hooks(deps)?),
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
fn query_total_weight(deps: Deps) -> StdResult<TotalWeightResponse> {
|
||||
let weight = TOTAL.load(deps.storage)?;
|
||||
Ok(TotalWeightResponse { weight })
|
||||
}
|
||||
|
||||
pub fn query_member(deps: Deps, addr: String, height: Option<u64>) -> StdResult<MemberResponse> {
|
||||
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),
|
||||
@@ -197,7 +183,7 @@ pub fn query_member(deps: Deps, addr: String, height: Option<u64>) -> StdResult<
|
||||
const MAX_LIMIT: u32 = 30;
|
||||
const DEFAULT_LIMIT: u32 = 10;
|
||||
|
||||
pub fn query_list_members(
|
||||
fn list_members(
|
||||
deps: Deps,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
@@ -219,3 +205,352 @@ pub fn query_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,25 +1,19 @@
|
||||
use cosmwasm_std::{OverflowError, StdError};
|
||||
use cosmwasm_std::StdError;
|
||||
use thiserror::Error;
|
||||
|
||||
use cw_controllers::{AdminError, HookError};
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum ContractError {
|
||||
#[error("{0}")]
|
||||
#[error(transparent)]
|
||||
Std(#[from] StdError),
|
||||
|
||||
#[error("{0}")]
|
||||
#[error(transparent)]
|
||||
Hook(#[from] HookError),
|
||||
|
||||
#[error("{0}")]
|
||||
#[error(transparent)]
|
||||
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.
|
||||
#[cw_serde]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct Cw4GroupContract(pub Cw4Contract);
|
||||
|
||||
impl Deref for Cw4GroupContract {
|
||||
@@ -41,17 +41,3 @@ 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,25 +1,6 @@
|
||||
/*!
|
||||
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,24 +1,16 @@
|
||||
use cosmwasm_std::Addr;
|
||||
use cw4::{
|
||||
MEMBERS_CHANGELOG, MEMBERS_CHECKPOINTS, MEMBERS_KEY, TOTAL_KEY, TOTAL_KEY_CHANGELOG,
|
||||
TOTAL_KEY_CHECKPOINTS,
|
||||
};
|
||||
use cw4::TOTAL_KEY;
|
||||
use cw_controllers::{Admin, Hooks};
|
||||
use cw_storage_plus::{SnapshotItem, SnapshotMap, Strategy};
|
||||
use cw_storage_plus::{Item, SnapshotMap, Strategy};
|
||||
|
||||
pub const ADMIN: Admin = Admin::new("admin");
|
||||
pub const HOOKS: Hooks = Hooks::new("cw4-hooks");
|
||||
|
||||
pub const TOTAL: SnapshotItem<u64> = SnapshotItem::new(
|
||||
TOTAL_KEY,
|
||||
TOTAL_KEY_CHECKPOINTS,
|
||||
TOTAL_KEY_CHANGELOG,
|
||||
Strategy::EveryBlock,
|
||||
);
|
||||
pub const TOTAL: Item<u64> = Item::new(TOTAL_KEY);
|
||||
|
||||
pub const MEMBERS: SnapshotMap<&Addr, u64> = SnapshotMap::new(
|
||||
MEMBERS_KEY,
|
||||
MEMBERS_CHECKPOINTS,
|
||||
MEMBERS_CHANGELOG,
|
||||
cw4::MEMBERS_KEY,
|
||||
cw4::MEMBERS_CHECKPOINTS,
|
||||
cw4::MEMBERS_CHANGELOG,
|
||||
Strategy::EveryBlock,
|
||||
);
|
||||
|
||||
@@ -1,439 +0,0 @@
|
||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
|
||||
use cosmwasm_std::{from_slice, Addr, Api, DepsMut, OwnedDeps, Querier, Storage, SubMsg};
|
||||
use cw4::{member_key, Member, MemberChangedHookMsg, MemberDiff, TOTAL_KEY};
|
||||
use cw_controllers::{AdminError, HookError};
|
||||
|
||||
use crate::contract::{
|
||||
execute, instantiate, query_list_members, query_member, query_total_weight, update_members,
|
||||
};
|
||||
use crate::state::{ADMIN, HOOKS};
|
||||
use crate::ContractError;
|
||||
use nym_group_contract_common::msg::{ExecuteMsg, InstantiateMsg};
|
||||
|
||||
const INIT_ADMIN: &str = "juan";
|
||||
const USER1: &str = "somebody";
|
||||
const USER2: &str = "else";
|
||||
const USER3: &str = "funny";
|
||||
|
||||
fn set_up(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();
|
||||
set_up(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(), None).unwrap();
|
||||
assert_eq!(17, res.weight);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_member_queries() {
|
||||
let mut deps = mock_dependencies();
|
||||
set_up(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 = query_list_members(deps.as_ref(), None, None).unwrap();
|
||||
assert_eq!(members.members.len(), 2);
|
||||
// TODO: assert the set is proper
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_members_instantiation() {
|
||||
let mut deps = mock_dependencies();
|
||||
|
||||
let msg = InstantiateMsg {
|
||||
admin: Some(INIT_ADMIN.into()),
|
||||
members: vec![
|
||||
Member {
|
||||
addr: USER1.into(),
|
||||
weight: 5,
|
||||
},
|
||||
Member {
|
||||
addr: USER2.into(),
|
||||
weight: 6,
|
||||
},
|
||||
Member {
|
||||
addr: USER1.into(),
|
||||
weight: 6,
|
||||
},
|
||||
],
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
let err = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap_err();
|
||||
assert_eq!(
|
||||
err,
|
||||
ContractError::DuplicateMember {
|
||||
member: USER1.to_string()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_members_execution() {
|
||||
let mut deps = mock_dependencies();
|
||||
|
||||
set_up(deps.as_mut());
|
||||
|
||||
let add = vec![
|
||||
Member {
|
||||
addr: USER3.into(),
|
||||
weight: 15,
|
||||
},
|
||||
Member {
|
||||
addr: USER3.into(),
|
||||
weight: 11,
|
||||
},
|
||||
];
|
||||
|
||||
let height = mock_env().block.height;
|
||||
let err = update_members(
|
||||
deps.as_mut(),
|
||||
height + 5,
|
||||
Addr::unchecked(INIT_ADMIN),
|
||||
add,
|
||||
vec![],
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
err,
|
||||
ContractError::DuplicateMember {
|
||||
member: USER3.to_string()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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 = query_list_members(deps.as_ref(), None, None).unwrap();
|
||||
assert_eq!(count, members.members.len());
|
||||
|
||||
let total = query_total_weight(deps.as_ref(), None).unwrap();
|
||||
assert_eq!(sum, total.weight); // 17 - 11 + 15 = 21
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_new_remove_old_member() {
|
||||
let mut deps = mock_dependencies();
|
||||
set_up(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();
|
||||
set_up(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();
|
||||
set_up(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();
|
||||
set_up(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();
|
||||
set_up(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)
|
||||
// order of added users is not guaranteed to be preserved
|
||||
let diffs = vec![
|
||||
MemberDiff::new(USER3, None, Some(5)),
|
||||
MemberDiff::new(USER1, Some(11), Some(20)),
|
||||
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());
|
||||
dbg!(&res.messages);
|
||||
dbg!(&msg1);
|
||||
dbg!(&msg2);
|
||||
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();
|
||||
set_up(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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn total_at_height() {
|
||||
let mut deps = mock_dependencies();
|
||||
set_up(deps.as_mut());
|
||||
|
||||
let height = mock_env().block.height;
|
||||
|
||||
// Test the values from instantiate
|
||||
let total = query_total_weight(deps.as_ref(), None).unwrap();
|
||||
assert_eq!(17, total.weight);
|
||||
// Note all values were set at height, the beginning of that block was all None
|
||||
let total = query_total_weight(deps.as_ref(), Some(height)).unwrap();
|
||||
assert_eq!(0, total.weight);
|
||||
// This will get us the values at the start of the block after instantiate (expected initial values)
|
||||
let total = query_total_weight(deps.as_ref(), Some(height + 1)).unwrap();
|
||||
assert_eq!(17, total.weight);
|
||||
}
|
||||
@@ -12,7 +12,7 @@ cw-controllers = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.4.0" }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.5.0" }
|
||||
nym-name-service-common = { path = "../../common/cosmwasm-smart-contracts/name-service" }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
serde = { version = "1.0.155", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -30,12 +30,12 @@ fn names<'a>() -> IndexedMap<'a, NameId, RegisteredName, NameIndex<'a>> {
|
||||
let indexes = NameIndex {
|
||||
name: UniqueIndex::new(|d| d.name.to_string(), NAMES_NAME_IDX_NAMESPACE),
|
||||
address: MultiIndex::new(
|
||||
|_pk, d| d.address.to_string(),
|
||||
|d| d.address.to_string(),
|
||||
NAMES_PK_NAMESPACE,
|
||||
NAMES_ADDRESS_IDX_NAMESPACE,
|
||||
),
|
||||
owner: MultiIndex::new(
|
||||
|_pk, d| d.owner.clone(),
|
||||
|d| d.owner.clone(),
|
||||
NAMES_PK_NAMESPACE,
|
||||
NAMES_OWNER_IDX_NAMESPACE,
|
||||
),
|
||||
@@ -188,6 +188,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
type TestDeps = OwnedDeps<MemoryStorage, MockApi, MockQuerier>;
|
||||
|
||||
#[rstest::fixture]
|
||||
fn deps() -> TestDeps {
|
||||
instantiate_test_contract()
|
||||
@@ -691,7 +692,6 @@ mod tests {
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn max_page_limit_is_applied() {
|
||||
// WIP(JON)
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,13 @@ edition = "2021"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
bs58 = "0.4.0"
|
||||
cosmwasm-std = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.4.0" }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.5.0" }
|
||||
nym-service-provider-directory-common = { path = "../../common/cosmwasm-smart-contracts/service-provider-directory" }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
serde = { version = "1.0.155", default-features = false, features = ["derive"] }
|
||||
@@ -22,5 +23,8 @@ thiserror = "1.0.39"
|
||||
vergen = { version = "=7.4.3", default-features = false, features = ["build", "git", "rustc"] }
|
||||
|
||||
[dev-dependencies]
|
||||
cw-multi-test = { workspace = true }
|
||||
anyhow = "1.0.40"
|
||||
cw-multi-test = { workspace = true }
|
||||
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
|
||||
rand_chacha = "0.2"
|
||||
rstest = "0.17.0"
|
||||
|
||||
@@ -13,3 +13,5 @@ pub const SERVICE_ID_COUNTER_KEY: &str = "sidc";
|
||||
pub const SERVICES_PK_NAMESPACE: &str = "sernames";
|
||||
pub const SERVICES_ANNOUNCER_IDX_NAMESPACE: &str = "serown";
|
||||
pub const SERVICES_NYM_ADDRESS_IDX_NAMESPACE: &str = "sernyma";
|
||||
|
||||
pub const SIGNING_NONCES_NAMESPACE: &str = "sn";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
error::{ContractError, Result},
|
||||
state::{self, Config},
|
||||
Result, SpContractError,
|
||||
};
|
||||
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response};
|
||||
use nym_service_provider_directory_common::msg::{
|
||||
@@ -34,22 +34,25 @@ pub fn instantiate(
|
||||
.add_attribute("admin", info.sender))
|
||||
}
|
||||
|
||||
pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
pub fn migrate(
|
||||
deps: DepsMut<'_>,
|
||||
_env: Env,
|
||||
_msg: MigrateMsg,
|
||||
) -> Result<Response, SpContractError> {
|
||||
// Note: don't remove this particular bit of code as we have to ALWAYS check whether we have to
|
||||
// update the stored version
|
||||
let version: Version =
|
||||
CONTRACT_VERSION
|
||||
.parse()
|
||||
.map_err(|error: semver::Error| ContractError::SemVerFailure {
|
||||
value: CONTRACT_VERSION.to_string(),
|
||||
error_message: error.to_string(),
|
||||
})?;
|
||||
let version: Version = CONTRACT_VERSION.parse().map_err(|error: semver::Error| {
|
||||
SpContractError::SemVerFailure {
|
||||
value: CONTRACT_VERSION.to_string(),
|
||||
error_message: error.to_string(),
|
||||
}
|
||||
})?;
|
||||
|
||||
let storage_version_raw = cw2::get_contract_version(deps.storage)?.version;
|
||||
let storage_version: Version =
|
||||
storage_version_raw
|
||||
.parse()
|
||||
.map_err(|error: semver::Error| ContractError::SemVerFailure {
|
||||
.map_err(|error: semver::Error| SpContractError::SemVerFailure {
|
||||
value: storage_version_raw,
|
||||
error_message: error.to_string(),
|
||||
})?;
|
||||
@@ -69,12 +72,12 @@ pub fn execute(
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
) -> Result<Response, SpContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::Announce {
|
||||
nym_address: client_address,
|
||||
service_type,
|
||||
} => execute::announce(deps, env, info, client_address, service_type),
|
||||
service,
|
||||
owner_signature,
|
||||
} => execute::announce(deps, env, info, service, owner_signature),
|
||||
ExecuteMsg::DeleteId { service_id } => execute::delete_id(deps, info, service_id),
|
||||
ExecuteMsg::DeleteNymAddress { nym_address } => {
|
||||
execute::delete_nym_address(deps, info, nym_address)
|
||||
@@ -95,6 +98,9 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<Binary> {
|
||||
QueryMsg::All { limit, start_after } => {
|
||||
to_binary(&query::query_all_paged(deps, limit, start_after)?)
|
||||
}
|
||||
QueryMsg::SigningNonce { address } => {
|
||||
to_binary(&query::query_current_signing_nonce(deps, address)?)
|
||||
}
|
||||
QueryMsg::Config {} => to_binary(&query::query_config(deps)?),
|
||||
QueryMsg::GetContractVersion {} => to_binary(&query::query_contract_version()),
|
||||
QueryMsg::GetCW2ContractVersion {} => to_binary(&cw2::get_contract_version(deps.storage)?),
|
||||
@@ -107,16 +113,19 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::test_helpers::{
|
||||
assert::{assert_config, assert_empty, assert_not_found, assert_service, assert_services},
|
||||
fixture::service_fixture,
|
||||
helpers::{get_attribute, nyms},
|
||||
assert::{
|
||||
assert_config, assert_current_nonce, assert_empty, assert_not_found, assert_service,
|
||||
assert_services,
|
||||
},
|
||||
fixture::new_service_details_with_sign,
|
||||
helpers::{get_attribute, nyms, test_rng},
|
||||
};
|
||||
|
||||
use cosmwasm_std::{
|
||||
testing::{mock_dependencies, mock_env, mock_info},
|
||||
Addr, Coin,
|
||||
};
|
||||
use nym_service_provider_directory_common::{msg::ExecuteMsg, ServiceId, ServiceInfo};
|
||||
use nym_service_provider_directory_common::{msg::ExecuteMsg, Service, ServiceId};
|
||||
|
||||
const DENOM: &str = "unym";
|
||||
|
||||
@@ -140,27 +149,33 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn announce_fails_incorrect_deposit() {
|
||||
fn announce_fails_incorrect_deposit_too_small() {
|
||||
let mut rng = test_rng();
|
||||
let mut deps = mock_dependencies();
|
||||
let msg = InstantiateMsg::new(nyms(100));
|
||||
let info = mock_info("creator", &[]);
|
||||
let admin = info.sender.clone();
|
||||
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
|
||||
assert_eq!(res.messages.len(), 0);
|
||||
|
||||
// Announce
|
||||
let msg: ExecuteMsg = service_fixture().into();
|
||||
let announcer = service_fixture().announcer.to_string();
|
||||
// Setup service
|
||||
let deposit = nyms(99);
|
||||
let announcer = "steve";
|
||||
let (service, owner_signature) =
|
||||
new_service_details_with_sign(deps.as_mut(), &mut rng, "nym", announcer, deposit);
|
||||
let msg = ExecuteMsg::Announce {
|
||||
service,
|
||||
owner_signature,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
execute(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
mock_info(&announcer, &[nyms(99)]),
|
||||
mock_info(announcer, &[nyms(99)]),
|
||||
msg.clone()
|
||||
)
|
||||
.unwrap_err(),
|
||||
ContractError::InsufficientDeposit {
|
||||
SpContractError::InsufficientDeposit {
|
||||
funds: 99u128.into(),
|
||||
deposit_required: 100u128.into(),
|
||||
}
|
||||
@@ -170,11 +185,55 @@ mod tests {
|
||||
execute(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
mock_info(&announcer, &[nyms(101)]),
|
||||
msg
|
||||
mock_info(announcer, &[nyms(100)]),
|
||||
msg,
|
||||
)
|
||||
.unwrap_err(),
|
||||
ContractError::TooLargeDeposit {
|
||||
SpContractError::InvalidEd25519Signature,
|
||||
);
|
||||
}
|
||||
|
||||
// Announcing a service fails due to the signed deposit being different from the deposit in
|
||||
// the message.
|
||||
#[test]
|
||||
fn announce_fails_incorrect_deposit_too_large() {
|
||||
let mut rng = test_rng();
|
||||
let mut deps = mock_dependencies();
|
||||
let msg = InstantiateMsg::new(nyms(100));
|
||||
let info = mock_info("creator", &[]);
|
||||
let admin = info.sender.clone();
|
||||
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
|
||||
assert_eq!(res.messages.len(), 0);
|
||||
|
||||
// Setup service
|
||||
let deposit = nyms(101);
|
||||
let announcer = "steve";
|
||||
let (service, owner_signature) =
|
||||
new_service_details_with_sign(deps.as_mut(), &mut rng, "nym", announcer, deposit);
|
||||
let msg = ExecuteMsg::Announce {
|
||||
service,
|
||||
owner_signature,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
execute(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
mock_info(announcer, &[nyms(100)]),
|
||||
msg.clone()
|
||||
)
|
||||
.unwrap_err(),
|
||||
SpContractError::InvalidEd25519Signature,
|
||||
);
|
||||
assert_eq!(
|
||||
execute(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
mock_info(announcer, &[nyms(101)]),
|
||||
msg,
|
||||
)
|
||||
.unwrap_err(),
|
||||
SpContractError::TooLargeDeposit {
|
||||
funds: 101u128.into(),
|
||||
deposit_required: 100u128.into(),
|
||||
}
|
||||
@@ -186,15 +245,26 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn announce_success() {
|
||||
let mut rng = test_rng();
|
||||
let mut deps = mock_dependencies();
|
||||
let msg = InstantiateMsg::new(nyms(100));
|
||||
let info = mock_info("creator", &[]);
|
||||
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
|
||||
assert_eq!(res.messages.len(), 0);
|
||||
assert_current_nonce(deps.as_ref(), &Addr::unchecked("steve"), 0);
|
||||
|
||||
// Setup service
|
||||
let deposit = nyms(100);
|
||||
let owner = "steve";
|
||||
let (service, owner_signature) =
|
||||
new_service_details_with_sign(deps.as_mut(), &mut rng, "nym", owner, deposit.clone());
|
||||
|
||||
// Announce
|
||||
let msg: ExecuteMsg = service_fixture().into();
|
||||
let info = mock_info("steve", &[nyms(100)]);
|
||||
let msg = ExecuteMsg::Announce {
|
||||
service: service.clone(),
|
||||
owner_signature,
|
||||
};
|
||||
let info = mock_info("steve", &[deposit.clone()]);
|
||||
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
|
||||
|
||||
// Check that the service has had service id assigned to it
|
||||
@@ -208,10 +278,17 @@ mod tests {
|
||||
"network_requester".to_string()
|
||||
);
|
||||
|
||||
// Check that the nonce has been incremented, but only for the owner
|
||||
assert_current_nonce(deps.as_ref(), &Addr::unchecked("steve"), 1);
|
||||
assert_current_nonce(deps.as_ref(), &Addr::unchecked("timmy"), 0);
|
||||
|
||||
// The expected announced service
|
||||
let expected_service = ServiceInfo {
|
||||
let expected_service = Service {
|
||||
service_id: expected_id,
|
||||
service: service_fixture(),
|
||||
service,
|
||||
announcer: Addr::unchecked("steve"),
|
||||
block_height: 12345,
|
||||
deposit,
|
||||
};
|
||||
assert_services(deps.as_ref(), &[expected_service.clone()]);
|
||||
assert_service(deps.as_ref(), &expected_service);
|
||||
@@ -219,6 +296,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn delete() {
|
||||
let mut rng = test_rng();
|
||||
let mut deps = mock_dependencies();
|
||||
let msg = InstantiateMsg::new(Coin::new(100, "unym"));
|
||||
let info = mock_info("creator", &[]);
|
||||
@@ -226,16 +304,25 @@ mod tests {
|
||||
assert_eq!(res.messages.len(), 0);
|
||||
|
||||
// Announce
|
||||
let msg: ExecuteMsg = service_fixture().into();
|
||||
let info_steve = mock_info("steve", &[nyms(100)]);
|
||||
assert_eq!(info_steve.sender, service_fixture().announcer);
|
||||
execute(deps.as_mut(), mock_env(), info_steve, msg).unwrap();
|
||||
let deposit = nyms(100);
|
||||
let steve = "steve";
|
||||
let (service, owner_signature) =
|
||||
new_service_details_with_sign(deps.as_mut(), &mut rng, "nym", steve, deposit.clone());
|
||||
let msg = ExecuteMsg::Announce {
|
||||
service: service.clone(),
|
||||
owner_signature,
|
||||
};
|
||||
let info_steve = mock_info(steve, &[deposit.clone()]);
|
||||
execute(deps.as_mut(), mock_env(), info_steve.clone(), msg).unwrap();
|
||||
|
||||
// The expected announced service
|
||||
let expected_id = 1;
|
||||
let expected_service = ServiceInfo {
|
||||
let expected_service = Service {
|
||||
service_id: expected_id,
|
||||
service: service_fixture(),
|
||||
service,
|
||||
announcer: Addr::unchecked(steve),
|
||||
block_height: 12345,
|
||||
deposit,
|
||||
};
|
||||
assert_services(deps.as_ref(), &[expected_service]);
|
||||
|
||||
@@ -244,27 +331,23 @@ mod tests {
|
||||
let info_timmy = mock_info("timmy", &[]);
|
||||
assert_eq!(
|
||||
execute(deps.as_mut(), mock_env(), info_timmy, msg).unwrap_err(),
|
||||
ContractError::Unauthorized {
|
||||
SpContractError::Unauthorized {
|
||||
sender: Addr::unchecked("timmy")
|
||||
}
|
||||
);
|
||||
|
||||
// Removing an non-existent service will fail
|
||||
let msg = ExecuteMsg::delete_id(expected_id + 1);
|
||||
let info_announcer = MessageInfo {
|
||||
sender: service_fixture().announcer,
|
||||
funds: vec![],
|
||||
};
|
||||
assert_eq!(
|
||||
execute(deps.as_mut(), mock_env(), info_announcer.clone(), msg).unwrap_err(),
|
||||
ContractError::NotFound {
|
||||
execute(deps.as_mut(), mock_env(), info_steve.clone(), msg).unwrap_err(),
|
||||
SpContractError::NotFound {
|
||||
service_id: expected_id + 1
|
||||
}
|
||||
);
|
||||
|
||||
// Remove as correct announcer succeeds
|
||||
let msg = ExecuteMsg::delete_id(expected_id);
|
||||
let res = execute(deps.as_mut(), mock_env(), info_announcer, msg).unwrap();
|
||||
let res = execute(deps.as_mut(), mock_env(), info_steve, msg).unwrap();
|
||||
assert_eq!(
|
||||
get_attribute(&res, "delete_id", "service_id"),
|
||||
expected_id.to_string()
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
use crate::{
|
||||
constants::{MAX_NUMBER_OF_ALIASES_FOR_NYM_ADDRESS, MAX_NUMBER_OF_PROVIDERS_PER_ANNOUNCER},
|
||||
error::{ContractError, Result},
|
||||
state,
|
||||
state, Result, SpContractError,
|
||||
};
|
||||
use cosmwasm_std::{Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Response, Uint128};
|
||||
use nym_contracts_common::{
|
||||
signing::{MessageSignature, Verifier},
|
||||
IdentityKey,
|
||||
};
|
||||
use nym_service_provider_directory_common::{
|
||||
events::{new_announce_event, new_delete_id_event, new_update_deposit_required_event},
|
||||
NymAddress, Service, ServiceId, ServiceType,
|
||||
signing_types::construct_service_provider_announce_sign_payload,
|
||||
NymAddress, Service, ServiceDetails, ServiceId,
|
||||
};
|
||||
|
||||
use super::query;
|
||||
|
||||
fn ensure_correct_deposit(will_deposit: Uint128, deposit_required: Uint128) -> Result<()> {
|
||||
match will_deposit.cmp(&deposit_required) {
|
||||
std::cmp::Ordering::Less => Err(ContractError::InsufficientDeposit {
|
||||
std::cmp::Ordering::Less => Err(SpContractError::InsufficientDeposit {
|
||||
funds: will_deposit,
|
||||
deposit_required,
|
||||
}),
|
||||
std::cmp::Ordering::Equal => Ok(()),
|
||||
std::cmp::Ordering::Greater => Err(ContractError::TooLargeDeposit {
|
||||
std::cmp::Ordering::Greater => Err(SpContractError::TooLargeDeposit {
|
||||
funds: will_deposit,
|
||||
deposit_required,
|
||||
}),
|
||||
@@ -30,7 +34,7 @@ fn ensure_max_services_per_announcer(deps: Deps, announcer: Addr) -> Result<()>
|
||||
if current_entries.services.len() < MAX_NUMBER_OF_PROVIDERS_PER_ANNOUNCER as usize {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ContractError::ReachedMaxProvidersForAdmin {
|
||||
Err(SpContractError::ReachedMaxProvidersForAdmin {
|
||||
max_providers: MAX_NUMBER_OF_PROVIDERS_PER_ANNOUNCER,
|
||||
announcer,
|
||||
})
|
||||
@@ -42,7 +46,7 @@ fn ensure_max_aliases_per_nym_address(deps: Deps, nym_address: NymAddress) -> Re
|
||||
if current_entries.services.len() < MAX_NUMBER_OF_ALIASES_FOR_NYM_ADDRESS as usize {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ContractError::ReachedMaxAliasesForNymAddress {
|
||||
Err(SpContractError::ReachedMaxAliasesForNymAddress {
|
||||
max_aliases: MAX_NUMBER_OF_ALIASES_FOR_NYM_ADDRESS,
|
||||
nym_address,
|
||||
})
|
||||
@@ -50,10 +54,10 @@ fn ensure_max_aliases_per_nym_address(deps: Deps, nym_address: NymAddress) -> Re
|
||||
}
|
||||
|
||||
fn ensure_service_exists(deps: Deps, service_id: ServiceId) -> Result<()> {
|
||||
if state::services::has_service(deps.storage, service_id) {
|
||||
if state::has_service(deps.storage, service_id) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ContractError::NotFound { service_id })
|
||||
Err(SpContractError::NotFound { service_id })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +65,7 @@ fn ensure_sender_authorized(info: MessageInfo, service: &Service) -> Result<()>
|
||||
if info.sender == service.announcer {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ContractError::Unauthorized {
|
||||
Err(SpContractError::Unauthorized {
|
||||
sender: info.sender,
|
||||
})
|
||||
}
|
||||
@@ -74,31 +78,82 @@ fn return_deposit(service_to_delete: &Service) -> BankMsg {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_announce_signature(
|
||||
deps: Deps<'_>,
|
||||
sender: Addr,
|
||||
deposit: Coin,
|
||||
service: ServiceDetails,
|
||||
signature: MessageSignature,
|
||||
) -> Result<()> {
|
||||
// recover the public key
|
||||
let public_key = decode_ed25519_identity_key(&service.identity_key)?;
|
||||
|
||||
// reconstruct the payload
|
||||
let nonce = state::get_signing_nonce(deps.storage, sender.clone())?;
|
||||
|
||||
let msg = construct_service_provider_announce_sign_payload(nonce, sender, deposit, service);
|
||||
|
||||
if deps.api.verify_message(msg, signature, &public_key)? {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SpContractError::InvalidEd25519Signature)
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_ed25519_identity_key(encoded: &IdentityKey) -> Result<[u8; 32]> {
|
||||
let mut public_key = [0u8; 32];
|
||||
let used = bs58::decode(encoded)
|
||||
.into(&mut public_key)
|
||||
.map_err(|err| SpContractError::MalformedEd25519IdentityKey(err.to_string()))?;
|
||||
|
||||
if used != 32 {
|
||||
return Err(SpContractError::MalformedEd25519IdentityKey(
|
||||
"Too few bytes provided for the public key".into(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(public_key)
|
||||
}
|
||||
|
||||
/// Announce a new service. It will be assigned a new service provider id.
|
||||
pub fn announce(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
nym_address: NymAddress,
|
||||
service_type: ServiceType,
|
||||
service: ServiceDetails,
|
||||
owner_signature: MessageSignature,
|
||||
) -> Result<Response> {
|
||||
ensure_max_services_per_announcer(deps.as_ref(), info.sender.clone())?;
|
||||
ensure_max_aliases_per_nym_address(deps.as_ref(), nym_address.clone())?;
|
||||
ensure_max_aliases_per_nym_address(deps.as_ref(), service.nym_address.clone())?;
|
||||
|
||||
let deposit_required = state::deposit_required(deps.storage)?;
|
||||
let denom = deposit_required.denom.clone();
|
||||
let will_deposit = cw_utils::must_pay(&info, &denom)
|
||||
.map_err(|err| ContractError::DepositRequired { source: err })?;
|
||||
.map_err(|err| SpContractError::DepositRequired { source: err })?;
|
||||
ensure_correct_deposit(will_deposit, deposit_required.amount)?;
|
||||
|
||||
let deposit = Coin::new(will_deposit.u128(), denom);
|
||||
|
||||
// Check that the sender actually owns the service provider by checking the signature
|
||||
verify_announce_signature(
|
||||
deps.as_ref(),
|
||||
info.sender.clone(),
|
||||
deposit.clone(),
|
||||
service.clone(),
|
||||
owner_signature,
|
||||
)?;
|
||||
|
||||
state::increment_signing_nonce(deps.storage, info.sender.clone())?;
|
||||
|
||||
let service_id = state::next_service_id_counter(deps.storage)?;
|
||||
let new_service = Service {
|
||||
nym_address,
|
||||
service_type,
|
||||
service_id,
|
||||
service,
|
||||
announcer: info.sender,
|
||||
block_height: env.block.height,
|
||||
deposit: Coin::new(will_deposit.u128(), denom),
|
||||
deposit,
|
||||
};
|
||||
let service_id = state::services::save(deps.storage, &new_service)?;
|
||||
state::save(deps.storage, &new_service)?;
|
||||
|
||||
Ok(Response::new().add_event(new_announce_event(service_id, new_service)))
|
||||
}
|
||||
@@ -106,15 +161,15 @@ pub fn announce(
|
||||
/// Delete an exsisting service.
|
||||
pub fn delete_id(deps: DepsMut, info: MessageInfo, service_id: ServiceId) -> Result<Response> {
|
||||
ensure_service_exists(deps.as_ref(), service_id)?;
|
||||
let service_to_delete = state::services::load_id(deps.storage, service_id)?;
|
||||
let service_to_delete = state::load_id(deps.storage, service_id)?;
|
||||
ensure_sender_authorized(info, &service_to_delete)?;
|
||||
|
||||
state::services::remove(deps.storage, service_id)?;
|
||||
state::remove(deps.storage, service_id)?;
|
||||
let return_deposit_msg = return_deposit(&service_to_delete);
|
||||
|
||||
Ok(Response::new()
|
||||
.add_message(return_deposit_msg)
|
||||
.add_event(new_delete_id_event(service_id, service_to_delete)))
|
||||
.add_event(new_delete_id_event(service_to_delete)))
|
||||
}
|
||||
|
||||
/// Delete an existing service by nym address. If there are multiple entries for a given nym
|
||||
@@ -128,15 +183,12 @@ pub(crate) fn delete_nym_address(
|
||||
let services_to_delete = query::query_nym_address(deps.as_ref(), nym_address)?.services;
|
||||
|
||||
for service_to_delete in services_to_delete {
|
||||
if info.sender == service_to_delete.service.announcer {
|
||||
state::services::remove(deps.storage, service_to_delete.service_id)?;
|
||||
let return_deposit_msg = return_deposit(&service_to_delete.service);
|
||||
if info.sender == service_to_delete.announcer {
|
||||
state::remove(deps.storage, service_to_delete.service_id)?;
|
||||
let return_deposit_msg = return_deposit(&service_to_delete);
|
||||
response = response
|
||||
.add_message(return_deposit_msg)
|
||||
.add_event(new_delete_id_event(
|
||||
service_to_delete.service_id,
|
||||
service_to_delete.service,
|
||||
));
|
||||
.add_event(new_delete_id_event(service_to_delete));
|
||||
}
|
||||
}
|
||||
Ok(response)
|
||||
|
||||
@@ -1,31 +1,27 @@
|
||||
use cosmwasm_std::Deps;
|
||||
use nym_contracts_common::ContractBuildInformation;
|
||||
use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
|
||||
use nym_service_provider_directory_common::{
|
||||
response::{ConfigResponse, PagedServicesListResponse, ServicesListResponse},
|
||||
NymAddress, ServiceId, ServiceInfo,
|
||||
NymAddress, Service, ServiceId,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::Result,
|
||||
state::{self, services::PagedLoad},
|
||||
state::{self, PagedLoad},
|
||||
Result,
|
||||
};
|
||||
|
||||
pub fn query_id(deps: Deps, service_id: ServiceId) -> Result<ServiceInfo> {
|
||||
let service = state::services::load_id(deps.storage, service_id)?;
|
||||
Ok(ServiceInfo {
|
||||
service_id,
|
||||
service,
|
||||
})
|
||||
pub fn query_id(deps: Deps, service_id: ServiceId) -> Result<Service> {
|
||||
state::load_id(deps.storage, service_id)
|
||||
}
|
||||
|
||||
pub fn query_announcer(deps: Deps, announcer: String) -> Result<ServicesListResponse> {
|
||||
let announcer = deps.api.addr_validate(&announcer)?;
|
||||
let services = state::services::load_announcer(deps.storage, announcer)?;
|
||||
let services = state::load_announcer(deps.storage, announcer)?;
|
||||
Ok(ServicesListResponse::new(services))
|
||||
}
|
||||
|
||||
pub fn query_nym_address(deps: Deps, nym_address: NymAddress) -> Result<ServicesListResponse> {
|
||||
let services = state::services::load_nym_address(deps.storage, nym_address)?;
|
||||
let services = state::load_nym_address(deps.storage, nym_address)?;
|
||||
Ok(ServicesListResponse::new(services))
|
||||
}
|
||||
|
||||
@@ -38,7 +34,7 @@ pub fn query_all_paged(
|
||||
services,
|
||||
limit,
|
||||
start_next_after,
|
||||
} = state::services::load_all_paged(deps.storage, limit, start_after)?;
|
||||
} = state::load_all_paged(deps.storage, limit, start_after)?;
|
||||
Ok(PagedServicesListResponse::new(
|
||||
services,
|
||||
limit,
|
||||
@@ -46,6 +42,11 @@ pub fn query_all_paged(
|
||||
))
|
||||
}
|
||||
|
||||
pub fn query_current_signing_nonce(deps: Deps<'_>, address: String) -> Result<Nonce> {
|
||||
let address = deps.api.addr_validate(&address)?;
|
||||
state::get_signing_nonce(deps.storage, address)
|
||||
}
|
||||
|
||||
pub fn query_config(deps: Deps) -> Result<ConfigResponse> {
|
||||
let config = state::load_config(deps.storage)?;
|
||||
Ok(config.into())
|
||||
|
||||
@@ -1,355 +0,0 @@
|
||||
//! Integration tests using cw-multi-test.
|
||||
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_service_provider_directory_common::{
|
||||
response::{ConfigResponse, PagedServicesListResponse},
|
||||
NymAddress, Service, ServiceInfo, ServiceType,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
constants::SERVICE_DEFAULT_RETRIEVAL_LIMIT,
|
||||
error::ContractError,
|
||||
test_helpers::{fixture::service_info, helpers::nyms, test_setup::TestSetup},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn instantiate_contract() {
|
||||
TestSetup::new();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query_config() {
|
||||
assert_eq!(
|
||||
TestSetup::new().query_config(),
|
||||
ConfigResponse {
|
||||
deposit_required: nyms(100),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn announce_and_query_service() {
|
||||
let mut setup = TestSetup::new();
|
||||
assert_eq!(
|
||||
setup.query_all(),
|
||||
PagedServicesListResponse {
|
||||
services: vec![],
|
||||
per_page: SERVICE_DEFAULT_RETRIEVAL_LIMIT as usize,
|
||||
start_next_after: None,
|
||||
}
|
||||
);
|
||||
|
||||
// Announce a first service
|
||||
let announcer = Addr::unchecked("announcer");
|
||||
let nym_address = NymAddress::new("nymAddress");
|
||||
assert_eq!(setup.contract_balance(), nyms(0));
|
||||
assert_eq!(setup.balance(&announcer), nyms(250));
|
||||
setup.announce_net_req(nym_address.clone(), announcer.clone());
|
||||
|
||||
// Deposit is deposited to contract and deducted from announcers's balance
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert_eq!(setup.balance(&announcer), nyms(150));
|
||||
|
||||
// We can query the full service list
|
||||
assert_eq!(
|
||||
setup.query_all(),
|
||||
PagedServicesListResponse {
|
||||
services: vec![ServiceInfo {
|
||||
service_id: 1,
|
||||
service: Service {
|
||||
nym_address: nym_address.clone(),
|
||||
service_type: ServiceType::NetworkRequester,
|
||||
announcer: announcer.clone(),
|
||||
block_height: 12345,
|
||||
deposit: nyms(100),
|
||||
},
|
||||
}],
|
||||
per_page: SERVICE_DEFAULT_RETRIEVAL_LIMIT as usize,
|
||||
start_next_after: Some(1),
|
||||
}
|
||||
);
|
||||
|
||||
// ... and we can query by id
|
||||
assert_eq!(
|
||||
setup.query_id(1),
|
||||
ServiceInfo {
|
||||
service_id: 1,
|
||||
service: Service {
|
||||
nym_address: nym_address.clone(),
|
||||
service_type: ServiceType::NetworkRequester,
|
||||
announcer: announcer.clone(),
|
||||
block_height: 12345,
|
||||
deposit: nyms(100),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Announce a second service
|
||||
let announcer2 = Addr::unchecked("announcer2");
|
||||
let nym_address2 = NymAddress::new("nymAddress2");
|
||||
setup.announce_net_req(nym_address2.clone(), announcer2.clone());
|
||||
|
||||
assert_eq!(setup.contract_balance(), nyms(200));
|
||||
assert_eq!(
|
||||
setup.query_all(),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
service_info(1, nym_address, announcer),
|
||||
service_info(2, nym_address2, announcer2)
|
||||
],
|
||||
per_page: SERVICE_DEFAULT_RETRIEVAL_LIMIT as usize,
|
||||
start_next_after: Some(2),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_service() {
|
||||
let mut setup = TestSetup::new();
|
||||
setup.announce_net_req(NymAddress::new("nymAddress"), Addr::unchecked("announcer"));
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert_eq!(setup.balance("announcer"), nyms(150));
|
||||
assert!(!setup.query_all().services.is_empty());
|
||||
setup.delete(1, Addr::unchecked("announcer"));
|
||||
|
||||
// Deleting the service returns the deposit to the announcer
|
||||
assert_eq!(setup.contract_balance(), nyms(0));
|
||||
assert_eq!(setup.balance("announcer"), nyms(250));
|
||||
assert!(setup.query_all().services.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_announcer_can_delete_service() {
|
||||
let mut setup = TestSetup::new();
|
||||
assert_eq!(setup.contract_balance(), nyms(0));
|
||||
setup.announce_net_req(NymAddress::new("nymAddress"), Addr::unchecked("announcer"));
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert!(!setup.query_all().services.is_empty());
|
||||
|
||||
let delete_resp: ContractError = setup
|
||||
.try_delete(1, Addr::unchecked("not_announcer"))
|
||||
.unwrap_err()
|
||||
.downcast()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert_eq!(
|
||||
delete_resp,
|
||||
ContractError::Unauthorized {
|
||||
sender: Addr::unchecked("not_announcer")
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cant_delete_service_that_does_not_exist() {
|
||||
let mut setup = TestSetup::new();
|
||||
setup.announce_net_req(NymAddress::new("nymAddress"), Addr::unchecked("announcer"));
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert!(!setup.query_all().services.is_empty());
|
||||
|
||||
let delete_resp: ContractError = setup
|
||||
.try_delete(0, Addr::unchecked("announcer"))
|
||||
.unwrap_err()
|
||||
.downcast()
|
||||
.unwrap();
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert_eq!(delete_resp, ContractError::NotFound { service_id: 0 });
|
||||
|
||||
let delete_resp: ContractError = setup
|
||||
.try_delete(2, Addr::unchecked("announcer"))
|
||||
.unwrap_err()
|
||||
.downcast()
|
||||
.unwrap();
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert_eq!(delete_resp, ContractError::NotFound { service_id: 2 });
|
||||
|
||||
assert!(!setup.query_all().services.is_empty());
|
||||
setup.delete(1, Addr::unchecked("announcer"));
|
||||
assert_eq!(setup.contract_balance(), nyms(0));
|
||||
assert!(setup.query_all().services.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn announce_multiple_services_and_deleting_by_name() {
|
||||
let mut setup = TestSetup::new();
|
||||
let announcer1 = Addr::unchecked("wealthy_announcer_1");
|
||||
let announcer2 = Addr::unchecked("wealthy_announcer_2");
|
||||
let nym_address1 = NymAddress::new("nymAddress1");
|
||||
let nym_address2 = NymAddress::new("nymAddress2");
|
||||
|
||||
// We announce the same address three times, but with different annoucers
|
||||
assert_eq!(setup.contract_balance(), nyms(0));
|
||||
assert_eq!(setup.balance(&announcer1), nyms(1000));
|
||||
setup.announce_net_req(nym_address1.clone(), announcer1.clone());
|
||||
setup.announce_net_req(nym_address1.clone(), announcer1.clone());
|
||||
setup.announce_net_req(nym_address2.clone(), announcer1.clone());
|
||||
setup.announce_net_req(nym_address1.clone(), announcer2.clone());
|
||||
setup.announce_net_req(nym_address2.clone(), announcer2.clone());
|
||||
|
||||
assert_eq!(setup.contract_balance(), nyms(500));
|
||||
assert_eq!(setup.balance(&announcer1), nyms(700));
|
||||
assert_eq!(
|
||||
setup.query_all(),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
service_info(1, nym_address1.clone(), announcer1.clone()),
|
||||
service_info(2, nym_address1.clone(), announcer1.clone()),
|
||||
service_info(3, nym_address2.clone(), announcer1.clone()),
|
||||
service_info(4, nym_address1.clone(), announcer2.clone()),
|
||||
service_info(5, nym_address2.clone(), announcer2.clone()),
|
||||
],
|
||||
per_page: SERVICE_DEFAULT_RETRIEVAL_LIMIT as usize,
|
||||
start_next_after: Some(5),
|
||||
}
|
||||
);
|
||||
|
||||
// Even though multiple of them point to the same nym address, we only delete the ones we actually
|
||||
// own.
|
||||
setup.delete_nym_address(nym_address1.clone(), announcer1.clone());
|
||||
|
||||
assert_eq!(setup.contract_balance(), nyms(300));
|
||||
assert_eq!(setup.balance(&announcer1), nyms(900));
|
||||
assert_eq!(
|
||||
setup.query_all(),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
service_info(3, nym_address2.clone(), announcer1),
|
||||
service_info(4, nym_address1, announcer2.clone()),
|
||||
service_info(5, nym_address2, announcer2),
|
||||
],
|
||||
per_page: SERVICE_DEFAULT_RETRIEVAL_LIMIT as usize,
|
||||
start_next_after: Some(5),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// add multiple services, then query all but with a paging limit less than the number of services
|
||||
// added
|
||||
#[test]
|
||||
fn paging_works() {
|
||||
let mut setup = TestSetup::new();
|
||||
let announcer1 = Addr::unchecked("wealthy_announcer_1");
|
||||
let announcer2 = Addr::unchecked("wealthy_announcer_2");
|
||||
let nym_address1 = NymAddress::new("nymAddress1");
|
||||
let nym_address2 = NymAddress::new("nymAddress2");
|
||||
|
||||
// We announce the same address three times, but with different announcers
|
||||
setup.announce_net_req(nym_address1.clone(), announcer1.clone());
|
||||
setup.announce_net_req(nym_address1.clone(), announcer1.clone());
|
||||
setup.announce_net_req(nym_address2.clone(), announcer1.clone());
|
||||
setup.announce_net_req(nym_address1.clone(), announcer2.clone());
|
||||
setup.announce_net_req(nym_address2.clone(), announcer2.clone());
|
||||
|
||||
assert_eq!(
|
||||
setup.query_all_with_limit(Some(10), None),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
service_info(1, nym_address1.clone(), announcer1.clone()),
|
||||
service_info(2, nym_address1.clone(), announcer1.clone()),
|
||||
service_info(3, nym_address2.clone(), announcer1.clone()),
|
||||
service_info(4, nym_address1.clone(), announcer2.clone()),
|
||||
service_info(5, nym_address2.clone(), announcer2.clone()),
|
||||
],
|
||||
per_page: 10,
|
||||
start_next_after: Some(5),
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
setup.query_all_with_limit(Some(3), None),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
service_info(1, nym_address1.clone(), announcer1.clone()),
|
||||
service_info(2, nym_address1.clone(), announcer1.clone()),
|
||||
service_info(3, nym_address2.clone(), announcer1),
|
||||
],
|
||||
per_page: 3,
|
||||
start_next_after: Some(3),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
setup.query_all_with_limit(Some(3), Some(3)),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
service_info(4, nym_address1, announcer2.clone()),
|
||||
service_info(5, nym_address2, announcer2),
|
||||
],
|
||||
per_page: 3,
|
||||
start_next_after: Some(5),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn service_id_increases_for_new_services() {
|
||||
let mut setup = TestSetup::new();
|
||||
setup.announce_net_req(
|
||||
NymAddress::new("nymAddress1"),
|
||||
Addr::unchecked("announcer1"),
|
||||
);
|
||||
setup.announce_net_req(
|
||||
NymAddress::new("nymAddress2"),
|
||||
Addr::unchecked("announcer2"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
setup
|
||||
.query_all()
|
||||
.services
|
||||
.iter()
|
||||
.map(|s| s.service_id)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![1, 2],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn service_id_is_not_resused_when_deleting_and_then_adding_a_new_service() {
|
||||
let mut setup = TestSetup::new();
|
||||
setup.announce_net_req(
|
||||
NymAddress::new("nymAddress1"),
|
||||
Addr::unchecked("announcer1"),
|
||||
);
|
||||
setup.announce_net_req(
|
||||
NymAddress::new("nymAddress2"),
|
||||
Addr::unchecked("announcer2"),
|
||||
);
|
||||
setup.announce_net_req(
|
||||
NymAddress::new("nymAddress3"),
|
||||
Addr::unchecked("announcer3"),
|
||||
);
|
||||
|
||||
setup.delete(1, Addr::unchecked("announcer1"));
|
||||
setup.delete(3, Addr::unchecked("announcer3"));
|
||||
|
||||
assert_eq!(
|
||||
setup.query_all().services,
|
||||
vec![service_info(
|
||||
2,
|
||||
NymAddress::new("nymAddress2"),
|
||||
Addr::unchecked("announcer2")
|
||||
)]
|
||||
);
|
||||
|
||||
setup.announce_net_req(
|
||||
NymAddress::new("nymAddress4"),
|
||||
Addr::unchecked("announcer4"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
setup.query_all().services,
|
||||
vec![
|
||||
service_info(
|
||||
2,
|
||||
NymAddress::new("nymAddress2"),
|
||||
Addr::unchecked("announcer2")
|
||||
),
|
||||
service_info(
|
||||
4,
|
||||
NymAddress::new("nymAddress4"),
|
||||
Addr::unchecked("announcer4")
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_service_provider_directory_common::{
|
||||
response::PagedServicesListResponse, NymAddress, Service, ServiceDetails, ServiceType,
|
||||
};
|
||||
use rstest::rstest;
|
||||
|
||||
use crate::{
|
||||
constants::SERVICE_DEFAULT_RETRIEVAL_LIMIT,
|
||||
test_helpers::{fixture::new_service, helpers::nyms},
|
||||
SpContractError,
|
||||
};
|
||||
|
||||
use super::test_setup::TestSetup;
|
||||
|
||||
#[rstest::fixture]
|
||||
fn setup() -> TestSetup {
|
||||
TestSetup::new()
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn basic_announce(mut setup: TestSetup) {
|
||||
assert_eq!(
|
||||
setup.query_all(),
|
||||
PagedServicesListResponse {
|
||||
services: vec![],
|
||||
per_page: SERVICE_DEFAULT_RETRIEVAL_LIMIT as usize,
|
||||
start_next_after: None,
|
||||
}
|
||||
);
|
||||
|
||||
// Announce a first service
|
||||
let announcer = Addr::unchecked("announcer");
|
||||
let nym_address = NymAddress::new("nymAddress");
|
||||
assert_eq!(setup.contract_balance(), nyms(0));
|
||||
assert_eq!(setup.balance(&announcer), nyms(250));
|
||||
assert_eq!(setup.query_signing_nonce(announcer.to_string()), 0);
|
||||
|
||||
let service = setup.new_service(&nym_address);
|
||||
let payload = setup.payload_to_sign(&announcer, &nyms(100), &service.service);
|
||||
let service = service.sign(payload);
|
||||
setup.announce_net_req(&service, &announcer);
|
||||
|
||||
// Deposit is deposited to contract and deducted from announcers's balance
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert_eq!(setup.balance(&announcer), nyms(150));
|
||||
|
||||
// The signing nonce has been incremented
|
||||
assert_eq!(setup.query_signing_nonce(announcer.to_string()), 1);
|
||||
|
||||
// We can query the full service list
|
||||
assert_eq!(
|
||||
setup.query_all(),
|
||||
PagedServicesListResponse {
|
||||
services: vec![Service {
|
||||
service_id: 1,
|
||||
service: ServiceDetails {
|
||||
nym_address: nym_address.clone(),
|
||||
service_type: ServiceType::NetworkRequester,
|
||||
identity_key: service.identity_key().to_string(),
|
||||
},
|
||||
announcer: announcer.clone(),
|
||||
block_height: 12345,
|
||||
deposit: nyms(100),
|
||||
}],
|
||||
per_page: SERVICE_DEFAULT_RETRIEVAL_LIMIT as usize,
|
||||
start_next_after: Some(1),
|
||||
}
|
||||
);
|
||||
|
||||
// ... and we can query by id
|
||||
assert_eq!(
|
||||
setup.query_id(1),
|
||||
Service {
|
||||
service_id: 1,
|
||||
service: service.details().clone(),
|
||||
announcer: announcer.clone(),
|
||||
block_height: 12345,
|
||||
deposit: nyms(100),
|
||||
}
|
||||
);
|
||||
|
||||
// Announce a second service
|
||||
let announcer2 = Addr::unchecked("announcer2");
|
||||
let nym_address2 = NymAddress::new("nymAddress2");
|
||||
let service2 = setup.new_signed_service(&nym_address2, &announcer2, &nyms(100));
|
||||
setup.announce_net_req(&service2, &announcer2);
|
||||
assert_eq!(setup.query_signing_nonce(announcer2.to_string()), 1);
|
||||
|
||||
assert_eq!(setup.contract_balance(), nyms(200));
|
||||
assert_eq!(
|
||||
setup.query_all(),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
new_service(1, &nym_address, &announcer, service.identity_key()),
|
||||
new_service(2, &nym_address2, &announcer2, service2.identity_key())
|
||||
],
|
||||
per_page: SERVICE_DEFAULT_RETRIEVAL_LIMIT as usize,
|
||||
start_next_after: Some(2),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn announce_fails_when_announcer_mismatch(mut setup: TestSetup) {
|
||||
let announcer = Addr::unchecked("steve");
|
||||
let nym_address = NymAddress::new("foobar");
|
||||
let service = setup.new_signed_service(&nym_address, &announcer, &nyms(100));
|
||||
|
||||
// A difference announcer tries to announce the service
|
||||
let announcer2 = Addr::unchecked("timmy");
|
||||
|
||||
let resp: SpContractError = setup
|
||||
.try_announce_net_req(&service, &announcer2)
|
||||
.unwrap_err()
|
||||
.downcast()
|
||||
.unwrap();
|
||||
assert_eq!(resp, SpContractError::InvalidEd25519Signature);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn signing_nonce_is_increased_when_announcing(mut setup: TestSetup) {
|
||||
let announcer1 = Addr::unchecked("announcer1");
|
||||
let announcer2 = Addr::unchecked("announcer2");
|
||||
|
||||
assert_eq!(setup.query_signing_nonce(announcer1.to_string()), 0);
|
||||
assert_eq!(setup.query_signing_nonce(announcer2.to_string()), 0);
|
||||
|
||||
setup.sign_and_announce_net_req(&NymAddress::new("nymAddress1"), &announcer1, &nyms(100));
|
||||
|
||||
assert_eq!(setup.query_signing_nonce(announcer1.to_string()), 1);
|
||||
assert_eq!(setup.query_signing_nonce(announcer2.to_string()), 0);
|
||||
|
||||
setup.sign_and_announce_net_req(&NymAddress::new("nymAddress2"), &announcer2, &nyms(100));
|
||||
|
||||
assert_eq!(setup.query_signing_nonce(announcer1.to_string()), 1);
|
||||
assert_eq!(setup.query_signing_nonce(announcer2.to_string()), 1);
|
||||
|
||||
setup.sign_and_announce_net_req(&NymAddress::new("nymAddress3"), &announcer2, &nyms(100));
|
||||
|
||||
assert_eq!(setup.query_signing_nonce(announcer1.to_string()), 1);
|
||||
assert_eq!(setup.query_signing_nonce(announcer2.to_string()), 2);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn creating_two_services_in_a_row_without_announcing_fails(mut setup: TestSetup) {
|
||||
let announcer = Addr::unchecked("wealthy_announcer_1");
|
||||
let nym_address1 = NymAddress::new("nymAddress1");
|
||||
let nym_address2 = NymAddress::new("nymAddress2");
|
||||
let deposit = nyms(100);
|
||||
|
||||
let s1 = setup.new_signed_service(&nym_address1, &announcer, &deposit);
|
||||
|
||||
// This second service will be signed with the same nonce
|
||||
let s2 = setup.new_signed_service(&nym_address2, &announcer, &deposit);
|
||||
|
||||
// Announce the first service works, and this increments the nonce
|
||||
setup.announce_net_req(&s1, &announcer);
|
||||
|
||||
// Now the nonce has been incremented, and the signature will not match
|
||||
let resp: SpContractError = setup
|
||||
.try_announce_net_req(&s2, &announcer)
|
||||
.unwrap_err()
|
||||
.downcast()
|
||||
.unwrap();
|
||||
assert_eq!(resp, SpContractError::InvalidEd25519Signature,);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn announcing_the_same_service_twice_fails(mut setup: TestSetup) {
|
||||
let announcer = Addr::unchecked("wealthy_announcer_1");
|
||||
let nym_address = NymAddress::new("nymAddress1");
|
||||
|
||||
let s1 = setup.new_signed_service(&nym_address, &announcer, &nyms(100));
|
||||
setup.announce_net_req(&s1, &announcer);
|
||||
|
||||
// Now the nonce has been incremented, and the signature will not match
|
||||
let resp: SpContractError = setup
|
||||
.try_announce_net_req(&s1, &announcer)
|
||||
.unwrap_err()
|
||||
.downcast()
|
||||
.unwrap();
|
||||
assert_eq!(resp, SpContractError::InvalidEd25519Signature);
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_service_provider_directory_common::{response::PagedServicesListResponse, NymAddress};
|
||||
|
||||
use crate::{
|
||||
constants::SERVICE_DEFAULT_RETRIEVAL_LIMIT,
|
||||
test_helpers::{fixture::new_service, helpers::nyms},
|
||||
SpContractError,
|
||||
};
|
||||
|
||||
use super::test_setup::TestSetup;
|
||||
|
||||
#[test]
|
||||
fn delete_service() {
|
||||
let mut setup = TestSetup::new();
|
||||
setup.sign_and_announce_net_req(
|
||||
&NymAddress::new("nymAddress"),
|
||||
&Addr::unchecked("announcer"),
|
||||
&nyms(100),
|
||||
);
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert_eq!(setup.balance("announcer"), nyms(150));
|
||||
assert!(!setup.query_all().services.is_empty());
|
||||
setup.delete(1, &Addr::unchecked("announcer"));
|
||||
|
||||
// Deleting the service returns the deposit to the announcer
|
||||
assert_eq!(setup.contract_balance(), nyms(0));
|
||||
assert_eq!(setup.balance("announcer"), nyms(250));
|
||||
assert!(setup.query_all().services.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_announcer_can_delete_service() {
|
||||
let mut setup = TestSetup::new();
|
||||
assert_eq!(setup.contract_balance(), nyms(0));
|
||||
setup.sign_and_announce_net_req(
|
||||
&NymAddress::new("nymAddress"),
|
||||
&Addr::unchecked("announcer"),
|
||||
&nyms(100),
|
||||
);
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert!(!setup.query_all().services.is_empty());
|
||||
|
||||
let delete_resp: SpContractError = setup
|
||||
.try_delete(1, &Addr::unchecked("not_announcer"))
|
||||
.unwrap_err()
|
||||
.downcast()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert_eq!(
|
||||
delete_resp,
|
||||
SpContractError::Unauthorized {
|
||||
sender: Addr::unchecked("not_announcer")
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cant_delete_service_that_does_not_exist() {
|
||||
let mut setup = TestSetup::new();
|
||||
setup.sign_and_announce_net_req(
|
||||
&NymAddress::new("nymAddress"),
|
||||
&Addr::unchecked("announcer"),
|
||||
&nyms(100),
|
||||
);
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert!(!setup.query_all().services.is_empty());
|
||||
|
||||
let delete_resp: SpContractError = setup
|
||||
.try_delete(0, &Addr::unchecked("announcer"))
|
||||
.unwrap_err()
|
||||
.downcast()
|
||||
.unwrap();
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert_eq!(delete_resp, SpContractError::NotFound { service_id: 0 });
|
||||
|
||||
let delete_resp: SpContractError = setup
|
||||
.try_delete(2, &Addr::unchecked("announcer"))
|
||||
.unwrap_err()
|
||||
.downcast()
|
||||
.unwrap();
|
||||
assert_eq!(setup.contract_balance(), nyms(100));
|
||||
assert_eq!(delete_resp, SpContractError::NotFound { service_id: 2 });
|
||||
|
||||
assert!(!setup.query_all().services.is_empty());
|
||||
setup.delete(1, &Addr::unchecked("announcer"));
|
||||
assert_eq!(setup.contract_balance(), nyms(0));
|
||||
assert!(setup.query_all().services.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn announce_multiple_services_and_deleting_by_name() {
|
||||
let mut setup = TestSetup::new();
|
||||
let announcer1 = Addr::unchecked("wealthy_announcer_1");
|
||||
let announcer2 = Addr::unchecked("wealthy_announcer_2");
|
||||
let nym_address1 = NymAddress::new("nymAddress1");
|
||||
let nym_address2 = NymAddress::new("nymAddress2");
|
||||
let deposit = nyms(100);
|
||||
|
||||
// We announce the same address three times, but with different annoucers
|
||||
assert_eq!(setup.contract_balance(), nyms(0));
|
||||
assert_eq!(setup.balance(&announcer1), nyms(1000));
|
||||
let s1 = setup.sign_and_announce_net_req(&nym_address1, &announcer1, &deposit);
|
||||
let s2 = setup.sign_and_announce_net_req(&nym_address1, &announcer1, &deposit);
|
||||
let s3 = setup.sign_and_announce_net_req(&nym_address2, &announcer1, &deposit);
|
||||
let s4 = setup.sign_and_announce_net_req(&nym_address1, &announcer2, &deposit);
|
||||
let s5 = setup.sign_and_announce_net_req(&nym_address2, &announcer2, &deposit);
|
||||
|
||||
assert_eq!(setup.contract_balance(), nyms(500));
|
||||
assert_eq!(setup.balance(&announcer1), nyms(700));
|
||||
assert_eq!(
|
||||
setup.query_all(),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
new_service(1, &nym_address1, &announcer1, s1.identity_key()),
|
||||
new_service(2, &nym_address1, &announcer1, s2.identity_key()),
|
||||
new_service(3, &nym_address2, &announcer1, s3.identity_key()),
|
||||
new_service(4, &nym_address1, &announcer2, s4.identity_key()),
|
||||
new_service(5, &nym_address2, &announcer2, s5.identity_key()),
|
||||
],
|
||||
per_page: SERVICE_DEFAULT_RETRIEVAL_LIMIT as usize,
|
||||
start_next_after: Some(5),
|
||||
}
|
||||
);
|
||||
|
||||
// Even though multiple of them point to the same nym address, we only delete the ones we actually
|
||||
// own.
|
||||
setup.delete_nym_address(&nym_address1, &announcer1);
|
||||
|
||||
assert_eq!(setup.contract_balance(), nyms(300));
|
||||
assert_eq!(setup.balance(&announcer1), nyms(900));
|
||||
assert_eq!(
|
||||
setup.query_all(),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
new_service(3, &nym_address2, &announcer1, s3.identity_key()),
|
||||
new_service(4, &nym_address1, &announcer2, s4.identity_key()),
|
||||
new_service(5, &nym_address2, &announcer2, s5.identity_key()),
|
||||
],
|
||||
per_page: SERVICE_DEFAULT_RETRIEVAL_LIMIT as usize,
|
||||
start_next_after: Some(5),
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//! Integration tests using cw-multi-test.
|
||||
|
||||
mod announce;
|
||||
mod delete;
|
||||
mod query;
|
||||
mod service_id;
|
||||
mod test_service;
|
||||
mod test_setup;
|
||||
|
||||
#[test]
|
||||
fn instantiate_contract() {
|
||||
test_setup::TestSetup::new();
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_service_provider_directory_common::{
|
||||
response::{ConfigResponse, PagedServicesListResponse},
|
||||
NymAddress,
|
||||
};
|
||||
|
||||
use crate::test_helpers::{fixture::new_service, helpers::nyms};
|
||||
|
||||
use super::test_setup::TestSetup;
|
||||
|
||||
#[test]
|
||||
fn query_config() {
|
||||
assert_eq!(
|
||||
TestSetup::new().query_config(),
|
||||
ConfigResponse {
|
||||
deposit_required: nyms(100),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// add multiple services, then query all but with a paging limit less than the number of services
|
||||
// added
|
||||
#[test]
|
||||
fn paging_works() {
|
||||
let mut setup = TestSetup::new();
|
||||
let announcer1 = Addr::unchecked("wealthy_announcer_1");
|
||||
let announcer2 = Addr::unchecked("wealthy_announcer_2");
|
||||
let nym_address1 = NymAddress::new("nymAddress1");
|
||||
let nym_address2 = NymAddress::new("nymAddress2");
|
||||
let deposit = nyms(100);
|
||||
|
||||
// We announce the same address three times, but with different announcers
|
||||
let s1 = setup.sign_and_announce_net_req(&nym_address1, &announcer1, &deposit);
|
||||
let s2 = setup.sign_and_announce_net_req(&nym_address1, &announcer1, &deposit);
|
||||
let s3 = setup.sign_and_announce_net_req(&nym_address2, &announcer1, &deposit);
|
||||
let s4 = setup.sign_and_announce_net_req(&nym_address1, &announcer2, &deposit);
|
||||
let s5 = setup.sign_and_announce_net_req(&nym_address2, &announcer2, &deposit);
|
||||
|
||||
assert_eq!(
|
||||
setup.query_all_with_limit(Some(10), None),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
new_service(1, &nym_address1, &announcer1, s1.identity_key()),
|
||||
new_service(2, &nym_address1, &announcer1, s2.identity_key()),
|
||||
new_service(3, &nym_address2, &announcer1, s3.identity_key()),
|
||||
new_service(4, &nym_address1, &announcer2, s4.identity_key()),
|
||||
new_service(5, &nym_address2, &announcer2, s5.identity_key()),
|
||||
],
|
||||
per_page: 10,
|
||||
start_next_after: Some(5),
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
setup.query_all_with_limit(Some(3), None),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
new_service(1, &nym_address1, &announcer1, s1.identity_key()),
|
||||
new_service(2, &nym_address1, &announcer1, s2.identity_key()),
|
||||
new_service(3, &nym_address2, &announcer1, s3.identity_key()),
|
||||
],
|
||||
per_page: 3,
|
||||
start_next_after: Some(3),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
setup.query_all_with_limit(Some(3), Some(3)),
|
||||
PagedServicesListResponse {
|
||||
services: vec![
|
||||
new_service(4, &nym_address1, &announcer2, s4.identity_key()),
|
||||
new_service(5, &nym_address2, &announcer2, s5.identity_key()),
|
||||
],
|
||||
per_page: 3,
|
||||
start_next_after: Some(5),
|
||||
}
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user