Compare commits

..

43 Commits

Author SHA1 Message Date
Rachyandco 2bc7448b08 correct link 2023-06-07 13:45:24 +02:00
Tommy Verrall 42acbfe806 Update build-and-upload-binaries-ci.yml
temp use rust version 1.69.0 for ci build uploads, will need to switch and use: cosmwasm/rust-optimizer in the near future
2023-06-07 11:24:53 +02:00
farbanas 7c55483585 Merge branch 'master' into develop 2023-06-07 10:43:11 +02:00
farbanas c04cc9a4cf Merge branch 'release/v1.1.20' 2023-06-06 10:54:17 +02:00
farbanas 17258d1445 updated locks and versions bumped 2023-06-06 10:35:42 +02:00
farbanas 8f3d7606f5 bump crate versions 2023-06-06 10:07:06 +02:00
farbanas fd97f0e8ca update changelogs and version for release 2023-06-06 10:00:11 +02:00
Jędrzej Stuczyński d4ce1635a8 Fixed incorrect assertion when sending replies (#3515) 2023-06-05 14:36:44 +01:00
Jędrzej Stuczyński 2bc564ad01 updating managed keys after gateway registration (#3514) 2023-06-05 14:09:28 +01:00
Tommy Verrall 5910bcbc02 Update build-and-upload-binaries-ci.yml
add the service provider and name service contract to build output
2023-06-05 14:49:49 +02:00
Tommy Verrall 8c63fe9d0d Merge pull request #3507 from nymtech/jon/feat/reduce-shutdown-timeout-in-socks5
Reduce SHUTDOWN_TIMEOUT to 3 sec
2023-06-05 10:30:34 +01:00
Pierre Dommerc 6e5a1973da fix(wallet): fix bonding data refresh (#3499) 2023-06-05 11:07:23 +02:00
Pierre Dommerc 1aa11887aa fix(wallet): fix bonding data refresh (#3499) 2023-06-05 11:05:25 +02:00
Jon Häggblad 07740cbf08 Reduce SHUTDOWN_TIMEOUT to 3 sec
Can't think of a scenario where we don't want to close one at the same
time as the other, but let's be conservative and keep it a very low
number for now.
2023-06-05 10:42:38 +02:00
Jon Häggblad 87cb8a6b20 Sign when announcing service providers to the directory contract (#3459)
* create_payload and call from nym-cli

* Remove some commented out code

* wip

* Service announce now compiles

* Fix other compilation issues

* Move ServiceDetails into Service

* Move service_id inside Service type

* wip: start sorting out tests

* wip: sorting out testing

* wip: first announce test now works

* wip: more work on announce test

* Move nonce

* Add check for nonce

* Extract out some helpers to separate files

* reenable state::services tests

* wip: start going through the integration tests

* All integration tests reenabled

* Remove some unused stuff

* Iterate on integration tests

* More iteration on test setup

* Rename to test_setup.rs

* Add more tests specific to signing

* Tweak

* Another nonce test and reorg

* Rename to announce.rs and delete.rs

* Tidy

* Make some inner modules private

* Use IdentityKey alias

* Update nym-api contract cache

* Fix that nym-cli was asking for signing nonce from wrong contract

* Add sign comment to network-requester

* Uploaded updated service provider contract to qwerty

* Allow large enum variant

* lock files

* Remove dbg

* Move error.rs to service-provider common

* Update code for moving errors.rs to common crate

* Rename to SpContractError

* constants module not pub

* lock file

* rustfmt

* Move IdentityKey type to contract-common

* clippy
2023-06-05 10:32:58 +02:00
Jon Häggblad 2977b8f25f Fix clippy for 1.70 (#3505) 2023-06-05 08:54:18 +01:00
pierre 9ae4fd04ac ci(ns5-android): fix workflow 2023-06-02 15:44:39 +02:00
pierre 4470969bec ci(ns5-android): update workflow to create GH release 2023-06-02 14:49:14 +02:00
pierre 1a4c3a7709 chore(ns5-android): add app metadata for listing 2023-06-02 14:19:17 +02:00
Pierre Dommerc 99b31920d5 fix(ns5-android): make lib calling callbacks (#3496) 2023-06-02 13:59:10 +02:00
mx 019b3299f2 Merge pull request #3435 from Pawnflake/release/v1.1.19
mixnode documentation update
2023-06-02 09:40:50 +00:00
mx d684957423 Update documentation/docs/src/nodes/mix-node-setup.md
Co-authored-by: ️2FakTor️ <twofaktor@protonmail.com>
2023-06-02 09:20:56 +00:00
mx cb4eda4c62 Update documentation/docs/src/nodes/mix-node-setup.md
Co-authored-by: ️2FakTor️ <twofaktor@protonmail.com>
2023-06-02 09:20:42 +00:00
mx ac5f380ee2 Merge pull request #3485 from nymtech/feature/docs-1-1-20
removing hardcoded version numbers
2023-06-02 09:20:10 +00:00
mx 4e278ca07d reintroduced hardcoding for links for moment 2023-06-02 10:52:28 +02:00
mx cd6a725875 Merge pull request #3493 from twofaktor/patch-1
[BUG] network requester documentation update
2023-06-02 08:42:20 +00:00
pierre 62ccb6b4cd build(ns5-android): add product flavors config 2023-06-01 19:20:32 +02:00
pierre 365e0134b4 build(nc-native-android): clean build script 2023-05-31 17:31:24 +02:00
⚡️2FakTor⚡️ d5514a060c Update network-requester-setup.md 2023-05-31 15:22:11 +02:00
pierre 8432c30f6c build(nc-native-android): add gradle build universal apk 2023-05-31 14:20:49 +02:00
pierre c2764f90b3 ci(nc-native-android): update github workflow to build unsigned apks 2023-05-31 13:14:44 +02:00
mx 958b6d37ee Merge pull request #3481 from twofaktor/patch-1
[BUG] network requester documentation update
2023-05-30 14:40:33 +00:00
mx 5e36bb014c removing hardcoded versoin numbers 2023-05-30 16:10:15 +02:00
⚡️2FakTor⚡️ 8d821881ae Update network-requester-setup.md 2023-05-30 14:48:51 +02:00
mx fca9761145 Merge pull request #3141 from nymtech/chore/update-community-links-in-readme
updated readme with new developer chat links + new docs links
2023-05-30 12:05:14 +00:00
mx 11481e4d13 Merge branch 'release/v1.1.20' into chore/update-community-links-in-readme 2023-05-30 12:04:45 +00:00
pierre a6a39d1234 chore(nc-native-android): remove outdated todo 2023-05-30 13:58:30 +02:00
pierre 5f35d54fcb build(nc-native-android): fix script build paths 2023-05-30 13:40:52 +02:00
Pawnflake c8b82a9553 removed comment http and https 2023-05-20 07:23:13 +08:00
mx 00c2f5359c Merge pull request #3428 from nymtech/feature/updated-sandbox-docs
added sandbox sync docs
2023-05-16 14:53:51 +00:00
mx 1a4e0f4e08 added sandbox sync docs 2023-05-16 16:44:39 +02:00
Pawnflake 69230a10cb Added ufw restart after adding ports and removed port 80 and 443 from the command 2023-05-16 10:10:41 +08:00
mx 3a0c8f3f4e updated readme with new developer chat links + new docs links 2023-03-03 13:32:44 +01:00
251 changed files with 4104 additions and 15413 deletions
@@ -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
-138
View File
@@ -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: ???
+102
View File
@@ -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
+30
View File
@@ -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
View File
@@ -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
View File
@@ -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"
+6 -2
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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"
+23 -5
View File
@@ -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",
+13 -4
View File
@@ -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],
)
+3 -2
View File
@@ -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 }
@@ -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
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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"
+2 -2
View File
@@ -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"
+2 -2
View File
@@ -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 -1
View File
@@ -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`].
+1 -1
View File
@@ -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",
+115 -239
View File
@@ -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
View File
@@ -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,
),
+1 -1
View File
@@ -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(),
+3 -3
View File
@@ -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 }
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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,
),
+1 -1
View File
@@ -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")]
+2 -2
View File
@@ -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,
),
+2 -2
View File
@@ -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(),
+2 -2
View File
@@ -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");
+3 -3
View File
@@ -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"
+5 -5
View File
@@ -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 }
+367 -32
View File
@@ -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);
}
}
+4 -10
View File
@@ -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 },
}
+3 -17
View File
@@ -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(())
}
-19
View File
@@ -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;
+6 -14
View File
@@ -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,
);
-439
View File
@@ -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);
}
+1 -1
View File
@@ -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"] }
+3 -3
View File
@@ -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