diff --git a/.github/workflows/connect-desktop-ci.yml b/.github/workflows/connect-desktop-ci.yml index 76f39572dc..8fe9a4bbd4 100644 --- a/.github/workflows/connect-desktop-ci.yml +++ b/.github/workflows/connect-desktop-ci.yml @@ -23,7 +23,12 @@ jobs: node-version: 18 - name: Install Yarn run: npm install -g yarn - + - name: Install Rust stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - name: Install project dependencies run: cd ../.. && yarn --network-timeout 100000 diff --git a/.github/workflows/nym-connect-publish-macos.yml b/.github/workflows/nym-connect-publish-macos.yml index 21ec00ce1e..45dac7e87a 100644 --- a/.github/workflows/nym-connect-publish-macos.yml +++ b/.github/workflows/nym-connect-publish-macos.yml @@ -19,7 +19,7 @@ jobs: outputs: release_id: ${{ steps.create-release.outputs.id }} - release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }} + release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }} version: ${{ steps.release-info.outputs.version }} filename: ${{ steps.release-info.outputs.filename }} file_hash: ${{ steps.release-info.outputs.file_hash }} @@ -31,10 +31,20 @@ jobs: uses: actions/setup-node@v3 with: node-version: 18 + - name: Install Rust stable uses: actions-rs/toolchain@v1 with: toolchain: stable + target: wasm32-unknown-unknown + + - name: Install wasm-pack + run: | + export WASM_PACK_VERSION="v0.12.1" + curl -LO https://github.com/rustwasm/wasm-pack/releases/download/${WASM_PACK_VERSION}/wasm-pack-${WASM_PACK_VERSION}-x86_64-apple-darwin.tar.gz + tar xvzf wasm-pack-${WASM_PACK_VERSION}-x86_64-apple-darwin.tar.gz -C $HOME/.cargo/bin --strip-components=1 + rm wasm-pack-${WASM_PACK_VERSION}-x86_64-apple-darwin.tar.gz + - name: Install the Apple developer certificate for code signing env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} @@ -78,6 +88,8 @@ jobs: APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + SENTRY_DSN_RUST: ${{ secrets.SENTRY_DSN_RUST }} + SENTRY_DSN_JS: ${{ secrets.SENTRY_DSN_JS }} run: yarn && yarn build - name: Upload Artifact @@ -103,9 +115,10 @@ jobs: - id: release-info name: Prepare release info run: | - semver="${${{ github.ref_name }}##nym-connect-}" && semver="${semver##v}" - echo "version=$semver" >> "$GITHUB_OUTPUT" - echo "filename=nym-connect_$version_x64.dmg" >> "$GITHUB_OUTPUT" + ref=${{ github.ref_name }} + semver="${ref##nym-connect-}" && semver="${semver##v}" + echo "version=${semver}" >> "$GITHUB_OUTPUT" + echo "filename=nym-connect_${version}_x64_en-US.msi" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/dmg/nym-connect_*_x64.dmg') }}" >> "$GITHUB_OUTPUT" push-release-data: diff --git a/.github/workflows/nym-connect-publish-ubuntu.yml b/.github/workflows/nym-connect-publish-ubuntu.yml index f71c277e0c..cc5e1f3f00 100644 --- a/.github/workflows/nym-connect-publish-ubuntu.yml +++ b/.github/workflows/nym-connect-publish-ubuntu.yml @@ -19,7 +19,7 @@ jobs: outputs: release_id: ${{ steps.create-release.outputs.id }} - release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }} + release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }} version: ${{ steps.release-info.outputs.version }} filename: ${{ steps.release-info.outputs.filename }} file_hash: ${{ steps.release-info.outputs.file_hash }} @@ -61,6 +61,8 @@ jobs: env: TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + SENTRY_DSN_RUST: ${{ secrets.SENTRY_DSN_RUST }} + SENTRY_DSN_JS: ${{ secrets.SENTRY_DSN_JS }} - name: Upload Artifact uses: actions/upload-artifact@v3 @@ -80,11 +82,11 @@ jobs: - id: release-info name: Prepare release info run: | - semver="${${{ github.ref_name }}##nym-connect-}" && semver="${semver##v}" - echo "version=$semver" >> "$GITHUB_OUTPUT" - echo "filename=nym-connect_$version_amd64.AppImage" >> "$GITHUB_OUTPUT" + ref=${{ github.ref_name }} + semver="${ref##nym-connect-}" && semver="${semver##v}" + echo "version=${semver}" >> "$GITHUB_OUTPUT" + echo "filename=nym-connect_${version}_amd64.AppImage" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/appimage/nym-connect_*_amd64.AppImage') }}" >> "$GITHUB_OUTPUT" - push-release-data: if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }} uses: ./.github/workflows/push-release-data.yml diff --git a/.github/workflows/nym-connect-publish-windows10.yml b/.github/workflows/nym-connect-publish-windows10.yml index 8c793d88d0..1b4b1be2cc 100644 --- a/.github/workflows/nym-connect-publish-windows10.yml +++ b/.github/workflows/nym-connect-publish-windows10.yml @@ -19,7 +19,7 @@ jobs: outputs: release_id: ${{ steps.create-release.outputs.id }} - release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }} + release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }} version: ${{ steps.release-info.outputs.version }} filename: ${{ steps.release-info.outputs.filename }} file_hash: ${{ steps.release-info.outputs.file_hash }} @@ -79,6 +79,8 @@ jobs: WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + SENTRY_DSN_RUST: ${{ secrets.SENTRY_DSN_RUST }} + SENTRY_DSN_JS: ${{ secrets.SENTRY_DSN_JS }} run: yarn build - name: Upload Artifact @@ -99,9 +101,10 @@ jobs: - id: release-info name: Prepare release info run: | - semver="${${{ github.ref_name }}##nym-connect-}" && semver="${semver##v}" - echo "version=$semver" >> "$GITHUB_OUTPUT" - echo "filename=nym-connect_$version_x64_en-US.msi" >> "$GITHUB_OUTPUT" + ref=${{ github.ref_name }} + semver="${ref##nym-connect-}" && semver="${semver##v}" + echo "version=${semver}" >> "$GITHUB_OUTPUT" + echo "filename=nym-connect_${version}_x64_en-US.msi" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/msi/nym-connect_*_x64_en-US.msi') }}" >> "$GITHUB_OUTPUT" push-release-data: diff --git a/.github/workflows/nym-wallet-publish-macos.yml b/.github/workflows/nym-wallet-publish-macos.yml index c21e99493d..408a3b7406 100644 --- a/.github/workflows/nym-wallet-publish-macos.yml +++ b/.github/workflows/nym-wallet-publish-macos.yml @@ -19,7 +19,7 @@ jobs: outputs: release_id: ${{ steps.create-release.outputs.id }} - release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }} + release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }} version: ${{ steps.release-info.outputs.version }} filename: ${{ steps.release-info.outputs.filename }} file_hash: ${{ steps.release-info.outputs.file_hash }} @@ -99,12 +99,14 @@ jobs: files: | nym-wallet/target/release/bundle/dmg/*.dmg nym-wallet/target/release/bundle/macos/*.app.tar.gz* + - id: release-info name: Prepare release info run: | - semver="${${{ github.ref_name }}##nym-wallet-}" && semver="${semver##v}" - echo "version=$semver" >> "$GITHUB_OUTPUT" - echo "filename=nym-wallet_$version_x64.dmg" >> "$GITHUB_OUTPUT" + ref=${{ github.ref_name }} + semver="${ref##nym-wallet-}" && semver="${semver##v}" + echo "version=${semver}" >> "$GITHUB_OUTPUT" + echo "filename=nym-wallet_${version}_x64.dmg" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/dmg/nym-wallet_*_x64.dmg') }}" >> "$GITHUB_OUTPUT" push-release-data: diff --git a/.github/workflows/nym-wallet-publish-ubuntu.yml b/.github/workflows/nym-wallet-publish-ubuntu.yml index 5cfe79df98..9e9ee904cd 100644 --- a/.github/workflows/nym-wallet-publish-ubuntu.yml +++ b/.github/workflows/nym-wallet-publish-ubuntu.yml @@ -19,7 +19,7 @@ jobs: outputs: release_id: ${{ steps.create-release.outputs.id }} - release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }} + release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }} version: ${{ steps.release-info.outputs.version }} filename: ${{ steps.release-info.outputs.filename }} file_hash: ${{ steps.release-info.outputs.file_hash }} @@ -61,6 +61,13 @@ jobs: TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + - name: Upload Artifact + uses: actions/upload-artifact@v3 + with: + name: nym-wallet_1.0.0_amd64.AppImage.tar.gz + path: nym-wallet/target/release/bundle/appimage/nym-wallet*.AppImage.tar.gz + retention-days: 30 + - id: create-release name: Upload to release based on tag name uses: softprops/action-gh-release@v1 @@ -73,9 +80,10 @@ jobs: - id: release-info name: Prepare release info run: | - semver="${${{ github.ref_name }}##nym-wallet-}" && semver="${semver##v}" - echo "version=$semver" >> "$GITHUB_OUTPUT" - echo "filename=nym-wallet_$version_amd64.AppImage" >> "$GITHUB_OUTPUT" + ref=${{ github.ref_name }} + semver="${ref##nym-wallet-}" && semver="${semver##v}" + echo "version=${semver}" >> "$GITHUB_OUTPUT" + echo "filename=nym-wallet_${version}_amd64.AppImage" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/appimage/nym-wallet_*_amd64.AppImage') }}" >> "$GITHUB_OUTPUT" push-release-data: diff --git a/.github/workflows/nym-wallet-publish-windows10.yml b/.github/workflows/nym-wallet-publish-windows10.yml index 4ce6f38173..56a332a75d 100644 --- a/.github/workflows/nym-wallet-publish-windows10.yml +++ b/.github/workflows/nym-wallet-publish-windows10.yml @@ -19,7 +19,7 @@ jobs: outputs: release_id: ${{ steps.create-release.outputs.id }} - release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }} + release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }} version: ${{ steps.release-info.outputs.version }} filename: ${{ steps.release-info.outputs.filename }} file_hash: ${{ steps.release-info.outputs.file_hash }} @@ -96,12 +96,14 @@ jobs: files: | nym-wallet/target/release/bundle/msi/*.msi nym-wallet/target/release/bundle/msi/*.msi.zip* + - id: release-info name: Prepare release info run: | - semver="${${{ github.ref_name }}##nym-wallet-}" && semver="${semver##v}" - echo "version=$semver" >> "$GITHUB_OUTPUT" - echo "filename=nym-wallet_$version_x64_en-US.msi" >> "$GITHUB_OUTPUT" + ref=${{ github.ref_name }} + semver="${ref##nym-wallet-}" && semver="${semver##v}" + echo "version=${semver}" >> "$GITHUB_OUTPUT" + echo "filename=nym-wallet_${version}_x64_en-US.msi" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/msi/nym-wallet_*_x64_en-US.msi') }}" >> "$GITHUB_OUTPUT" push-release-data: diff --git a/Cargo.lock b/Cargo.lock index b0e87f87c7..f93b037760 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -359,6 +359,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" @@ -400,18 +406,18 @@ dependencies = [ [[package]] name = "bip32" -version = "0.3.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873faa4363bfc54c36a48321da034c92a0645a363eed34d948683ffc1706e37f" +checksum = "7e141fb0f8be1c7b45887af94c88b182472b57c96b56773250ae00cd6a14a164" dependencies = [ - "bs58 0.4.0", - "hmac 0.11.0", - "k256", + "bs58 0.5.0", + "hmac 0.12.1", + "k256 0.13.1", "once_cell", "pbkdf2", "rand_core 0.6.4", - "ripemd160", - "sha2 0.9.9", + "ripemd", + "sha2 0.10.6", "subtle 2.4.1", "zeroize", ] @@ -472,7 +478,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -486,7 +492,7 @@ dependencies = [ "cc", "cfg-if", "constant_time_eq", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -495,7 +501,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", "generic-array 0.14.7", ] @@ -508,12 +513,6 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - [[package]] name = "bls12_381" version = "0.5.0" @@ -547,8 +546,14 @@ name = "bs58" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" dependencies = [ - "sha2 0.9.9", + "sha2 0.10.6", ] [[package]] @@ -583,6 +588,9 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "cast" @@ -860,8 +868,8 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2895653b4d9f1538a83970077cb01dfc77a4810524e51a110944688e916b18e" dependencies = [ - "prost 0.11.9", - "prost-types 0.11.9", + "prost", + "prost-types", "tonic", "tracing-core", ] @@ -878,7 +886,7 @@ dependencies = [ "futures", "hdrhistogram", "humantime 2.1.0", - "prost-types 0.11.9", + "prost-types", "serde", "serde_json", "thread_local", @@ -892,9 +900,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.7.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "constant_time_eq" @@ -938,27 +946,27 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cosmos-sdk-proto" -version = "0.12.3" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" dependencies = [ - "prost 0.10.4", - "prost-types 0.10.1", + "prost", + "prost-types", "tendermint-proto", ] [[package]] name = "cosmrs" -version = "0.7.1" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af13955d6f356272e6def9ff5e2450a7650df536d8934f47052a20c76513d2f6" dependencies = [ "bip32", "cosmos-sdk-proto", - "ecdsa", + "ecdsa 0.16.8", "eyre", "getrandom 0.2.10", - "k256", - "prost 0.10.4", - "prost-types 0.10.1", + "k256 0.13.1", "rand_core 0.6.4", "serde", "serde_json", @@ -970,39 +978,66 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.0.1" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970d1d705862179b5d7a233ae36f02f21c4ec1b8075fe60c77fc5b43471331fa" +checksum = "75836a10cb9654c54e77ee56da94d592923092a10b369cdb0dbd56acefc16340" dependencies = [ - "digest 0.9.0", + "digest 0.10.7", "ed25519-zebra", - "k256", + "k256 0.11.6", "rand_core 0.6.4", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.0.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4" +checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" dependencies = [ "syn 1.0.109", ] [[package]] -name = "cosmwasm-std" -version = "1.0.0" +name = "cosmwasm-schema" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195" +checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc" +dependencies = [ + "cosmwasm-schema-derive", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-std" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb" dependencies = [ "base64 0.13.1", "cosmwasm-crypto", "cosmwasm-derive", + "derivative", "forward_ref", + "hex", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.5.1", + "sha2 0.10.6", "thiserror", "uint", ] @@ -1185,9 +1220,21 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.3.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", @@ -1318,11 +1365,25 @@ dependencies = [ ] [[package]] -name = "cw-controllers" -version = "0.13.4" +name = "curve25519-dalek-ng" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", +] + +[[package]] +name = "cw-controllers" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071" +dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "cw-utils", @@ -1333,9 +1394,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a" +checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" dependencies = [ "cosmwasm-std", "schemars", @@ -1344,22 +1405,26 @@ dependencies = [ [[package]] name = "cw-utils" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b" +checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw2", "schemars", + "semver 1.0.17", "serde", "thiserror", ] [[package]] name = "cw2" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" +checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", @@ -1367,11 +1432,12 @@ dependencies = [ ] [[package]] -name = "cw3" -version = "0.13.4" +name = "cw20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b" +checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-utils", "schemars", @@ -1379,11 +1445,27 @@ dependencies = [ ] [[package]] -name = "cw4" -version = "0.13.4" +name = "cw3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd" +checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa" dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "cw20", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw4" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b" +dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", @@ -1450,11 +1532,33 @@ dependencies = [ [[package]] name = "der" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1510,11 +1614,12 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle 2.4.1", ] @@ -1586,14 +1691,28 @@ checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "ecdsa" -version = "0.13.4" +version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der 0.7.7", + "digest 0.10.7", + "elliptic-curve 0.13.5", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki 0.7.2", ] [[package]] @@ -1603,7 +1722,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ "serde", - "signature", + "signature 1.6.4", +] + +[[package]] +name = "ed25519" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.1.0", +] + +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", ] [[package]] @@ -1613,7 +1755,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek", - "ed25519", + "ed25519 1.5.3", "rand 0.7.3", "serde", "serde_bytes", @@ -1644,18 +1786,39 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" -version = "0.11.12" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "ff 0.11.1", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", "generic-array 0.14.7", - "group 0.11.0", + "group 0.12.1", + "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.2", + "digest 0.10.7", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", "subtle 2.4.1", "zeroize", ] @@ -1841,6 +2004,26 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle 2.4.1", +] + [[package]] name = "figment" version = "0.10.8" @@ -2225,6 +2408,28 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core 0.6.4", + "subtle 2.4.1", +] + [[package]] name = "h2" version = "0.3.19" @@ -2448,7 +2653,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -2900,25 +3105,28 @@ dependencies = [ [[package]] name = "k256" -version = "0.10.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", - "ecdsa", - "elliptic-curve", - "sec1", - "sha2 0.9.9", - "sha3", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.6", ] [[package]] -name = "keccak" -version = "0.1.4" +name = "k256" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ - "cpufeatures", + "cfg-if", + "ecdsa 0.16.8", + "elliptic-curve 0.13.5", + "once_cell", + "sha2 0.10.6", + "signature 2.1.0", ] [[package]] @@ -2958,7 +3166,7 @@ name = "ledger" version = "0.1.0" dependencies = [ "bip32", - "k256", + "k256 0.13.1", "ledger-transport", "ledger-transport-hid", "thiserror", @@ -3458,7 +3666,7 @@ dependencies = [ "anyhow", "cosmrs", "eyre", - "k256", + "k256 0.13.1", "nym-cli-commands", "nym-validator-client", "serde", @@ -3505,7 +3713,7 @@ dependencies = [ "cw-utils", "handlebars", "humantime-serde", - "k256", + "k256 0.13.1", "log", "nym-bin-common", "nym-coconut-bandwidth-contract-common", @@ -3758,7 +3966,7 @@ dependencies = [ "bs58 0.4.0", "cipher 0.4.4", "ctr 0.9.2", - "digest 0.10.6", + "digest 0.10.7", "ed25519-dalek", "generic-array 0.14.7", "hkdf 0.12.3", @@ -3912,6 +4120,8 @@ dependencies = [ name = "nym-group-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", + "cw-controllers", "cw4", "schemars", "serde", @@ -3950,7 +4160,7 @@ dependencies = [ "rand_chacha 0.3.1", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.4.1", "serde_repr", "thiserror", "time 0.3.21", @@ -4036,7 +4246,9 @@ dependencies = [ name = "nym-multisig-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw-storage-plus", "cw-utils", "cw3", "cw4", @@ -4651,11 +4863,12 @@ dependencies = [ "nym-vesting-contract", "nym-vesting-contract-common", "openssl", - "prost 0.10.4", + "prost", "reqwest", "serde", "serde_json", "sha2 0.9.9", + "tendermint-rpc", "thiserror", "tokio", "ts-rs", @@ -5011,11 +5224,12 @@ checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "pbkdf2" -version = "0.9.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "crypto-mac 0.11.1", + "digest 0.10.7", + "hmac 0.12.1", ] [[package]] @@ -5163,13 +5377,22 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", - "zeroize", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.7", + "spki 0.7.2", ] [[package]] @@ -5330,16 +5553,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "prost" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e" -dependencies = [ - "bytes", - "prost-derive 0.10.1", -] - [[package]] name = "prost" version = "0.11.9" @@ -5347,20 +5560,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive 0.11.9", -] - -[[package]] -name = "prost-derive" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", + "prost-derive", ] [[package]] @@ -5376,23 +5576,13 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "prost-types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" -dependencies = [ - "bytes", - "prost 0.10.4", -] - [[package]] name = "prost-types" version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "prost 0.11.9", + "prost", ] [[package]] @@ -5790,15 +5980,25 @@ dependencies = [ [[package]] name = "rfc6979" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", - "hmac 0.11.0", + "crypto-bigint 0.4.9", + "hmac 0.12.1", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle 2.4.1", +] + [[package]] name = "ring" version = "0.16.20" @@ -5815,14 +6015,12 @@ dependencies = [ ] [[package]] -name = "ripemd160" -version = "0.9.1" +name = "ripemd" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug 0.3.0", + "digest 0.10.7", ] [[package]] @@ -6167,13 +6365,28 @@ dependencies = [ [[package]] name = "sec1" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "der", + "base16ct 0.1.1", + "der 0.6.1", "generic-array 0.14.7", - "pkcs8", + "pkcs8 0.9.0", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.7", + "generic-array 0.14.7", + "pkcs8 0.10.2", "subtle 2.4.1", "zeroize", ] @@ -6258,6 +6471,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-json-wasm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +dependencies = [ + "serde", +] + [[package]] name = "serde-wasm-bindgen" version = "0.5.0" @@ -6364,7 +6586,7 @@ checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -6375,7 +6597,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -6399,19 +6621,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha3" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug 0.3.0", + "digest 0.10.7", ] [[package]] @@ -6455,11 +6665,21 @@ dependencies = [ [[package]] name = "signature" -version = "1.4.0" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.9.0", + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", "rand_core 0.6.4", ] @@ -6562,12 +6782,22 @@ dependencies = [ [[package]] name = "spki" -version = "0.5.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.7", ] [[package]] @@ -6867,6 +7097,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "syn" version = "1.0.109" @@ -6931,28 +7167,28 @@ dependencies = [ [[package]] name = "tendermint" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca881fa4dedd2b46334f13be7fbc8cc1549ba4be5a833fe4e73d1a1baaf7949" +checksum = "3f0a7d05cf78524782337f8edd55cbc578d159a16ad4affe2135c92f7dbac7f0" dependencies = [ - "async-trait", "bytes", - "ed25519", - "ed25519-dalek", + "digest 0.10.7", + "ed25519 2.2.1", + "ed25519-consensus", "flex-error", "futures", - "k256", + "k256 0.13.1", "num-traits", "once_cell", - "prost 0.10.4", - "prost-types 0.10.1", - "ripemd160", + "prost", + "prost-types", + "ripemd", "serde", "serde_bytes", "serde_json", "serde_repr", - "sha2 0.9.9", - "signature", + "sha2 0.10.6", + "signature 2.1.0", "subtle 2.4.1", "subtle-encoding", "tendermint-proto", @@ -6962,9 +7198,9 @@ dependencies = [ [[package]] name = "tendermint-config" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c56ee93f4e9b7e7daba86d171f44572e91b741084384d0ae00df7991873dfd" +checksum = "71a72dbbea6dde12045d261f2c70c0de039125675e8a026c8d5ad34522756372" dependencies = [ "flex-error", "serde", @@ -6976,16 +7212,16 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71f925d74903f4abbdc4af0110635a307b3cb05b175fdff4a7247c14a4d0874" +checksum = "c0cec054567d16d85e8c3f6a3139963d1a66d9d3051ed545d31562550e9bcc3d" dependencies = [ "bytes", "flex-error", "num-derive", "num-traits", - "prost 0.10.4", - "prost-types 0.10.1", + "prost", + "prost-types", "serde", "serde_bytes", "subtle-encoding", @@ -6994,9 +7230,9 @@ dependencies = [ [[package]] name = "tendermint-rpc" -version = "0.23.7" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63f57ee05a1e927887191c76d1b139de9fa40c180b9f8727ee44377242a6" +checksum = "bd2cc789170db5a35d4e0bb2490035c03ef96df08f119bee25fd8dab5a09aa25" dependencies = [ "async-trait", "bytes", @@ -7009,9 +7245,11 @@ dependencies = [ "hyper-rustls", "peg", "pin-project", + "semver 1.0.17", "serde", "serde_bytes", "serde_json", + "subtle 2.4.1", "subtle-encoding", "tendermint", "tendermint-config", @@ -7368,7 +7606,7 @@ dependencies = [ "hyper-timeout", "percent-encoding", "pin-project", - "prost 0.11.9", + "prost", "tokio", "tokio-stream", "tower", diff --git a/Cargo.toml b/Cargo.toml index 2b862330e3..934c5b2c75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,19 +118,23 @@ anyhow = "1.0.71" async-trait = "0.1.64" bip39 = { version = "2.0.0", features = ["zeroize"] } cfg-if = "1.0.0" -cosmwasm-derive = "=1.0.0" -cosmwasm-schema = "=1.0.0" -cosmwasm-std = "=1.0.0" -cosmwasm-storage = "=1.0.0" -cw-controllers = "=0.13.4" -cw-storage-plus = "=0.13.4" -cw-utils = "=0.13.4" -cw2 = { version = "=0.13.4" } -cw3 = { version = "=0.13.4" } -cw3-fixed-multisig = { version = "=0.13.4" } -cw4 = { version = "=0.13.4" } +cosmwasm-derive = "=1.2.5" +cosmwasm-schema = "=1.2.5" +cosmwasm-std = "=1.2.5" +cosmwasm-storage = "=1.2.5" +cosmrs = "=0.14.0" +# same version as used by cosmrs +tendermint-rpc = "=0.32" +cw-utils = "=1.0.1" +cw-storage-plus = "=1.0.1" +cw2 = { version = "=1.0.1" } +cw3 = { version = "=1.0.1" } +cw3-fixed-multisig = { version = "=1.0.1" } +cw4 = { version = "=1.0.1" } +cw-controllers = { version = "=1.0.1" } dotenvy = "0.15.6" generic-array = "0.14.7" +k256 = "0.13" getrandom = "0.2.10" lazy_static = "1.4.0" log = "0.4" diff --git a/clients/credential/Cargo.toml b/clients/credential/Cargo.toml index 8d161359ec..8ceb14bc98 100644 --- a/clients/credential/Cargo.toml +++ b/clients/credential/Cargo.toml @@ -21,5 +21,5 @@ nym-credential-storage = { path = "../../common/credential-storage" } nym-bin-common = { path = "../../common/bin-common"} nym-network-defaults = { path = "../../common/network-defaults" } nym-pemstore = { path = "../../common/pemstore" } -nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] } +nym-validator-client = { path = "../../common/client-libs/validator-client" } diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index 0be4139630..6064304a90 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -48,7 +48,7 @@ nym-sphinx = { path = "../../common/nymsphinx" } nym-pemstore = { path = "../../common/pemstore" } nym-task = { path = "../../common/task" } nym-topology = { path = "../../common/topology" } -nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] } +nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] } nym-client-websocket-requests = { path = "websocket-requests" } [dev-dependencies] diff --git a/clients/webassembly/Cargo.lock b/clients/webassembly/Cargo.lock index 823fbbb252..f0733eac1b 100644 --- a/clients/webassembly/Cargo.lock +++ b/clients/webassembly/Cargo.lock @@ -217,6 +217,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" @@ -237,18 +243,18 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bip32" -version = "0.3.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873faa4363bfc54c36a48321da034c92a0645a363eed34d948683ffc1706e37f" +checksum = "7e141fb0f8be1c7b45887af94c88b182472b57c96b56773250ae00cd6a14a164" dependencies = [ - "bs58", - "hmac 0.11.0", - "k256", + "bs58 0.5.0", + "hmac 0.12.1", + "k256 0.13.1", "once_cell", "pbkdf2", "rand_core 0.6.4", - "ripemd160", - "sha2 0.9.9", + "ripemd", + "sha2 0.10.6", "subtle 2.4.1", "zeroize", ] @@ -309,7 +315,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -323,7 +329,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "constant_time_eq", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -332,7 +338,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", "generic-array 0.14.7", ] @@ -345,12 +350,6 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - [[package]] name = "bls12_381" version = "0.5.0" @@ -384,8 +383,14 @@ name = "bs58" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" dependencies = [ - "sha2 0.9.9", + "sha2 0.10.6", ] [[package]] @@ -411,6 +416,9 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "cc" @@ -577,9 +585,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.7.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" [[package]] name = "constant_time_eq" @@ -605,8 +613,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cosmos-sdk-proto" -version = "0.12.3" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" dependencies = [ "prost", "prost-types", @@ -615,17 +624,16 @@ dependencies = [ [[package]] name = "cosmrs" -version = "0.7.1" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af13955d6f356272e6def9ff5e2450a7650df536d8934f47052a20c76513d2f6" dependencies = [ "bip32", "cosmos-sdk-proto", - "ecdsa", + "ecdsa 0.16.8", "eyre", "getrandom 0.2.10", - "k256", - "prost", - "prost-types", + "k256 0.13.1", "rand_core 0.6.4", "serde", "serde_json", @@ -637,39 +645,66 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.0.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970d1d705862179b5d7a233ae36f02f21c4ec1b8075fe60c77fc5b43471331fa" +checksum = "0d076a08ec01ed23c4396aca98ec73a38fa1fee5f310465add52b4108181c7a8" dependencies = [ - "digest 0.9.0", + "digest 0.10.7", "ed25519-zebra", - "k256", + "k256 0.11.6", "rand_core 0.6.4", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.0.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4" +checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" dependencies = [ "syn 1.0.109", ] [[package]] -name = "cosmwasm-std" -version = "1.0.0" +name = "cosmwasm-schema" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195" +checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc" +dependencies = [ + "cosmwasm-schema-derive", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-std" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb" dependencies = [ "base64 0.13.1", "cosmwasm-crypto", "cosmwasm-derive", + "derivative", "forward_ref", + "hex", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.5.1", + "sha2 0.10.6", "thiserror", "uint", ] @@ -783,9 +818,21 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.3.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", @@ -866,11 +913,25 @@ dependencies = [ ] [[package]] -name = "cw-controllers" -version = "0.13.4" +name = "curve25519-dalek-ng" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", +] + +[[package]] +name = "cw-controllers" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071" +dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "cw-utils", @@ -881,9 +942,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a" +checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" dependencies = [ "cosmwasm-std", "schemars", @@ -892,22 +953,26 @@ dependencies = [ [[package]] name = "cw-utils" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b" +checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw2", "schemars", + "semver 1.0.17", "serde", "thiserror", ] [[package]] name = "cw2" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" +checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", @@ -915,11 +980,12 @@ dependencies = [ ] [[package]] -name = "cw3" -version = "0.13.4" +name = "cw20" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b" +checksum = "011c45920f8200bd5d32d4fe52502506f64f2f75651ab408054d4cfc75ca3a9b" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-utils", "schemars", @@ -927,11 +993,27 @@ dependencies = [ ] [[package]] -name = "cw4" -version = "0.13.4" +name = "cw3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd" +checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa" dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "cw20", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw4" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b" +dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", @@ -953,11 +1035,33 @@ dependencies = [ [[package]] name = "der" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -980,11 +1084,12 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle 2.4.1", ] @@ -1050,14 +1155,28 @@ checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "ecdsa" -version = "0.13.4" +version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der 0.7.7", + "digest 0.10.7", + "elliptic-curve 0.13.5", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki 0.7.2", ] [[package]] @@ -1067,7 +1186,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ "serde", - "signature", + "signature 1.6.4", +] + +[[package]] +name = "ed25519" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.1.0", +] + +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", ] [[package]] @@ -1077,7 +1219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek", - "ed25519", + "ed25519 1.5.3", "rand 0.7.3", "serde", "serde_bytes", @@ -1108,18 +1250,39 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" -version = "0.11.12" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "ff 0.11.1", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", "generic-array 0.14.7", - "group 0.11.0", + "group 0.12.1", + "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.2", + "digest 0.10.7", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", "subtle 2.4.1", "zeroize", ] @@ -1232,6 +1395,26 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle 2.4.1", +] + [[package]] name = "flate2" version = "1.0.26" @@ -1524,6 +1707,28 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core 0.6.4", + "subtle 2.4.1", +] + [[package]] name = "h2" version = "0.3.19" @@ -1707,7 +1912,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -1978,25 +2183,28 @@ dependencies = [ [[package]] name = "k256" -version = "0.10.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", - "sec1", - "sha2 0.9.9", - "sha3", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.6", ] [[package]] -name = "keccak" -version = "0.1.4" +name = "k256" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ - "cpufeatures", + "cfg-if 1.0.0", + "ecdsa 0.16.8", + "elliptic-curve 0.13.5", + "once_cell", + "sha2 0.10.6", + "signature 2.1.0", ] [[package]] @@ -2212,7 +2420,7 @@ dependencies = [ name = "nym-api-requests" version = "0.1.0" dependencies = [ - "bs58", + "bs58 0.4.0", "cosmrs", "cosmwasm-std", "getset", @@ -2304,7 +2512,7 @@ version = "1.1.1" dependencies = [ "anyhow", "async-trait", - "bs58", + "bs58 0.4.0", "console_error_panic_hook", "futures", "js-sys", @@ -2342,7 +2550,7 @@ name = "nym-coconut" version = "0.5.0" dependencies = [ "bls12_381 0.6.0", - "bs58", + "bs58 0.4.0", "digest 0.9.0", "ff 0.11.1", "getrandom 0.2.10", @@ -2383,7 +2591,7 @@ dependencies = [ name = "nym-coconut-interface" version = "0.1.0" dependencies = [ - "bs58", + "bs58 0.4.0", "getset", "nym-coconut", "serde", @@ -2407,7 +2615,7 @@ dependencies = [ name = "nym-contracts-common" version = "0.5.0" dependencies = [ - "bs58", + "bs58 0.4.0", "cosmwasm-std", "schemars", "serde", @@ -2444,10 +2652,10 @@ version = "0.4.0" dependencies = [ "aes 0.8.2", "blake3", - "bs58", + "bs58 0.4.0", "cipher 0.4.4", "ctr 0.9.2", - "digest 0.10.6", + "digest 0.10.7", "ed25519-dalek", "generic-array 0.14.7", "hkdf 0.12.3", @@ -2469,7 +2677,7 @@ version = "0.1.0" dependencies = [ "bitvec", "bls12_381 0.6.0", - "bs58", + "bs58 0.4.0", "ff 0.11.1", "group 0.11.0", "lazy_static", @@ -2519,7 +2727,7 @@ dependencies = [ name = "nym-gateway-requests" version = "0.1.0" dependencies = [ - "bs58", + "bs58 0.4.0", "futures", "generic-array 0.14.7", "log", @@ -2540,6 +2748,8 @@ dependencies = [ name = "nym-group-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", + "cw-controllers", "cw4", "schemars", "serde", @@ -2549,14 +2759,14 @@ dependencies = [ name = "nym-mixnet-contract-common" version = "0.6.0" dependencies = [ - "bs58", + "bs58 0.4.0", "cosmwasm-std", "humantime-serde", "log", "nym-contracts-common", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.4.1", "serde_repr", "thiserror", "time", @@ -2566,7 +2776,9 @@ dependencies = [ name = "nym-multisig-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw-storage-plus", "cw-utils", "cw3", "cw4", @@ -2724,7 +2936,7 @@ dependencies = [ name = "nym-sphinx-anonymous-replies" version = "0.1.0" dependencies = [ - "bs58", + "bs58 0.4.0", "nym-crypto", "nym-sphinx-addressing", "nym-sphinx-params", @@ -2848,7 +3060,7 @@ name = "nym-topology" version = "0.1.0" dependencies = [ "async-trait", - "bs58", + "bs58 0.4.0", "log", "nym-bin-common", "nym-crypto", @@ -2897,6 +3109,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.9.9", + "tendermint-rpc", "thiserror", "tokio", "url", @@ -3085,11 +3298,12 @@ checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "pbkdf2" -version = "0.9.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "crypto-mac 0.11.1", + "digest 0.10.7", + "hmac 0.12.1", ] [[package]] @@ -3214,13 +3428,22 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", - "zeroize", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.7", + "spki 0.7.2", ] [[package]] @@ -3303,9 +3526,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.10.4" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", "prost-derive", @@ -3313,9 +3536,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.10.1" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", @@ -3326,11 +3549,10 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.10.1" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "bytes", "prost", ] @@ -3549,15 +3771,25 @@ dependencies = [ [[package]] name = "rfc6979" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", - "hmac 0.11.0", + "crypto-bigint 0.4.9", + "hmac 0.12.1", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle 2.4.1", +] + [[package]] name = "ring" version = "0.16.20" @@ -3574,14 +3806,12 @@ dependencies = [ ] [[package]] -name = "ripemd160" -version = "0.9.1" +name = "ripemd" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug 0.3.0", + "digest 0.10.7", ] [[package]] @@ -3742,13 +3972,28 @@ dependencies = [ [[package]] name = "sec1" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "der", + "base16ct 0.1.1", + "der 0.6.1", "generic-array 0.14.7", - "pkcs8", + "pkcs8 0.9.0", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.7", + "generic-array 0.14.7", + "pkcs8 0.10.2", "subtle 2.4.1", "zeroize", ] @@ -3818,6 +4063,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-json-wasm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +dependencies = [ + "serde", +] + [[package]] name = "serde-wasm-bindgen" version = "0.5.0" @@ -3924,7 +4178,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -3948,19 +4202,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha3" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug 0.3.0", + "digest 0.10.7", ] [[package]] @@ -3974,11 +4216,21 @@ dependencies = [ [[package]] name = "signature" -version = "1.4.0" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.9.0", + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", "rand_core 0.6.4", ] @@ -4016,7 +4268,7 @@ dependencies = [ "aes 0.7.5", "arrayref", "blake2 0.8.1", - "bs58", + "bs58 0.4.0", "byteorder", "chacha", "curve25519-dalek", @@ -4048,12 +4300,22 @@ dependencies = [ [[package]] name = "spki" -version = "0.5.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.7", ] [[package]] @@ -4292,6 +4554,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "syn" version = "1.0.109" @@ -4335,28 +4603,28 @@ dependencies = [ [[package]] name = "tendermint" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca881fa4dedd2b46334f13be7fbc8cc1549ba4be5a833fe4e73d1a1baaf7949" +checksum = "3f0a7d05cf78524782337f8edd55cbc578d159a16ad4affe2135c92f7dbac7f0" dependencies = [ - "async-trait", "bytes", - "ed25519", - "ed25519-dalek", + "digest 0.10.7", + "ed25519 2.2.1", + "ed25519-consensus", "flex-error", "futures", - "k256", + "k256 0.13.1", "num-traits", "once_cell", "prost", "prost-types", - "ripemd160", + "ripemd", "serde", "serde_bytes", "serde_json", "serde_repr", - "sha2 0.9.9", - "signature", + "sha2 0.10.6", + "signature 2.1.0", "subtle 2.4.1", "subtle-encoding", "tendermint-proto", @@ -4366,9 +4634,9 @@ dependencies = [ [[package]] name = "tendermint-config" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c56ee93f4e9b7e7daba86d171f44572e91b741084384d0ae00df7991873dfd" +checksum = "71a72dbbea6dde12045d261f2c70c0de039125675e8a026c8d5ad34522756372" dependencies = [ "flex-error", "serde", @@ -4380,9 +4648,9 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71f925d74903f4abbdc4af0110635a307b3cb05b175fdff4a7247c14a4d0874" +checksum = "c0cec054567d16d85e8c3f6a3139963d1a66d9d3051ed545d31562550e9bcc3d" dependencies = [ "bytes", "flex-error", @@ -4398,9 +4666,9 @@ dependencies = [ [[package]] name = "tendermint-rpc" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63f57ee05a1e927887191c76d1b139de9fa40c180b9f8727ee44377242a6" +checksum = "d119d83a130537fc4a98c3c9eb6899ebe857fea4860400a61675bfb5f0b35129" dependencies = [ "async-trait", "bytes", @@ -4413,9 +4681,11 @@ dependencies = [ "hyper-rustls", "peg", "pin-project", + "semver 1.0.17", "serde", "serde_bytes", "serde_json", + "subtle 2.4.1", "subtle-encoding", "tendermint", "tendermint-config", diff --git a/clients/webassembly/src/client/config.rs b/clients/webassembly/src/client/config.rs index e531f072d1..07d9ab5f5f 100644 --- a/clients/webassembly/src/client/config.rs +++ b/clients/webassembly/src/client/config.rs @@ -4,7 +4,7 @@ // due to expansion of #[wasm_bindgen] macro on `Debug` Config struct #![allow(clippy::drop_non_drop)] // another issue due to #[wasm_bindgen] and `Copy` trait -#![allow(clippy::drop_copy)] +#![allow(dropping_copy_types)] use nym_client_core::config::{ Acknowledgements as ConfigAcknowledgements, Config as BaseClientConfig, diff --git a/common/bandwidth-controller/Cargo.toml b/common/bandwidth-controller/Cargo.toml index 40fe146d81..5b168d8b9f 100644 --- a/common/bandwidth-controller/Cargo.toml +++ b/common/bandwidth-controller/Cargo.toml @@ -18,7 +18,6 @@ nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric" nym-network-defaults = { path = "../network-defaults" } nym-validator-client = { path = "../client-libs/validator-client", default-features = false } - [target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client] path = "../client-libs/validator-client" -features = ["nyxd-client"] +features = ["signing"] diff --git a/common/bandwidth-controller/src/acquire/mod.rs b/common/bandwidth-controller/src/acquire/mod.rs index cfaf8dc473..cdf72c3b00 100644 --- a/common/bandwidth-controller/src/acquire/mod.rs +++ b/common/bandwidth-controller/src/acquire/mod.rs @@ -10,8 +10,8 @@ use nym_crypto::asymmetric::{encryption, identity}; use nym_network_defaults::VOUCHER_INFO; use nym_validator_client::nyxd::traits::CoconutBandwidthSigningClient; use nym_validator_client::nyxd::traits::DkgQueryClient; -use nym_validator_client::nyxd::tx::Hash; use nym_validator_client::nyxd::Coin; +use nym_validator_client::nyxd::Hash; use nym_validator_client::CoconutApiClient; use rand::rngs::OsRng; use state::{KeyPair, State}; diff --git a/common/client-core/Cargo.toml b/common/client-core/Cargo.toml index b8b1403d86..d5ae32dbd2 100644 --- a/common/client-core/Cargo.toml +++ b/common/client-core/Cargo.toml @@ -45,7 +45,7 @@ nym-network-defaults = { path = "../network-defaults" } [target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client] path = "../client-libs/validator-client" -features = ["nyxd-client"] +features = ["signing", "http-client"] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream] version = "0.1.11" diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index 6ad04678f8..1c23d562f9 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -38,6 +38,7 @@ use wasm_utils::websocket::JSWebsocket; #[cfg(target_arch = "wasm32")] type WsConn = JSWebsocket; +const CONCURRENT_GATEWAYS_MEASURED: usize = 20; const MEASUREMENTS: usize = 3; #[cfg(not(target_arch = "wasm32"))] @@ -158,23 +159,29 @@ pub(super) async fn choose_gateway_by_latency( rng: &mut R, gateways: &[gateway::Node], ) -> Result { - info!("choosing gateway by latency..."); + info!( + "choosing gateway by latency, pinging {} gateways ...", + gateways.len() + ); - let mut gateways_with_latency = Vec::new(); - for gateway in gateways { - let id = *gateway.identity(); - trace!("measuring latency to {id}..."); - let with_latency = match measure_latency(gateway).await { - Ok(res) => res, - Err(err) => { - warn!("failed to measure {id}: {err}"); - continue; - } - }; - debug!("{id}: {:?}", with_latency.latency); - gateways_with_latency.push(with_latency) - } + let gateways_with_latency = Arc::new(tokio::sync::Mutex::new(Vec::new())); + futures::stream::iter(gateways) + .for_each_concurrent(CONCURRENT_GATEWAYS_MEASURED, |gateway| async { + let id = *gateway.identity(); + trace!("measuring latency to {id}..."); + match measure_latency(gateway).await { + Ok(with_latency) => { + debug!("{id}: {:?}", with_latency.latency); + gateways_with_latency.lock().await.push(with_latency); + } + Err(err) => { + warn!("failed to measure {id}: {err}"); + } + }; + }) + .await; + let gateways_with_latency = gateways_with_latency.lock().await; let chosen = gateways_with_latency .choose_weighted(rng, |item| 1. / item.latency.as_secs_f32()) .expect("invalid selection weight!"); diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index 3e0428e643..31475887bc 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -35,27 +35,30 @@ nym-coconut-interface = { path = "../../coconut-interface" } nym-network-defaults = { path = "../../network-defaults" } nym-api-requests = { path = "../../../nym-api/nym-api-requests" } -# required for nyxd-client -# at some point it might be possible to make it wasm-compatible -# perhaps after https://github.com/cosmos/cosmos-rust/pull/97 is resolved (and tendermint-rs is updated) -async-trait = { workspace = true, optional = true } +async-trait = { workspace = true } bip39 = { workspace = true, features = ["rand"], optional = true } -nym-config = { path = "../../config", optional = true } -cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true } +nym-config = { path = "../../config" } +cosmrs = { workspace = true, features = ["bip32", "cosmwasm"] } +#cosmrs = { workspace = true, features = ["bip32", "rpc", "cosmwasm"], optional = true } # note that this has the same version as used by cosmrs + +# import it just for the `Client` trait +tendermint-rpc = { workspace = true } + eyre = { version = "0.6", optional = true } -cw3 = { workspace = true, optional = true } -cw4 = { workspace = true, optional = true } -prost = { version = "0.10", default-features = false, optional = true } -flate2 = { version = "1.0.20", optional = true } -sha2 = { version = "0.9.5", optional = true } -itertools = { version = "0.10", optional = true } +cw3 = { workspace = true } +cw4 = { workspace = true } +prost = { version = "0.11", default-features = false } +flate2 = { version = "1.0.20" } +sha2 = { version = "0.9.5" } +itertools = { version = "0.10" } zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] } -cosmwasm-std = { workspace = true, optional = true } +cosmwasm-std = { workspace = true } [dev-dependencies] bip39 = { workspace = true } -cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32"] } +#cosmrs = { workspace = true, features = ["rpc", "bip32"] } +cosmrs = { workspace = true, features = ["bip32"] } tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] } ts-rs = "6.1.2" @@ -64,35 +67,23 @@ name = "offline_signing" # it should only really require the "signing" feature, # but that would require another round of refactoring to make it possible # (traits would need to be moved around and refactored themselves) -required-features = ["nyxd-client"] +required-features = ["http-client", "signing"] [[example]] name = "query_service_provider_directory" -required-features = ["nyxd-client"] +# TODO: validate the requirements +required-features = ["http-client", "signing"] [[example]] name = "query_name_service" -required-features = ["nyxd-client"] +# TODO: validate the requirements +required-features = ["http-client", "signing"] [features] -nyxd-client = [ - "async-trait", - "cosmrs", - "cosmwasm-std", - "cw3", - "cw4", - "flate2", - "itertools", - "openssl", - "prost", - "sha2", - "signing" -] +http-client = ["cosmrs/rpc", "openssl"] signing = [ "bip39", - "cosmrs", "eyre", - "nym-config", "zeroize" ] generate-ts = [] diff --git a/common/client-libs/validator-client/examples/offline_signing.rs b/common/client-libs/validator-client/examples/offline_signing.rs index d48db60e53..a21bfa4475 100644 --- a/common/client-libs/validator-client/examples/offline_signing.rs +++ b/common/client-libs/validator-client/examples/offline_signing.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use cosmrs::bank::MsgSend; -use cosmrs::rpc::{self, HttpClient}; +use cosmrs::rpc::HttpClient; use cosmrs::tx::Msg; use cosmrs::{tx, AccountId, Coin, Denom}; use nym_validator_client::nyxd::CosmWasmClient; @@ -54,7 +54,7 @@ async fn main() { denom, amount: 2500u32.into(), }, - 100000, + 100000u32, ); let tx_raw = tx_signer @@ -70,7 +70,7 @@ async fn main() { .unwrap(); // broadcast the tx - let res = rpc::Client::broadcast_tx_commit(&broadcaster, tx_bytes.into()) + let res = tendermint_rpc::client::Client::broadcast_tx_commit(&broadcaster, tx_bytes) .await .unwrap(); diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 1ccf62a67e..54539403fb 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -16,37 +16,35 @@ pub use nym_mixnet_contract_common::{ }; use url::Url; -#[cfg(feature = "nyxd-client")] use crate::nyxd::traits::{DkgQueryClient, MixnetQueryClient}; -#[cfg(feature = "nyxd-client")] -use crate::nyxd::{self, CosmWasmClient, NyxdClient, QueryNyxdClient, SigningNyxdClient}; -#[cfg(feature = "nyxd-client")] -use crate::signing::direct_wallet::DirectSecp256k1HdWallet; -#[cfg(feature = "nyxd-client")] +#[cfg(feature = "http-client")] +use crate::nyxd::QueryNyxdClient; +use crate::nyxd::{self, CosmWasmClient, NyxdClient}; use nym_api_requests::models::MixNodeBondAnnotated; -#[cfg(feature = "nyxd-client")] use nym_coconut_dkg_common::{types::EpochId, verification_key::ContractVKShare}; -#[cfg(feature = "nyxd-client")] use nym_coconut_interface::Base58; -#[cfg(feature = "nyxd-client")] use nym_mixnet_contract_common::{ families::{Family, FamilyHead}, mixnode::MixNodeBond, pending_events::{PendingEpochEvent, PendingIntervalEvent}, Delegation, RewardedSetNodeStatus, UnbondedMixnode, }; -#[cfg(feature = "nyxd-client")] use nym_network_defaults::NymNetworkDetails; -#[cfg(feature = "nyxd-client")] use std::str::FromStr; -#[cfg(feature = "nyxd-client")] +#[cfg(all(feature = "signing", feature = "http-client"))] +use crate::nyxd::SigningNyxdClient; +#[cfg(all(feature = "signing", feature = "http-client"))] +use crate::signing::direct_wallet::DirectSecp256k1HdWallet; + #[must_use] #[derive(Debug, Clone)] pub struct Config { api_url: Url, nyxd_url: Url, + // TODO: until refactored, this is a dead field under some features + #[allow(dead_code)] nyxd_config: nyxd::Config, mixnode_page_limit: Option, @@ -55,7 +53,6 @@ pub struct Config { rewarded_set_page_limit: Option, } -#[cfg(feature = "nyxd-client")] impl Config { pub fn try_from_nym_network_details( details: &NymNetworkDetails, @@ -119,7 +116,6 @@ impl Config { } } -#[cfg(feature = "nyxd-client")] pub struct Client { mixnode_page_limit: Option, gateway_page_limit: Option, @@ -131,7 +127,7 @@ pub struct Client { pub nyxd: NyxdClient, } -#[cfg(feature = "nyxd-client")] +#[cfg(all(feature = "signing", feature = "http-client"))] impl Client> { pub fn new_signing( config: Config, @@ -165,7 +161,7 @@ impl Client> { } } -#[cfg(feature = "nyxd-client")] +#[cfg(feature = "http-client")] impl Client { pub fn new_query(config: Config) -> Result, ValidatorClientError> { let nym_api_client = nym_api::Client::new(config.api_url.clone()); @@ -189,7 +185,6 @@ impl Client { } // nyxd wrappers -#[cfg(feature = "nyxd-client")] impl Client { // use case: somebody initialised client without a contract in order to upload and initialise one // and now they want to actually use it without making new client @@ -572,7 +567,6 @@ impl Client { } // validator-api wrappers -#[cfg(feature = "nyxd-client")] impl Client { pub fn change_nym_api(&mut self, new_endpoint: Url) { self.nym_api.change_url(new_endpoint) @@ -635,11 +629,9 @@ pub struct CoconutApiClient { pub api_client: NymApiClient, pub verification_key: VerificationKey, pub node_id: NodeIndex, - #[cfg(feature = "nyxd-client")] pub cosmos_address: cosmrs::AccountId, } -#[cfg(feature = "nyxd-client")] impl CoconutApiClient { pub async fn all_coconut_api_clients( client: &C, diff --git a/common/client-libs/validator-client/src/connection_tester.rs b/common/client-libs/validator-client/src/connection_tester.rs index 5d89e86d05..800399d415 100644 --- a/common/client-libs/validator-client/src/connection_tester.rs +++ b/common/client-libs/validator-client/src/connection_tester.rs @@ -120,7 +120,7 @@ async fn test_nyxd_connection( ) .await { - Ok(Err(NyxdError::TendermintError(e))) => { + Ok(Err(NyxdError::TendermintErrorRpc(e))) => { // If we get a tendermint-rpc error, we classify the node as not contactable log::warn!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e); false diff --git a/common/client-libs/validator-client/src/error.rs b/common/client-libs/validator-client/src/error.rs index 08da52c626..a675a27717 100644 --- a/common/client-libs/validator-client/src/error.rs +++ b/common/client-libs/validator-client/src/error.rs @@ -15,7 +15,6 @@ pub enum ValidatorClientError { #[error("One of the provided URLs was malformed - {0}")] MalformedUrlProvided(#[from] url::ParseError), - #[cfg(feature = "nyxd-client")] #[error("nyxd request failed - {0}")] NyxdError(#[from] crate::nyxd::error::NyxdError), diff --git a/common/client-libs/validator-client/src/lib.rs b/common/client-libs/validator-client/src/lib.rs index 5d18c251c0..ab0cff646e 100644 --- a/common/client-libs/validator-client/src/lib.rs +++ b/common/client-libs/validator-client/src/lib.rs @@ -2,11 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 pub mod client; -#[cfg(feature = "nyxd-client")] +#[cfg(feature = "http-client")] pub mod connection_tester; pub mod error; pub mod nym_api; -#[cfg(feature = "nyxd-client")] pub mod nyxd; #[cfg(feature = "signing")] @@ -16,5 +15,4 @@ pub use crate::error::ValidatorClientError; pub use client::NymApiClient; pub use nym_api_requests::*; -#[cfg(feature = "nyxd-client")] pub use client::{Client, CoconutApiClient, Config}; diff --git a/common/client-libs/validator-client/src/nyxd/coin.rs b/common/client-libs/validator-client/src/nyxd/coin.rs index 3ac51997b8..f780bd32f3 100644 --- a/common/client-libs/validator-client/src/nyxd/coin.rs +++ b/common/client-libs/validator-client/src/nyxd/coin.rs @@ -52,7 +52,6 @@ impl<'a> Div for &'a Coin { } else { implicit_gas_limit.u128() as u64 } - .into() } } @@ -191,32 +190,32 @@ mod tests { let amount = Coin::new(3938, "unym"); let gas_price = "0.025unym".parse().unwrap(); let res = amount / gas_price; - assert_eq!(157520, res.value()); + assert_eq!(157520, res); let amount = Coin::new(1234567890, "unym"); let gas_price = "0.025unym".parse().unwrap(); let res = amount / gas_price; - assert_eq!(49382715600, res.value()); + assert_eq!(49382715600, res); let amount = Coin::new(1, "unym"); let gas_price = "0.025unym".parse().unwrap(); let res = amount / gas_price; - assert_eq!(40, res.value()); + assert_eq!(40, res); let amount = Coin::new(150_000_000, "unym"); let gas_price = "0.001234unym".parse().unwrap(); let res = amount / gas_price; - assert_eq!(121555915721, res.value()); + assert_eq!(121555915721, res); let amount = Coin::new(150_000_000, "unym"); let gas_price = "1unym".parse().unwrap(); let res = amount / gas_price; - assert_eq!(150_000_000, res.value()); + assert_eq!(150_000_000, res); let amount = Coin::new(150_000_000, "unym"); let gas_price = "1234.56unym".parse().unwrap(); let res = amount / gas_price; - assert_eq!(121500, res.value()); + assert_eq!(121500, res); } #[test] diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs index 9be833c68b..4a512a02db 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs @@ -1,15 +1,16 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::nyxd; use crate::nyxd::coin::Coin; use crate::nyxd::cosmwasm_client::helpers::{create_pagination, next_page_key}; use crate::nyxd::cosmwasm_client::types::{ - Account, Code, CodeDetails, Contract, ContractCodeHistoryEntry, ContractCodeId, - SequenceResponse, SimulateResponse, + Account, CodeDetails, Contract, ContractCodeId, SequenceResponse, SimulateResponse, }; use crate::nyxd::error::NyxdError; +use crate::nyxd::TendermintClient; use async_trait::async_trait; +use cosmrs::cosmwasm::{CodeInfoResponse, ContractCodeHistoryEntry}; use cosmrs::proto::cosmos::auth::v1beta1::{QueryAccountRequest, QueryAccountResponse}; use cosmrs::proto::cosmos::bank::v1beta1::{ QueryAllBalancesRequest, QueryAllBalancesResponse, QueryBalanceRequest, QueryBalanceResponse, @@ -18,22 +19,28 @@ use cosmrs::proto::cosmos::bank::v1beta1::{ use cosmrs::proto::cosmos::tx::v1beta1::{ SimulateRequest, SimulateResponse as ProtoSimulateResponse, }; -use cosmrs::proto::cosmwasm::wasm::v1::*; -use cosmrs::rpc::endpoint::block::Response as BlockResponse; -use cosmrs::rpc::endpoint::broadcast; -use cosmrs::rpc::endpoint::tx::Response as TxResponse; -use cosmrs::rpc::query::Query; -use cosmrs::rpc::{self, HttpClient, Order}; -use cosmrs::tendermint::abci::Transaction; -use cosmrs::tendermint::{abci, block, chain}; -use cosmrs::{tx, AccountId, Coin as CosmosCoin, Tx}; +use cosmrs::proto::cosmwasm::wasm::v1::{ + QueryCodeRequest, QueryCodeResponse, QueryCodesRequest, QueryCodesResponse, + QueryContractHistoryRequest, QueryContractHistoryResponse, QueryContractInfoRequest, + QueryContractInfoResponse, QueryContractsByCodeRequest, QueryContractsByCodeResponse, + QueryRawContractStateRequest, QueryRawContractStateResponse, QuerySmartContractStateRequest, + QuerySmartContractStateResponse, +}; +use cosmrs::tendermint::{block, chain, Hash}; +use cosmrs::{AccountId, Coin as CosmosCoin, Tx}; use prost::Message; use serde::{Deserialize, Serialize}; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use std::time::Duration; +use tendermint_rpc::{ + endpoint::{block::Response as BlockResponse, broadcast, tx::Response as TxResponse}, + query::Query, + Order, +}; +#[cfg(feature = "http-client")] #[async_trait] -impl CosmWasmClient for HttpClient { +impl CosmWasmClient for cosmrs::rpc::HttpClient { fn broadcast_polling_rate(&self) -> Duration { Duration::from_secs(4) } @@ -44,7 +51,7 @@ impl CosmWasmClient for HttpClient { } #[async_trait] -pub trait CosmWasmClient: rpc::Client { +pub trait CosmWasmClient: TendermintClient { // this should probably get redesigned, but I'm leaving those like that temporarily to fix // the underlying issue more quickly fn broadcast_polling_rate(&self) -> Duration; @@ -55,7 +62,7 @@ pub trait CosmWasmClient: rpc::Client { // require proof? async fn make_abci_query( &self, - path: Option, + path: Option, req: Req, ) -> Result where @@ -81,7 +88,7 @@ pub trait CosmWasmClient: rpc::Client { // TODO: the return type should probably be changed to a non-proto, type-safe Account alternative async fn get_account(&self, address: &AccountId) -> Result, NyxdError> { - let path = Some("/cosmos.auth.v1beta1.Query/Account".parse().unwrap()); + let path = Some("/cosmos.auth.v1beta1.Query/Account".to_owned()); let req = QueryAccountRequest { address: address.to_string(), @@ -119,7 +126,7 @@ pub trait CosmWasmClient: rpc::Client { address: &AccountId, search_denom: String, ) -> Result, NyxdError> { - let path = Some("/cosmos.bank.v1beta1.Query/Balance".parse().unwrap()); + let path = Some("/cosmos.bank.v1beta1.Query/Balance".to_owned()); let req = QueryBalanceRequest { address: address.to_string(), @@ -137,7 +144,7 @@ pub trait CosmWasmClient: rpc::Client { } async fn get_all_balances(&self, address: &AccountId) -> Result, NyxdError> { - let path = Some("/cosmos.bank.v1beta1.Query/AllBalances".parse().unwrap()); + let path = Some("/cosmos.bank.v1beta1.Query/AllBalances".to_owned()); let mut raw_balances = Vec::new(); let mut pagination = None; @@ -168,7 +175,7 @@ pub trait CosmWasmClient: rpc::Client { } async fn get_total_supply(&self) -> Result, NyxdError> { - let path = Some("/cosmos.bank.v1beta1.Query/TotalSupply".parse().unwrap()); + let path = Some("/cosmos.bank.v1beta1.Query/TotalSupply".to_owned()); let mut supply = Vec::new(); let mut pagination = None; @@ -195,7 +202,7 @@ pub trait CosmWasmClient: rpc::Client { .map_err(|_| NyxdError::SerializationError("Coins".to_owned())) } - async fn get_tx(&self, id: tx::Hash) -> Result { + async fn get_tx(&self, id: Hash) -> Result { Ok(self.tx(id, false).await?) } @@ -231,30 +238,36 @@ pub trait CosmWasmClient: rpc::Client { } /// Broadcast a transaction, returning immediately. - async fn broadcast_tx_async( - &self, - tx: Transaction, - ) -> Result { - Ok(rpc::Client::broadcast_tx_async(self, tx).await?) + async fn broadcast_tx_async(&self, tx: T) -> Result + where + T: Into> + Send, + { + Ok(tendermint_rpc::client::Client::broadcast_tx_async(self, tx).await?) } /// Broadcast a transaction, returning the response from `CheckTx`. - async fn broadcast_tx_sync( - &self, - tx: Transaction, - ) -> Result { - Ok(rpc::Client::broadcast_tx_sync(self, tx).await?) + async fn broadcast_tx_sync(&self, tx: T) -> Result + where + T: Into> + Send, + { + Ok(tendermint_rpc::client::Client::broadcast_tx_sync(self, tx).await?) } /// Broadcast a transaction, returning the response from `DeliverTx`. - async fn broadcast_tx_commit( + async fn broadcast_tx_commit( &self, - tx: Transaction, - ) -> Result { - Ok(rpc::Client::broadcast_tx_commit(self, tx).await?) + tx: T, + ) -> Result + where + T: Into> + Send, + { + Ok(tendermint_rpc::client::Client::broadcast_tx_commit(self, tx).await?) } - async fn broadcast_tx(&self, tx: Transaction) -> Result { + async fn broadcast_tx(&self, tx: T) -> Result + where + T: Into> + Send, + { let broadcasted = CosmWasmClient::broadcast_tx_sync(self, tx).await?; if broadcasted.code.is_err() { @@ -290,8 +303,8 @@ pub trait CosmWasmClient: rpc::Client { } } - async fn get_codes(&self) -> Result, NyxdError> { - let path = Some("/cosmwasm.wasm.v1.Query/Codes".parse().unwrap()); + async fn get_codes(&self) -> Result, NyxdError> { + let path = Some("/cosmwasm.wasm.v1.Query/Codes".to_owned()); let mut raw_codes = Vec::new(); let mut pagination = None; @@ -311,14 +324,14 @@ pub trait CosmWasmClient: rpc::Client { } } - raw_codes + Ok(raw_codes .into_iter() .map(TryFrom::try_from) - .collect::>() + .collect::>()?) } async fn get_code_details(&self, code_id: ContractCodeId) -> Result { - let path = Some("/cosmwasm.wasm.v1.Query/Code".parse().unwrap()); + let path = Some("/cosmwasm.wasm.v1.Query/Code".to_owned()); let req = QueryCodeRequest { code_id }; @@ -333,7 +346,7 @@ pub trait CosmWasmClient: rpc::Client { } } async fn get_contracts(&self, code_id: ContractCodeId) -> Result, NyxdError> { - let path = Some("/cosmwasm.wasm.v1.Query/ContractsByCode".parse().unwrap()); + let path = Some("/cosmwasm.wasm.v1.Query/ContractsByCode".to_owned()); let mut raw_contracts = Vec::new(); let mut pagination = None; @@ -364,7 +377,7 @@ pub trait CosmWasmClient: rpc::Client { } async fn get_contract(&self, address: &AccountId) -> Result { - let path = Some("/cosmwasm.wasm.v1.Query/ContractInfo".parse().unwrap()); + let path = Some("/cosmwasm.wasm.v1.Query/ContractInfo".to_owned()); let req = QueryContractInfoRequest { address: address.to_string(), @@ -389,7 +402,7 @@ pub trait CosmWasmClient: rpc::Client { &self, address: &AccountId, ) -> Result, NyxdError> { - let path = Some("/cosmwasm.wasm.v1.Query/ContractHistory".parse().unwrap()); + let path = Some("/cosmwasm.wasm.v1.Query/ContractHistory".to_owned()); let mut raw_entries = Vec::new(); let mut pagination = None; @@ -412,10 +425,10 @@ pub trait CosmWasmClient: rpc::Client { } } - raw_entries + Ok(raw_entries .into_iter() .map(TryFrom::try_from) - .collect::>() + .collect::>()?) } async fn query_contract_raw( @@ -423,7 +436,7 @@ pub trait CosmWasmClient: rpc::Client { address: &AccountId, query_data: Vec, ) -> Result, NyxdError> { - let path = Some("/cosmwasm.wasm.v1.Query/RawContractState".parse().unwrap()); + let path = Some("/cosmwasm.wasm.v1.Query/RawContractState".to_owned()); let req = QueryRawContractStateRequest { address: address.to_string(), @@ -479,7 +492,7 @@ pub trait CosmWasmClient: rpc::Client { tx: Option, tx_bytes: Vec, ) -> Result { - let path = Some("/cosmos.tx.v1beta1.Service/Simulate".parse().unwrap()); + let path = Some("/cosmos.tx.v1beta1.Service/Simulate".to_owned()); let req = SimulateRequest { tx: tx.map(Into::into), diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs index 0dd7f98ead..64c2fd25fa 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/helpers.rs @@ -3,12 +3,7 @@ use crate::nyxd::error::NyxdError; use cosmrs::proto::cosmos::base::query::v1beta1::{PageRequest, PageResponse}; -use cosmrs::proto::cosmos::base::v1beta1::Coin as ProtoCoin; -use cosmrs::rpc::endpoint::broadcast; -use cosmrs::Coin; -use flate2::write::GzEncoder; -use flate2::Compression; -use std::io::Write; +use tendermint_rpc::endpoint::broadcast; pub(crate) trait CheckResponse: Sized { fn check_response(self) -> Result; @@ -21,7 +16,7 @@ impl CheckResponse for broadcast::tx_commit::Response { hash: self.hash, height: Some(self.height), code: self.check_tx.code.value(), - raw_log: self.check_tx.log.value().to_owned(), + raw_log: self.check_tx.log, }); } @@ -30,7 +25,7 @@ impl CheckResponse for broadcast::tx_commit::Response { hash: self.hash, height: Some(self.height), code: self.deliver_tx.code.value(), - raw_log: self.deliver_tx.log.value().to_owned(), + raw_log: self.deliver_tx.log, }); } @@ -45,7 +40,7 @@ impl CheckResponse for crate::nyxd::TxResponse { hash: self.hash, height: Some(self.height), code: self.tx_result.code.value(), - raw_log: self.tx_result.log.value().to_owned(), + raw_log: self.tx_result.log, }); } @@ -53,7 +48,12 @@ impl CheckResponse for crate::nyxd::TxResponse { } } +#[cfg(feature = "signing")] pub(crate) fn compress_wasm_code(code: &[u8]) -> Result, NyxdError> { + use flate2::write::GzEncoder; + use flate2::Compression; + use std::io::Write; + // using compression level 9, same as cosmjs, that optimises for size let mut encoder = GzEncoder::new(Vec::new(), Compression::best()); encoder @@ -83,14 +83,3 @@ pub(crate) fn next_page_key(pagination_info: Option) -> Option) -> Result, NyxdError> { - value - .into_iter() - .map(|proto_coin| { - Coin::try_from(&proto_coin).map_err(|_| NyxdError::MalformedCoin { - coin_representation: format!("{:?}", proto_coin), - }) - }) - .collect() -} diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/logs.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/logs.rs index 3912500f47..d16b3055c2 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/logs.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/logs.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use crate::nyxd::error::NyxdError; -use cosmrs::tendermint::abci; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -49,7 +48,7 @@ fn parse_raw_str_logs(raw: &str) -> Result, NyxdError> { Ok(logs) } -pub fn parse_raw_logs(raw: abci::Log) -> Result, NyxdError> { +pub fn parse_raw_logs(raw: String) -> Result, NyxdError> { parse_raw_str_logs(raw.as_ref()) } diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs index f668cc27a1..0a2d2c7c32 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/mod.rs @@ -1,17 +1,22 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +#[cfg(feature = "http-client")] use crate::nyxd::error::NyxdError; -use crate::nyxd::GasPrice; +#[cfg(feature = "http-client")] use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl}; +#[cfg(feature = "http-client")] use std::convert::TryInto; pub mod client; mod helpers; pub mod logs; -pub mod signing_client; pub mod types; +#[cfg(feature = "signing")] +pub mod signing_client; + +#[cfg(feature = "http-client")] pub fn connect(endpoint: U) -> Result where U: TryInto, @@ -19,10 +24,11 @@ where Ok(HttpClient::new(endpoint)?) } +#[cfg(all(feature = "signing", feature = "http-client"))] pub fn connect_with_signer( endpoint: U, signer: S, - gas_price: GasPrice, + gas_price: crate::nyxd::GasPrice, ) -> Result, NyxdError> where U: TryInto, diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs index 88919743cd..f4d6a41829 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs @@ -9,29 +9,37 @@ use crate::nyxd::error::NyxdError; use crate::nyxd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER}; use crate::nyxd::{Coin, GasAdjustable, GasPrice, TxResponse}; use crate::signing::signer::OfflineSigner; -use crate::signing::tx_signer::TxSigner; use crate::signing::SignerData; use async_trait::async_trait; +use cosmrs::abci::GasInfo; use cosmrs::bank::MsgSend; use cosmrs::distribution::MsgWithdrawDelegatorReward; use cosmrs::feegrant::{ AllowedMsgAllowance, BasicAllowance, MsgGrantAllowance, MsgRevokeAllowance, }; use cosmrs::proto::cosmos::tx::signing::v1beta1::SignMode; -use cosmrs::rpc::endpoint::broadcast; -use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest}; use cosmrs::staking::{MsgDelegate, MsgUndelegate}; -use cosmrs::tx::{self, Msg, Raw}; -use cosmrs::{cosmwasm, rpc, AccountId, Any, Tx}; +use cosmrs::tx::{self, Msg}; +use cosmrs::{cosmwasm, AccountId, Any, Tx}; use log::debug; use serde::Serialize; use sha2::Digest; use sha2::Sha256; use std::convert::TryInto; use std::time::{Duration, SystemTime}; +use tendermint_rpc::endpoint::broadcast; -const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4); -const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60); +#[cfg(feature = "http-client")] +use crate::signing::tx_signer::TxSigner; + +#[cfg(feature = "http-client")] +use tendermint_rpc::{Error as TendermintRpcError, SimpleRequest}; + +#[cfg(feature = "http-client")] +use cosmrs::rpc::{HttpClient, HttpClientUrl}; + +pub const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4); +pub const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60); fn empty_fee() -> tx::Fee { tx::Fee { @@ -123,7 +131,10 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .check_response()?; let logs = parse_raw_logs(tx_res.tx_result.log)?; - let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); + let gas_info = GasInfo { + gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(), + gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(), + }; // TODO: should those strings be extracted into some constants? // the reason I think unwrap here is fine is that if the transaction succeeded and those @@ -184,8 +195,10 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .check_response()?; let logs = parse_raw_logs(tx_res.tx_result.log)?; - let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); - + let gas_info = GasInfo { + gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(), + gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(), + }; // TODO: should those strings be extracted into some constants? // the reason I think unwrap here is fine is that if the transaction succeeded and those // fields do not exist or address is malformed, there's no way we can recover, we're probably connected @@ -212,7 +225,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { fee: Fee, memo: impl Into + Send + 'static, ) -> Result { - let change_admin_msg = cosmwasm::MsgUpdateAdmin { + let change_admin_msg = sealed::cosmwasm::MsgUpdateAdmin { sender: sender_address.clone(), new_admin: new_admin.clone(), contract: contract_address.clone(), @@ -225,8 +238,10 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .await? .check_response()?; - let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); - + let gas_info = GasInfo { + gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(), + gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(), + }; Ok(ChangeAdminResult { logs: parse_raw_logs(tx_res.tx_result.log)?, transaction_hash: tx_res.hash, @@ -241,7 +256,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { fee: Fee, memo: impl Into + Send + 'static, ) -> Result { - let change_admin_msg = cosmwasm::MsgClearAdmin { + let change_admin_msg = sealed::cosmwasm::MsgClearAdmin { sender: sender_address.clone(), contract: contract_address.clone(), } @@ -253,8 +268,10 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .await? .check_response()?; - let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); - + let gas_info = GasInfo { + gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(), + gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(), + }; Ok(ChangeAdminResult { logs: parse_raw_logs(tx_res.tx_result.log)?, transaction_hash: tx_res.hash, @@ -288,8 +305,10 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .await? .check_response()?; - let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); - + let gas_info = GasInfo { + gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(), + gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(), + }; Ok(MigrateResult { logs: parse_raw_logs(tx_res.tx_result.log)?, transaction_hash: tx_res.hash, @@ -323,11 +342,13 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .await? .check_response()?; - let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); - + let gas_info = GasInfo { + gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(), + gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(), + }; Ok(ExecuteResult { logs: parse_raw_logs(tx_res.tx_result.log)?, - data: tx_res.tx_result.data, + data: tx_res.tx_result.data.into(), transaction_hash: tx_res.hash, gas_info, }) @@ -364,11 +385,13 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .await? .check_response()?; - let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used); - + let gas_info = GasInfo { + gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(), + gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(), + }; Ok(ExecuteResult { logs: parse_raw_logs(tx_res.tx_result.log)?, - data: tx_res.tx_result.data, + data: tx_res.tx_result.data.into(), transaction_hash: tx_res.hash, gas_info, }) @@ -602,7 +625,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .to_bytes() .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; - CosmWasmClient::broadcast_tx_async(self, tx_bytes.into()).await + CosmWasmClient::broadcast_tx_async(self, tx_bytes).await } /// Broadcast a transaction, returning the response from `CheckTx`. @@ -622,7 +645,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .to_bytes() .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; - CosmWasmClient::broadcast_tx_sync(self, tx_bytes.into()).await + CosmWasmClient::broadcast_tx_sync(self, tx_bytes).await } /// Broadcast a transaction, returning the response from `DeliverTx`. @@ -643,7 +666,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .to_bytes() .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; - CosmWasmClient::broadcast_tx_commit(self, tx_bytes.into()).await + CosmWasmClient::broadcast_tx_commit(self, tx_bytes).await } /// Broadcast a transaction to the network and monitors its inclusion in a block. @@ -664,7 +687,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .to_bytes() .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; - self.broadcast_tx(tx_bytes.into()).await + self.broadcast_tx(tx_bytes).await } async fn sign( @@ -709,6 +732,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { ) -> Result; } +#[cfg(feature = "http-client")] #[derive(Debug)] pub struct Client { // TODO: somehow nicely hide this guy if we decide to use our client in offline mode, @@ -722,6 +746,7 @@ pub struct Client { broadcast_timeout: Duration, } +#[cfg(feature = "http-client")] impl Client { pub fn connect_with_signer( endpoint: U, @@ -770,12 +795,13 @@ impl Client { } } +#[cfg(feature = "http-client")] #[async_trait] -impl rpc::Client for Client +impl tendermint_rpc::client::Client for Client where S: Send + Sync, { - async fn perform(&self, request: R) -> Result + async fn perform(&self, request: R) -> Result where R: SimpleRequest, { @@ -783,6 +809,7 @@ where } } +#[cfg(feature = "http-client")] #[async_trait] impl CosmWasmClient for Client where @@ -797,6 +824,7 @@ where } } +#[cfg(feature = "http-client")] #[async_trait] impl SigningCosmWasmClient for Client where @@ -820,7 +848,7 @@ where fee: tx::Fee, memo: impl Into + Send + 'static, signer_data: SignerData, - ) -> Result { + ) -> Result { Ok(self .tx_signer .sign_amino(signer_address, messages, fee, memo, signer_data)?) @@ -833,9 +861,173 @@ where fee: tx::Fee, memo: impl Into + Send + 'static, signer_data: SignerData, - ) -> Result { + ) -> Result { Ok(self .tx_signer .sign_direct(signer_address, messages, fee, memo, signer_data)?) } } + +// a temporary bypass until https://github.com/cosmos/cosmos-rust/pull/419 is merged +mod sealed { + pub mod cosmwasm { + use cosmrs::{proto, tx::Msg, AccountId, ErrorReport, Result}; + + /// MsgUpdateAdmin sets a new admin for a smart contract + #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] + pub struct MsgUpdateAdmin { + /// Sender is the that actor that signed the messages + pub sender: AccountId, + + /// NewAdmin address to be set + pub new_admin: AccountId, + + /// Contract is the address of the smart contract + pub contract: AccountId, + } + + impl Msg for MsgUpdateAdmin { + type Proto = proto::cosmwasm::wasm::v1::MsgUpdateAdmin; + } + + impl TryFrom for MsgUpdateAdmin { + type Error = ErrorReport; + + fn try_from( + proto: proto::cosmwasm::wasm::v1::MsgUpdateAdmin, + ) -> Result { + MsgUpdateAdmin::try_from(&proto) + } + } + + impl TryFrom<&proto::cosmwasm::wasm::v1::MsgUpdateAdmin> for MsgUpdateAdmin { + type Error = ErrorReport; + + fn try_from( + proto: &proto::cosmwasm::wasm::v1::MsgUpdateAdmin, + ) -> Result { + Ok(MsgUpdateAdmin { + sender: proto.sender.parse()?, + new_admin: proto.new_admin.parse()?, + contract: proto.contract.parse()?, + }) + } + } + + impl From for proto::cosmwasm::wasm::v1::MsgUpdateAdmin { + fn from(msg: MsgUpdateAdmin) -> proto::cosmwasm::wasm::v1::MsgUpdateAdmin { + proto::cosmwasm::wasm::v1::MsgUpdateAdmin::from(&msg) + } + } + + impl From<&MsgUpdateAdmin> for proto::cosmwasm::wasm::v1::MsgUpdateAdmin { + fn from(msg: &MsgUpdateAdmin) -> proto::cosmwasm::wasm::v1::MsgUpdateAdmin { + proto::cosmwasm::wasm::v1::MsgUpdateAdmin { + sender: msg.sender.to_string(), + new_admin: msg.new_admin.to_string(), + contract: msg.contract.to_string(), + } + } + } + + /// MsgUpdateAdminResponse returns empty data + #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] + pub struct MsgUpdateAdminResponse {} + + impl Msg for MsgUpdateAdminResponse { + type Proto = proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse; + } + + impl TryFrom for MsgUpdateAdminResponse { + type Error = ErrorReport; + + fn try_from( + _proto: proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse, + ) -> Result { + Ok(MsgUpdateAdminResponse {}) + } + } + + impl From for proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse { + fn from( + _msg: MsgUpdateAdminResponse, + ) -> proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse { + proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse {} + } + } + + /// MsgClearAdmin removes any admin stored for a smart contract + #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] + pub struct MsgClearAdmin { + /// Sender is the that actor that signed the messages + pub sender: AccountId, + + /// Contract is the address of the smart contract + pub contract: AccountId, + } + + impl Msg for MsgClearAdmin { + type Proto = proto::cosmwasm::wasm::v1::MsgClearAdmin; + } + + impl TryFrom for MsgClearAdmin { + type Error = ErrorReport; + + fn try_from(proto: proto::cosmwasm::wasm::v1::MsgClearAdmin) -> Result { + MsgClearAdmin::try_from(&proto) + } + } + + impl TryFrom<&proto::cosmwasm::wasm::v1::MsgClearAdmin> for MsgClearAdmin { + type Error = ErrorReport; + + fn try_from(proto: &proto::cosmwasm::wasm::v1::MsgClearAdmin) -> Result { + Ok(MsgClearAdmin { + sender: proto.sender.parse()?, + contract: proto.contract.parse()?, + }) + } + } + + impl From for proto::cosmwasm::wasm::v1::MsgClearAdmin { + fn from(msg: MsgClearAdmin) -> proto::cosmwasm::wasm::v1::MsgClearAdmin { + proto::cosmwasm::wasm::v1::MsgClearAdmin::from(&msg) + } + } + + impl From<&MsgClearAdmin> for proto::cosmwasm::wasm::v1::MsgClearAdmin { + fn from(msg: &MsgClearAdmin) -> proto::cosmwasm::wasm::v1::MsgClearAdmin { + proto::cosmwasm::wasm::v1::MsgClearAdmin { + sender: msg.sender.to_string(), + contract: msg.contract.to_string(), + } + } + } + + /// MsgClearAdminResponse returns empty data + #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] + pub struct MsgClearAdminResponse {} + + impl Msg for MsgClearAdminResponse { + type Proto = proto::cosmwasm::wasm::v1::MsgClearAdminResponse; + } + + impl TryFrom for MsgClearAdminResponse { + type Error = ErrorReport; + + fn try_from( + _proto: proto::cosmwasm::wasm::v1::MsgClearAdminResponse, + ) -> Result { + Ok(MsgClearAdminResponse {}) + } + } + + impl From for proto::cosmwasm::wasm::v1::MsgClearAdminResponse { + fn from( + _msg: MsgClearAdminResponse, + ) -> proto::cosmwasm::wasm::v1::MsgClearAdminResponse { + proto::cosmwasm::wasm::v1::MsgClearAdminResponse {} + } + } + } +} diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/types.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/types.rs index dda27cd8ba..757c8ed201 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/types.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/types.rs @@ -3,36 +3,35 @@ // TODO: There's a significant argument to pull those out of the package and make a PR on https://github.com/cosmos/cosmos-rust/ -use crate::nyxd::cosmwasm_client::helpers::parse_proto_coin_vec; use crate::nyxd::cosmwasm_client::logs::Log; use crate::nyxd::error::NyxdError; -use cosmrs::crypto::PublicKey; +use cosmrs::auth::{BaseAccount, ModuleAccount}; +use cosmrs::cosmwasm::{CodeInfoResponse, ContractInfo}; use cosmrs::proto::cosmos::auth::v1beta1::{ BaseAccount as ProtoBaseAccount, ModuleAccount as ProtoModuleAccount, }; -use cosmrs::proto::cosmos::base::abci::v1beta1::{ - GasInfo as ProtoGasInfo, Result as ProtoAbciResult, -}; +use cosmrs::proto::cosmos::base::abci::v1beta1::Result as ProtoAbciResult; use cosmrs::proto::cosmos::tx::v1beta1::SimulateResponse as ProtoSimulateResponse; use cosmrs::proto::cosmos::vesting::v1beta1::{ BaseVestingAccount as ProtoBaseVestingAccount, ContinuousVestingAccount as ProtoContinuousVestingAccount, - DelayedVestingAccount as ProtoDelayedVestingAccount, Period as ProtoPeriod, + DelayedVestingAccount as ProtoDelayedVestingAccount, PeriodicVestingAccount as ProtoPeriodicVestingAccount, PermanentLockedAccount as ProtoPermanentLockedAccount, }; -use cosmrs::proto::cosmwasm::wasm::v1::{ - CodeInfoResponse, ContractCodeHistoryEntry as ProtoContractCodeHistoryEntry, - ContractCodeHistoryOperationType, ContractInfo as ProtoContractInfo, +use cosmrs::tendermint::{abci, Hash}; +use cosmrs::tx::{AccountNumber, SequenceNumber}; +use cosmrs::vesting::{ + BaseVestingAccount, ContinuousVestingAccount, DelayedVestingAccount, PeriodicVestingAccount, + PermanentLockedAccount, }; -use cosmrs::tendermint::abci; -use cosmrs::tendermint::abci::Data; -use cosmrs::tx::{AccountNumber, Gas, SequenceNumber}; -use cosmrs::{tx, AccountId, Any, Coin as CosmosCoin}; +use cosmrs::{AccountId, Any, Coin as CosmosCoin}; use prost::Message; use serde::Serialize; use std::convert::{TryFrom, TryInto}; +pub use cosmrs::abci::GasInfo; + pub type ContractCodeId = u64; #[derive(Serialize)] @@ -44,215 +43,6 @@ pub struct SequenceResponse { pub sequence: SequenceNumber, } -/// BaseAccount defines a base account type. It contains all the necessary fields -/// for basic account functionality. Any custom account type should extend this -/// type for additional functionality (e.g. vesting). -#[derive(Debug)] -pub struct BaseAccount { - /// Bech32 account address - pub address: AccountId, - pub pubkey: Option, - pub account_number: AccountNumber, - pub sequence: SequenceNumber, -} - -impl TryFrom for BaseAccount { - type Error = NyxdError; - - fn try_from(value: ProtoBaseAccount) -> Result { - let address: AccountId = value - .address - .parse() - .map_err(|_| NyxdError::MalformedAccountAddress(value.address.clone()))?; - - let pubkey = value - .pub_key - .map(PublicKey::try_from) - .transpose() - .map_err(|_| NyxdError::InvalidPublicKey(address.clone()))?; - - Ok(BaseAccount { - address, - pubkey, - account_number: value.account_number, - sequence: value.sequence, - }) - } -} - -/// ModuleAccount defines an account for modules that holds coins on a pool. -#[derive(Debug)] -pub struct ModuleAccount { - pub base_account: Option, - pub name: String, - pub permissions: Vec, -} - -impl TryFrom for ModuleAccount { - type Error = NyxdError; - - fn try_from(value: ProtoModuleAccount) -> Result { - let base_account = value.base_account.map(TryFrom::try_from).transpose()?; - - Ok(ModuleAccount { - base_account, - name: value.name, - permissions: value.permissions, - }) - } -} - -/// BaseVestingAccount implements the VestingAccount interface. It contains all -/// the necessary fields needed for any vesting account implementation. -#[derive(Debug)] -pub struct BaseVestingAccount { - pub base_account: Option, - pub original_vesting: Vec, - pub delegated_free: Vec, - pub delegated_vesting: Vec, - pub end_time: i64, -} - -impl TryFrom for BaseVestingAccount { - type Error = NyxdError; - - fn try_from(value: ProtoBaseVestingAccount) -> Result { - let base_account = value.base_account.map(TryFrom::try_from).transpose()?; - - let original_vesting = parse_proto_coin_vec(value.original_vesting)?; - let delegated_free = parse_proto_coin_vec(value.delegated_free)?; - let delegated_vesting = parse_proto_coin_vec(value.delegated_vesting)?; - - Ok(BaseVestingAccount { - base_account, - original_vesting, - delegated_free, - delegated_vesting, - end_time: value.end_time, - }) - } -} - -/// ContinuousVestingAccount implements the VestingAccount interface. It -/// continuously vests by unlocking coins linearly with respect to time. -#[derive(Debug)] -pub struct ContinuousVestingAccount { - pub base_vesting_account: Option, - pub start_time: i64, -} - -impl TryFrom for ContinuousVestingAccount { - type Error = NyxdError; - - fn try_from(value: ProtoContinuousVestingAccount) -> Result { - let base_vesting_account = value - .base_vesting_account - .map(TryFrom::try_from) - .transpose()?; - - Ok(ContinuousVestingAccount { - base_vesting_account, - start_time: value.start_time, - }) - } -} - -/// DelayedVestingAccount implements the VestingAccount interface. It vests all -/// coins after a specific time, but non prior. In other words, it keeps them -/// locked until a specified time. -#[derive(Debug)] -pub struct DelayedVestingAccount { - pub base_vesting_account: Option, -} - -impl TryFrom for DelayedVestingAccount { - type Error = NyxdError; - - fn try_from(value: ProtoDelayedVestingAccount) -> Result { - let base_vesting_account = value - .base_vesting_account - .map(TryFrom::try_from) - .transpose()?; - - Ok(DelayedVestingAccount { - base_vesting_account, - }) - } -} - -/// Period defines a length of time and amount of coins that will vest. -#[derive(Debug)] -pub struct Period { - pub length: i64, - pub amount: Vec, -} - -impl TryFrom for Period { - type Error = NyxdError; - - fn try_from(value: ProtoPeriod) -> Result { - Ok(Period { - length: value.length, - amount: parse_proto_coin_vec(value.amount)?, - }) - } -} - -/// PeriodicVestingAccount implements the VestingAccount interface. It -/// periodically vests by unlocking coins during each specified period. -#[derive(Debug)] -pub struct PeriodicVestingAccount { - pub base_vesting_account: Option, - pub start_time: i64, - pub vesting_periods: Vec, -} - -impl TryFrom for PeriodicVestingAccount { - type Error = NyxdError; - - fn try_from(value: ProtoPeriodicVestingAccount) -> Result { - let base_vesting_account = value - .base_vesting_account - .map(TryFrom::try_from) - .transpose()?; - - let vesting_periods = value - .vesting_periods - .into_iter() - .map(TryFrom::try_from) - .collect::>()?; - - Ok(PeriodicVestingAccount { - base_vesting_account, - start_time: value.start_time, - vesting_periods, - }) - } -} - -/// PermanentLockedAccount implements the VestingAccount interface. It does -/// not ever release coins, locking them indefinitely. Coins in this account can -/// still be used for delegating and for governance votes even while locked. -#[derive(Debug)] -pub struct PermanentLockedAccount { - pub base_vesting_account: Option, -} - -impl TryFrom for PermanentLockedAccount { - type Error = NyxdError; - - fn try_from(value: ProtoPermanentLockedAccount) -> Result { - let base_vesting_account = value - .base_vesting_account - .map(TryFrom::try_from) - .transpose()?; - - Ok(PermanentLockedAccount { - base_vesting_account, - }) - } -} - #[derive(Debug)] pub enum Account { Base(BaseAccount), @@ -335,183 +125,32 @@ impl TryFrom for Account { } } -#[derive(Debug)] -pub struct Code { - pub code_id: ContractCodeId, - - /// Bech32 account address - pub creator: AccountId, - - /// sha256 hash of the code stored - pub data_hash: Vec, -} - -impl TryFrom for Code { - type Error = NyxdError; - - fn try_from(value: CodeInfoResponse) -> Result { - let CodeInfoResponse { - code_id, - creator, - data_hash, - } = value; - - let creator = creator - .parse() - .map_err(|_| NyxdError::MalformedAccountAddress(creator))?; - - Ok(Code { - code_id, - creator, - data_hash, - }) - } -} - #[derive(Debug)] pub struct CodeDetails { - pub code_info: Code, + pub code_info: CodeInfoResponse, /// The original wasm bytes pub data: Vec, } impl CodeDetails { - pub fn new(code_info: Code, data: Vec) -> Self { + pub fn new(code_info: CodeInfoResponse, data: Vec) -> Self { CodeDetails { code_info, data } } } -#[derive(Debug)] -pub(crate) struct ContractInfo { - code_id: ContractCodeId, - creator: AccountId, - admin: Option, - label: String, -} - -impl TryFrom for ContractInfo { - type Error = NyxdError; - - fn try_from(value: ProtoContractInfo) -> Result { - let ProtoContractInfo { - code_id, - creator, - admin, - label, - .. - } = value; - - let admin = if admin.is_empty() { - None - } else { - Some( - admin - .parse() - .map_err(|_| NyxdError::MalformedAccountAddress(admin))?, - ) - }; - - Ok(ContractInfo { - code_id, - creator: creator - .parse() - .map_err(|_| NyxdError::MalformedAccountAddress(creator))?, - admin, - label, - }) - } -} - #[derive(Debug)] pub struct Contract { pub address: AccountId, - pub code_id: ContractCodeId, - - /// Bech32 account address - pub creator: AccountId, - - /// Bech32-encoded admin address - pub admin: Option, - - pub label: String, + pub contract_info: ContractInfo, } impl Contract { pub(crate) fn new(address: AccountId, contract_info: ContractInfo) -> Self { Contract { address, - code_id: contract_info.code_id, - creator: contract_info.creator, - admin: contract_info.admin, - label: contract_info.label, - } - } -} - -#[derive(Clone, Copy, Debug)] -pub enum ContractCodeHistoryEntryOperation { - Init, - Genesis, - Migrate, -} - -#[derive(Debug)] -pub struct ContractCodeHistoryEntry { - /// The source of this history entry - pub operation: ContractCodeHistoryEntryOperation, - pub code_id: ContractCodeId, - pub msg_json: String, -} - -impl TryFrom for ContractCodeHistoryEntry { - type Error = NyxdError; - - fn try_from(value: ProtoContractCodeHistoryEntry) -> Result { - let operation = match ContractCodeHistoryOperationType::from_i32(value.operation) - .ok_or(NyxdError::InvalidContractHistoryOperation)? - { - ContractCodeHistoryOperationType::Unspecified => { - return Err(NyxdError::InvalidContractHistoryOperation) - } - ContractCodeHistoryOperationType::Init => ContractCodeHistoryEntryOperation::Init, - ContractCodeHistoryOperationType::Genesis => ContractCodeHistoryEntryOperation::Genesis, - ContractCodeHistoryOperationType::Migrate => ContractCodeHistoryEntryOperation::Migrate, - }; - - Ok(ContractCodeHistoryEntry { - operation, - code_id: value.code_id, - msg_json: String::from_utf8(value.msg) - .map_err(|_| NyxdError::DeserializationError("Contract history msg".to_owned()))?, - }) - } -} - -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize)] -pub struct GasInfo { - /// GasWanted is the maximum units of work we allow this tx to perform. - pub gas_wanted: Gas, - - /// GasUsed is the amount of gas actually consumed. - pub gas_used: Gas, -} - -impl From for GasInfo { - fn from(value: ProtoGasInfo) -> Self { - GasInfo { - gas_wanted: value.gas_wanted.into(), - gas_used: value.gas_used.into(), - } - } -} - -impl GasInfo { - pub fn new(gas_wanted: Gas, gas_used: Gas) -> Self { - GasInfo { - gas_wanted, - gas_used, + contract_info, } } } @@ -535,31 +174,15 @@ impl TryFrom for AbciResult { type Error = NyxdError; fn try_from(value: ProtoAbciResult) -> Result { - let mut events = Vec::with_capacity(value.events.len()); - - for proto_event in value.events.into_iter() { - let type_str = proto_event.r#type; - - let mut attributes = Vec::with_capacity(proto_event.attributes.len()); - for proto_attribute in proto_event.attributes.into_iter() { - let stringified_ked = String::from_utf8(proto_attribute.key) - .map_err(|_| NyxdError::DeserializationError("EventAttributeKey".to_owned()))?; - let stringified_value = String::from_utf8(proto_attribute.value) - .map_err(|_| NyxdError::DeserializationError("EventAttributeKey".to_owned()))?; - - attributes.push(abci::tag::Tag { - key: stringified_ked.parse().unwrap(), - value: stringified_value.parse().unwrap(), - }) - } - - events.push(abci::Event { - type_str, - attributes, - }) - } + let events = value + .events + .into_iter() + .map(TryInto::try_into) + .collect::, _>>()?; + #[allow(deprecated)] Ok(AbciResult { + // TODO: make sure this actually works since technically we're converting from 0.37 protobuf definition as opposed to 0.34... data: value.data, log: value.log, events, @@ -578,7 +201,10 @@ impl TryFrom for SimulateResponse { fn try_from(value: ProtoSimulateResponse) -> Result { Ok(SimulateResponse { - gas_info: value.gas_info.map(|gas_info| gas_info.into()), + gas_info: value + .gas_info + .map(|gas_info| gas_info.try_into()) + .transpose()?, result: value.result.map(|result| result.try_into()).transpose()?, }) } @@ -608,7 +234,7 @@ pub struct UploadResult { pub logs: Vec, /// Transaction hash (might be used as transaction ID) - pub transaction_hash: tx::Hash, + pub transaction_hash: Hash, pub gas_info: GasInfo, } @@ -645,7 +271,7 @@ pub struct InstantiateResult { pub logs: Vec, /// Transaction hash (might be used as transaction ID) - pub transaction_hash: tx::Hash, + pub transaction_hash: Hash, pub gas_info: GasInfo, } @@ -655,7 +281,7 @@ pub struct ChangeAdminResult { pub logs: Vec, /// Transaction hash (might be used as transaction ID) - pub transaction_hash: tx::Hash, + pub transaction_hash: Hash, pub gas_info: GasInfo, } @@ -665,7 +291,7 @@ pub struct MigrateResult { pub logs: Vec, /// Transaction hash (might be used as transaction ID) - pub transaction_hash: tx::Hash, + pub transaction_hash: Hash, pub gas_info: GasInfo, } @@ -674,10 +300,10 @@ pub struct MigrateResult { pub struct ExecuteResult { pub logs: Vec, - pub data: Data, + pub data: Vec, /// Transaction hash (might be used as transaction ID) - pub transaction_hash: tx::Hash, + pub transaction_hash: Hash, pub gas_info: GasInfo, } diff --git a/common/client-libs/validator-client/src/nyxd/error.rs b/common/client-libs/validator-client/src/nyxd/error.rs index b778a56188..b63be6e75c 100644 --- a/common/client-libs/validator-client/src/nyxd/error.rs +++ b/common/client-libs/validator-client/src/nyxd/error.rs @@ -2,34 +2,39 @@ // SPDX-License-Identifier: Apache-2.0 use crate::nyxd::cosmwasm_client::types::ContractCodeId; +use cosmrs::tendermint::Hash; use cosmrs::{ - rpc::endpoint::abci_query::AbciQuery, - tendermint::{ - abci::{self, Code as AbciCode}, - block, - }, - tx, AccountId, + tendermint::{abci::Code as AbciCode, block}, + AccountId, }; +use std::{io, time::Duration}; +use tendermint_rpc::endpoint::abci_query::AbciQuery; use thiserror::Error; +#[cfg(feature = "signing")] use crate::signing::direct_wallet::DirectSecp256k1HdWalletError; -pub use cosmrs::rpc::{ + +pub use cosmrs::tendermint::error::Error as TendermintError; +pub use tendermint_rpc::{ error::{Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail}, response_error::{Code, ResponseError}, }; -use std::{io, time::Duration}; #[derive(Debug, Error)] pub enum NyxdError { #[error("No contract address is available to perform the call: {0}")] NoContractAddressAvailable(String), + #[cfg(feature = "signing")] #[error(transparent)] WalletError(#[from] DirectSecp256k1HdWalletError), - #[error("There was an issue on the cosmrs side - {0}")] + #[error("There was an issue on the cosmrs side: {0}")] CosmrsError(#[from] cosmrs::Error), + #[error("There was an issue on the cosmrs side: {0}")] + CosmrsErrorReport(#[from] cosmrs::ErrorReport), + #[error("Failed to derive account address")] AccountDerivationError, @@ -43,7 +48,10 @@ pub enum NyxdError { InvalidTxHash(String), #[error("Tendermint RPC request failed - {0}")] - TendermintError(#[from] TendermintRpcError), + TendermintErrorRpc(#[from] TendermintRpcError), + + #[error("tendermint library failure: {0}")] + TendermintError(#[from] TendermintError), #[error("Failed when attempting to serialize data ({0})")] SerializationError(String), @@ -91,7 +99,7 @@ pub enum NyxdError { "Error when broadcasting tx {hash} at height {height:?}. Error occurred during CheckTx phase. Code: {code}; Raw log: {raw_log}" )] BroadcastTxErrorCheckTx { - hash: tx::Hash, + hash: Hash, height: Option, code: u32, raw_log: String, @@ -101,7 +109,7 @@ pub enum NyxdError { "Error when broadcasting tx {hash} at height {height:?}. Error occurred during DeliverTx phase. Code: {code}; Raw log: {raw_log}" )] BroadcastTxErrorDeliverTx { - hash: tx::Hash, + hash: Hash, height: Option, code: u32, raw_log: String, @@ -116,7 +124,7 @@ pub enum NyxdError { #[error("Abci query failed with code {code} - {log}")] AbciError { code: u32, - log: abci::Log, + log: String, pretty_log: Option, }, @@ -130,7 +138,7 @@ pub enum NyxdError { NoBaseAccountInformationAvailable, #[error("Transaction with ID {hash} has been submitted but not yet found on the chain. You might want to check for it later. There was a total wait of {} seconds", .timeout.as_secs())] - BroadcastTimeout { hash: tx::Hash, timeout: Duration }, + BroadcastTimeout { hash: Hash, timeout: Duration }, #[error("Cosmwasm std error: {0}")] CosmwasmStdError(#[from] cosmwasm_std::StdError), @@ -148,7 +156,7 @@ pub fn parse_abci_query_result(query_result: AbciQuery) -> Result Ok(query_result), AbciCode::Err(code) => Err(NyxdError::AbciError { - code, + code: code.into(), log: query_result.log.clone(), pretty_log: try_parse_abci_log(&query_result.log), }), @@ -157,11 +165,8 @@ pub fn parse_abci_query_result(query_result: AbciQuery) -> Result Option { - if log - .value() - .contains("Maximum amount of locked coins has already been pledged") - { +fn try_parse_abci_log(log: &str) -> Option { + if log.contains("Maximum amount of locked coins has already been pledged") { Some("Maximum amount of locked tokens has already been used. You can only use up to 10% of your locked tokens for bonding and delegating.".to_string()) } else { None @@ -171,7 +176,7 @@ fn try_parse_abci_log(log: &abci::Log) -> Option { impl NyxdError { pub fn is_tendermint_response_timeout(&self) -> bool { match &self { - NyxdError::TendermintError(TendermintRpcError( + NyxdError::TendermintErrorRpc(TendermintRpcError( TendermintRpcErrorDetail::Response(err), _, )) => { @@ -198,7 +203,7 @@ impl NyxdError { pub fn is_tendermint_response_duplicate(&self) -> bool { match &self { - NyxdError::TendermintError(TendermintRpcError( + NyxdError::TendermintErrorRpc(TendermintRpcError( TendermintRpcErrorDetail::Response(err), _, )) => { diff --git a/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs b/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs index 10ae3a7bb7..caccceb436 100644 --- a/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs +++ b/common/client-libs/validator-client/src/nyxd/fee/gas_price.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::nyxd::error::NyxdError; -use cosmrs::tx::Gas; use cosmrs::Coin; +use cosmrs::Gas; use cosmwasm_std::{Decimal, Fraction, Uint128}; use nym_config::defaults; use std::ops::Mul; @@ -25,7 +25,7 @@ impl<'a> Mul for &'a GasPrice { type Output = Coin; fn mul(self, gas_limit: Gas) -> Self::Output { - let limit_uint128 = Uint128::from(gas_limit.value()); + let limit_uint128 = Uint128::from(gas_limit); let mut amount = self.amount * limit_uint128; let gas_price_numerator = self.amount.numerator(); @@ -122,7 +122,7 @@ mod tests { fn gas_limit_multiplication() { // real world example that caused an issue when the result was rounded down let gas_price: GasPrice = "0.025upunk".parse().unwrap(); - let gas_limit: Gas = 157500u64.into(); + let gas_limit: Gas = 157500u64; let fee = &gas_price * gas_limit; // the failing behaviour was result value of 3937 diff --git a/common/client-libs/validator-client/src/nyxd/fee/mod.rs b/common/client-libs/validator-client/src/nyxd/fee/mod.rs index e1ee4b4d5c..0d67544203 100644 --- a/common/client-libs/validator-client/src/nyxd/fee/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/fee/mod.rs @@ -116,8 +116,8 @@ impl GasAdjustable for Gas { if adjustment == 1.0 { *self } else { - let adjusted = (self.value() as f32 * adjustment).ceil(); - (adjusted as u64).into() + let adjusted = (*self as f32 * adjustment).ceil(); + adjusted as u64 } } } @@ -125,48 +125,15 @@ impl GasAdjustable for Gas { // a workaround to provide serde implementation for tx::Fee. We don't want to ever expose any of those // types to the public and ideally they will get replaced by proper implementation inside comrs mod sealed { - use cosmrs::tx::{self, Gas}; - use cosmrs::Coin as CosmosCoin; - use cosmrs::{AccountId, Decimal as CosmosDecimal, Denom as CosmosDenom}; + use cosmrs::tx::{self}; + use cosmrs::{AccountId, Denom as CosmosDenom}; + use cosmrs::{Coin as CosmosCoin, Gas}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; - fn cosmos_denom_inner_getter(val: &CosmosDenom) -> String { - val.as_ref().to_string() - } - - #[derive(Serialize, Deserialize)] - #[serde(remote = "CosmosDenom")] - struct Denom(#[serde(getter = "cosmos_denom_inner_getter")] String); - - impl From for CosmosDenom { - fn from(val: Denom) -> Self { - val.0.parse().unwrap() - } - } - - 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 for CosmosDecimal { - fn from(val: Decimal) -> Self { - val.0.into() - } - } - #[derive(Serialize, Deserialize, Clone)] struct Coin { - #[serde(with = "Denom")] denom: CosmosDenom, - #[serde(with = "Decimal")] - amount: CosmosDecimal, + amount: u128, } impl From for CosmosCoin { diff --git a/common/client-libs/validator-client/src/nyxd/mod.rs b/common/client-libs/validator-client/src/nyxd/mod.rs index 58890e8621..404124df8d 100644 --- a/common/client-libs/validator-client/src/nyxd/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/mod.rs @@ -1,51 +1,67 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nyxd::cosmwasm_client::signing_client; -use crate::nyxd::cosmwasm_client::types::{ - Account, ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, - InstantiateResult, MigrateResult, SequenceResponse, SimulateResponse, UploadResult, -}; +use crate::nyxd::cosmwasm_client::types::Account; use crate::nyxd::error::NyxdError; -use crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER; -use crate::signing::direct_wallet::DirectSecp256k1HdWallet; -use crate::signing::signer::OfflineSigner; -use cosmrs::cosmwasm; -use cosmrs::rpc::endpoint::block::Response as BlockResponse; -use cosmrs::rpc::query::Query; -use cosmrs::rpc::Error as TendermintRpcError; -use cosmrs::rpc::HttpClientUrl; -use cosmrs::tx::Msg; use log::{debug, trace}; use nym_network_defaults::{ChainDetails, NymNetworkDetails}; use serde::{Deserialize, Serialize}; -use std::convert::TryInto; -use std::time::SystemTime; +use tendermint_rpc::{endpoint::block::Response as BlockResponse, query::Query}; + +#[cfg(feature = "http-client")] +use tendermint_rpc::Error as TendermintRpcError; pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; -pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; pub use crate::nyxd::fee::Fee; pub use coin::Coin; pub use cosmrs::bank::MsgSend; -pub use cosmrs::rpc::endpoint::tx::Response as TxResponse; -pub use cosmrs::rpc::endpoint::validators::Response as ValidatorResponse; -pub use cosmrs::rpc::HttpClient as QueryNyxdClient; -pub use cosmrs::rpc::Paging; -pub use cosmrs::tendermint::abci::responses::{DeliverTx, Event}; -pub use cosmrs::tendermint::abci::tag::Tag; +pub use cosmrs::tendermint::abci::{response::DeliverTx, Event, EventAttribute}; pub use cosmrs::tendermint::block::Height; -pub use cosmrs::tendermint::hash; +pub use cosmrs::tendermint::hash::{self, Algorithm, Hash}; pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo; pub use cosmrs::tendermint::Time as TendermintTime; -pub use cosmrs::tx::{self, Gas}; +pub use cosmrs::tx::{self}; pub use cosmrs::Coin as CosmosCoin; -pub use cosmrs::{bip32, AccountId, Decimal, Denom}; -use cosmwasm_std::Addr; +pub use cosmrs::Gas; +pub use cosmrs::{bip32, AccountId, Denom}; pub use cosmwasm_std::Coin as CosmWasmCoin; pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment}; -pub use signing_client::Client as SigningNyxdClient; -pub use traits::{VestingQueryClient, VestingSigningClient}; +pub use tendermint_rpc::{client::Client as TendermintClient, Request, Response, SimpleRequest}; +pub use tendermint_rpc::{ + endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse}, + Paging, +}; +#[cfg(feature = "http-client")] +pub use cosmrs::rpc::{HttpClient as QueryNyxdClient, HttpClientUrl}; + +#[cfg(all(feature = "signing", feature = "http-client"))] +use crate::nyxd::cosmwasm_client::signing_client; +#[cfg(feature = "signing")] +use crate::nyxd::cosmwasm_client::types::{ + ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult, + MigrateResult, SequenceResponse, SimulateResponse, UploadResult, +}; +#[cfg(all(feature = "signing", feature = "http-client"))] +use crate::signing::direct_wallet::DirectSecp256k1HdWallet; +#[cfg(all(feature = "signing", feature = "http-client"))] +use crate::signing::signer::OfflineSigner; +#[cfg(feature = "signing")] +use cosmrs::cosmwasm; +#[cfg(feature = "signing")] +use cosmrs::tx::Msg; +#[cfg(feature = "signing")] +use cosmwasm_std::Addr; +#[cfg(feature = "signing")] +use std::time::SystemTime; + +#[cfg(feature = "signing")] +pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; + +#[cfg(all(feature = "signing", feature = "http-client"))] +pub use signing_client::Client as SigningNyxdClient; + +#[cfg(all(feature = "signing", feature = "http-client"))] pub type DirectSigningNyxdClient = SigningNyxdClient; pub mod coin; @@ -152,10 +168,13 @@ impl Config { pub struct NyxdClient { client: C, config: Config, + // TODO: refactor because that field is only really used for signing + #[allow(dead_code)] client_address: Option>, simulated_gas_multiplier: f32, } +#[cfg(feature = "http-client")] impl NyxdClient { pub fn connect(config: Config, endpoint: U) -> Result, NyxdError> where @@ -165,11 +184,12 @@ impl NyxdClient { client: QueryNyxdClient::new(endpoint)?, config, client_address: None, - simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, + simulated_gas_multiplier: crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER, }) } } +#[cfg(all(feature = "signing", feature = "http-client"))] impl NyxdClient> { // TODO: rename this one pub fn connect_with_mnemonic( @@ -187,12 +207,14 @@ impl NyxdClient> { } } +#[cfg(all(feature = "signing", feature = "http-client"))] impl NyxdClient> where S: OfflineSigner, // I have no idea why S::Error: Into bound wouldn't do the trick NyxdError: From, { + #[cfg(feature = "http-client")] pub fn connect_with_signer( config: Config, endpoint: U, @@ -214,10 +236,11 @@ where client: SigningNyxdClient::connect_with_signer(endpoint, signer, gas_price)?, config, client_address: Some(client_address), - simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, + simulated_gas_multiplier: crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER, }) } + #[cfg(feature = "http-client")] pub fn change_endpoint(&mut self, new_endpoint: U) -> Result<(), NyxdError> where U: TryInto, @@ -230,133 +253,47 @@ where } } -impl NyxdClient { - pub fn current_config(&self) -> &Config { - &self.config - } - - pub fn current_chain_details(&self) -> &ChainDetails { - &self.config.chain_details - } - - pub fn set_mixnet_contract_address(&mut self, address: AccountId) { - self.config.mixnet_contract_address = Some(address); - } - - pub fn set_vesting_contract_address(&mut self, address: AccountId) { - self.config.vesting_contract_address = Some(address); - } - - pub fn set_bandwidth_claim_contract_address(&mut self, address: AccountId) { - self.config.bandwidth_claim_contract_address = Some(address); - } - - pub fn set_coconut_bandwidth_contract_address(&mut self, address: AccountId) { - self.config.coconut_bandwidth_contract_address = Some(address); - } - - pub fn set_multisig_contract_address(&mut self, address: AccountId) { - self.config.multisig_contract_address = Some(address); - } - - pub fn set_service_provider_contract_address(&mut self, address: AccountId) { - self.config.service_provider_contract_address = Some(address); - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn mixnet_contract_address(&self) -> &AccountId { - self.config.mixnet_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn vesting_contract_address(&self) -> &AccountId { - self.config.vesting_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn bandwidth_claim_contract_address(&self) -> &AccountId { - self.config - .bandwidth_claim_contract_address - .as_ref() - .unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn coconut_bandwidth_contract_address(&self) -> &AccountId { - self.config - .coconut_bandwidth_contract_address - .as_ref() - .unwrap() - } - - pub fn group_contract_address(&self) -> &AccountId { - self.config.group_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn multisig_contract_address(&self) -> &AccountId { - self.config.multisig_contract_address.as_ref().unwrap() - } - - // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits - // note: what unwrap is doing here is just moving a failure that would have normally - // occurred in `connect` when attempting to parse an empty address, - // so it's not introducing new source of failure (just moves it) - pub fn coconut_dkg_contract_address(&self) -> &AccountId { - self.config.coconut_dkg_contract_address.as_ref().unwrap() - } - - // The service provider directory contract is optional, so we return an Option not a Result - pub fn service_provider_contract_address(&self) -> Option<&AccountId> { - self.config.service_provider_contract_address.as_ref() - } - - // The name service contract is optional, so we return an Option not a Result - pub fn name_service_contract_address(&self) -> Option<&AccountId> { - self.config.name_service_contract_address.as_ref() - } - - pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) { - self.simulated_gas_multiplier = multiplier; - } - - pub async fn query_contract_smart( - &self, - contract: &AccountId, - query_msg: &M, - ) -> Result +#[cfg(feature = "signing")] +impl NyxdClient +where + C: SigningCosmWasmClient + Sync, +{ + pub fn address(&self) -> &AccountId where - C: CosmWasmClient + Sync, - M: ?Sized + Serialize + Sync, - for<'a> T: Deserialize<'a>, + C: SigningCosmWasmClient, { - self.client.query_contract_smart(contract, query_msg).await + // if this is a signing client (as required by the trait bound), it must have the address set + &self.client_address.as_ref().unwrap()[0] } - pub async fn query_contract_raw( - &self, - contract: &AccountId, - query_data: Vec, - ) -> Result, NyxdError> + pub fn cw_address(&self) -> Addr where - C: CosmWasmClient + Sync, + C: SigningCosmWasmClient, { - self.client.query_contract_raw(contract, query_data).await + // the call to unchecked is fine here as we're converting directly from `AccountId` + // which must have been a valid bech32 address + Addr::unchecked(self.address().as_ref()) + } + + pub async fn account_sequence(&self) -> Result + where + C: SigningCosmWasmClient + Sync, + { + self.client.get_sequence(self.address()).await + } + + pub fn signer(&self) -> &::Signer + where + C: SigningCosmWasmClient, + { + self.client.signer() + } + + pub fn gas_price(&self) -> &GasPrice + where + C: SigningCosmWasmClient, + { + self.client.gas_price() } pub fn wrap_contract_execute_message( @@ -377,175 +314,6 @@ impl NyxdClient { }) } - pub fn address(&self) -> &AccountId - where - C: SigningCosmWasmClient, - { - // if this is a signing client (as required by the trait bound), it must have the address set - &self.client_address.as_ref().unwrap()[0] - } - - pub fn cw_address(&self) -> Addr - where - C: SigningCosmWasmClient, - { - // the call to unchecked is fine here as we're converting directly from `AccountId` - // which must have been a valid bech32 address - Addr::unchecked(self.address().as_ref()) - } - - pub fn signer(&self) -> &::Signer - where - C: SigningCosmWasmClient, - { - self.client.signer() - } - - pub fn gas_price(&self) -> &GasPrice - where - C: SigningCosmWasmClient, - { - self.client.gas_price() - } - - pub fn gas_adjustment(&self) -> GasAdjustment { - self.simulated_gas_multiplier - } - - // ============= - // CHAIN RELATED - // ============= - - // CHAIN QUERIES - - pub async fn account_sequence(&self) -> Result - where - C: SigningCosmWasmClient + Sync, - { - self.client.get_sequence(self.address()).await - } - - pub async fn get_account_details( - &self, - address: &AccountId, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_account(address).await - } - - pub async fn get_account_public_key( - &self, - address: &AccountId, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - if let Some(account) = self.client.get_account(address).await? { - let base_account = account.try_get_base_account()?; - return Ok(base_account.pubkey); - } - - Ok(None) - } - - pub async fn get_current_block_timestamp(&self) -> Result - where - C: CosmWasmClient + Sync, - { - self.get_block_timestamp(None).await - } - - pub async fn get_block_timestamp( - &self, - height: Option, - ) -> Result - where - C: CosmWasmClient + Sync, - { - Ok(self.client.get_block(height).await?.block.header.time) - } - - pub async fn get_block(&self, height: Option) -> Result - where - C: CosmWasmClient + Sync, - { - self.client.get_block(height).await - } - - pub async fn get_current_block_height(&self) -> Result - where - C: CosmWasmClient + Sync, - { - self.client.get_height().await - } - - /// Obtains the hash of a block specified by the provided height. - /// - /// # Arguments - /// - /// * `height`: height of the block for which we want to obtain the hash. - pub async fn get_block_hash(&self, height: u32) -> Result - where - C: CosmWasmClient + Sync, - { - self.client - .get_block(Some(height)) - .await - .map(|block| block.block_id.hash) - } - - pub async fn get_validators( - &self, - height: u64, - paging: Paging, - ) -> Result - where - C: CosmWasmClient + Sync, - { - Ok(self.client.validators(height as u32, paging).await?) - } - - pub async fn get_balance( - &self, - address: &AccountId, - denom: String, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_balance(address, denom).await - } - - pub async fn get_all_balances(&self, address: &AccountId) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_all_balances(address).await - } - - pub async fn get_tx(&self, id: tx::Hash) -> Result - where - C: CosmWasmClient + Sync, - { - self.client.get_tx(id).await - } - - pub async fn search_tx(&self, query: Query) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.search_tx(query).await - } - - pub async fn get_total_supply(&self) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_total_supply().await - } - pub async fn simulate(&self, messages: I) -> Result where C: SigningCosmWasmClient + Sync, @@ -762,3 +530,264 @@ impl NyxdClient { .await } } + +impl NyxdClient { + pub fn current_config(&self) -> &Config { + &self.config + } + + pub fn current_chain_details(&self) -> &ChainDetails { + &self.config.chain_details + } + + pub fn set_mixnet_contract_address(&mut self, address: AccountId) { + self.config.mixnet_contract_address = Some(address); + } + + pub fn set_vesting_contract_address(&mut self, address: AccountId) { + self.config.vesting_contract_address = Some(address); + } + + pub fn set_bandwidth_claim_contract_address(&mut self, address: AccountId) { + self.config.bandwidth_claim_contract_address = Some(address); + } + + pub fn set_coconut_bandwidth_contract_address(&mut self, address: AccountId) { + self.config.coconut_bandwidth_contract_address = Some(address); + } + + pub fn set_multisig_contract_address(&mut self, address: AccountId) { + self.config.multisig_contract_address = Some(address); + } + + pub fn set_service_provider_contract_address(&mut self, address: AccountId) { + self.config.service_provider_contract_address = Some(address); + } + + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits + // note: what unwrap is doing here is just moving a failure that would have normally + // occurred in `connect` when attempting to parse an empty address, + // so it's not introducing new source of failure (just moves it) + pub fn mixnet_contract_address(&self) -> &AccountId { + self.config.mixnet_contract_address.as_ref().unwrap() + } + + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits + // note: what unwrap is doing here is just moving a failure that would have normally + // occurred in `connect` when attempting to parse an empty address, + // so it's not introducing new source of failure (just moves it) + pub fn vesting_contract_address(&self) -> &AccountId { + self.config.vesting_contract_address.as_ref().unwrap() + } + + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits + // note: what unwrap is doing here is just moving a failure that would have normally + // occurred in `connect` when attempting to parse an empty address, + // so it's not introducing new source of failure (just moves it) + pub fn bandwidth_claim_contract_address(&self) -> &AccountId { + self.config + .bandwidth_claim_contract_address + .as_ref() + .unwrap() + } + + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits + // note: what unwrap is doing here is just moving a failure that would have normally + // occurred in `connect` when attempting to parse an empty address, + // so it's not introducing new source of failure (just moves it) + pub fn coconut_bandwidth_contract_address(&self) -> &AccountId { + self.config + .coconut_bandwidth_contract_address + .as_ref() + .unwrap() + } + + pub fn group_contract_address(&self) -> &AccountId { + self.config.group_contract_address.as_ref().unwrap() + } + + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits + // note: what unwrap is doing here is just moving a failure that would have normally + // occurred in `connect` when attempting to parse an empty address, + // so it's not introducing new source of failure (just moves it) + pub fn multisig_contract_address(&self) -> &AccountId { + self.config.multisig_contract_address.as_ref().unwrap() + } + + // TODO: this should get changed into Result<&AccountId, NyxdError> (or Option<&AccountId> in future commits + // note: what unwrap is doing here is just moving a failure that would have normally + // occurred in `connect` when attempting to parse an empty address, + // so it's not introducing new source of failure (just moves it) + pub fn coconut_dkg_contract_address(&self) -> &AccountId { + self.config.coconut_dkg_contract_address.as_ref().unwrap() + } + + // The service provider directory contract is optional, so we return an Option not a Result + pub fn service_provider_contract_address(&self) -> Option<&AccountId> { + self.config.service_provider_contract_address.as_ref() + } + + // The name service contract is optional, so we return an Option not a Result + pub fn name_service_contract_address(&self) -> Option<&AccountId> { + self.config.name_service_contract_address.as_ref() + } + + pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) { + self.simulated_gas_multiplier = multiplier; + } + + pub async fn query_contract_smart( + &self, + contract: &AccountId, + query_msg: &M, + ) -> Result + where + C: CosmWasmClient + Sync, + M: ?Sized + Serialize + Sync, + for<'a> T: Deserialize<'a>, + { + self.client.query_contract_smart(contract, query_msg).await + } + + pub async fn query_contract_raw( + &self, + contract: &AccountId, + query_data: Vec, + ) -> Result, NyxdError> + where + C: CosmWasmClient + Sync, + { + self.client.query_contract_raw(contract, query_data).await + } + + pub fn gas_adjustment(&self) -> GasAdjustment { + self.simulated_gas_multiplier + } + + // ============= + // CHAIN RELATED + // ============= + + // CHAIN QUERIES + + pub async fn get_account_details( + &self, + address: &AccountId, + ) -> Result, NyxdError> + where + C: CosmWasmClient + Sync, + { + self.client.get_account(address).await + } + + pub async fn get_account_public_key( + &self, + address: &AccountId, + ) -> Result, NyxdError> + where + C: CosmWasmClient + Sync, + { + if let Some(account) = self.client.get_account(address).await? { + let base_account = account.try_get_base_account()?; + return Ok(base_account.pubkey); + } + + Ok(None) + } + + pub async fn get_current_block_timestamp(&self) -> Result + where + C: CosmWasmClient + Sync, + { + self.get_block_timestamp(None).await + } + + pub async fn get_block_timestamp( + &self, + height: Option, + ) -> Result + where + C: CosmWasmClient + Sync, + { + Ok(self.client.get_block(height).await?.block.header.time) + } + + pub async fn get_block(&self, height: Option) -> Result + where + C: CosmWasmClient + Sync, + { + self.client.get_block(height).await + } + + pub async fn get_current_block_height(&self) -> Result + where + C: CosmWasmClient + Sync, + { + self.client.get_height().await + } + + /// Obtains the hash of a block specified by the provided height. + /// + /// # Arguments + /// + /// * `height`: height of the block for which we want to obtain the hash. + pub async fn get_block_hash(&self, height: u32) -> Result + where + C: CosmWasmClient + Sync, + { + self.client + .get_block(Some(height)) + .await + .map(|block| block.block_id.hash) + } + + pub async fn get_validators( + &self, + height: u64, + paging: Paging, + ) -> Result + where + C: CosmWasmClient + Sync, + { + Ok(self.client.validators(height as u32, paging).await?) + } + + pub async fn get_balance( + &self, + address: &AccountId, + denom: String, + ) -> Result, NyxdError> + where + C: CosmWasmClient + Sync, + { + self.client.get_balance(address, denom).await + } + + pub async fn get_all_balances(&self, address: &AccountId) -> Result, NyxdError> + where + C: CosmWasmClient + Sync, + { + self.client.get_all_balances(address).await + } + + pub async fn get_tx(&self, id: Hash) -> Result + where + C: CosmWasmClient + Sync, + { + self.client.get_tx(id).await + } + + pub async fn search_tx(&self, query: Query) -> Result, NyxdError> + where + C: CosmWasmClient + Sync, + { + self.client.search_tx(query).await + } + + pub async fn get_total_supply(&self) -> Result, NyxdError> + where + C: CosmWasmClient + Sync, + { + self.client.get_total_supply().await + } +} diff --git a/common/client-libs/validator-client/src/nyxd/traits/mod.rs b/common/client-libs/validator-client/src/nyxd/traits/mod.rs index 55fc8c9215..9f6dac5354 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/mod.rs @@ -8,19 +8,24 @@ mod dkg_query_client; mod group_query_client; mod mixnet_query_client; mod multisig_query_client; +mod name_service_query_client; +mod sp_directory_query_client; mod vesting_query_client; +#[cfg(feature = "signing")] mod coconut_bandwidth_signing_client; +#[cfg(feature = "signing")] mod dkg_signing_client; +#[cfg(feature = "signing")] mod mixnet_signing_client; +#[cfg(feature = "signing")] mod multisig_signing_client; -mod vesting_signing_client; - -mod sp_directory_query_client; -mod sp_directory_signing_client; - -mod name_service_query_client; +#[cfg(feature = "signing")] mod name_service_signing_client; +#[cfg(feature = "signing")] +mod sp_directory_signing_client; +#[cfg(feature = "signing")] +mod vesting_signing_client; pub use coconut_bandwidth_query_client::CoconutBandwidthQueryClient; pub use dkg_query_client::DkgQueryClient; @@ -31,10 +36,17 @@ pub use name_service_query_client::NameServiceQueryClient; pub use sp_directory_query_client::SpDirectoryQueryClient; pub use vesting_query_client::VestingQueryClient; +#[cfg(feature = "signing")] pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient; +#[cfg(feature = "signing")] pub use dkg_signing_client::DkgSigningClient; +#[cfg(feature = "signing")] pub use mixnet_signing_client::MixnetSigningClient; +#[cfg(feature = "signing")] pub use multisig_signing_client::MultisigSigningClient; +#[cfg(feature = "signing")] pub use name_service_signing_client::NameServiceSigningClient; +#[cfg(feature = "signing")] pub use sp_directory_signing_client::SpDirectorySigningClient; +#[cfg(feature = "signing")] pub use vesting_signing_client::VestingSigningClient; diff --git a/common/client-libs/validator-client/src/signing/mod.rs b/common/client-libs/validator-client/src/signing/mod.rs index f2301db26e..d9fa0c30e4 100644 --- a/common/client-libs/validator-client/src/signing/mod.rs +++ b/common/client-libs/validator-client/src/signing/mod.rs @@ -63,7 +63,6 @@ impl SignerData { } } - #[cfg(feature = "nyxd-client")] pub fn new_from_sequence_response( response: crate::nyxd::cosmwasm_client::types::SequenceResponse, chain_id: chain::Id, diff --git a/common/commands/Cargo.toml b/common/commands/Cargo.toml index 9f77a9e4ae..28f319b875 100644 --- a/common/commands/Cargo.toml +++ b/common/commands/Cargo.toml @@ -14,7 +14,7 @@ clap = { version = "4.0", features = ["derive"] } cw-utils = { workspace = true } handlebars = "3.0.1" humantime-serde = "1.0" -k256 = { version = "0.10", features = ["ecdsa", "sha256"] } +k256 = { workspace = true, features = ["ecdsa", "sha256"] } log = { workspace = true } rand = {version = "0.6", features = ["std"] } serde = { version = "1.0", features = ["derive"] } @@ -25,10 +25,10 @@ toml = "0.5.6" url = "2.2" tap = "1" -cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } +cosmrs = { workspace = true } cosmwasm-std = { workspace = true } -nym-validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] } +nym-validator-client = { path = "../client-libs/validator-client", features = ["signing", "http-client"] } nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] } nym-network-defaults = { path = "../network-defaults" } diff --git a/common/commands/src/validator/cosmwasm/generators/multisig.rs b/common/commands/src/validator/cosmwasm/generators/multisig.rs index cb610845c6..37f05a5e71 100644 --- a/common/commands/src/validator/cosmwasm/generators/multisig.rs +++ b/common/commands/src/validator/cosmwasm/generators/multisig.rs @@ -56,6 +56,8 @@ pub async fn generate(args: Args) { .expect("threshold can't be converted to Decimal"), }, max_voting_period: Duration::Time(args.max_voting_period), + executor: None, + proposal_deposit: None, coconut_bandwidth_contract_address: coconut_bandwidth_contract_address.to_string(), coconut_dkg_contract_address: coconut_dkg_contract_address.to_string(), }; diff --git a/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs b/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs index 87ecdfb3f8..ec10a60193 100644 --- a/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/vesting_delegate_to_mixnode.rs @@ -6,7 +6,7 @@ use log::info; use nym_mixnet_contract_common::{Coin, MixId}; use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs b/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs index cd1521dea6..4f8dac045e 100644 --- a/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/vesting_undelegate_from_mixnode.rs @@ -5,7 +5,7 @@ use clap::Parser; use log::info; use nym_mixnet_contract_common::MixId; use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs index 6d3c480296..e734ddc97d 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/vesting_update_config.rs @@ -6,7 +6,7 @@ use clap::Parser; use log::info; use nym_mixnet_contract_common::GatewayConfigUpdate; use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs index d6625a878b..21f771c0b3 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/vesting_bond_gateway.rs @@ -7,7 +7,7 @@ use log::{info, warn}; use nym_contracts_common::signing::MessageSignature; use nym_mixnet_contract_common::{Coin, Gateway}; use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT}; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/families/create_family.rs b/common/commands/src/validator/mixnet/operators/mixnode/families/create_family.rs index 6a3dc6d2da..f5ff9e3419 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/families/create_family.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/families/create_family.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_validator_client::nyxd::traits::MixnetSigningClient; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/families/join_family.rs b/common/commands/src/validator/mixnet/operators/mixnode/families/join_family.rs index e585fe2ec3..8b888e8bd6 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/families/join_family.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/families/join_family.rs @@ -8,7 +8,7 @@ use nym_contracts_common::signing::MessageSignature; use nym_crypto::asymmetric::identity; use nym_mixnet_contract_common::families::FamilyHead; use nym_validator_client::nyxd::traits::MixnetSigningClient; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/families/kick_family_member.rs b/common/commands/src/validator/mixnet/operators/mixnode/families/kick_family_member.rs index 0dc0b840d1..9305c56918 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/families/kick_family_member.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/families/kick_family_member.rs @@ -6,7 +6,7 @@ use clap::Parser; use log::info; use nym_crypto::asymmetric::identity; use nym_validator_client::nyxd::traits::MixnetSigningClient; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/families/leave_family.rs b/common/commands/src/validator/mixnet/operators/mixnode/families/leave_family.rs index 1ffb2c46b6..5cb06e7762 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/families/leave_family.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/families/leave_family.rs @@ -7,7 +7,7 @@ use log::info; use nym_crypto::asymmetric::identity; use nym_mixnet_contract_common::families::FamilyHead; use nym_validator_client::nyxd::traits::MixnetSigningClient; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs b/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs index e4af971aa3..5b956b929f 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/settings/vesting_update_config.rs @@ -6,7 +6,7 @@ use clap::Parser; use log::info; use nym_mixnet_contract_common::MixNodeConfigUpdate; use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs index dd172c8d51..9928dec18f 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_bond_mixnode.rs @@ -11,7 +11,7 @@ use nym_mixnet_contract_common::{MixNode, Percent}; use nym_network_defaults::{ DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT, }; -use nym_validator_client::nyxd::{CosmWasmCoin, VestingSigningClient}; +use nym_validator_client::nyxd::{traits::VestingSigningClient, CosmWasmCoin}; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs index 131e210646..d38d7f61dc 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_decrease_pledge.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::Coin; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs index b0839f97de..0774fd89d6 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_pledge_more.rs @@ -4,7 +4,7 @@ use clap::Parser; use log::info; use nym_mixnet_contract_common::Coin; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs index be2d99a35c..49f717a5af 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/vesting_unbond_mixnode.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::VestingSigningClient; +use nym_validator_client::nyxd::traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/signature/helpers.rs b/common/commands/src/validator/signature/helpers.rs index 1c2d05c9e1..d612c0f994 100644 --- a/common/commands/src/validator/signature/helpers.rs +++ b/common/commands/src/validator/signature/helpers.rs @@ -10,7 +10,7 @@ pub fn secp256k1_verify_with_public_key( public_key_as_bytes: &[u8], signature_as_hex: String, message: String, -) -> Result<(), k256::ecdsa::signature::Error> { +) -> Result<(), k256::ecdsa::Error> { let verifying_key = VerifyingKey::from_sec1_bytes(public_key_as_bytes)?; let signature = Signature::from_str(&signature_as_hex)?; let message_as_bytes = message.into_bytes(); diff --git a/common/commands/src/validator/transactions/get_transaction.rs b/common/commands/src/validator/transactions/get_transaction.rs index 3d1b53e7b2..6ad1bbbd2f 100644 --- a/common/commands/src/validator/transactions/get_transaction.rs +++ b/common/commands/src/validator/transactions/get_transaction.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use crate::context::QueryClient; use crate::utils::show_error; -use cosmrs::tx::Hash; +use cosmrs::tendermint::Hash; use serde_json::json; #[derive(Debug, Parser)] diff --git a/common/commands/src/validator/vesting/balance.rs b/common/commands/src/validator/vesting/balance.rs index 3d5c340027..d5f5af20fe 100644 --- a/common/commands/src/validator/vesting/balance.rs +++ b/common/commands/src/validator/vesting/balance.rs @@ -5,7 +5,7 @@ use clap::Parser; use cosmrs::AccountId; use log::{error, info}; -use nym_validator_client::nyxd::{Coin, VestingQueryClient}; +use nym_validator_client::nyxd::{traits::VestingQueryClient, Coin}; use crate::context::QueryClient; use crate::utils::show_error; diff --git a/common/commands/src/validator/vesting/create_vesting_schedule.rs b/common/commands/src/validator/vesting/create_vesting_schedule.rs index e861cc227e..524cdf67e1 100644 --- a/common/commands/src/validator/vesting/create_vesting_schedule.rs +++ b/common/commands/src/validator/vesting/create_vesting_schedule.rs @@ -8,8 +8,8 @@ use log::info; use nym_mixnet_contract_common::Coin; use nym_network_defaults::NymNetworkDetails; +use nym_validator_client::nyxd::traits::VestingSigningClient; use nym_validator_client::nyxd::AccountId; -use nym_validator_client::nyxd::VestingSigningClient; use nym_validator_client::nyxd::{CosmosCoin, Denom}; use nym_vesting_contract_common::messages::VestingSpecification; use nym_vesting_contract_common::PledgeCap; diff --git a/common/commands/src/validator/vesting/query_vesting_schedule.rs b/common/commands/src/validator/vesting/query_vesting_schedule.rs index 397d4b9075..9c8facfb70 100644 --- a/common/commands/src/validator/vesting/query_vesting_schedule.rs +++ b/common/commands/src/validator/vesting/query_vesting_schedule.rs @@ -6,7 +6,7 @@ use cosmrs::AccountId; use cosmwasm_std::Coin as CosmWasmCoin; use log::{error, info}; -use nym_validator_client::nyxd::{Coin, VestingQueryClient}; +use nym_validator_client::nyxd::{traits::VestingQueryClient, Coin}; use crate::context::QueryClient; use crate::utils::show_error; diff --git a/common/commands/src/validator/vesting/withdraw_vested.rs b/common/commands/src/validator/vesting/withdraw_vested.rs index d98a958cf1..a85e960a1a 100644 --- a/common/commands/src/validator/vesting/withdraw_vested.rs +++ b/common/commands/src/validator/vesting/withdraw_vested.rs @@ -4,7 +4,10 @@ use clap::Parser; use log::info; -use nym_validator_client::nyxd::{Coin, VestingQueryClient, VestingSigningClient}; +use nym_validator_client::nyxd::{ + traits::{VestingQueryClient, VestingSigningClient}, + Coin, +}; use crate::context::SigningClient; use crate::utils::show_error; diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs index 20f1e2700d..8fffcc81eb 100644 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs @@ -14,7 +14,7 @@ pub struct InstantiateMsg { pub mix_denom: String, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { DepositFunds { data: DepositData }, diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/spend_credential.rs b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/spend_credential.rs index ffece73fd4..7cb47dc73b 100644 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/spend_credential.rs +++ b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/spend_credential.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use crate::msg::ExecuteMsg; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct SpendCredentialData { funds: Coin, blinded_serial_number: String, @@ -43,7 +43,7 @@ pub enum SpendCredentialStatus { Spent, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct SpendCredential { funds: Coin, blinded_serial_number: String, @@ -74,7 +74,7 @@ impl SpendCredential { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct PagedSpendCredentialResponse { pub spend_credentials: Vec, pub per_page: usize, @@ -95,7 +95,7 @@ impl PagedSpendCredentialResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct SpendCredentialResponse { pub spend_credential: Option, } diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/signing/mod.rs b/common/cosmwasm-smart-contracts/contracts-common/src/signing/mod.rs index 206152957b..23018c9249 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/src/signing/mod.rs +++ b/common/cosmwasm-smart-contracts/contracts-common/src/signing/mod.rs @@ -15,7 +15,7 @@ pub type Nonce = u32; // define this type explicitly for [hopefully] better usability // (so you wouldn't need to worry about whether you should use bytes, bs58, etc.) -#[derive(Clone, Debug, PartialEq, JsonSchema)] +#[derive(Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct MessageSignature(Vec); impl MessageSignature { diff --git a/common/cosmwasm-smart-contracts/group-contract/Cargo.toml b/common/cosmwasm-smart-contracts/group-contract/Cargo.toml index 9f57c84154..2856c44925 100644 --- a/common/cosmwasm-smart-contracts/group-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/group-contract/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +cosmwasm-schema = { workspace = true } cw4 = { workspace = true } +cw-controllers = { workspace = true } schemars = "0.8" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/common/cosmwasm-smart-contracts/group-contract/src/msg.rs b/common/cosmwasm-smart-contracts/group-contract/src/msg.rs index a5a473135d..3f95227492 100644 --- a/common/cosmwasm-smart-contracts/group-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/group-contract/src/msg.rs @@ -1,13 +1,7 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - +use cosmwasm_schema::{cw_serde, QueryResponses}; use cw4::Member; -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct InstantiateMsg { /// The admin is the only account that can update the group state. /// Omit it to make the group immutable. @@ -15,8 +9,7 @@ pub struct InstantiateMsg { pub members: Vec, } -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub enum ExecuteMsg { /// Change the admin UpdateAdmin { admin: Option }, @@ -32,23 +25,24 @@ pub enum ExecuteMsg { RemoveHook { addr: String }, } -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] -#[serde(rename_all = "snake_case")] +#[cw_serde] +#[derive(QueryResponses)] pub enum QueryMsg { - /// Return AdminResponse + #[returns(cw_controllers::AdminResponse)] Admin {}, - /// Return TotalWeightResponse - TotalWeight {}, - /// Returns MembersListResponse + #[returns(cw4::TotalWeightResponse)] + TotalWeight { at_height: Option }, + #[returns(cw4::MemberListResponse)] ListMembers { start_after: Option, limit: Option, }, - /// Returns MemberResponse + #[returns(cw4::MemberResponse)] Member { addr: String, at_height: Option, }, - /// Shows all registered hooks. Returns HooksResponse. + /// Shows all registered hooks. + #[returns(cw_controllers::HooksResponse)] Hooks {}, } diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs index 876ce7b515..55294498b8 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs @@ -37,7 +37,7 @@ pub fn generate_owner_storage_subkey( } } -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] pub struct Delegation { /// Address of the owner of this delegation. pub owner: Addr, @@ -114,7 +114,7 @@ impl Delegation { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct PagedMixNodeDelegationsResponse { pub delegations: Vec, pub start_next_after: Option, @@ -129,7 +129,7 @@ impl PagedMixNodeDelegationsResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct PagedDelegatorDelegationsResponse { pub delegations: Vec, pub start_next_after: Option<(MixId, OwnerProxySubKey)>, @@ -147,7 +147,7 @@ impl PagedDelegatorDelegationsResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct MixNodeDelegationResponse { pub delegation: Option, pub mixnode_still_bonded: bool, @@ -162,7 +162,7 @@ impl MixNodeDelegationResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct PagedAllDelegationsResponse { pub delegations: Vec, pub start_next_after: Option, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs index 5d39b2c150..c312d42bf7 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs @@ -23,7 +23,7 @@ pub struct Gateway { pub version: String, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct GatewayBond { pub pledge_amount: Coin, pub owner: Addr, @@ -132,7 +132,7 @@ impl GatewayConfigUpdate { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct PagedGatewayResponse { pub nodes: Vec, pub per_page: usize, @@ -153,13 +153,13 @@ impl PagedGatewayResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct GatewayOwnershipResponse { pub address: Addr, pub gateway: Option, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct GatewayBondResponse { pub identity: IdentityKey, pub gateway: Option, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs index fc2b14d71e..5afdbd827e 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs @@ -489,7 +489,7 @@ impl CurrentIntervalResponse { } } -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct PendingEpochEventsResponse { pub seconds_until_executable: i64, pub events: Vec, @@ -510,7 +510,7 @@ impl PendingEpochEventsResponse { } } -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct PendingIntervalEventsResponse { pub seconds_until_executable: i64, pub events: Vec, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs index 6d5ca55e7b..69f1a89209 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs @@ -33,7 +33,7 @@ impl RewardedSetNodeStatus { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct MixNodeDetails { pub bond_information: MixNodeBond, pub rewarding_details: MixNodeRewarding, @@ -86,7 +86,7 @@ impl MixNodeDetails { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct MixNodeRewarding { /// Information provided by the operator that influence the cost function. pub cost_params: MixNodeCostParams, @@ -465,7 +465,7 @@ impl MixNodeRewarding { } // operator information + data assigned by the contract(s) -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct MixNodeBond { /// Unique id assigned to the bonded mixnode. pub mix_id: MixId, @@ -559,7 +559,7 @@ pub struct MixNode { pub version: String, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct MixNodeCostParams { pub profit_margin_percent: Percent, @@ -686,7 +686,7 @@ impl MixNodeConfigUpdate { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct PagedMixnodeBondsResponse { pub nodes: Vec, pub per_page: usize, @@ -703,7 +703,7 @@ impl PagedMixnodeBondsResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct PagedMixnodesDetailsResponse { pub nodes: Vec, pub per_page: usize, @@ -745,19 +745,19 @@ impl PagedUnbondedMixnodesResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct MixOwnershipResponse { pub address: Addr, pub mixnode_details: Option, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct MixnodeDetailsResponse { pub mix_id: MixId, pub mixnode_details: Option, } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] pub struct MixnodeRewardingDetailsResponse { pub mix_id: MixId, pub rewarding_details: Option, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs index 886c5b46b1..5f4db1ab90 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs @@ -7,19 +7,19 @@ use crate::{BlockHeight, EpochEventId, IntervalEventId, MixId}; use cosmwasm_std::{Addr, Coin}; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct PendingEpochEvent { pub id: EpochEventId, pub event: PendingEpochEventData, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct PendingEpochEventData { pub created_at: BlockHeight, pub kind: PendingEpochEventKind, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub enum PendingEpochEventKind { // can't just pass the `Delegation` struct here as it's impossible to determine // `cumulative_reward_ratio` ahead of time @@ -68,19 +68,19 @@ impl From<(EpochEventId, PendingEpochEventData)> for PendingEpochEvent { } } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct PendingIntervalEvent { pub id: IntervalEventId, pub event: PendingIntervalEventData, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct PendingIntervalEventData { pub created_at: BlockHeight, pub kind: PendingIntervalEventKind, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub enum PendingIntervalEventKind { ChangeMixCostParams { mix_id: MixId, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/mod.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/mod.rs index ee091e6b13..650e39a2ff 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/mod.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/mod.rs @@ -35,7 +35,7 @@ pub struct RewardDistribution { pub delegates: Decimal, } -#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq)] +#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] pub struct PendingRewardResponse { pub amount_staked: Option, pub amount_earned: Option, @@ -46,7 +46,7 @@ pub struct PendingRewardResponse { pub mixnode_still_fully_bonded: bool, } -#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq)] +#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] pub struct EstimatedCurrentEpochRewardResponse { pub original_stake: Option, diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs index e1df97f04a..e0a8109f98 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs @@ -22,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)] +#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, PartialEq, Eq)] pub struct LayerAssignment { mix_id: MixId, layer: Layer, @@ -118,7 +118,7 @@ impl Index for LayerDistribution { } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct ContractState { pub owner: Addr, // only the owner account can update state pub rewarding_validator_address: Addr, @@ -130,7 +130,7 @@ pub struct ContractState { pub params: ContractStateParams, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct ContractStateParams { /// Minimum amount a delegator must stake in orders for his delegation to get accepted. pub minimum_mixnode_delegation: Option, diff --git a/common/cosmwasm-smart-contracts/multisig-contract/Cargo.toml b/common/cosmwasm-smart-contracts/multisig-contract/Cargo.toml index 4688654e1b..2a6753267e 100644 --- a/common/cosmwasm-smart-contracts/multisig-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/multisig-contract/Cargo.toml @@ -9,6 +9,8 @@ edition = "2021" cw-utils = { workspace = true } cw3 = { workspace = true } cw4 = { workspace= true } +cw-storage-plus = { workspace = true } +cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } schemars = "0.8" serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/common/cosmwasm-smart-contracts/multisig-contract/src/error.rs b/common/cosmwasm-smart-contracts/multisig-contract/src/error.rs index b10fa6b506..ead941cad0 100644 --- a/common/cosmwasm-smart-contracts/multisig-contract/src/error.rs +++ b/common/cosmwasm-smart-contracts/multisig-contract/src/error.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use cosmwasm_std::StdError; -use cw_utils::ThresholdError; +use cw3::DepositError; +use cw_utils::{PaymentError, ThresholdError}; use thiserror::Error; @@ -17,9 +18,6 @@ pub enum ContractError { #[error("Group contract invalid address '{addr}'")] InvalidGroup { addr: String }, - #[error("Coconut bandwidth contract address not found")] - InvalidCoconutBandwidth {}, - #[error("Unauthorized")] Unauthorized {}, @@ -43,4 +41,10 @@ pub enum ContractError { #[error("Cannot close completed or passed proposals")] WrongCloseStatus {}, + + #[error("{0}")] + Payment(#[from] PaymentError), + + #[error("{0}")] + Deposit(#[from] DepositError), } diff --git a/common/cosmwasm-smart-contracts/multisig-contract/src/lib.rs b/common/cosmwasm-smart-contracts/multisig-contract/src/lib.rs index a968ed14e5..64af48ecf9 100644 --- a/common/cosmwasm-smart-contracts/multisig-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/multisig-contract/src/lib.rs @@ -1,2 +1,3 @@ pub mod error; pub mod msg; +pub mod state; diff --git a/common/cosmwasm-smart-contracts/multisig-contract/src/msg.rs b/common/cosmwasm-smart-contracts/multisig-contract/src/msg.rs index db37835f64..15b959fa14 100644 --- a/common/cosmwasm-smart-contracts/multisig-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/multisig-contract/src/msg.rs @@ -1,15 +1,15 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - +use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{CosmosMsg, Empty}; -use cw3::Vote; +use cw3::{UncheckedDepositInfo, Vote}; use cw4::MemberChangedHookMsg; use cw_utils::{Duration, Expiration, Threshold}; -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +use crate::state::Executor; + +#[cw_serde] pub struct InstantiateMsg { // this is the group contract that contains the member list pub group_addr: String, @@ -17,11 +17,15 @@ pub struct InstantiateMsg { pub coconut_dkg_contract_address: String, pub threshold: Threshold, pub max_voting_period: Duration, + // who is able to execute passed proposals + // None means that anyone can execute + pub executor: Option, + /// The cost of creating a proposal (if any). + pub proposal_deposit: Option, } // TODO: add some T variants? Maybe good enough as fixed Empty for now -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub enum ExecuteMsg { Propose { title: String, @@ -45,41 +49,44 @@ pub enum ExecuteMsg { } // We can also add this as a cw3 extension -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] -#[serde(rename_all = "snake_case")] +#[cw_serde] +#[derive(QueryResponses)] pub enum QueryMsg { - /// Return ThresholdResponse + #[returns(cw_utils::ThresholdResponse)] Threshold {}, - /// Returns ProposalResponse + #[returns(cw3::ProposalResponse)] Proposal { proposal_id: u64 }, - /// Returns ProposalListResponse + #[returns(cw3::ProposalListResponse)] ListProposals { start_after: Option, limit: Option, }, - /// Returns ProposalListResponse + #[returns(cw3::ProposalListResponse)] ReverseProposals { start_before: Option, limit: Option, }, - /// Returns VoteResponse + #[returns(cw3::VoteResponse)] Vote { proposal_id: u64, voter: String }, - /// Returns VoteListResponse + #[returns(cw3::VoteListResponse)] ListVotes { proposal_id: u64, start_after: Option, limit: Option, }, - /// Returns VoterInfo + #[returns(cw3::VoterResponse)] Voter { address: String }, - /// Returns VoterListResponse + #[returns(cw3::VoterListResponse)] ListVoters { start_after: Option, limit: Option, }, + /// Gets the current configuration. + #[returns(crate::state::Config)] + Config {}, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] + +#[cw_serde] pub struct MigrateMsg { pub coconut_bandwidth_address: String, pub coconut_dkg_address: String, diff --git a/common/cosmwasm-smart-contracts/multisig-contract/src/state.rs b/common/cosmwasm-smart-contracts/multisig-contract/src/state.rs new file mode 100644 index 0000000000..d3ce181ba1 --- /dev/null +++ b/common/cosmwasm-smart-contracts/multisig-contract/src/state.rs @@ -0,0 +1,59 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, QuerierWrapper}; +use cw3::DepositInfo; +use cw4::Cw4Contract; +use cw_storage_plus::Item; +use cw_utils::{Duration, Threshold}; + +use crate::error::ContractError; + +/// Defines who is able to execute proposals once passed +#[cw_serde] +pub enum Executor { + /// Any member of the voting group, even with 0 points + Member, + /// Only the given address + Only(Addr), +} + +#[cw_serde] +pub struct Config { + pub threshold: Threshold, + pub max_voting_period: Duration, + // Total weight and voters are queried from this contract + pub group_addr: Cw4Contract, + pub coconut_bandwidth_addr: Addr, + pub coconut_dkg_addr: Addr, + // who is able to execute passed proposals + // None means that anyone can execute + pub executor: Option, + /// The price, if any, of creating a new proposal. + pub proposal_deposit: Option, +} + +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 = Item::new("config"); diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs index 75d916ddad..6c456674a5 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs @@ -28,7 +28,7 @@ pub enum Period { After, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct PledgeData { pub amount: Coin, pub block_time: Timestamp, @@ -49,7 +49,7 @@ impl PledgeData { } #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub enum PledgeCap { Percent(Percent), Absolute(Uint128), // This has to be in unym @@ -77,7 +77,7 @@ impl Default for PledgeCap { } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] pub struct OriginalVestingResponse { pub amount: Coin, pub number_of_periods: usize, diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs index 226f39a9c0..f4d7147e2c 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs @@ -55,7 +55,7 @@ impl VestingSpecification { } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { // Families diff --git a/common/credentials/Cargo.toml b/common/credentials/Cargo.toml index 3f029494dd..6bb10d5cb6 100644 --- a/common/credentials/Cargo.toml +++ b/common/credentials/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] } -cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } +cosmrs = { workspace = true } thiserror = "1.0" # I guess temporarily until we get serde support in coconut up and running diff --git a/common/credentials/src/coconut/bandwidth.rs b/common/credentials/src/coconut/bandwidth.rs index 9c87e75341..7e458456df 100644 --- a/common/credentials/src/coconut/bandwidth.rs +++ b/common/credentials/src/coconut/bandwidth.rs @@ -6,14 +6,14 @@ // right now this has no double-spending protection, spender binding, etc // it's the simplest possible case +use cosmrs::tendermint::hash::Algorithm; +use cosmrs::tendermint::Hash; use nym_coconut_interface::{ hash_to_scalar, prepare_blind_sign, Attribute, BlindSignRequest, Credential, Parameters, PrivateAttribute, PublicAttribute, Signature, VerificationKey, }; use nym_crypto::asymmetric::{encryption, identity}; -use cosmrs::tx::Hash; - use super::utils::prepare_credential_for_spending; use crate::error::Error; @@ -129,7 +129,9 @@ impl BandwidthVoucher { let binding_number = Option::::from(PrivateAttribute::from_bytes(&buff)) .ok_or_else(scalar_err)?; buff.copy_from_slice(&bytes[2 * 32..3 * 32]); - let tx_hash = Hash::new(buff); + let tx_hash = Hash::from_bytes(Algorithm::Sha256, &buff).map_err(|_| { + Error::BandwidthVoucherDeserializationError(String::from("Invalid transaction Hash")) + })?; buff.copy_from_slice(&bytes[3 * 32..4 * 32]); let signing_key = identity::PrivateKey::from_bytes(&buff).map_err(|_| { Error::BandwidthVoucherDeserializationError(String::from("Invalid key")) @@ -282,6 +284,7 @@ pub fn prepare_for_spending( #[cfg(test)] mod test { use super::*; + use cosmrs::tendermint::hash::Algorithm; use nym_coconut_interface::Base58; use rand::rngs::OsRng; @@ -292,7 +295,7 @@ mod test { ¶ms, "1234".to_string(), "voucher info".to_string(), - Hash::new([0; 32]), + Hash::from_bytes(Algorithm::Sha256, &[0; 32]).unwrap(), identity::PrivateKey::from_base58_string( identity::KeyPair::new(&mut rng) .private_key() diff --git a/common/dkg/benches/benchmarks.rs b/common/dkg/benches/benchmarks.rs index 0aed2e4192..483c6d20e9 100644 --- a/common/dkg/benches/benchmarks.rs +++ b/common/dkg/benches/benchmarks.rs @@ -10,10 +10,10 @@ use nym_dkg::bte::proof_discrete_log::ProofOfDiscreteLog; use nym_dkg::bte::proof_sharing::ProofOfSecretSharing; use nym_dkg::bte::{ decrypt_share, encrypt_shares, keygen, proof_chunking, proof_sharing, setup, DecryptionKey, - PublicKey, + Params, PublicKey, }; use nym_dkg::interpolation::polynomial::Polynomial; -use nym_dkg::{Dealing, NodeIndex, Share}; +use nym_dkg::{combine_shares, Dealing, NodeIndex, Share, Threshold}; use rand_core::{RngCore, SeedableRng}; use std::collections::BTreeMap; @@ -49,6 +49,37 @@ fn prepare_keys( (receivers, dks) } +fn prepare_resharing( + mut rng: impl RngCore, + params: &Params, + nodes: usize, + threshold: Threshold, +) -> (BTreeMap, Vec) { + let (receivers, mut dks) = prepare_keys(&mut rng, nodes); + + let first_dealings = receivers + .keys() + .map(|&dealer_index| { + Dealing::create(&mut rng, params, dealer_index, threshold, &receivers, None).0 + }) + .collect::>(); + + let mut derived_secrets = Vec::new(); + for (i, ref mut dk) in dks.iter_mut().enumerate() { + let shares = first_dealings + .iter() + .map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap()) + .collect(); + + let recovered_secret = + combine_shares(shares, &receivers.keys().copied().collect::>()).unwrap(); + + derived_secrets.push(recovered_secret) + } + + (receivers, derived_secrets) +} + pub fn creating_dealing_for_3_parties(c: &mut Criterion) { let dummy_seed = [42u8; 32]; let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed); @@ -73,6 +104,33 @@ pub fn creating_dealing_for_3_parties(c: &mut Criterion) { }); } +pub fn creating_reshared_dealing_for_3_parties(c: &mut Criterion) { + let dummy_seed = [42u8; 32]; + let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed); + let params = setup(); + let threshold = 2; + + let (receivers, mut derived_secrets) = prepare_resharing(&mut rng, ¶ms, 3, threshold); + + c.bench_function( + "creating single re-shared dealing for 3 parties (threshold 2)", + |b| { + b.iter(|| { + black_box({ + Dealing::create( + &mut rng, + ¶ms, + receivers.keys().next().copied().unwrap(), + threshold, + &receivers, + Some(derived_secrets.pop().unwrap()), + ) + }) + }) + }, + ); +} + pub fn verifying_dealing_made_for_3_parties_and_recovering_share(c: &mut Criterion) { let dummy_seed = [42u8; 32]; let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed); @@ -129,6 +187,33 @@ pub fn creating_dealing_for_20_parties(c: &mut Criterion) { ); } +pub fn creating_reshared_dealing_for_20_parties(c: &mut Criterion) { + let dummy_seed = [42u8; 32]; + let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed); + let params = setup(); + let threshold = 14; + + let (receivers, mut derived_secrets) = prepare_resharing(&mut rng, ¶ms, 20, threshold); + + c.bench_function( + "creating single re-shared dealing for 20 parties (threshold 14)", + |b| { + b.iter(|| { + black_box({ + Dealing::create( + &mut rng, + ¶ms, + receivers.keys().next().copied().unwrap(), + threshold, + &receivers, + Some(derived_secrets.pop().unwrap()), + ) + }) + }) + }, + ); +} + pub fn verifying_dealing_made_for_20_parties_and_recovering_share(c: &mut Criterion) { let dummy_seed = [42u8; 32]; let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed); @@ -185,6 +270,33 @@ pub fn creating_dealing_for_100_parties(c: &mut Criterion) { ); } +pub fn creating_reshared_dealing_for_100_parties(c: &mut Criterion) { + let dummy_seed = [42u8; 32]; + let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed); + let params = setup(); + let threshold = 67; + + let (receivers, mut derived_secrets) = prepare_resharing(&mut rng, ¶ms, 100, threshold); + + c.bench_function( + "creating single re-shared dealing for 100 parties (threshold 67)", + |b| { + b.iter(|| { + black_box({ + Dealing::create( + &mut rng, + ¶ms, + receivers.keys().next().copied().unwrap(), + threshold, + &receivers, + Some(derived_secrets.pop().unwrap()), + ) + }) + }) + }, + ); +} + pub fn verifying_dealing_made_for_100_parties_and_recovering_share(c: &mut Criterion) { let dummy_seed = [42u8; 32]; let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed); @@ -472,6 +584,13 @@ criterion_group!( creating_dealing_for_100_parties, ); +criterion_group!( + reshared_dealings_creation, + creating_reshared_dealing_for_3_parties, + creating_reshared_dealing_for_20_parties, + creating_reshared_dealing_for_100_parties, +); + // note: in our setting each party will have to create at least 4 dealings (one per attribute in credential) // and verify 99 * 4 of them (4 from each other dealer) criterion_group!( @@ -501,6 +620,7 @@ criterion_group!( criterion_main!( utils, dealings_creation, + reshared_dealings_creation, dealings_verification, proofs_of_knowledge, encryption diff --git a/common/ledger/Cargo.toml b/common/ledger/Cargo.toml index 9bea382a73..5ea9badd52 100644 --- a/common/ledger/Cargo.toml +++ b/common/ledger/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bip32 = "0.3.0" -k256 = "0.10.4" +bip32 = "0.5.1" +k256 = { workspace = true } ledger-transport = "0.10.0" ledger-transport-hid = "0.10.0" thiserror = "1" \ No newline at end of file diff --git a/common/ledger/src/addr_secp256k1.rs b/common/ledger/src/addr_secp256k1.rs index 682161be9d..d6c5957481 100644 --- a/common/ledger/src/addr_secp256k1.rs +++ b/common/ledger/src/addr_secp256k1.rs @@ -27,7 +27,7 @@ impl TryFrom>> for AddrSecp256k1Response { } let (pub_key, addr) = bytes.split_at(33); - let public_key = k256::PublicKey::from_bytes( + let public_key = PublicKey::from_bytes( PublicKeyBytes::try_from(pub_key).expect("Public key should be 33 bytes"), )?; let address = String::from_utf8(addr.to_vec()).unwrap(); diff --git a/common/mixnode-common/Cargo.toml b/common/mixnode-common/Cargo.toml index 9a3f0ce851..62fec9422a 100644 --- a/common/mixnode-common/Cargo.toml +++ b/common/mixnode-common/Cargo.toml @@ -36,9 +36,7 @@ nym-sphinx-framing = { path = "../nymsphinx/framing" } nym-sphinx-params = { path = "../nymsphinx/params" } nym-sphinx-types = { path = "../nymsphinx/types" } nym-task = { path = "../task" } -nym-validator-client = { path = "../client-libs/validator-client", features = [ - "nyxd-client", -] } +nym-validator-client = { path = "../client-libs/validator-client" } nym-bin-common = { path = "../bin-common" } cfg-if = "1.0.0" diff --git a/common/socks5-client-core/Cargo.toml b/common/socks5-client-core/Cargo.toml index 1057955242..9ca792f8b1 100644 --- a/common/socks5-client-core/Cargo.toml +++ b/common/socks5-client-core/Cargo.toml @@ -26,7 +26,7 @@ nym-service-providers-common = { path = "../../service-providers/common" } nym-socks5-requests = { path = "../socks5/requests" } nym-sphinx = { path = "../nymsphinx" } nym-task = { path = "../task" } -nym-validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"] } +nym-validator-client = { path = "../client-libs/validator-client" } [features] default = [] diff --git a/common/types/Cargo.toml b/common/types/Cargo.toml index 2c106260c2..c99bbf3e30 100644 --- a/common/types/Cargo.toml +++ b/common/types/Cargo.toml @@ -20,11 +20,9 @@ url = "2.2" ts-rs = "6.1.2" cosmwasm-std = { workspace = true } -cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } +cosmrs = { workspace = true } -nym-validator-client = { path = "../../common/client-libs/validator-client", features = [ - "nyxd-client", -] } +nym-validator-client = { path = "../../common/client-libs/validator-client" } nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } nym-vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } nym-config = { path = "../../common/config" } diff --git a/common/types/src/gas.rs b/common/types/src/gas.rs index cf63a32d60..9c389cc863 100644 --- a/common/types/src/gas.rs +++ b/common/types/src/gas.rs @@ -1,4 +1,4 @@ -use cosmrs::tx::Gas as CosmrsGas; +use cosmrs::Gas as CosmrsGas; use nym_validator_client::nyxd::cosmwasm_client::types::GasInfo as ValidatorClientGasInfo; use serde::{Deserialize, Serialize}; @@ -21,8 +21,14 @@ impl Gas { impl From for Gas { fn from(gas: CosmrsGas) -> Self { + Gas { gas_units: gas } + } +} + +impl From for Gas { + fn from(value: i64) -> Self { Gas { - gas_units: gas.value(), + gas_units: value.try_into().unwrap_or_default(), } } } diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 1db4c0bd8a..0c3efd0a24 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -50,9 +50,9 @@ checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.3.3" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" +checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" dependencies = [ "arrayref", "arrayvec", @@ -260,23 +260,23 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.7.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "constant_time_eq" -version = "0.2.5" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "cosmwasm-crypto" -version = "1.0.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd" +checksum = "75836a10cb9654c54e77ee56da94d592923092a10b369cdb0dbd56acefc16340" dependencies = [ - "digest 0.9.0", + "digest 0.10.7", "ed25519-zebra", "k256", "rand_core 0.6.4", @@ -285,45 +285,62 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.0.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4" +checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.0.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772e80bbad231a47a2068812b723a1ff81dd4a0d56c9391ac748177bea3a61da" +checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc" dependencies = [ + "cosmwasm-schema-derive", "schemars", + "serde", "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] name = "cosmwasm-std" -version = "1.0.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195" +checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb" dependencies = [ "base64 0.13.1", "cosmwasm-crypto", "cosmwasm-derive", + "derivative", "forward_ref", + "hex", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.5.1", + "sha2 0.10.6", "thiserror", "uint", ] [[package]] name = "cosmwasm-storage" -version = "1.0.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d18403b07304d15d304dad11040d45bbcaf78d603b4be3fb5e2685c16f9229b5" +checksum = "a3737a3aac48f5ed883b5b73bfb731e77feebd8fc6b43419844ec2971072164d" dependencies = [ "cosmwasm-std", "serde", @@ -361,9 +378,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", @@ -374,9 +391,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -389,9 +406,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.3.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ "generic-array 0.14.6", "rand_core 0.6.4", @@ -454,10 +471,11 @@ dependencies = [ [[package]] name = "cw-controllers" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa" +checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "cw-utils", @@ -468,17 +486,17 @@ dependencies = [ [[package]] name = "cw-multi-test" -version = "0.13.4" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f9a8ab7c3c29ec93cb7a39ce4b14a05e053153b4a17ef7cf2246af1b7c087e" +checksum = "2a18afd2e201221c6d72a57f0886ef2a22151bbc9e6db7af276fde8a91081042" dependencies = [ "anyhow", "cosmwasm-std", - "cosmwasm-storage", "cw-storage-plus", "cw-utils", "derivative", "itertools", + "k256", "prost", "schemars", "serde", @@ -487,9 +505,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a" +checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" dependencies = [ "cosmwasm-std", "schemars", @@ -498,22 +516,26 @@ dependencies = [ [[package]] name = "cw-utils" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b" +checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw2", "schemars", + "semver", "serde", "thiserror", ] [[package]] name = "cw2" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" +checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", @@ -521,11 +543,12 @@ dependencies = [ ] [[package]] -name = "cw3" -version = "0.13.4" +name = "cw20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b" +checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-utils", "schemars", @@ -533,11 +556,45 @@ dependencies = [ ] [[package]] -name = "cw3-fixed-multisig" -version = "0.13.4" +name = "cw20-base" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54aa54c13f405ec4ab36b6217538bc957d439eee58f89312db05a79caf6706" +checksum = "afcd279230b08ed8afd8be5828221622bd5b9ce25d0b01d58bad626c6ce0169c" dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-storage-plus", + "cw-utils", + "cw2", + "cw20", + "schemars", + "semver", + "serde", + "thiserror", +] + +[[package]] +name = "cw3" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "cw20", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw3-fixed-multisig" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e2415adb201e5e89dab34edf59d7dc166bc558526de009a49ae66276c9119a" +dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "cw-utils", @@ -550,14 +607,15 @@ dependencies = [ [[package]] name = "cw3-flex-multisig" -version = "0.13.1" +version = "1.0.0" dependencies = [ - "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", "cw-storage-plus", "cw-utils", "cw2", + "cw20", + "cw20-base", "cw3", "cw3-fixed-multisig", "cw4", @@ -565,15 +623,15 @@ dependencies = [ "nym-group-contract-common", "nym-multisig-contract-common", "schemars", - "serde", ] [[package]] name = "cw4" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd" +checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", @@ -582,7 +640,7 @@ dependencies = [ [[package]] name = "cw4-group" -version = "0.13.4" +version = "1.0.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -599,11 +657,12 @@ dependencies = [ [[package]] name = "der" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", + "zeroize", ] [[package]] @@ -654,9 +713,9 @@ checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "ecdsa" -version = "0.13.4" +version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ "der", "elliptic-curve", @@ -683,7 +742,7 @@ dependencies = [ "ed25519", "rand 0.7.3", "serde", - "sha2", + "sha2 0.9.9", "zeroize", ] @@ -698,7 +757,7 @@ dependencies = [ "hex", "rand_core 0.6.4", "serde", - "sha2", + "sha2 0.9.9", "zeroize", ] @@ -710,16 +769,18 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" -version = "0.11.12" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ "base16ct", "crypto-bigint", "der", + "digest 0.10.7", "ff", "generic-array 0.14.6", "group", + "pkcs8", "rand_core 0.6.4", "sec1", "subtle 2.4.1", @@ -778,9 +839,9 @@ dependencies = [ [[package]] name = "ff" -version = "0.11.1" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ "rand_core 0.6.4", "subtle 2.4.1", @@ -974,9 +1035,9 @@ dependencies = [ [[package]] name = "group" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" dependencies = [ "ff", "rand_core 0.6.4", @@ -992,15 +1053,6 @@ dependencies = [ "ahash", ] -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.1" @@ -1020,7 +1072,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" dependencies = [ "digest 0.9.0", - "hmac", + "hmac 0.11.0", ] [[package]] @@ -1033,6 +1085,15 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "humantime" version = "2.1.0" @@ -1083,7 +1144,7 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "libc", "windows-sys 0.48.0", ] @@ -1123,15 +1184,14 @@ dependencies = [ [[package]] name = "k256" -version = "0.10.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", - "sec1", - "sha2", + "sha2 0.10.6", ] [[package]] @@ -1217,9 +1277,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] @@ -1252,11 +1312,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] @@ -1347,6 +1407,8 @@ dependencies = [ name = "nym-group-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", + "cw-controllers", "cw4", "schemars", "serde", @@ -1387,7 +1449,7 @@ dependencies = [ "nym-contracts-common", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.4.1", "serde_repr", "thiserror", "time", @@ -1397,7 +1459,9 @@ dependencies = [ name = "nym-multisig-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw-storage-plus", "cw-utils", "cw3", "cw4", @@ -1595,13 +1659,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ "der", "spki", - "zeroize", ] [[package]] @@ -1827,12 +1890,12 @@ checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "rfc6979" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ "crypto-bigint", - "hmac", + "hmac 0.12.1", "zeroize", ] @@ -1941,10 +2004,11 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sec1" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ + "base16ct", "der", "generic-array 0.14.6", "pkcs8", @@ -1976,6 +2040,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-json-wasm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.160" @@ -2034,12 +2107,23 @@ dependencies = [ ] [[package]] -name = "signature" -version = "1.4.0" +name = "sha2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ - "digest 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", "rand_core 0.6.4", ] @@ -2067,20 +2151,20 @@ dependencies = [ "curve25519-dalek", "digest 0.9.0", "hkdf", - "hmac", + "hmac 0.11.0", "lioness", "log", "rand 0.7.3", "rand_distr", - "sha2", + "sha2 0.9.9", "subtle 2.4.1", ] [[package]] name = "spki" -version = "0.5.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", "der", diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index 9d1fb0ac6a..1b20380ad7 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -32,16 +32,17 @@ incremental = false overflow-checks = true [workspace.dependencies] -cosmwasm-crypto = "=1.0.0" -cosmwasm-derive = "=1.0.0" -cosmwasm-schema = "=1.0.0" -cosmwasm-std = "=1.0.0" -cosmwasm-storage = "=1.0.0" -cw-controllers = "=0.13.4" -cw-multi-test = "=0.13.4" -cw-storage-plus = "=0.13.4" -cw-utils = "=0.13.4" -cw2 = "=0.13.4" -cw3 = "=0.13.4" -cw3-fixed-multisig = "=0.13.4" -cw4 = "=0.13.4" +cosmwasm-crypto = "=1.2.5" +cosmwasm-derive = "=1.2.5" +cosmwasm-schema = "=1.2.5" +cosmwasm-std = "=1.2.5" +cosmwasm-storage = "=1.2.5" +cw-controllers = "=1.0.1" +cw-multi-test = "=0.16.4" +cw-storage-plus = "=1.0.1" +cw-utils = "=1.0.1" +cw2 = "=1.0.1" +cw3 = "=1.0.1" +cw3-fixed-multisig = "=1.0.1" +cw4 = "=1.0.1" +cw20 = "=1.0.1" diff --git a/contracts/coconut-dkg/src/verification_key_shares/storage.rs b/contracts/coconut-dkg/src/verification_key_shares/storage.rs index fae9a59d10..51a7fbb03e 100644 --- a/contracts/coconut-dkg/src/verification_key_shares/storage.rs +++ b/contracts/coconut-dkg/src/verification_key_shares/storage.rs @@ -30,7 +30,7 @@ impl<'a> IndexList for VkShareIndex<'a> { pub(crate) fn vk_shares<'a>() -> IndexedMap<'a, VKShareKey<'a>, ContractVKShare, VkShareIndex<'a>> { let indexes = VkShareIndex { epoch_id: MultiIndex::new( - |d| d.epoch_id, + |_pk, d| d.epoch_id, VK_SHARES_PK_NAMESPACE, VK_SHARES_EPOCH_ID_IDX_NAMESPACE, ), diff --git a/contracts/coconut-test/src/helpers.rs b/contracts/coconut-test/src/helpers.rs index c679176177..e0f667612d 100644 --- a/contracts/coconut-test/src/helpers.rs +++ b/contracts/coconut-test/src/helpers.rs @@ -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}; diff --git a/contracts/coconut-test/src/spend_credential_creates_proposal.rs b/contracts/coconut-test/src/spend_credential_creates_proposal.rs index 317f28a024..abcf761da5 100644 --- a/contracts/coconut-test/src/spend_credential_creates_proposal.rs +++ b/contracts/coconut-test/src/spend_credential_creates_proposal.rs @@ -45,6 +45,8 @@ fn spend_credential_creates_proposal() { threshold: Threshold::AbsolutePercentage { percentage: Decimal::from_ratio(2u128, 3u128), }, + executor: None, + proposal_deposit: None, max_voting_period: Duration::Height(1000), coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(), diff --git a/contracts/coconut-test/src/submit_vk_creates_proposal.rs b/contracts/coconut-test/src/submit_vk_creates_proposal.rs index d111b1f0c7..4e1d5dc346 100644 --- a/contracts/coconut-test/src/submit_vk_creates_proposal.rs +++ b/contracts/coconut-test/src/submit_vk_creates_proposal.rs @@ -52,6 +52,8 @@ fn dkg_proposal() { threshold: Threshold::AbsolutePercentage { percentage: Decimal::from_ratio(1u128, 1u128), }, + executor: None, + proposal_deposit: None, max_voting_period: Duration::Time(1000), coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(), diff --git a/contracts/mixnet/src/delegations/queries.rs b/contracts/mixnet/src/delegations/queries.rs index 0de9d151cf..5a5d07c32f 100644 --- a/contracts/mixnet/src/delegations/queries.rs +++ b/contracts/mixnet/src/delegations/queries.rs @@ -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); diff --git a/contracts/mixnet/src/delegations/storage.rs b/contracts/mixnet/src/delegations/storage.rs index b74bb870b0..5d3e6b45f2 100644 --- a/contracts/mixnet/src/delegations/storage.rs +++ b/contracts/mixnet/src/delegations/storage.rs @@ -27,12 +27,12 @@ impl<'a> IndexList for DelegationIndex<'a> { pub(crate) fn delegations<'a>() -> IndexedMap<'a, PrimaryKey, Delegation, DelegationIndex<'a>> { let indexes = DelegationIndex { owner: MultiIndex::new( - |d| d.owner.clone(), + |_pk, d| d.owner.clone(), DELEGATION_PK_NAMESPACE, DELEGATION_OWNER_IDX_NAMESPACE, ), mixnode: MultiIndex::new( - |d| d.mix_id, + |_pk, d| d.mix_id, DELEGATION_PK_NAMESPACE, DELEGATION_MIXNODE_IDX_NAMESPACE, ), diff --git a/contracts/mixnet/src/mixnodes/storage.rs b/contracts/mixnet/src/mixnodes/storage.rs index fdf744e0bd..2007456463 100644 --- a/contracts/mixnet/src/mixnodes/storage.rs +++ b/contracts/mixnet/src/mixnodes/storage.rs @@ -39,12 +39,12 @@ pub(crate) fn unbonded_mixnodes<'a>( ) -> IndexedMap<'a, MixId, UnbondedMixnode, UnbondedMixnodeIndex<'a>> { let indexes = UnbondedMixnodeIndex { owner: MultiIndex::new( - |d| d.owner.clone(), + |_pk, d| d.owner.clone(), UNBONDED_MIXNODES_PK_NAMESPACE, UNBONDED_MIXNODES_OWNER_IDX_NAMESPACE, ), identity_key: MultiIndex::new( - |d| d.identity_key.clone(), + |_pk, d| d.identity_key.clone(), UNBONDED_MIXNODES_PK_NAMESPACE, UNBONDED_MIXNODES_IDENTITY_IDX_NAMESPACE, ), diff --git a/contracts/mixnet/src/rewards/transactions.rs b/contracts/mixnet/src/rewards/transactions.rs index b73c66a499..19ca0e8c83 100644 --- a/contracts/mixnet/src/rewards/transactions.rs +++ b/contracts/mixnet/src/rewards/transactions.rs @@ -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(), diff --git a/contracts/mixnet/src/support/helpers.rs b/contracts/mixnet/src/support/helpers.rs index e39541f599..094d96eaba 100644 --- a/contracts/mixnet/src/support/helpers.rs +++ b/contracts/mixnet/src/support/helpers.rs @@ -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 { 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 diff --git a/contracts/multisig/cw3-flex-multisig/Cargo.toml b/contracts/multisig/cw3-flex-multisig/Cargo.toml index 0c45bb149d..b849828069 100644 --- a/contracts/multisig/cw3-flex-multisig/Cargo.toml +++ b/contracts/multisig/cw3-flex-multisig/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "cw3-flex-multisig" -version = "0.13.1" +version = "1.0.0" authors = ["Ethan Frey "] -edition = "2018" +edition = "2021" description = "Implementing cw3 with multiple voting patterns and dynamic groups" license = "Apache-2.0" repository = "https://github.com/CosmWasm/cw-plus" @@ -13,6 +13,7 @@ documentation = "https://docs.cosmwasm.com" crate-type = ["cdylib", "rlib"] [features] +backtraces = ["cosmwasm-std/backtraces"] # use library feature to disable all instantiate/execute/query exports library = [] @@ -22,15 +23,15 @@ cw2 = { workspace = true } cw3 = { workspace = true } cw3-fixed-multisig = { workspace = true, features = ["library"] } cw4 = { workspace = true } +cw20 = { workspace = true } cw-storage-plus = { workspace = true } cosmwasm-std = { workspace = true } schemars = "0.8.1" -serde = { version = "1.0.103", default-features = false, features = ["derive"] } nym-group-contract-common = { path = "../../../common/cosmwasm-smart-contracts/group-contract" } nym-multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts/multisig-contract" } [dev-dependencies] -cosmwasm-schema = { version = "1.0.0" } -cw4-group = { path = "../cw4-group", version = "0.13.4" } +cw4-group = { path = "../cw4-group", version = "1.0.0" } cw-multi-test = { workspace = true } +cw20-base = "1.0.0" diff --git a/contracts/multisig/cw3-flex-multisig/src/contract.rs b/contracts/multisig/cw3-flex-multisig/src/contract.rs index 24c460b0a2..9a0c8eacad 100644 --- a/contracts/multisig/cw3-flex-multisig/src/contract.rs +++ b/contracts/multisig/cw3-flex-multisig/src/contract.rs @@ -8,18 +8,19 @@ use cosmwasm_std::{ }; use cw2::set_contract_version; + use cw3::{ - ProposalListResponse, ProposalResponse, Status, Vote, VoteInfo, VoteListResponse, VoteResponse, - VoterDetail, VoterListResponse, VoterResponse, + Ballot, Proposal, ProposalListResponse, ProposalResponse, Status, Vote, VoteInfo, + VoteListResponse, VoteResponse, VoterDetail, VoterListResponse, VoterResponse, Votes, }; -use cw3_fixed_multisig::state::{next_id, Ballot, Proposal, Votes, BALLOTS, PROPOSALS}; +use cw3_fixed_multisig::state::{next_id, BALLOTS, PROPOSALS}; use cw4::{Cw4Contract, MemberChangedHookMsg, MemberDiff}; use cw_storage_plus::Bound; use cw_utils::{maybe_addr, Expiration, ThresholdResponse}; -use crate::state::{Config, CONFIG}; use nym_multisig_contract_common::error::ContractError; use nym_multisig_contract_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; +use nym_multisig_contract_common::state::{Config, CONFIG}; // version info for migration info const CONTRACT_NAME: &str = "crates.io:cw3-flex-multisig"; @@ -46,6 +47,11 @@ pub fn instantiate( let total_weight = group_addr.total_weight(&deps.querier)?; msg.threshold.validate(total_weight)?; + let proposal_deposit = msg + .proposal_deposit + .map(|deposit| deposit.into_checked(deps.as_ref())) + .transpose()?; + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; let cfg = Config { @@ -54,21 +60,14 @@ pub fn instantiate( group_addr, coconut_bandwidth_addr, coconut_dkg_addr, + executor: msg.executor, + proposal_deposit, }; CONFIG.save(deps.storage, &cfg)?; Ok(Response::default()) } -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(deps: DepsMut<'_>, _env: Env, msg: MigrateMsg) -> Result { - let mut cfg = CONFIG.load(deps.storage)?; - cfg.coconut_bandwidth_addr = deps.api.addr_validate(&msg.coconut_bandwidth_address)?; - cfg.coconut_dkg_addr = deps.api.addr_validate(&msg.coconut_dkg_address)?; - CONFIG.save(deps.storage, &cfg)?; - Ok(Default::default()) -} - #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( deps: DepsMut, @@ -105,6 +104,11 @@ pub fn execute_propose( // only members of the multisig can create a proposal let cfg = CONFIG.load(deps.storage)?; + // Check that the native deposit was paid (as needed). + if let Some(deposit) = cfg.proposal_deposit.as_ref() { + deposit.check_native_deposit_paid(&info)?; + } + // Only the coconut bandwidth or dkg contracts can create proposals if info.sender != cfg.coconut_bandwidth_addr && info.sender != cfg.coconut_dkg_addr { return Err(ContractError::Unauthorized {}); @@ -122,6 +126,15 @@ pub fn execute_propose( return Err(ContractError::WrongExpiration {}); } + // Take the cw20 token deposit, if required. We do this before + // creating the proposal struct below so that we can avoid a clone + // and move the loaded deposit info into it. + let take_deposit_msg = if let Some(deposit_info) = cfg.proposal_deposit.as_ref() { + deposit_info.get_take_deposit_messages(&info.sender, &env.contract.address)? + } else { + vec![] + }; + // create a proposal let mut prop = Proposal { title, @@ -133,6 +146,8 @@ pub fn execute_propose( votes: Votes::yes(vote_power), threshold: cfg.threshold, total_weight: cfg.group_addr.total_weight(&deps.querier)?, + proposer: info.sender.clone(), + deposit: cfg.proposal_deposit, }; prop.update_status(&env.block); let id = next_id(deps.storage)?; @@ -146,6 +161,7 @@ pub fn execute_propose( BALLOTS.save(deps.storage, (id, &info.sender), &ballot)?; Ok(Response::new() + .add_messages(take_deposit_msg) .add_attribute("action", "propose") .add_attribute("sender", info.sender) .add_attribute("proposal_id", id.to_string()) @@ -164,9 +180,11 @@ pub fn execute_vote( // ensure proposal exists and can be voted on let mut prop = PROPOSALS.load(deps.storage, proposal_id)?; - if prop.status != Status::Open { + // Allow voting on Passed and Rejected proposals too, + if ![Status::Open, Status::Passed, Status::Rejected].contains(&prop.status) { return Err(ContractError::NotOpen {}); } + // if they are not expired if prop.expires.is_expired(&env.block) { return Err(ContractError::Expired {}); } @@ -206,21 +224,31 @@ pub fn execute_execute( info: MessageInfo, proposal_id: u64, ) -> Result { - // anyone can trigger this if the vote passed - let mut prop = PROPOSALS.load(deps.storage, proposal_id)?; // we allow execution even after the proposal "expiration" as long as all vote come in before // that point. If it was approved on time, it can be executed any time. - if prop.current_status(&env.block) != Status::Passed { + prop.update_status(&env.block); + if prop.status != Status::Passed { return Err(ContractError::WrongExecuteStatus {}); } + let cfg = CONFIG.load(deps.storage)?; + cfg.authorize(&deps.querier, &info.sender)?; + // set it to executed prop.status = Status::Executed; PROPOSALS.save(deps.storage, proposal_id, &prop)?; + // Unconditionally refund here. + let response = match prop.deposit { + Some(deposit) => { + Response::new().add_message(deposit.get_return_deposit_message(&prop.proposer)?) + } + None => Response::new(), + }; + // dispatch all proposed messages - Ok(Response::new() + Ok(response .add_messages(prop.msgs) .add_attribute("action", "execute") .add_attribute("sender", info.sender) @@ -236,10 +264,11 @@ pub fn execute_close( // anyone can trigger this if the vote passed let mut prop = PROPOSALS.load(deps.storage, proposal_id)?; - if [Status::Executed, Status::Rejected, Status::Passed] - .iter() - .any(|x| *x == prop.status) - { + if [Status::Executed, Status::Rejected, Status::Passed].contains(&prop.status) { + return Err(ContractError::WrongCloseStatus {}); + } + // Avoid closing of Passed due to expiration proposals + if prop.current_status(&env.block) == Status::Passed { return Err(ContractError::WrongCloseStatus {}); } if !prop.expires.is_expired(&env.block) { @@ -250,7 +279,15 @@ pub fn execute_close( prop.status = Status::Rejected; PROPOSALS.save(deps.storage, proposal_id, &prop)?; - Ok(Response::new() + // Refund the deposit if we have been configured to do so. + let mut response = Response::new(); + if let Some(deposit) = prop.deposit { + if deposit.refund_failed_proposals { + response = response.add_message(deposit.get_return_deposit_message(&prop.proposer)?) + } + } + + Ok(response .add_attribute("action", "close") .add_attribute("sender", info.sender) .add_attribute("proposal_id", proposal_id.to_string())) @@ -294,6 +331,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { QueryMsg::ListVoters { start_after, limit } => { to_binary(&list_voters(deps, start_after, limit)?) } + QueryMsg::Config {} => to_binary(&query_config(deps)?), } } @@ -303,6 +341,10 @@ fn query_threshold(deps: Deps) -> StdResult { Ok(cfg.threshold.to_response(total_weight)) } +fn query_config(deps: Deps) -> StdResult { + CONFIG.load(deps.storage) +} + fn query_proposal(deps: Deps, env: Env, id: u64) -> StdResult { let prop = PROPOSALS.load(deps.storage, id)?; let status = prop.current_status(&env.block); @@ -314,6 +356,8 @@ fn query_proposal(deps: Deps, env: Env, id: u64) -> StdResult msgs: prop.msgs, status, expires: prop.expires, + proposer: prop.proposer, + deposit: prop.deposit, threshold, }) } @@ -370,6 +414,8 @@ fn map_proposal( msgs: prop.msgs, status, expires: prop.expires, + deposit: prop.deposit, + proposer: prop.proposer, threshold, } }) @@ -440,15 +486,26 @@ fn list_voters( Ok(VoterListResponse { voters }) } +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(deps: DepsMut<'_>, _env: Env, msg: MigrateMsg) -> Result { + let mut cfg = CONFIG.load(deps.storage)?; + cfg.coconut_bandwidth_addr = deps.api.addr_validate(&msg.coconut_bandwidth_address)?; + cfg.coconut_dkg_addr = deps.api.addr_validate(&msg.coconut_dkg_address)?; + CONFIG.save(deps.storage, &cfg)?; + Ok(Default::default()) +} + #[cfg(test)] mod tests { - use cosmwasm_std::{coin, coins, Addr, BankMsg, Coin, Decimal, Timestamp}; + use cosmwasm_std::{coin, coins, Addr, BankMsg, Coin, Decimal, Timestamp, Uint128}; use cw2::{query_contract_info, ContractVersion}; + use cw20::{Cw20Coin, UncheckedDenom}; + use cw3::{DepositError, UncheckedDepositInfo}; use cw4::{Cw4ExecuteMsg, Member}; use cw4_group::helpers::Cw4GroupContract; use cw_multi_test::{ - next_block, App, AppBuilder, AppResponse, Contract, ContractWrapper, Executor, + next_block, App, AppBuilder, BankSudo, Contract, ContractWrapper, Executor, SudoMsg, }; use cw_utils::{Duration, Threshold}; @@ -461,9 +518,8 @@ mod tests { const VOTER4: &str = "voter0004"; const VOTER5: &str = "voter0005"; const SOMEBODY: &str = "somebody"; - const TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = - "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; - const TEST_COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; + const BANDWIDTH_CONTRACT: &str = "coconut_bandwidth_addr"; + const DKG_CONTRACT: &str = "coconut_dkg_addr"; fn member>(addr: T, weight: u64) -> Member { Member { @@ -490,6 +546,15 @@ mod tests { Box::new(contract) } + fn contract_cw20() -> Box> { + let contract = ContractWrapper::new( + cw20_base::contract::execute, + cw20_base::contract::instantiate, + cw20_base::contract::query, + ); + Box::new(contract) + } + fn mock_app(init_funds: &[Coin]) -> App { AppBuilder::new().build(|router, _, storage| { router @@ -510,40 +575,27 @@ mod tests { .unwrap() } - fn propose_and_vote( - app: &mut App, - flex_addr: Addr, - proposal: ExecuteMsg, - voter: &str, - ) -> AppResponse { - let proposer = TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(); - let res = app - .execute_contract(Addr::unchecked(proposer), flex_addr.clone(), &proposal, &[]) - .unwrap(); - - let yes_vote = ExecuteMsg::Vote { - proposal_id: res.custom_attrs(1)[2].value.parse().unwrap(), - vote: Vote::Yes, - }; - let _ = app.execute_contract(Addr::unchecked(voter), flex_addr, &yes_vote, &[]); - - res - } - + #[allow(clippy::too_many_arguments)] #[track_caller] fn instantiate_flex( app: &mut App, group: Addr, + coconut_bandwidth_contract_address: Addr, + coconut_dkg_contract_address: Addr, threshold: Threshold, max_voting_period: Duration, + executor: Option, + proposal_deposit: Option, ) -> Addr { let flex_id = app.store_code(contract_flex()); let msg = InstantiateMsg { group_addr: group.to_string(), + coconut_bandwidth_contract_address: coconut_bandwidth_contract_address.to_string(), + coconut_dkg_contract_address: coconut_dkg_contract_address.to_string(), threshold, max_voting_period, - coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), - coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(), + executor, + proposal_deposit, }; app.instantiate_contract(flex_id, Addr::unchecked(OWNER), &msg, &[], "flex", None) .unwrap() @@ -568,6 +620,8 @@ mod tests { max_voting_period, init_funds, multisig_as_group_admin, + None, + None, ) } @@ -578,11 +632,11 @@ mod tests { max_voting_period: Duration, init_funds: Vec, multisig_as_group_admin: bool, + executor: Option, + proposal_deposit: Option, ) -> (Addr, Addr) { - let coconut_bandwidth_contract = TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(); - // 1. Instantiate group contract with members (and OWNER as admin) and coconut bandwidth contract + // 1. Instantiate group contract with members (and OWNER as admin) let members = vec![ - member(coconut_bandwidth_contract, 0), member(OWNER, 0), member(VOTER1, 1), member(VOTER2, 2), @@ -594,7 +648,16 @@ mod tests { app.update_block(next_block); // 2. Set up Multisig backed by this group - let flex_addr = instantiate_flex(app, group_addr.clone(), threshold, max_voting_period); + let flex_addr = instantiate_flex( + app, + group_addr.clone(), + Addr::unchecked(BANDWIDTH_CONTRACT), + Addr::unchecked(DKG_CONTRACT), + threshold, + max_voting_period, + executor, + proposal_deposit, + ); app.update_block(next_block); // 3. (Optional) Set the multisig as the group owner @@ -641,6 +704,16 @@ mod tests { } } + fn text_proposal() -> ExecuteMsg { + let (_, title, description) = proposal_info(); + ExecuteMsg::Propose { + title, + description, + msgs: vec![], + latest: None, + } + } + #[test] fn test_instantiate_works() { let mut app = mock_app(&[]); @@ -654,13 +727,15 @@ mod tests { // Zero required weight fails let instantiate_msg = InstantiateMsg { group_addr: group_addr.to_string(), + coconut_bandwidth_contract_address: BANDWIDTH_CONTRACT.to_string(), + coconut_dkg_contract_address: DKG_CONTRACT.to_string(), threshold: Threshold::ThresholdQuorum { threshold: Decimal::zero(), quorum: Decimal::percent(1), }, max_voting_period, - coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), - coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(), + executor: None, + proposal_deposit: None, }; let err = app .instantiate_contract( @@ -680,10 +755,12 @@ mod tests { // Total weight less than required weight not allowed let instantiate_msg = InstantiateMsg { group_addr: group_addr.to_string(), + coconut_bandwidth_contract_address: BANDWIDTH_CONTRACT.to_string(), + coconut_dkg_contract_address: DKG_CONTRACT.to_string(), threshold: Threshold::AbsoluteCount { weight: 100 }, max_voting_period, - coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), - coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(), + executor: None, + proposal_deposit: None, }; let err = app .instantiate_contract( @@ -703,10 +780,12 @@ mod tests { // All valid let instantiate_msg = InstantiateMsg { group_addr: group_addr.to_string(), + coconut_bandwidth_contract_address: BANDWIDTH_CONTRACT.to_string(), + coconut_dkg_contract_address: DKG_CONTRACT.to_string(), threshold: Threshold::AbsoluteCount { weight: 1 }, max_voting_period, - coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(), - coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(), + executor: None, + proposal_deposit: None, }; let flex_addr = app .instantiate_contract( @@ -720,7 +799,7 @@ mod tests { .unwrap(); // Verify contract version set properly - let version = query_contract_info(&app, flex_addr.clone()).unwrap(); + let version = query_contract_info(&app.wrap(), flex_addr.clone()).unwrap(); assert_eq!( ContractVersion { contract: CONTRACT_NAME.to_string(), @@ -760,7 +839,7 @@ mod tests { setup_test_case_fixed(&mut app, required_weight, voting_period, init_funds, false); let proposal = pay_somebody_proposal(); - // Only voters can propose + // Only special addresses can propose let err = app .execute_contract(Addr::unchecked(SOMEBODY), flex_addr.clone(), &proposal, &[]) .unwrap_err(); @@ -779,13 +858,44 @@ mod tests { }; let err = app .execute_contract( - Addr::unchecked(TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string()), - flex_addr, + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), &proposal_wrong_exp, &[], ) .unwrap_err(); assert_eq!(ContractError::WrongExpiration {}, err.downcast().unwrap()); + + // Proposal from special address works + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); + assert_eq!( + res.custom_attrs(1), + [ + ("action", "propose"), + ("sender", BANDWIDTH_CONTRACT), + ("proposal_id", "1"), + ("status", "Open"), + ], + ); + let res = app + .execute_contract(Addr::unchecked(DKG_CONTRACT), flex_addr, &proposal, &[]) + .unwrap(); + assert_eq!( + res.custom_attrs(1), + [ + ("action", "propose"), + ("sender", DKG_CONTRACT), + ("proposal_id", "2"), + ("status", "Open"), + ], + ); } fn get_tally(app: &App, flex_addr: &str, proposal_id: u64) -> u64 { @@ -826,49 +936,6 @@ mod tests { } } - #[test] - fn test_proposer_limited_to_coconut_bandwidth() { - let init_funds = coins(10, "BTC"); - let mut app = mock_app(&init_funds); - - let voting_period = Duration::Time(2000000); - let threshold = Threshold::ThresholdQuorum { - threshold: Decimal::percent(80), - quorum: Decimal::percent(20), - }; - let (flex_addr, _) = setup_test_case(&mut app, threshold, voting_period, init_funds, false); - let proposal = pay_somebody_proposal(); - - let err = app - .execute_contract(Addr::unchecked(OWNER), flex_addr.clone(), &proposal, &[]) - .unwrap_err(); - assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap()); - - let err = app - .execute_contract(Addr::unchecked(VOTER1), flex_addr.clone(), &proposal, &[]) - .unwrap_err(); - assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap()); - - let err = app - .execute_contract(Addr::unchecked(SOMEBODY), flex_addr.clone(), &proposal, &[]) - .unwrap_err(); - assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap()); - - let proposer = TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(); - let res = app - .execute_contract(Addr::unchecked(&proposer), flex_addr, &proposal, &[]) - .unwrap(); - assert_eq!( - res.custom_attrs(1), - [ - ("action", "propose"), - ("sender", &proposer), - ("proposal_id", "1"), - ("status", "Open"), - ], - ); - } - #[test] fn test_proposal_queries() { let init_funds = coins(10, "BTC"); @@ -879,26 +946,73 @@ mod tests { threshold: Decimal::percent(80), quorum: Decimal::percent(20), }; - let (flex_addr, _) = setup_test_case(&mut app, threshold, voting_period, init_funds, false); + let (flex_addr, _) = setup_test_case( + &mut app, + threshold, + voting_period, + init_funds, + false, + None, + None, + ); // create proposal with 1 vote power let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER1); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); let proposal_id1: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id: proposal_id1, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER1), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); // another proposal immediately passes app.update_block(next_block); let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER4); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); let proposal_id2: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id: proposal_id2, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER4), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); // expire them both app.update_block(expire(voting_period)); // add one more open proposal, 2 votes let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER2); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); let proposal_id3: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id: proposal_id3, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER2), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); let proposed_at = app.block_info(); // next block, let's query them all... make sure status is properly updated (1 should be rejected in query) @@ -954,6 +1068,8 @@ mod tests { threshold: Decimal::percent(80), quorum: Decimal::percent(20), }, + proposer: Addr::unchecked(BANDWIDTH_CONTRACT), + deposit: None, }; assert_eq!(&expected, &res.proposals[0]); } @@ -968,11 +1084,26 @@ mod tests { quorum: Decimal::percent(1), }; let voting_period = Duration::Time(2000000); - let (flex_addr, _) = setup_test_case(&mut app, threshold, voting_period, init_funds, false); + let (flex_addr, _) = setup_test_case( + &mut app, + threshold, + voting_period, + init_funds, + false, + None, + None, + ); // create proposal with 0 vote power let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, OWNER); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); @@ -1066,13 +1197,38 @@ mod tests { ], ); - // non-Open proposals cannot be voted - let err = app + // Passed proposals can still be voted (while they are not expired or executed) + let res = app .execute_contract(Addr::unchecked(VOTER5), flex_addr.clone(), &yes_vote, &[]) - .unwrap_err(); - assert_eq!(ContractError::NotOpen {}, err.downcast().unwrap()); + .unwrap(); + // Verify + assert_eq!( + res.custom_attrs(1), + [ + ("action", "vote"), + ("sender", VOTER5), + ("proposal_id", proposal_id.to_string().as_str()), + ("status", "Passed") + ] + ); // query individual votes + // initial (with 0 weight) + let voter = BANDWIDTH_CONTRACT.into(); + let vote: VoteResponse = app + .wrap() + .query_wasm_smart(&flex_addr, &QueryMsg::Vote { proposal_id, voter }) + .unwrap(); + assert_eq!( + vote.vote.unwrap(), + VoteInfo { + proposal_id, + voter: BANDWIDTH_CONTRACT.into(), + vote: Vote::Yes, + weight: 0 + } + ); + // nay sayer let voter = VOTER2.into(); let vote: VoteResponse = app @@ -1090,7 +1246,7 @@ mod tests { ); // non-voter - let voter = VOTER5.into(); + let voter = SOMEBODY.into(); let vote: VoteResponse = app .wrap() .query_wasm_smart(&flex_addr, &QueryMsg::Vote { proposal_id, voter }) @@ -1099,7 +1255,14 @@ mod tests { // create proposal with 0 vote power let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, OWNER); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); @@ -1115,7 +1278,7 @@ mod tests { // Powerful voter opposes it, so it rejects let res = app - .execute_contract(Addr::unchecked(VOTER4), flex_addr, &no_vote, &[]) + .execute_contract(Addr::unchecked(VOTER4), flex_addr.clone(), &no_vote, &[]) .unwrap(); assert_eq!( @@ -1127,6 +1290,25 @@ mod tests { ("status", "Rejected"), ], ); + + // Rejected proposals can still be voted (while they are not expired) + let yes_vote = ExecuteMsg::Vote { + proposal_id, + vote: Vote::Yes, + }; + let res = app + .execute_contract(Addr::unchecked(VOTER5), flex_addr, &yes_vote, &[]) + .unwrap(); + + assert_eq!( + res.custom_attrs(1), + [ + ("action", "vote"), + ("sender", VOTER5), + ("proposal_id", proposal_id.to_string().as_str()), + ("status", "Rejected"), + ], + ); } #[test] @@ -1139,7 +1321,15 @@ mod tests { quorum: Decimal::percent(1), }; let voting_period = Duration::Time(2000000); - let (flex_addr, _) = setup_test_case(&mut app, threshold, voting_period, init_funds, true); + let (flex_addr, _) = setup_test_case( + &mut app, + threshold, + voting_period, + init_funds, + true, + None, + None, + ); // ensure we have cash to cover the proposal let contract_bal = app.wrap().query_balance(&flex_addr, "BTC").unwrap(); @@ -1147,7 +1337,14 @@ mod tests { // create proposal with 0 vote power let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, OWNER); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); @@ -1227,6 +1424,142 @@ mod tests { ); } + #[test] + fn execute_with_executor_member() { + let init_funds = coins(10, "BTC"); + let mut app = mock_app(&init_funds); + + let threshold = Threshold::ThresholdQuorum { + threshold: Decimal::percent(51), + quorum: Decimal::percent(1), + }; + let voting_period = Duration::Time(2000000); + let (flex_addr, _) = setup_test_case( + &mut app, + threshold, + voting_period, + init_funds, + true, + Some(nym_multisig_contract_common::state::Executor::Member), // set executor as Member of voting group + None, + ); + + // create proposal with 0 vote power + let proposal = pay_somebody_proposal(); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); + + // Get the proposal id from the logs + let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + + // Vote it, so it passes + let vote = ExecuteMsg::Vote { + proposal_id, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER4), flex_addr.clone(), &vote, &[]) + .unwrap(); + + let execution = ExecuteMsg::Execute { proposal_id }; + let err = app + .execute_contract( + Addr::unchecked(Addr::unchecked("anyone")), // anyone is not allowed to execute + flex_addr.clone(), + &execution, + &[], + ) + .unwrap_err(); + assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap()); + + app.execute_contract( + Addr::unchecked(Addr::unchecked(VOTER2)), // member of voting group is allowed to execute + flex_addr, + &execution, + &[], + ) + .unwrap(); + } + + #[test] + fn execute_with_executor_only() { + let init_funds = coins(10, "BTC"); + let mut app = mock_app(&init_funds); + + let threshold = Threshold::ThresholdQuorum { + threshold: Decimal::percent(51), + quorum: Decimal::percent(1), + }; + let voting_period = Duration::Time(2000000); + let (flex_addr, _) = setup_test_case( + &mut app, + threshold, + voting_period, + init_funds, + true, + Some(nym_multisig_contract_common::state::Executor::Only( + Addr::unchecked(VOTER3), + )), // only VOTER3 can execute proposal + None, + ); + + // create proposal with 0 vote power + let proposal = pay_somebody_proposal(); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); + + // Get the proposal id from the logs + let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + + // Vote it, so it passes + let vote = ExecuteMsg::Vote { + proposal_id, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER4), flex_addr.clone(), &vote, &[]) + .unwrap(); + + let execution = ExecuteMsg::Execute { proposal_id }; + let err = app + .execute_contract( + Addr::unchecked(Addr::unchecked("anyone")), // anyone is not allowed to execute + flex_addr.clone(), + &execution, + &[], + ) + .unwrap_err(); + assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap()); + + let err = app + .execute_contract( + Addr::unchecked(Addr::unchecked(VOTER1)), // VOTER1 is not allowed to execute + flex_addr.clone(), + &execution, + &[], + ) + .unwrap_err(); + assert_eq!(ContractError::Unauthorized {}, err.downcast().unwrap()); + + app.execute_contract( + Addr::unchecked(Addr::unchecked(VOTER3)), // VOTER3 is allowed to execute + flex_addr, + &execution, + &[], + ) + .unwrap(); + } + #[test] fn proposal_pass_on_expiration() { let init_funds = coins(10, "BTC"); @@ -1243,6 +1576,8 @@ mod tests { Duration::Time(voting_period), init_funds, true, + None, + None, ); // ensure we have cash to cover the proposal @@ -1251,7 +1586,14 @@ mod tests { // create proposal with 0 vote power let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, OWNER); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); @@ -1287,6 +1629,17 @@ mod tests { .unwrap(); assert_eq!(prop.status, Status::Passed); + // Closing should NOT be possible + let err = app + .execute_contract( + Addr::unchecked(SOMEBODY), + flex_addr.clone(), + &ExecuteMsg::Close { proposal_id }, + &[], + ) + .unwrap_err(); + assert_eq!(ContractError::WrongCloseStatus {}, err.downcast().unwrap()); + // Execution should now be possible. let res = app .execute_contract( @@ -1316,11 +1669,26 @@ mod tests { quorum: Decimal::percent(1), }; let voting_period = Duration::Height(2000000); - let (flex_addr, _) = setup_test_case(&mut app, threshold, voting_period, init_funds, true); + let (flex_addr, _) = setup_test_case( + &mut app, + threshold, + voting_period, + init_funds, + true, + None, + None, + ); // create proposal with 0 vote power let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, OWNER); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); @@ -1365,14 +1733,34 @@ mod tests { quorum: Decimal::percent(1), }; let voting_period = Duration::Time(20000); - let (flex_addr, group_addr) = - setup_test_case(&mut app, threshold, voting_period, init_funds, false); + let (flex_addr, group_addr) = setup_test_case( + &mut app, + threshold, + voting_period, + init_funds, + false, + None, + None, + ); // VOTER1 starts a proposal to send some tokens (1/4 votes) let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER1); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER1), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); let prop_status = |app: &App, proposal_id: u64| -> Status { let query_prop = QueryMsg::Proposal { proposal_id }; let prop: ProposalResponse = app @@ -1430,9 +1818,22 @@ mod tests { // make a second proposal let proposal2 = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal2, VOTER1); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal2, + &[], + ) + .unwrap(); // Get the proposal id from the logs let proposal_id2: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id: proposal_id2, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER1), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); // VOTER2 can pass this alone with the updated vote (newer height ignores snapshot) let yes_vote = ExecuteMsg::Vote { @@ -1500,18 +1901,44 @@ mod tests { msgs: vec![update_msg], latest: None, }; - let res = propose_and_vote(&mut app, flex_addr.clone(), update_proposal, VOTER1); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &update_proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let update_proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id: update_proposal_id, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER1), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); // next block... app.update_block(|b| b.height += 1); // VOTER1 starts a proposal to send some tokens let cash_proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), cash_proposal, VOTER1); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &cash_proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let cash_proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id: cash_proposal_id, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER1), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); assert_ne!(cash_proposal_id, update_proposal_id); // query proposal state @@ -1593,14 +2020,34 @@ mod tests { quorum: Decimal::percent(1), }; let voting_period = Duration::Time(20000); - let (flex_addr, group_addr) = - setup_test_case(&mut app, threshold, voting_period, init_funds, false); + let (flex_addr, group_addr) = setup_test_case( + &mut app, + threshold, + voting_period, + init_funds, + false, + None, + None, + ); // VOTER3 starts a proposal to send some tokens (3/12 votes) let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER3); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER3), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); let prop_status = |app: &App| -> Status { let query_prop = QueryMsg::Proposal { proposal_id }; let prop: ProposalResponse = app @@ -1640,9 +2087,22 @@ mod tests { // new proposal can be passed single-handedly by newbie let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, newbie); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let proposal_id2: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id: proposal_id2, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(newbie), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); // check proposal2 status let query_prop = QueryMsg::Proposal { @@ -1673,13 +2133,28 @@ mod tests { voting_period, init_funds, false, + None, + None, ); // VOTER3 starts a proposal to send some tokens (3 votes) let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER3); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER3), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); let prop_status = |app: &App| -> Status { let query_prop = QueryMsg::Proposal { proposal_id }; let prop: ProposalResponse = app @@ -1741,13 +2216,28 @@ mod tests { voting_period, init_funds, false, + None, + None, ); // create proposal let proposal = pay_somebody_proposal(); - let res = propose_and_vote(&mut app, flex_addr.clone(), proposal, VOTER5); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); // Get the proposal id from the logs let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER5), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); let prop_status = |app: &App| -> Status { let query_prop = QueryMsg::Proposal { proposal_id }; let prop: ProposalResponse = app @@ -1778,4 +2268,490 @@ mod tests { .unwrap(); assert_eq!(prop_status(&app), Status::Passed); } + + #[test] + fn test_instantiate_with_invalid_deposit() { + let mut app = App::default(); + + let flex_id = app.store_code(contract_flex()); + + let group_addr = instantiate_group( + &mut app, + vec![Member { + addr: OWNER.to_string(), + weight: 10, + }], + ); + + // Instantiate with an invalid cw20 token. + let instantiate = InstantiateMsg { + group_addr: group_addr.to_string(), + coconut_bandwidth_contract_address: BANDWIDTH_CONTRACT.to_string(), + coconut_dkg_contract_address: DKG_CONTRACT.to_string(), + threshold: Threshold::AbsoluteCount { weight: 10 }, + max_voting_period: Duration::Time(10), + executor: None, + proposal_deposit: Some(UncheckedDepositInfo { + amount: Uint128::new(1), + refund_failed_proposals: true, + denom: UncheckedDenom::Cw20(group_addr.to_string()), + }), + }; + + let err: ContractError = app + .instantiate_contract( + flex_id, + Addr::unchecked(OWNER), + &instantiate, + &[], + "Bad cw20", + None, + ) + .unwrap_err() + .downcast() + .unwrap(); + + assert_eq!(err, ContractError::Deposit(DepositError::InvalidCw20 {})); + + // Instantiate with a zero amount. + let instantiate = InstantiateMsg { + group_addr: group_addr.to_string(), + coconut_bandwidth_contract_address: BANDWIDTH_CONTRACT.to_string(), + coconut_dkg_contract_address: DKG_CONTRACT.to_string(), + threshold: Threshold::AbsoluteCount { weight: 10 }, + max_voting_period: Duration::Time(10), + executor: None, + proposal_deposit: Some(UncheckedDepositInfo { + amount: Uint128::zero(), + refund_failed_proposals: true, + denom: UncheckedDenom::Native("native".to_string()), + }), + }; + + let err: ContractError = app + .instantiate_contract( + flex_id, + Addr::unchecked(OWNER), + &instantiate, + &[], + "Bad cw20", + None, + ) + .unwrap_err() + .downcast() + .unwrap(); + + assert_eq!(err, ContractError::Deposit(DepositError::ZeroDeposit {})) + } + + #[test] + fn test_cw20_proposal_deposit() { + let mut app = App::default(); + + let cw20_id = app.store_code(contract_cw20()); + + let cw20_addr = app + .instantiate_contract( + cw20_id, + Addr::unchecked(OWNER), + &cw20_base::msg::InstantiateMsg { + name: "Token".to_string(), + symbol: "TOKEN".to_string(), + decimals: 6, + initial_balances: vec![ + Cw20Coin { + address: VOTER4.to_string(), + amount: Uint128::new(10), + }, + Cw20Coin { + address: BANDWIDTH_CONTRACT.to_string(), + amount: Uint128::new(10), + }, + Cw20Coin { + address: OWNER.to_string(), + amount: Uint128::new(10), + }, + ], + mint: None, + marketing: None, + }, + &[], + "Token", + None, + ) + .unwrap(); + + let (flex_addr, _) = setup_test_case( + &mut app, + Threshold::AbsoluteCount { weight: 10 }, + Duration::Height(10), + vec![], + true, + None, + Some(UncheckedDepositInfo { + amount: Uint128::new(10), + denom: UncheckedDenom::Cw20(cw20_addr.to_string()), + refund_failed_proposals: true, + }), + ); + + app.execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + cw20_addr.clone(), + &cw20::Cw20ExecuteMsg::IncreaseAllowance { + spender: flex_addr.to_string(), + amount: Uint128::new(10), + expires: None, + }, + &[], + ) + .unwrap(); + + // Make a proposal that will pass. + let proposal = text_proposal(); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); + // Get the proposal id from the logs + let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER4), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); + + // Make sure the deposit was transfered. + let balance: cw20::BalanceResponse = app + .wrap() + .query_wasm_smart( + cw20_addr.clone(), + &cw20::Cw20QueryMsg::Balance { + address: BANDWIDTH_CONTRACT.to_string(), + }, + ) + .unwrap(); + assert_eq!(balance.balance, Uint128::zero()); + + let balance: cw20::BalanceResponse = app + .wrap() + .query_wasm_smart( + cw20_addr.clone(), + &cw20::Cw20QueryMsg::Balance { + address: flex_addr.to_string(), + }, + ) + .unwrap(); + assert_eq!(balance.balance, Uint128::new(10)); + + app.execute_contract( + Addr::unchecked(VOTER4), + flex_addr.clone(), + &ExecuteMsg::Execute { proposal_id: 1 }, + &[], + ) + .unwrap(); + + // Make sure the deposit was returned. + let balance: cw20::BalanceResponse = app + .wrap() + .query_wasm_smart( + cw20_addr.clone(), + &cw20::Cw20QueryMsg::Balance { + address: VOTER4.to_string(), + }, + ) + .unwrap(); + assert_eq!(balance.balance, Uint128::new(10)); + + let balance: cw20::BalanceResponse = app + .wrap() + .query_wasm_smart( + cw20_addr.clone(), + &cw20::Cw20QueryMsg::Balance { + address: flex_addr.to_string(), + }, + ) + .unwrap(); + assert_eq!(balance.balance, Uint128::zero()); + + app.execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + cw20_addr.clone(), + &cw20::Cw20ExecuteMsg::IncreaseAllowance { + spender: flex_addr.to_string(), + amount: Uint128::new(10), + expires: None, + }, + &[], + ) + .unwrap(); + + // Make a proposal that fails. + let proposal = text_proposal(); + app.execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[], + ) + .unwrap(); + + // Check that the deposit was transfered. + let balance: cw20::BalanceResponse = app + .wrap() + .query_wasm_smart( + cw20_addr.clone(), + &cw20::Cw20QueryMsg::Balance { + address: flex_addr.to_string(), + }, + ) + .unwrap(); + assert_eq!(balance.balance, Uint128::new(10)); + + // Fail the proposal. + app.execute_contract( + Addr::unchecked(VOTER4), + flex_addr.clone(), + &ExecuteMsg::Vote { + proposal_id: 2, + vote: Vote::No, + }, + &[], + ) + .unwrap(); + + // Expire the proposal. + app.update_block(|b| b.height += 10); + + app.execute_contract( + Addr::unchecked(VOTER4), + flex_addr, + &ExecuteMsg::Close { proposal_id: 2 }, + &[], + ) + .unwrap(); + + // Make sure the deposit was returned despite the proposal failing. + let balance: cw20::BalanceResponse = app + .wrap() + .query_wasm_smart( + cw20_addr, + &cw20::Cw20QueryMsg::Balance { + address: VOTER4.to_string(), + }, + ) + .unwrap(); + assert_eq!(balance.balance, Uint128::new(10)); + } + + #[test] + fn proposal_deposit_no_failed_refunds() { + let mut app = App::default(); + + let (flex_addr, _) = setup_test_case( + &mut app, + Threshold::AbsoluteCount { weight: 10 }, + Duration::Height(10), + vec![], + true, + None, + Some(UncheckedDepositInfo { + amount: Uint128::new(10), + denom: UncheckedDenom::Native("TOKEN".to_string()), + refund_failed_proposals: false, + }), + ); + + app.sudo(SudoMsg::Bank(BankSudo::Mint { + to_address: BANDWIDTH_CONTRACT.to_string(), + amount: vec![Coin { + amount: Uint128::new(10), + denom: "TOKEN".to_string(), + }], + })) + .unwrap(); + + // Make a proposal that fails. + let proposal = text_proposal(); + app.execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[Coin { + amount: Uint128::new(10), + denom: "TOKEN".to_string(), + }], + ) + .unwrap(); + + // Check that the deposit was transfered. + let balance = app + .wrap() + .query_balance(OWNER, "TOKEN".to_string()) + .unwrap(); + assert_eq!(balance.amount, Uint128::zero()); + + // Fail the proposal. + app.execute_contract( + Addr::unchecked(VOTER4), + flex_addr.clone(), + &ExecuteMsg::Vote { + proposal_id: 1, + vote: Vote::No, + }, + &[], + ) + .unwrap(); + + // Expire the proposal. + app.update_block(|b| b.height += 10); + + app.execute_contract( + Addr::unchecked(VOTER4), + flex_addr, + &ExecuteMsg::Close { proposal_id: 1 }, + &[], + ) + .unwrap(); + + // Check that the deposit wasn't returned. + let balance = app + .wrap() + .query_balance(OWNER, "TOKEN".to_string()) + .unwrap(); + assert_eq!(balance.amount, Uint128::zero()); + } + + #[test] + fn test_native_proposal_deposit() { + let mut app = App::default(); + + app.sudo(SudoMsg::Bank(BankSudo::Mint { + to_address: VOTER4.to_string(), + amount: vec![Coin { + amount: Uint128::new(10), + denom: "TOKEN".to_string(), + }], + })) + .unwrap(); + + app.sudo(SudoMsg::Bank(BankSudo::Mint { + to_address: BANDWIDTH_CONTRACT.to_string(), + amount: vec![Coin { + amount: Uint128::new(10), + denom: "TOKEN".to_string(), + }], + })) + .unwrap(); + + let (flex_addr, _) = setup_test_case( + &mut app, + Threshold::AbsoluteCount { weight: 10 }, + Duration::Height(10), + vec![], + true, + None, + Some(UncheckedDepositInfo { + amount: Uint128::new(10), + denom: UncheckedDenom::Native("TOKEN".to_string()), + refund_failed_proposals: true, + }), + ); + + // Make a proposal that will pass. + let proposal = text_proposal(); + let res = app + .execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[Coin { + amount: Uint128::new(10), + denom: "TOKEN".to_string(), + }], + ) + .unwrap(); + // Get the proposal id from the logs + let proposal_id: u64 = res.custom_attrs(1)[2].value.parse().unwrap(); + let yes_vote = ExecuteMsg::Vote { + proposal_id, + vote: Vote::Yes, + }; + app.execute_contract(Addr::unchecked(VOTER4), flex_addr.clone(), &yes_vote, &[]) + .unwrap(); + + // Make sure the deposit was transfered. + let balance = app + .wrap() + .query_balance(flex_addr.clone(), "TOKEN") + .unwrap(); + assert_eq!(balance.amount, Uint128::new(10)); + + app.execute_contract( + Addr::unchecked(VOTER4), + flex_addr.clone(), + &ExecuteMsg::Execute { proposal_id: 1 }, + &[], + ) + .unwrap(); + + // Make sure the deposit was returned. + let balance = app.wrap().query_balance(VOTER4, "TOKEN").unwrap(); + assert_eq!(balance.amount, Uint128::new(10)); + + // Make a proposal that fails. + let proposal = text_proposal(); + app.execute_contract( + Addr::unchecked(BANDWIDTH_CONTRACT), + flex_addr.clone(), + &proposal, + &[Coin { + amount: Uint128::new(10), + denom: "TOKEN".to_string(), + }], + ) + .unwrap(); + + let balance = app + .wrap() + .query_balance(flex_addr.clone(), "TOKEN") + .unwrap(); + assert_eq!(balance.amount, Uint128::new(10)); + + // Fail the proposal. + app.execute_contract( + Addr::unchecked(VOTER4), + flex_addr.clone(), + &ExecuteMsg::Vote { + proposal_id: 2, + vote: Vote::No, + }, + &[], + ) + .unwrap(); + + // Expire the proposal. + app.update_block(|b| b.height += 10); + + app.execute_contract( + Addr::unchecked(VOTER4), + flex_addr, + &ExecuteMsg::Close { proposal_id: 2 }, + &[], + ) + .unwrap(); + + // Make sure the deposit was returned despite the proposal failing. + let balance = app + .wrap() + .query_balance(BANDWIDTH_CONTRACT, "TOKEN") + .unwrap(); + assert_eq!(balance.amount, Uint128::new(10)); + } } diff --git a/contracts/multisig/cw3-flex-multisig/src/lib.rs b/contracts/multisig/cw3-flex-multisig/src/lib.rs index 3407c199dd..bc3020f956 100644 --- a/contracts/multisig/cw3-flex-multisig/src/lib.rs +++ b/contracts/multisig/cw3-flex-multisig/src/lib.rs @@ -1,2 +1,25 @@ +/*! +This builds on [`cw3_fixed_multisig`] with a more +powerful implementation of the [cw3 spec](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw3/README.md). +It is a multisig contract that is backed by a +[cw4 (group)](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw4/README.md) contract, which independently +maintains the voter set. + +This provides 2 main advantages: + +* You can create two different multisigs with different voting thresholds + backed by the same group. Thus, you can have a 50% vote, and a 67% vote + that always use the same voter set, but can take other actions. +* TODO: It allows dynamic multisig groups. + + +In addition to the dynamic voting set, the main difference with the native +Cosmos SDK multisig, is that it aggregates the signatures on chain, with +visible proposals (like `x/gov` in the Cosmos SDK), rather than requiring +signers to share signatures off chain. + +For more information on this contract, please check out the +[README](https://github.com/CosmWasm/cw-plus/blob/main/contracts/cw3-flex-multisig/README.md). + */ + pub mod contract; -pub mod state; diff --git a/contracts/multisig/cw3-flex-multisig/src/state.rs b/contracts/multisig/cw3-flex-multisig/src/state.rs deleted file mode 100644 index 0826f1014e..0000000000 --- a/contracts/multisig/cw3-flex-multisig/src/state.rs +++ /dev/null @@ -1,20 +0,0 @@ -use cosmwasm_std::Addr; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use cw4::Cw4Contract; -use cw_storage_plus::Item; -use cw_utils::{Duration, Threshold}; - -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] -pub struct Config { - pub threshold: Threshold, - pub max_voting_period: Duration, - // Total weight and voters are queried from this contract - pub group_addr: Cw4Contract, - pub coconut_bandwidth_addr: Addr, - pub coconut_dkg_addr: Addr, -} - -// unique items -pub const CONFIG: Item = Item::new("config"); diff --git a/contracts/multisig/cw4-group/.cargo/config b/contracts/multisig/cw4-group/.cargo/config index 7d1a066c82..4c5472df3b 100644 --- a/contracts/multisig/cw4-group/.cargo/config +++ b/contracts/multisig/cw4-group/.cargo/config @@ -1,5 +1,5 @@ [alias] -wasm = "build --release --target wasm32-unknown-unknown" -wasm-debug = "build --target wasm32-unknown-unknown" +wasm = "build --release --lib --target wasm32-unknown-unknown" +wasm-debug = "build --lib --target wasm32-unknown-unknown" unit-test = "test --lib" -schema = "run --example schema" +schema = "run --bin schema" \ No newline at end of file diff --git a/contracts/multisig/cw4-group/Cargo.toml b/contracts/multisig/cw4-group/Cargo.toml index 4605ec5bcc..180ee66b9d 100644 --- a/contracts/multisig/cw4-group/Cargo.toml +++ b/contracts/multisig/cw4-group/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "cw4-group" -version = "0.13.4" +version = "1.0.0" authors = ["Ethan Frey "] -edition = "2018" +edition = "2021" description = "Simple cw4 implementation of group membership controlled by admin " license = "Apache-2.0" repository = "https://github.com/CosmWasm/cw-plus" @@ -20,6 +20,8 @@ exclude = [ crate-type = ["cdylib", "rlib"] [features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] # use library feature to disable all instantiate/execute/query exports library = [] @@ -31,10 +33,8 @@ cw2 = { workspace = true } cw4 = { workspace = true } cw-controllers = { workspace = true } cw-storage-plus = { workspace = true } +cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { version = "1.0.23" } - -[dev-dependencies] -cosmwasm-schema = { workspace = true } diff --git a/contracts/multisig/cw4-group/src/contract.rs b/contracts/multisig/cw4-group/src/contract.rs index 543388c6fa..a6069f3b23 100644 --- a/contracts/multisig/cw4-group/src/contract.rs +++ b/contracts/multisig/cw4-group/src/contract.rs @@ -2,7 +2,7 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{ attr, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response, StdResult, - SubMsg, + SubMsg, Uint64, }; use cw2::set_contract_version; use cw4::{ @@ -13,6 +13,7 @@ use cw_storage_plus::Bound; use cw_utils::maybe_addr; use crate::error::ContractError; +use crate::helpers::validate_unique_members; use crate::state::{ADMIN, HOOKS, MEMBERS, TOTAL}; use nym_group_contract_common::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; @@ -39,21 +40,25 @@ pub fn instantiate( pub fn create( mut deps: DepsMut, admin: Option, - members: Vec, + mut members: Vec, height: u64, ) -> Result<(), ContractError> { + validate_unique_members(&mut members)?; + let members = members; // let go of mutability + let admin_addr = admin .map(|admin| deps.api.addr_validate(&admin)) .transpose()?; ADMIN.set(deps.branch(), admin_addr)?; - let mut total = 0u64; + let mut total = Uint64::zero(); for member in members.into_iter() { - total += member.weight; + let member_weight = Uint64::from(member.weight); + total = total.checked_add(member_weight)?; let member_addr = deps.api.addr_validate(&member.addr)?; - MEMBERS.save(deps.storage, &member_addr, &member.weight, height)?; + MEMBERS.save(deps.storage, &member_addr, &member_weight.u64(), height)?; } - TOTAL.save(deps.storage, &total)?; + TOTAL.save(deps.storage, &total.u64(), height)?; Ok(()) } @@ -115,20 +120,23 @@ pub fn update_members( deps: DepsMut, height: u64, sender: Addr, - to_add: Vec, + mut to_add: Vec, to_remove: Vec, ) -> Result { + validate_unique_members(&mut to_add)?; + let to_add = to_add; // let go of mutability + ADMIN.assert_admin(deps.as_ref(), &sender)?; - let mut total = TOTAL.load(deps.storage)?; + let mut total = Uint64::from(TOTAL.load(deps.storage)?); let mut diffs: Vec = vec![]; // add all new members and update total for add in to_add.into_iter() { let add_addr = deps.api.addr_validate(&add.addr)?; MEMBERS.update(deps.storage, &add_addr, height, |old| -> StdResult<_> { - total -= old.unwrap_or_default(); - total += add.weight; + total = total.checked_sub(Uint64::from(old.unwrap_or_default()))?; + total = total.checked_add(Uint64::from(add.weight))?; diffs.push(MemberDiff::new(add.addr, old, Some(add.weight))); Ok(add.weight) })?; @@ -140,12 +148,12 @@ pub fn update_members( // Only process this if they were actually in the list before if let Some(weight) = old { diffs.push(MemberDiff::new(remove, Some(weight), None)); - total -= weight; + total = total.checked_sub(Uint64::from(weight))?; MEMBERS.remove(deps.storage, &remove_addr, height)?; } } - TOTAL.save(deps.storage, &total)?; + TOTAL.save(deps.storage, &total.u64(), height)?; Ok(MemberChangedHookMsg { diffs }) } @@ -157,20 +165,26 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { at_height: height, } => to_binary(&query_member(deps, addr, height)?), QueryMsg::ListMembers { start_after, limit } => { - to_binary(&list_members(deps, start_after, limit)?) + to_binary(&query_list_members(deps, start_after, limit)?) + } + QueryMsg::TotalWeight { at_height: height } => { + to_binary(&query_total_weight(deps, height)?) } - QueryMsg::TotalWeight {} => to_binary(&query_total_weight(deps)?), QueryMsg::Admin {} => to_binary(&ADMIN.query_admin(deps)?), QueryMsg::Hooks {} => to_binary(&HOOKS.query_hooks(deps)?), } } -fn query_total_weight(deps: Deps) -> StdResult { - let weight = TOTAL.load(deps.storage)?; +pub fn query_total_weight(deps: Deps, height: Option) -> StdResult { + let weight = match height { + Some(h) => TOTAL.may_load_at_height(deps.storage, h), + None => TOTAL.may_load(deps.storage), + }? + .unwrap_or_default(); Ok(TotalWeightResponse { weight }) } -fn query_member(deps: Deps, addr: String, height: Option) -> StdResult { +pub fn query_member(deps: Deps, addr: String, height: Option) -> StdResult { let addr = deps.api.addr_validate(&addr)?; let weight = match height { Some(h) => MEMBERS.may_load_at_height(deps.storage, &addr, h), @@ -183,7 +197,7 @@ fn query_member(deps: Deps, addr: String, height: Option) -> StdResult, limit: Option, @@ -205,352 +219,3 @@ fn list_members( Ok(MemberListResponse { members }) } - -#[cfg(test)] -mod tests { - use super::*; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{from_slice, Api, OwnedDeps, Querier, Storage}; - use cw4::{member_key, TOTAL_KEY}; - use cw_controllers::{AdminError, HookError}; - - const INIT_ADMIN: &str = "juan"; - const USER1: &str = "somebody"; - const USER2: &str = "else"; - const USER3: &str = "funny"; - - fn do_instantiate(deps: DepsMut) { - let msg = InstantiateMsg { - admin: Some(INIT_ADMIN.into()), - members: vec![ - Member { - addr: USER1.into(), - weight: 11, - }, - Member { - addr: USER2.into(), - weight: 6, - }, - ], - }; - let info = mock_info("creator", &[]); - instantiate(deps, mock_env(), info, msg).unwrap(); - } - - #[test] - fn proper_instantiation() { - let mut deps = mock_dependencies(); - do_instantiate(deps.as_mut()); - - // it worked, let's query the state - let res = ADMIN.query_admin(deps.as_ref()).unwrap(); - assert_eq!(Some(INIT_ADMIN.into()), res.admin); - - let res = query_total_weight(deps.as_ref()).unwrap(); - assert_eq!(17, res.weight); - } - - #[test] - fn try_member_queries() { - let mut deps = mock_dependencies(); - do_instantiate(deps.as_mut()); - - let member1 = query_member(deps.as_ref(), USER1.into(), None).unwrap(); - assert_eq!(member1.weight, Some(11)); - - let member2 = query_member(deps.as_ref(), USER2.into(), None).unwrap(); - assert_eq!(member2.weight, Some(6)); - - let member3 = query_member(deps.as_ref(), USER3.into(), None).unwrap(); - assert_eq!(member3.weight, None); - - let members = list_members(deps.as_ref(), None, None).unwrap(); - assert_eq!(members.members.len(), 2); - // TODO: assert the set is proper - } - - fn assert_users( - deps: &OwnedDeps, - user1_weight: Option, - user2_weight: Option, - user3_weight: Option, - height: Option, - ) { - 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); - } -} diff --git a/contracts/multisig/cw4-group/src/error.rs b/contracts/multisig/cw4-group/src/error.rs index 8c88fb2255..bd5acb30fb 100644 --- a/contracts/multisig/cw4-group/src/error.rs +++ b/contracts/multisig/cw4-group/src/error.rs @@ -1,19 +1,25 @@ -use cosmwasm_std::StdError; +use cosmwasm_std::{OverflowError, StdError}; use thiserror::Error; use cw_controllers::{AdminError, HookError}; #[derive(Error, Debug, PartialEq)] pub enum ContractError { - #[error(transparent)] + #[error("{0}")] Std(#[from] StdError), - #[error(transparent)] + #[error("{0}")] Hook(#[from] HookError), - #[error(transparent)] + #[error("{0}")] Admin(#[from] AdminError), + #[error("{0}")] + Overflow(#[from] OverflowError), + #[error("Unauthorized")] Unauthorized {}, + + #[error("Message contained duplicate member: {member}")] + DuplicateMember { member: String }, } diff --git a/contracts/multisig/cw4-group/src/helpers.rs b/contracts/multisig/cw4-group/src/helpers.rs index eae8a2434e..c6b5d0187c 100644 --- a/contracts/multisig/cw4-group/src/helpers.rs +++ b/contracts/multisig/cw4-group/src/helpers.rs @@ -1,17 +1,17 @@ -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; use std::ops::Deref; +use cosmwasm_schema::cw_serde; use cosmwasm_std::{to_binary, Addr, CosmosMsg, StdResult, WasmMsg}; use cw4::{Cw4Contract, Member}; +use crate::ContractError; use nym_group_contract_common::msg::ExecuteMsg; /// Cw4GroupContract is a wrapper around Cw4Contract that provides a lot of helpers /// for working with cw4-group contracts. /// /// It extends Cw4Contract to add the extra calls from cw4-group. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] +#[cw_serde] pub struct Cw4GroupContract(pub Cw4Contract); impl Deref for Cw4GroupContract { @@ -41,3 +41,17 @@ impl Cw4GroupContract { self.encode_msg(msg) } } + +/// Sorts the slice and verifies all member addresses are unique. +pub fn validate_unique_members(members: &mut [Member]) -> Result<(), ContractError> { + members.sort_by(|a, b| a.addr.cmp(&b.addr)); + for (a, b) in members.iter().zip(members.iter().skip(1)) { + if a.addr == b.addr { + return Err(ContractError::DuplicateMember { + member: a.addr.clone(), + }); + } + } + + Ok(()) +} diff --git a/contracts/multisig/cw4-group/src/lib.rs b/contracts/multisig/cw4-group/src/lib.rs index 3870baf04b..06fd08f822 100644 --- a/contracts/multisig/cw4-group/src/lib.rs +++ b/contracts/multisig/cw4-group/src/lib.rs @@ -1,6 +1,25 @@ +/*! +This is a basic implementation of the [cw4 spec](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw4/README.md). +It fulfills all elements of the spec, including the raw query lookups, +and it designed to be used as a backing storage for +[cw3 compliant contracts](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw3/README.md). + +It stores a set of members along with an admin, and allows the admin to +update the state. Raw queries (intended for cross-contract queries) +can check a given member address and the total weight. Smart queries (designed +for client API) can do the same, and also query the admin address as well as +paginate over all members. + +For more information on this contract, please check out the +[README](https://github.com/CosmWasm/cw-plus/blob/main/contracts/cw4-group/README.md). + */ + pub mod contract; pub mod error; pub mod helpers; pub mod state; pub use crate::error::ContractError; + +#[cfg(test)] +mod tests; diff --git a/contracts/multisig/cw4-group/src/state.rs b/contracts/multisig/cw4-group/src/state.rs index 1b5003c985..10497fa99d 100644 --- a/contracts/multisig/cw4-group/src/state.rs +++ b/contracts/multisig/cw4-group/src/state.rs @@ -1,16 +1,24 @@ use cosmwasm_std::Addr; -use cw4::TOTAL_KEY; +use cw4::{ + MEMBERS_CHANGELOG, MEMBERS_CHECKPOINTS, MEMBERS_KEY, TOTAL_KEY, TOTAL_KEY_CHANGELOG, + TOTAL_KEY_CHECKPOINTS, +}; use cw_controllers::{Admin, Hooks}; -use cw_storage_plus::{Item, SnapshotMap, Strategy}; +use cw_storage_plus::{SnapshotItem, SnapshotMap, Strategy}; pub const ADMIN: Admin = Admin::new("admin"); pub const HOOKS: Hooks = Hooks::new("cw4-hooks"); -pub const TOTAL: Item = Item::new(TOTAL_KEY); - -pub const MEMBERS: SnapshotMap<&Addr, u64> = SnapshotMap::new( - cw4::MEMBERS_KEY, - cw4::MEMBERS_CHECKPOINTS, - cw4::MEMBERS_CHANGELOG, +pub const TOTAL: SnapshotItem = SnapshotItem::new( + TOTAL_KEY, + TOTAL_KEY_CHECKPOINTS, + TOTAL_KEY_CHANGELOG, + Strategy::EveryBlock, +); + +pub const MEMBERS: SnapshotMap<&Addr, u64> = SnapshotMap::new( + MEMBERS_KEY, + MEMBERS_CHECKPOINTS, + MEMBERS_CHANGELOG, Strategy::EveryBlock, ); diff --git a/contracts/multisig/cw4-group/src/tests.rs b/contracts/multisig/cw4-group/src/tests.rs new file mode 100644 index 0000000000..11f39f7084 --- /dev/null +++ b/contracts/multisig/cw4-group/src/tests.rs @@ -0,0 +1,439 @@ +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( + deps: &OwnedDeps, + user1_weight: Option, + user2_weight: Option, + user3_weight: Option, + height: Option, +) { + 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 = [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 [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); +} diff --git a/contracts/name-service/src/state/names.rs b/contracts/name-service/src/state/names.rs index 27af9f3407..3b2b02f82d 100644 --- a/contracts/name-service/src/state/names.rs +++ b/contracts/name-service/src/state/names.rs @@ -30,12 +30,12 @@ fn names<'a>() -> IndexedMap<'a, NameId, RegisteredName, NameIndex<'a>> { let indexes = NameIndex { name: UniqueIndex::new(|d| d.name.name.to_string(), NAMES_NAME_IDX_NAMESPACE), address: MultiIndex::new( - |d| d.name.address.to_string(), + |_pk, d| d.name.address.to_string(), NAMES_PK_NAMESPACE, NAMES_ADDRESS_IDX_NAMESPACE, ), owner: MultiIndex::new( - |d| d.owner.clone(), + |_pk, d| d.owner.clone(), NAMES_PK_NAMESPACE, NAMES_OWNER_IDX_NAMESPACE, ), diff --git a/contracts/service-provider-directory/src/state/services.rs b/contracts/service-provider-directory/src/state/services.rs index 54b9c27bcf..e5e68a21ff 100644 --- a/contracts/service-provider-directory/src/state/services.rs +++ b/contracts/service-provider-directory/src/state/services.rs @@ -26,12 +26,12 @@ impl<'a> IndexList for ServiceIndex<'a> { fn services<'a>() -> IndexedMap<'a, ServiceId, Service, ServiceIndex<'a>> { let indexes = ServiceIndex { nym_address: MultiIndex::new( - |d| d.service.nym_address.to_string(), + |_pk, d| d.service.nym_address.to_string(), SERVICES_PK_NAMESPACE, SERVICES_NYM_ADDRESS_IDX_NAMESPACE, ), announcer: MultiIndex::new( - |d| d.announcer.clone(), + |_pk, d| d.announcer.clone(), SERVICES_PK_NAMESPACE, SERVICES_ANNOUNCER_IDX_NAMESPACE, ), diff --git a/contracts/vesting/src/vesting/account/mod.rs b/contracts/vesting/src/vesting/account/mod.rs index 1e0aa25348..e59f27d1a2 100644 --- a/contracts/vesting/src/vesting/account/mod.rs +++ b/contracts/vesting/src/vesting/account/mod.rs @@ -25,7 +25,7 @@ fn generate_storage_key(storage: &mut dyn Storage) -> Result, diff --git a/documentation/dev-portal/book.toml b/documentation/dev-portal/book.toml index 3b81f82751..cf434a31f5 100644 --- a/documentation/dev-portal/book.toml +++ b/documentation/dev-portal/book.toml @@ -1,6 +1,6 @@ [book] title = "Nym Developer Portal" -authors = ["Max Hampshire"] +authors = ["Max Hampshire, Serinko, Alexia Lorenza Martinel"] language = "en" multilingual = false src = "src" @@ -47,10 +47,10 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install` # variables preprocessor: import variables into files # https://gitlab.com/tglman/mdbook-variables/ [preprocessor.variables.variables] -# code prerequisites versions minimum_rust_version = "1.66" -# TODO remove this in place of develop in next release -platform_release_version = "v1.1.21" +# vars for links: TODO think on how to streamline updating +platform_release_version = "v1.1.25" +wallet_release_version = "v1.2.7" [preprocessor.last-changed] command = "mdbook-last-changed" diff --git a/documentation/dev-portal/src/SUMMARY.md b/documentation/dev-portal/src/SUMMARY.md index be35e9c30a..aeae804662 100644 --- a/documentation/dev-portal/src/SUMMARY.md +++ b/documentation/dev-portal/src/SUMMARY.md @@ -40,8 +40,7 @@ # Community Resources - [Nym DevRel AMAs](community-resources/ama.md) -- [Community Applications](community-resources/community-applications.md) -- [Community Guides](community-resources/community-guides.md) +- [Community Applications and Guides](community-resources/community-applications-and-guides.md) - [Change Service Grantee Information](info-request.md) - [Rewards FAQ](community-resources/rewards-faq.md) --- diff --git a/documentation/dev-portal/src/community-resources/community-applications-and-guides.md b/documentation/dev-portal/src/community-resources/community-applications-and-guides.md new file mode 100644 index 0000000000..ba00922658 --- /dev/null +++ b/documentation/dev-portal/src/community-resources/community-applications-and-guides.md @@ -0,0 +1,116 @@ +# Community Applications + +We love seeing our developer community create applications using Nym. If you would like to share your application with the community, please submit a pull request to the `main` branch of the `nymtech/dev-portal` [repository](https://github.com/nymtech/dev-portal). + + + +## Pastenym + +>A [pastebin](https://pastebin.com) inspired project, offer a solution for sharing text with Nym products to offer full anonymity, even on metadata level. + +* [Github](https://github.com/notrustverify/pastenym) +* [Deployed App](https://pastenym.ch) + + + + +## Nostr-Nym + +> [Nostr-nym](https://github.com/notrustverify/nostr-nym) offer a solution to use [Nostr](https://nostr.how/en/what-is-nostr) protocol by giving the possibility to run a relay on mixnet. By using a nostr client compatible with the mixnet, users can protect their privacy to be able to use Nostr has the want, without being observed. + +* [Github](https://github.com/notrustverify/nostr-nym) +* [Deployed App](https://nostrnym.pnproxy.org/) +* [Documentation](https://blog.notrustverify.ch/nostr-relay-on-nym) + + + + +## Spook + +> Ethereum RPC request mixer uses the Nym network mixing service to anonymize RPC requests to the Ethereum network without revealing sensitive data and metadata. +* [Github](https://github.com/EdenBlockVC/spook) + + + + +## Ethereum Transaction Broadcaster + +> Ethereum Transaction Broadcaster that uses the Nym Mixnet to provide privacy and anonymity for transactions on the Ethereum network command-line interface. + +* [Github](https://github.com/noot/nym-ethtx) + + + + +## NymDrive + +> An open-source, decentralized, E2E encrypted, privacy friendly alternative to Google Drive/Dropbox, allowing for file encryption and decryption using the Nym Mixnet. +* [Github](https://github.com/saleel/nymdrive) +* [Demo](https://www.youtube.com/watch?v=5Rx73nw8NYI) +* [Presentation](https://docs.google.com/presentation/d/1MpvIK32Mx9VKLVfMTcvbeyrsKHHUsTvDQ-3n31dR0NE/edit#slide=id.p) + + + + +## Nym Dashboard + +> Developed by No Trust Verify, this dashboard is a great tool to get information about the mixnet, gateways and mixnodes. +* [Deployed App](https://status.notrustverify.ch/d/CW3L7dVVk/nym-mixnet?orgId=1) + + + + +## Is Nym Up + +> Explore whether we're up through IsNymUp, a tool that helps check the heath of the Nym network as well as some mixnet related statistics! +* [Deployed App](https://isnymup.com/) + + + + +## DarkFi over Nym + +> DarkFi leverages Nym's mixnet as a pluggable transport for IRCD, their p2p IRC variant. Users can anonymously connect to peers over the network, ensuring secure and private communication within the DarkFi ecosystem. +* [Github](https://github.com/darkrenaissance/darkfi) +* [Documentation](https://darkrenaissance.github.io/darkfi/clients/nym_outbound.html) + + + + +## Nymstr email + +> Experience secure and private email communication with ease using Nymstr email, which enables seamless transmission of emails over a SOCKS5 proxy and our NYM mixnet! +* [Github](https://github.com/dial0ut/nymstr-email) + + + + +## Minibolt + +> Anonymize your p2p inventory messages and mempool for your Bitcoin & Lightning full nodes on consumer PCs! +* [Github](https://github.com/minibolt-guide/minibolt) +* [Documentation](https://v2.minibolt.info/bonus-guides/system/nym-mixnet#proxying-bitcoin-core) + + + +

+ +# Community Guides + +We aren't the only ones writing documentation: the Nym developer community is also a great source of guides and resources, some of which we've included here. + +## No Trust Verify + +>No Trust Verify is a project that aims to build open-source, privacy-enhancing technologies that make it easier to use the Internet securely and anonymously. Their focus is on providing tools and services that make it simple for developers to create decentralized applications (dApps) that respect users' privacy. + +* [Awesome Nym list](https://notrustverify.github.io/awesome-nym/) ([GitHub](https://github.com/notrustverify/awesome-nym)) +* A lot of guides can be found on the [NTV Blog](https://blog.notrustverify.ch/) + + +## The Way of the NYMJA + +by Pineapple Proxy🍍 + +>Born out of a study group from Nym's Shipyard Academy, Pineapple Proxy has emerged as a cluster of motivated and skilled individuals who see the new internet taking shape. With vibecare at the heart of their approach, this zesty collective is on a mission to make privacy convenient for everyone via content, new tools, events, and novel experiences. They believe in collective intelligence, empathy, and collaboration as the means by which privacy will become a meaningful reality. +* [Website](https://pnproxy.org/welcome.html) ([GitHub](https://github.com/Pineapple-Proxy-DAO/web)) + diff --git a/documentation/dev-portal/src/community-resources/community-applications.md b/documentation/dev-portal/src/community-resources/community-applications.md deleted file mode 100644 index 9eada01337..0000000000 --- a/documentation/dev-portal/src/community-resources/community-applications.md +++ /dev/null @@ -1,61 +0,0 @@ -# Community Applications - -We love seeing our developer community create applications using Nym. If you would like to share your application with the community, please submit a pull request to the `main` branch of the `nymtech/dev-portal` [repository](https://github.com/nymtech/dev-portal). - - - -## Pastenym - ->A [pastebin](https://pastebin.com) inspired project, offer a solution for sharing text with Nym products to offer full anonymity, even on metadata level. - -* [Github](https://github.com/notrustverify/pastenym) -* [Deployed Website App](https://pastenym.ch) - - - - - -## Nostr-Nym - -> [Nostr-nym](https://github.com/notrustverify/nostr-nym) offer a solution to use [Nostr](https://nostr.how/) protocol by giving the possibility to run a relay on mixnet. By using a nostr client compatible with the mixnet, users can protect their privacy to be able to use Nostr has the want, without being observed. - -* [Github](https://github.com/notrustverify/nostr-nym) -* Deployed Website App coming soon - - - - - - -## Spook - -> Ethereum RPC request mixer uses the Nym network mixing service to anonymize RPC requests to the Ethereum network without revealing sensitive data and metadata. -* [Github](https://github.com/EdenBlockVC/spook) - - - - - -## Ethereum Transaction Broadcaster - -> Ethereum Transaction Broadcaster that uses the Nym Mixnet to provide privacy and anonymity for transactions on the Ethereum network command-line interface. - -* [Github](https://github.com/noot/nym-ethtx) - - - - - -## NymDrive - -> An open-source, decentralized, E2E encrypted, privacy friendly alternative to Google Drive/Dropbox, allowing for file encryption and decryption using the Nym Mixnet. -* [Github](https://github.com/saleel/nymdrive) -* [Demo](https://www.youtube.com/watch?v=5Rx73nw8NYI) -* [Presentation](https://docs.google.com/presentation/d/1MpvIK32Mx9VKLVfMTcvbeyrsKHHUsTvDQ-3n31dR0NE/edit#slide=id.p) - - - - - - - diff --git a/documentation/dev-portal/src/community-resources/community-guides.md b/documentation/dev-portal/src/community-resources/community-guides.md deleted file mode 100644 index 7a5973f6f2..0000000000 --- a/documentation/dev-portal/src/community-resources/community-guides.md +++ /dev/null @@ -1,17 +0,0 @@ -# Community Guides - -We aren't the only ones writing documentation: the Nym developer community is also a great source of guides and resources, some of which we've included here. - -## No Trust Verify - ->No Trust Verify is a project that aims to build open-source, privacy-enhancing technologies that make it easier to use the Internet securely and anonymously. Their focus is on providing tools and services that make it simple for developers to create decentralized applications (dApps) that respect users' privacy. - -* [Awesome Nym list](https://notrustverify.github.io/awesome-nym/) ([GitHub](https://github.com/notrustverify/awesome-nym)) -* A lot of guides can be found on the [NTV Blog](https://blog.notrustverify.ch/) - -## The Way of the NYMJA - -by Pineapple Proxy🍍 - ->Born out of a study group from Nym's Shipyard Academy, Pineapple Proxy has emerged as a cluster of motivated and skilled individuals who see the new internet taking shape. With vibecare at the heart of their approach, this zesty collective is on a mission to make privacy convenient for everyone via content, new tools, events, and novel experiences. They believe in collective intelligence, empathy, and collaboration as the means by which privacy will become a meaningful reality. -* [Website](https://pnproxy.org/welcome.html) ([GitHub](https://github.com/Pineapple-Proxy-DAO/web)) diff --git a/documentation/dev-portal/src/images/profile_picture/darkfi_over_nym_pp.png b/documentation/dev-portal/src/images/profile_picture/darkfi_over_nym_pp.png new file mode 100644 index 0000000000..9ae44d85c1 Binary files /dev/null and b/documentation/dev-portal/src/images/profile_picture/darkfi_over_nym_pp.png differ diff --git a/documentation/dev-portal/src/images/profile_picture/minibolt_pp.png b/documentation/dev-portal/src/images/profile_picture/minibolt_pp.png new file mode 100644 index 0000000000..ed059e6055 Binary files /dev/null and b/documentation/dev-portal/src/images/profile_picture/minibolt_pp.png differ diff --git a/documentation/dev-portal/src/images/profile_picture/nym_dashboard_pp.svg b/documentation/dev-portal/src/images/profile_picture/nym_dashboard_pp.svg new file mode 100644 index 0000000000..e91f3abdb4 --- /dev/null +++ b/documentation/dev-portal/src/images/profile_picture/nym_dashboard_pp.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + diff --git a/documentation/dev-portal/src/images/profile_picture/nymstr_email_pp.png b/documentation/dev-portal/src/images/profile_picture/nymstr_email_pp.png new file mode 100644 index 0000000000..404b9e2529 Binary files /dev/null and b/documentation/dev-portal/src/images/profile_picture/nymstr_email_pp.png differ diff --git a/documentation/dev-portal/src/integrations/mixnet-integration.md b/documentation/dev-portal/src/integrations/mixnet-integration.md index 43fea3e7e9..e4a328bf24 100644 --- a/documentation/dev-portal/src/integrations/mixnet-integration.md +++ b/documentation/dev-portal/src/integrations/mixnet-integration.md @@ -36,7 +36,7 @@ In order to ensure uptime and reliability, it is recommended that you run some p * If you're running a purely P2P application, then just integrating clients and having some method of sharing addresses should be enough to route your traffic through the mixnet. * If you're wanting to place the mixnet between your users' application instances and a server-based backend, you can use the [network requester](https://nymtech.net/docs/nodes/network-requester-setup.html) service provider binary to proxy these requests to your application backend, with the mixnet 'between' the user and your service, in order to prevent metadata leakage being broadcast to the internet. -* If you're wanting to route RPC requests through the mixnet to a blockchain, you will need to look into setting up some sort of service that does the transaction broadcasting for you. You can find examples of such projects on the [community applications](../community-resources/community-applications.md) page. +* If you're wanting to route RPC requests through the mixnet to a blockchain, you will need to look into setting up some sort of service that does the transaction broadcasting for you. You can find examples of such projects on the [community applications](../community-resources/community-applications-and-guides.md) page. ## Example application traffic flow ### Initialization diff --git a/documentation/dev-portal/src/quickstart/overview.md b/documentation/dev-portal/src/quickstart/overview.md index a9586172ed..b2ac4199c9 100644 --- a/documentation/dev-portal/src/quickstart/overview.md +++ b/documentation/dev-portal/src/quickstart/overview.md @@ -2,9 +2,10 @@ There are multiple options to quickly connect to Nym and see the network in action without the need for any code changes to your application. At most, these involve running Nym as a second process alongside an existing application in order to send traffic through the mixnet. -Demo application: +Demo applications: * a browser-based 'hello world' [chat application](https://chat-demo.nymtech.net). Either open in two browser windows and send messages to yourself, or share with a friend and send messages to each other through the mixnet! - +* a Coconut-scheme based [Credential Library](https://coco-demo.nymtech.net/). This is a WASM implementation of our Coconut libraries which generate raw Coconut credentials. Test it to create and re-randomize your own credentials! +
Proxy traffic with the Nym Socks5 client: * set up a plug-and-play connection with the [NymConnect](./nymconnect-gui.md) GUI for proxying Telegram, Electrum, Keybase or Blockstream Green traffic through the mixnet (~2 minutes). * [Download and run](./socks-proxy.md) the Nym Socks5 client via the CLI, for other desktop applications with SOCKS5 connection options (~30 minutes). diff --git a/documentation/dev-portal/src/tutorials/matrix.md b/documentation/dev-portal/src/tutorials/matrix.md index 39852fed05..7351ce85ee 100644 --- a/documentation/dev-portal/src/tutorials/matrix.md +++ b/documentation/dev-portal/src/tutorials/matrix.md @@ -3,7 +3,7 @@ Chat applications became an essential part of human communication. Matrix chat has end to end encryption on protocol level and Element app users can sort their communication into spaces and rooms. Now the Matrix communities can rely on network privacy as NymConnect supports Matrix chat protocol. -Currently there is no option in Matrix's Element client to set a socks5 proxy. In order to use Element via NymConnect users have to start it from the command-line. The setup is simple, for convenience a a keyboard shortcut setting can be easily done. +Currently there is no option in Matrix's Element client to set a Socks5 proxy. In order to use Element via NymConnect users have to start it from the command-line. The setup is simple, for convenience a keyboard shortcut setting or terminal alias can be easily done. ## Setup & Run @@ -35,10 +35,46 @@ Alternatively you can add a keybinding via the CLI, using whatever config files ### Create an alias If you prefer to simply shorten the length of the command (or all your keybindings are already taken) then you can simply create an alias for this long-winded command (this example aliases that command to the single word `element`, but you can replace it with whatever you like): +**Linux** + ```sh alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080" ``` -To make this alias persist, then add this to your `.bashrc` or `.zshrc` file (usually located in your `$HOME` directory) and `source` that file. +To make this alias persist, then add this to your `.bashrc` or `.zshrc` file (usually located in your `$HOME` directory) and `source` that file. This can be done by appending the alias command directly to the shell config file with one command. -Now you can run Element throught the mixnet with a single-word command. +For `bash` enter: + +```sh +alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080" >> ~/.bashrc +``` + +For `zsh` enter: + +```sh +alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080" >> ~/.zshrc +``` + +You can add the alias manually by opening your `$HOME` directory, enable hidden files (press `ctrl` + `h`) and open `.bashrc` or `.zshrc` file (based on your terminal setup) in a text editor, paste the string `alias element="element-desktop --proxy-server=socks5://127.0.0.1:1080"` to the the end, save and exit. Start a new terminal and run `element`. + +**Mac** + +```sh +alias element="open -a Element --args --proxy-server=socks5://127.0.0.1:1080" + +``` + +To make this alias persist, then add this to your `.zshrc` (or `.bashrc`/`.profile`) file (usually located in your `$HOME` directory) and `source` that file. This can be done by appending the alias command directly to the shell config file with one command. + +For `zsh` enter: + +```sh +alias element="open -a Element --args --proxy-server=socks5://127.0.0.1:1080" >> ~/.zshrc +``` + +For `.bashrc` or `.profile` just change the end of the command. + +You can add the alias manually by opening your `$HOME` directory, enable hidden files (in Finder press `Shift` + `Command` + `.`) and open `.zshrc` file (or `.bashrc`/`.profile`) in a text editor, paste the string `alias element="open -a Element --args --proxy-server=socks5://127.0.0.1:1080"` to the the end, save and exit. Start a new terminal and run `element`. + + +**Now you can run Element through the Nym Mixnet with a single-word command.** diff --git a/documentation/dev-portal/src/tutorials/simple-service-provider/sending-message.md b/documentation/dev-portal/src/tutorials/simple-service-provider/sending-message.md index a1a5f67ae9..d0083f0a01 100644 --- a/documentation/dev-portal/src/tutorials/simple-service-provider/sending-message.md +++ b/documentation/dev-portal/src/tutorials/simple-service-provider/sending-message.md @@ -12,7 +12,7 @@ Simply fill in the fields in your browser and click `Send`. In your browser you -This small project can be used as a template to start conceptualizing and developing more complex PEApps. Stay tuned for more soon, and if you're searching for inspiration check out the [community apps](../../community-resources/community-applications.md) list! +This small project can be used as a template to start conceptualizing and developing more complex PEApps. Stay tuned for more soon, and if you're searching for inspiration check out the [community apps](../../community-resources/community-applications-and-guides.md) list! diff --git a/documentation/dev-portal/theme/css/general.css b/documentation/dev-portal/theme/css/general.css index a06db43965..84dbfa3371 100644 --- a/documentation/dev-portal/theme/css/general.css +++ b/documentation/dev-portal/theme/css/general.css @@ -62,7 +62,8 @@ h3:target::before, h4:target::before, h5:target::before, h6:target::before { - display: inline-block; + /* display "none" in order to avoid default artifacts on the community-applications-and-guides page */ + display: none; content: "»"; margin-left: -30px; width: 30px; diff --git a/documentation/docs/book.toml b/documentation/docs/book.toml index b70997a556..996a7ec4e4 100644 --- a/documentation/docs/book.toml +++ b/documentation/docs/book.toml @@ -1,6 +1,6 @@ [book] title = "Nym Docs" -authors = ["Max Hampshire"] +authors = ["Max Hampshire, Serinko, Alexia Lorenza Martinel"] description = "Nym technical documentation" language = "en" multilingual = false # for the moment - ideally work on chinese, brazillian, spanish next @@ -49,8 +49,8 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install` [preprocessor.variables.variables] minimum_rust_version = "1.66" # vars for links: TODO think on how to streamline updating -platform_release_version = "v1.1.22" -wallet_release_version = "v1.2.5" +platform_release_version = "v1.1.25" +wallet_release_version = "v1.2.7" [preprocessor.last-changed] command = "mdbook-last-changed" diff --git a/documentation/docs/src/nodes/gateway-setup.md b/documentation/docs/src/nodes/gateway-setup.md index fe48fc8566..a933dc5b04 100644 --- a/documentation/docs/src/nodes/gateway-setup.md +++ b/documentation/docs/src/nodes/gateway-setup.md @@ -153,8 +153,6 @@ Follow these steps to upgrade your binary and update its config file: * re-run `init` with the same values as you used initially. **This will just update the config file, it will not overwrite existing keys**. * restart your gateway process with the new binary. -> Do **not** use the `upgrade` command: there is a known error with the command that will be fixed in a subsequent release. - #### Step 2: updating your node information in the smart contract Follow these steps to update the information about your node which is publically avaliable from the [Nym API](https://validator.nymtech.net/api/swagger/index.html) and information displayed on the [mixnet explorer](https://explorer.nymtech.net). diff --git a/documentation/docs/src/nodes/mix-node-setup.md b/documentation/docs/src/nodes/mix-node-setup.md index 408ef20b99..2f93797e1e 100644 --- a/documentation/docs/src/nodes/mix-node-setup.md +++ b/documentation/docs/src/nodes/mix-node-setup.md @@ -191,8 +191,6 @@ Follow these steps to upgrade your mix node binary and update its config file: * re-run `init` with the same values as you used initially. **This will just update the config file, it will not overwrite existing keys**. * restart your mix node process with the new binary. -> Do **not** use the `upgrade` command: there is a known error with the command that will be fixed in a subsequent release. - #### Step 2: updating your node information in the smart contract Follow these steps to update the information about your mix node which is publically avaliable from the [Nym API](https://validator.nymtech.net/api/swagger/index.html) and information displayed on the [mixnet explorer](https://explorer.nymtech.net). diff --git a/documentation/docs/src/nodes/network-requester-setup.md b/documentation/docs/src/nodes/network-requester-setup.md index 6ee560b5b4..6fe3ef7971 100644 --- a/documentation/docs/src/nodes/network-requester-setup.md +++ b/documentation/docs/src/nodes/network-requester-setup.md @@ -8,7 +8,6 @@ ``` - ## Network Requester Whitelist If you have access to a server, you can run the network requester, which allows Nym users to send outbound requests from their local machine through the mixnet to a server, which then makes the request on their behalf, shielding them (and their metadata) from clearnet, untrusted and unknown infrastructure, such as email or message client servers. @@ -65,6 +64,34 @@ p2pify.com 2001:67c:4e8::/48 2001:b28:f23c::/48 2a0a:f280::/32 + +# nym matrix server +nymtech.chat + +# generic matrix server backends +vector.im +matrix.org + +# monero desktop - mainnet +212.83.175.67 +212.83.172.165 +176.9.0.187 +88.198.163.90 +95.217.25.101 +136.244.105.131 +104.238.221.81 +66.85.74.134 +88.99.173.38 +51.79.173.165 + +# monero desktop - stagenet +162.210.173.150 +176.9.0.187 +88.99.173.38 +51.79.173.165 + +# alephium +alephium.org ``` ## Network Requester Directory @@ -193,25 +220,32 @@ sudo ufw enable sudo ufw status ``` -Finally open your requester's p2p port, as well as ports for ssh and incoming traffic connections: +Finally open your requester's ssh port to incoming administration connections: ``` -sudo ufw allow 22,9000/tcp +sudo ufw allow 22/tcp # check the status of the firewall sudo ufw status ``` -For more information about your requester's port configuration, check the [requester port reference table](./network-requester-setup.md#requester-port-reference) below. - ## Using your network requester The next thing to do is use your requester, share its address with friends (or whoever you want to help privacy-enhance their app traffic). Is this safe to do? If it was an open proxy, this would be unsafe, because any Nym user could make network requests to any system on the internet. To make things a bit less stressful for administrators, the Network Requester drops all incoming requests by default. In order for it to make requests, you need to add specific domains to the `allowed.list` file at `$HOME/.nym/service-providers/network-requester/allowed.list`. +### Global vs local allow lists +Your Network Requester will check for a domain against 2 lists before allowing traffic through for a particular domain or IP. + +* The first list is the default list on the [nymtech.net server](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt). Your Requester will not check against this list every time, but instead will keep a record of accepted domains in memory. + +* The second is the local `allowed.list` file. + ### Supporting custom domains with your network requester It is easy to add new domains and services to your network requester - simply find out which endpoints (both URLs and raw IP addresses are supported) you need to whitelist, and then add these endpoints to your `allowed.list`. +> In order to keep things more organised, you can now use comments in the `allow.list` like the example at the top of this page. + How to go about this? Have a look in your nym-network-requester config directory: ``` @@ -249,7 +283,3 @@ This command should return the following: ### Requester port reference All network-requester-specific port configuration can be found in `$HOME/.nym/service-providers/network-requester//config/config.toml`. If you do edit any port configs, remember to restart your client and requester processes. - -| Default port | Use | -|--------------|---------------------------| -| 9000 | Listen for Client traffic | diff --git a/documentation/docs/src/nodes/validator-setup.md b/documentation/docs/src/nodes/validator-setup.md index 153622740b..053a19253d 100644 --- a/documentation/docs/src/nodes/validator-setup.md +++ b/documentation/docs/src/nodes/validator-setup.md @@ -593,18 +593,19 @@ nyxd tx slashing unjail ### Upgrading your validator -Upgrading from `v0.26.0` -> `v0.31.1` process is fairly simple. Grab the v0.31.1 release tarball from the [`nyxd` releases page](https://github.com/nymtech/nyxd/releases), and untar it. Inside are two files: +Upgrading from `v0.31.1` -> `v0.32.0` process is fairly simple. Grab the `v0.32.0` release tarball from the [`nyxd` releases page](https://github.com/nymtech/nyxd/releases), and untar it. Inside are two files: -- the new validator (`nyxd`) v0.31.1 +- the new validator (`nyxd`) v0.32.0 - the new wasmvm (it depends on your platform, but most common filename is `libwasmvm.x86_64.so`) -Before the upgrade height, copy `libwasmvm.x86_64.so` to the default LD_LIBRARY_PATH on your system (on Ubuntu 20.04 this is `/lib/x86_64-linux-gnu/`): +Wait for the upgrade height to be reached and the chain to halt awaiting upgrade, then: -Then just swap in your new `nyxd` binary and restart once the halt height is reached. +* copy `libwasmvm.x86_64.so` to the default LD_LIBRARY_PATH on your system (on Ubuntu 20.04 this is `/lib/x86_64-linux-gnu/`) replacing your existing file with the same name. +* swap in your new `nyxd` binary and restart. -You can also use something like [Cosmovisor](https://github.com/cosmos/cosmos-sdk/tree/main/tools/cosmovisor) - grab the relevant information from the current upgrade proposal [here](https://nym.explorers.guru/proposal/8). +You can also use something like [Cosmovisor](https://github.com/cosmos/cosmos-sdk/tree/main/tools/cosmovisor) - grab the relevant information from the current upgrade proposal [here](https://nym.explorers.guru/proposal/9). -Note: Cosmovisor will swap the `nyxd` binary, but you'll need to already have the `libwasmvm.x86_64.so` in place. Luckily, the name of the wasmvm in v0.26.1 was `libwasmvm.so`, and the new name is `libwasmvm.x86_64.so`, so you can have it already sitting there without disrupting service. +Note: Cosmovisor will swap the `nyxd` binary, but you'll need to already have the `libwasmvm.x86_64.so` in place. #### Common reasons for your validator being jailed diff --git a/documentation/docs/src/sdk/rust.md b/documentation/docs/src/sdk/rust.md index 5c6a9c4d8f..68004b7576 100644 --- a/documentation/docs/src/sdk/rust.md +++ b/documentation/docs/src/sdk/rust.md @@ -107,4 +107,4 @@ The following code shows how you can use the SDK to create and use a [credential {{#include ../../../../sdk/rust/nym-sdk/examples/bandwidth.rs}} ``` -You can read more about Coconut credentials (also referred to as `zk-Nym`) [here](../cococnut.md). +You can read more about Coconut credentials (also referred to as `zk-Nym`) [here](../coconut.md). diff --git a/envs/sandbox.env b/envs/sandbox.env index 1b25b72237..f42a91a97d 100644 --- a/envs/sandbox.env +++ b/envs/sandbox.env @@ -3,22 +3,22 @@ CONFIGURED=true RUST_LOG=info RUST_BACKTRACE=1 -BECH32_PREFIX=nymt -MIX_DENOM=unymt -MIX_DENOM_DISPLAY=nymt -STAKE_DENOM=unyxt -STAKE_DENOM_DISPLAY=nyxt +BECH32_PREFIX=n +MIX_DENOM=unym +MIX_DENOM_DISPLAY=nym +STAKE_DENOM=unyx +STAKE_DENOM_DISPLAY=nyx DENOMS_EXPONENT=6 -REWARDING_VALIDATOR_ADDRESS="nymt1mxuweurc066kprnngtm8zmvam7m2nw26yatpmv" -MIXNET_CONTRACT_ADDRESS="nymt1dlsvvgey26ernlj0sq2afjluh3qd4ap0k9eerekfkw5algqrwqksaf2qf7" -VESTING_CONTRACT_ADDRESS="nymt19g9xuqrvz2frv905v3fc7puryfypluhg383q9zwsmedrlqekfgys62ykm4" -BANDWIDTH_CLAIM_CONTRACT_ADDRESS="nymt1rhmk9udessnv3r8f3eh2s03f45svnjaczpmcqz" -MULTISIG_CONTRACT_ADDRESS="nymt142dkm8xe9f0ytyarp7ww4kvclva65705jphxsk9exn3nqdsm8jkqnp06ac" -COCONUT_BANDWIDTH_CONTRACT_ADDRESS="nymt1ty0frysegskh6ndm3v96z5xdq66qzcu0aw7xcxlgp54jg0mjwlgqplc6v0" -COCONUT_DKG_CONTRACT_ADDRESS="nymt1gwk6muhmzeuxje7df7rjvqwl2vex0kj4t2hwuzmyx5k62kfusu5qk4k5z4" -GROUP_CONTRACT_ADDRESS="nymt14ry36mwauycz08v8ndcujghxz4hmua5epxcn0mamlr3suqe0l2qsqx5ya2" +REWARDING_VALIDATOR_ADDRESS=n1pefc2utwpy5w78p2kqdsfmpjxfwmn9d39k5mqa +MIXNET_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav +VESTING_CONTRACT_ADDRESS=n1unyuj8qnmygvzuex3dwmg9yzt9alhvyeat0uu0jedg2wj33efl5qackslz +BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 +COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n16a32stm6kknhq5cc8rx77elr66pygf2hfszw7wvpq746x3uffylqkjar4l +GROUP_CONTRACT_ADDRESS=n1pd7kfgvr5tpcv0xnlv46c4jsq9jg2r799xxrcwqdm4l2jhq2pjwqrmz5ju +MULTISIG_CONTRACT_ADDRESS=n14ph4e660eyqz0j36zlkaey4zgzexm5twkmjlqaequxr2cjm9eprqsmad6k +COCONUT_DKG_CONTRACT_ADDRESS=n1ahg0erc2fs6xx3j5m8sfx3ryuzdjh6kf6qm9plsf865fltekyrfsesac6a STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0" NYXD="https://sandbox-validator1.nymtech.net" -NYM_API="https://sandbox-validator1-api.nymtech.net/api" \ No newline at end of file +NYM_API="https://sandbox-nym-api1.nymtech.net/api" \ No newline at end of file diff --git a/explorer-api/Cargo.toml b/explorer-api/Cargo.toml index a317ccbe1b..5e1dec9f48 100644 --- a/explorer-api/Cargo.toml +++ b/explorer-api/Cargo.toml @@ -34,4 +34,4 @@ nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-co nym-network-defaults = { path = "../common/network-defaults" } nym-bin-common = { path = "../common/bin-common"} nym-task = { path = "../common/task" } -nym-validator-client = { path = "../common/client-libs/validator-client", features=["nyxd-client"] } +nym-validator-client = { path = "../common/client-libs/validator-client", features=["http-client"] } diff --git a/explorer-api/src/mix_node/models.rs b/explorer-api/src/mix_node/models.rs index 462d41c3b5..94ae6558d0 100644 --- a/explorer-api/src/mix_node/models.rs +++ b/explorer-api/src/mix_node/models.rs @@ -45,7 +45,7 @@ pub(crate) struct PrettyDetailedMixNodeBond { pub blacklisted: bool, } -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] pub struct SummedDelegations { pub owner: Addr, pub mix_id: MixId, diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index 41bd7ecfe3..763b3ab676 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -60,7 +60,7 @@ nym-sphinx = { path = "../common/nymsphinx" } nym-statistics-common = { path = "../common/statistics" } nym-task = { path = "../common/task" } nym-types = { path = "../common/types" } -nym-validator-client = { path = "../common/client-libs/validator-client", features = [ "nyxd-client" ] } +nym-validator-client = { path = "../common/client-libs/validator-client" } [build-dependencies] tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] } diff --git a/integrations/bity/Cargo.toml b/integrations/bity/Cargo.toml index 8bab2b3e77..db8bdc9420 100644 --- a/integrations/bity/Cargo.toml +++ b/integrations/bity/Cargo.toml @@ -8,15 +8,13 @@ rust-version = "1.56" serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "1.0" -k256 = { version = "0.10", features = ["ecdsa", "sha256"] } +k256 = { workspace = true, features = ["ecdsa", "sha256"] } eyre = "0.6.5" -cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } +cosmrs = { workspace = true } nym-cli-commands = { path = "../../common/commands" } -nym-validator-client = { path = "../../common/client-libs/validator-client", features = [ - "nyxd-client", -] } +nym-validator-client = { path = "../../common/client-libs/validator-client" } [dev-dependencies] anyhow = "1" diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index 4cfec3402a..f9b378f59d 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -92,7 +92,7 @@ nym-task = { path = "../common/task" } nym-topology = { path = "../common/topology" } nym-api-requests = { path = "nym-api-requests" } nym-validator-client = { path = "../common/client-libs/validator-client", features = [ - "nyxd-client", + "http-client", "signing" ] } nym-bin-common = { path = "../common/bin-common" } nym-node-tester-utils = { path = "../common/node-tester-utils" } diff --git a/nym-api/nym-api-requests/Cargo.toml b/nym-api/nym-api-requests/Cargo.toml index ed8dc1dce6..37aded5efa 100644 --- a/nym-api/nym-api-requests/Cargo.toml +++ b/nym-api/nym-api-requests/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] bs58 = "0.4.0" -cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } +cosmrs = { workspace = true } cosmwasm-std = { workspace = true, default-features = false } getset = "0.1.1" schemars = { version = "0.8", features = ["preserve_order"] } diff --git a/nym-api/src/coconut/deposit.rs b/nym-api/src/coconut/deposit.rs index 685fee70f3..3d6b8fb2de 100644 --- a/nym-api/src/coconut/deposit.rs +++ b/nym-api/src/coconut/deposit.rs @@ -35,14 +35,14 @@ pub async fn extract_encryption_key( .tx_result .events .iter() - .find(|event| event.type_str == format!("wasm-{}", DEPOSITED_FUNDS_EVENT_TYPE)) + .find(|event| event.kind == format!("wasm-{}", DEPOSITED_FUNDS_EVENT_TYPE)) .ok_or(CoconutError::DepositEventNotFound)? .attributes .as_ref(); - let deposit_value = attributes + let deposit_value: &str = attributes .iter() - .find(|tag| tag.key.as_ref() == DEPOSIT_VALUE) + .find(|tag| tag.key == DEPOSIT_VALUE) .ok_or(CoconutError::DepositValueNotFound)? .value .as_ref(); @@ -54,9 +54,9 @@ pub async fn extract_encryption_key( )); } - let deposit_info = attributes + let deposit_info: &str = attributes .iter() - .find(|tag| tag.key.as_ref() == DEPOSIT_INFO) + .find(|tag| tag.key == DEPOSIT_INFO) .ok_or(CoconutError::DepositInfoNotFound)? .value .as_ref(); @@ -69,21 +69,19 @@ pub async fn extract_encryption_key( } let verification_key = identity::PublicKey::from_base58_string( - attributes + &attributes .iter() - .find(|tag| tag.key.as_ref() == DEPOSIT_IDENTITY_KEY) + .find(|tag| tag.key == DEPOSIT_IDENTITY_KEY) .ok_or(CoconutError::DepositVerifKeyNotFound)? - .value - .as_ref(), + .value, )?; let encryption_key = encryption::PublicKey::from_base58_string( - attributes + &attributes .iter() - .find(|tag| tag.key.as_ref() == DEPOSIT_ENCRYPTION_KEY) + .find(|tag| tag.key == DEPOSIT_ENCRYPTION_KEY) .ok_or(CoconutError::DepositEncrKeyNotFound)? - .value - .as_ref(), + .value, )?; verification_key.verify(&message, &signature)?; @@ -97,8 +95,8 @@ mod test { use crate::coconut::tests::tx_entry_fixture; use nym_coconut::{prepare_blind_sign, BlindSignRequest, Parameters}; use nym_config::defaults::VOUCHER_INFO; - use nym_validator_client::nyxd::tx::Hash; - use nym_validator_client::nyxd::{Event, Tag}; + use nym_validator_client::nyxd::Hash; + use nym_validator_client::nyxd::{Event, EventAttribute}; use rand_07::rngs::OsRng; use std::str::FromStr; @@ -182,7 +180,7 @@ mod test { ); tx_entry.tx_result.events.push(Event { - type_str: format!("wasm-{}", DEPOSITED_FUNDS_EVENT_TYPE), + kind: format!("wasm-{}", DEPOSITED_FUNDS_EVENT_TYPE), attributes: vec![], }); let err = extract_encryption_key(&correct_request, tx_entry.clone()) @@ -193,9 +191,10 @@ mod test { CoconutError::DepositValueNotFound.to_string(), ); - tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![Tag { + tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![EventAttribute { key: DEPOSIT_VALUE.parse().unwrap(), value: "10".parse().unwrap(), + index: false, }]; let err = extract_encryption_key(&correct_request, tx_entry.clone()) .await @@ -206,9 +205,10 @@ mod test { .to_string(), ); - tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![Tag { + tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![EventAttribute { key: DEPOSIT_VALUE.parse().unwrap(), value: "1234".parse().unwrap(), + index: false, }]; let err = extract_encryption_key(&correct_request, tx_entry.clone()) .await @@ -219,13 +219,15 @@ mod test { ); tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![ - Tag { + EventAttribute { key: DEPOSIT_VALUE.parse().unwrap(), value: "1234".parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_INFO.parse().unwrap(), value: "bandwidth deposit info".parse().unwrap(), + index: false, }, ]; let err = extract_encryption_key(&correct_request, tx_entry.clone()) @@ -241,13 +243,15 @@ mod test { ); tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![ - Tag { + EventAttribute { key: DEPOSIT_VALUE.parse().unwrap(), value: "1234".parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_INFO.parse().unwrap(), value: VOUCHER_INFO.parse().unwrap(), + index: false, }, ]; let err = extract_encryption_key(&correct_request, tx_entry.clone()) @@ -259,17 +263,20 @@ mod test { ); tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![ - Tag { + EventAttribute { key: DEPOSIT_VALUE.parse().unwrap(), value: "1234".parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_INFO.parse().unwrap(), value: VOUCHER_INFO.parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_IDENTITY_KEY.parse().unwrap(), value: "verification key".parse().unwrap(), + index: false, }, ]; let err = extract_encryption_key(&correct_request, tx_entry.clone()) @@ -284,19 +291,22 @@ mod test { )); tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![ - Tag { + EventAttribute { key: DEPOSIT_VALUE.parse().unwrap(), value: "1234".parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_INFO.parse().unwrap(), value: VOUCHER_INFO.parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_IDENTITY_KEY.parse().unwrap(), value: "2eSxwquNJb2nZTEW5p4rbqjHfBaz9UaNhjHHiexPN4He" .parse() .unwrap(), + index: false, }, ]; let err = extract_encryption_key(&correct_request, tx_entry.clone()) @@ -308,23 +318,27 @@ mod test { ); tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![ - Tag { + EventAttribute { key: DEPOSIT_VALUE.parse().unwrap(), value: "1234".parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_INFO.parse().unwrap(), value: VOUCHER_INFO.parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_IDENTITY_KEY.parse().unwrap(), value: "6EJGMdEq7t8Npz54uPkftGsdmj7DKntLVputAnDfVZB2" .parse() .unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_ENCRYPTION_KEY.parse().unwrap(), value: "encryption key".parse().unwrap(), + index: false, }, ]; let err = extract_encryption_key(&correct_request, tx_entry.clone()) @@ -340,23 +354,27 @@ mod test { let expected_encryption_key = "HxnTpWTkgigSTAysVKLE8pEiUULHdTT1BxFfzfJvQRi6"; tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![ - Tag { + EventAttribute { key: DEPOSIT_VALUE.parse().unwrap(), value: "1234".parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_INFO.parse().unwrap(), value: VOUCHER_INFO.parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_IDENTITY_KEY.parse().unwrap(), value: "6EJGMdEq7t8Npz54uPkftGsdmj7DKntLVputAnDfVZB2" .parse() .unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_ENCRYPTION_KEY.parse().unwrap(), value: expected_encryption_key.parse().unwrap(), + index: false, }, ]; let err = extract_encryption_key(&correct_request, tx_entry.clone()) @@ -405,25 +423,29 @@ mod test { 4, ); tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![ - Tag { + EventAttribute { key: DEPOSIT_VALUE.parse().unwrap(), value: "1234".parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_INFO.parse().unwrap(), value: VOUCHER_INFO.parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_IDENTITY_KEY.parse().unwrap(), value: "64auwDkWan7R8yH1Mwe9dS4qXgrDBCUNDg3Q4KFnd2P5" .parse() .unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_ENCRYPTION_KEY.parse().unwrap(), value: "HxnTpWTkgigSTAysVKLE8pEiUULHdTT1BxFfzfJvQRi6" .parse() .unwrap(), + index: false, }, ]; let encryption_key = extract_encryption_key(&correct_request, tx_entry.clone()) diff --git a/nym-api/src/coconut/helpers.rs b/nym-api/src/coconut/helpers.rs index 225d04bc3b..775e761fea 100644 --- a/nym-api/src/coconut/helpers.rs +++ b/nym-api/src/coconut/helpers.rs @@ -11,7 +11,7 @@ pub(crate) fn accepted_vote_err(ret: Result<(), CoconutError>) -> Result<(), Coc let accepted_err = nym_multisig_contract_common::error::ContractError::NotOpen {}.to_string(); // If redundant voting is not the case, error out on all other error variants - if !log.value().contains(&accepted_err) { + if !log.contains(&accepted_err) { ret?; } } diff --git a/nym-api/src/coconut/tests.rs b/nym-api/src/coconut/tests.rs index 7ebf4908a7..37153f1370 100644 --- a/nym-api/src/coconut/tests.rs +++ b/nym-api/src/coconut/tests.rs @@ -28,7 +28,9 @@ use nym_validator_client::nym_api::routes::{ API_VERSION, BANDWIDTH, COCONUT_BLIND_SIGN, COCONUT_ROUTES, COCONUT_VERIFY_BANDWIDTH_CREDENTIAL, }; use nym_validator_client::nyxd::Coin; -use nym_validator_client::nyxd::{tx::Hash, AccountId, DeliverTx, Event, Fee, Tag, TxResponse}; +use nym_validator_client::nyxd::{ + AccountId, Algorithm, DeliverTx, Event, EventAttribute, Fee, Hash, TxResponse, +}; use crate::coconut::State; use crate::support::storage::NymApiStorage; @@ -365,7 +367,7 @@ impl super::client::Client for DummyClient { .add_attribute(NODE_INDEX, assigned_index.to_string())], }], data: Default::default(), - transaction_hash: Hash::new([0; 32]), + transaction_hash: Hash::from_bytes(Algorithm::Sha256, &[0; 32]).unwrap(), gas_info: Default::default(), }) } @@ -389,7 +391,7 @@ impl super::client::Client for DummyClient { Ok(ExecuteResult { logs: vec![], data: Default::default(), - transaction_hash: Hash::new([0; 32]), + transaction_hash: Hash::from_bytes(Algorithm::Sha256, &[0; 32]).unwrap(), gas_info: Default::default(), }) } @@ -443,6 +445,8 @@ impl super::client::Client for DummyClient { percentage: Decimal::from_ratio(2u32, 3u32), total_weight: 100, }, + proposer: Addr::unchecked(self.validator_address.as_ref()), + deposit: None, }; self.proposal_db .write() @@ -455,7 +459,7 @@ impl super::client::Client for DummyClient { .add_attribute(DKG_PROPOSAL_ID, proposal_id.to_string())], }], data: Default::default(), - transaction_hash: Hash::new([0; 32]), + transaction_hash: Hash::from_bytes(Algorithm::Sha256, &[0; 32]).unwrap(), gas_info: Default::default(), }) } @@ -496,7 +500,7 @@ pub fn tx_entry_fixture(tx_hash: &str) -> TxResponse { events: vec![], codespace: Default::default(), }, - tx: vec![].into(), + tx: vec![], proof: None, } } @@ -747,33 +751,37 @@ async fn blind_sign_correct() { let mut tx_entry = tx_entry_fixture(&tx_hash.to_string()); tx_entry.tx_result.events.push(Event { - type_str: format!("wasm-{}", DEPOSITED_FUNDS_EVENT_TYPE), + kind: format!("wasm-{}", DEPOSITED_FUNDS_EVENT_TYPE), attributes: vec![], }); tx_entry.tx_result.events.get_mut(0).unwrap().attributes = vec![ - Tag { + EventAttribute { key: DEPOSIT_VALUE.parse().unwrap(), value: "1234".parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_INFO.parse().unwrap(), value: VOUCHER_INFO.parse().unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_IDENTITY_KEY.parse().unwrap(), value: identity_keypair .public_key() .to_base58_string() .parse() .unwrap(), + index: false, }, - Tag { + EventAttribute { key: DEPOSIT_ENCRYPTION_KEY.parse().unwrap(), value: encryption_keypair .public_key() .to_base58_string() .parse() .unwrap(), + index: false, }, ]; tx_db @@ -904,6 +912,8 @@ async fn verification_of_bandwidth_credential() { percentage: Decimal::from_ratio(2u32, 3u32), total_weight: 100, }, + proposer: Addr::unchecked("proposer"), + deposit: None, }; // Test the endpoint with a different blinded serial number in the description diff --git a/nym-api/src/support/cli/mod.rs b/nym-api/src/support/cli/mod.rs index facbc6c7ff..1bcc518ad4 100644 --- a/nym-api/src/support/cli/mod.rs +++ b/nym-api/src/support/cli/mod.rs @@ -9,6 +9,7 @@ use anyhow::Result; use clap::Parser; use lazy_static::lazy_static; use nym_bin_common::build_information::BinaryBuildInformation; +use nym_config::defaults::var_names::NYXD; use nym_config::OptionalSet; use nym_validator_client::nyxd; @@ -109,7 +110,11 @@ pub(crate) struct CliArgs { pub(crate) fn override_config(config: Config, args: CliArgs) -> Config { config - .with_optional(Config::with_custom_nyxd_validator, args.nyxd_validator) + .with_optional_env( + Config::with_custom_nyxd_validator, + args.nyxd_validator, + NYXD, + ) .with_optional_env( Config::with_custom_mixnet_contract, args.mixnet_contract, diff --git a/nym-api/src/support/nyxd/mod.rs b/nym-api/src/support/nyxd/mod.rs index 8d2fe53dc5..e35098ee1f 100644 --- a/nym-api/src/support/nyxd/mod.rs +++ b/nym-api/src/support/nyxd/mod.rs @@ -35,13 +35,13 @@ use nym_validator_client::nyxd::{ cosmwasm_client::types::ExecuteResult, traits::{ CoconutBandwidthQueryClient, DkgQueryClient, DkgSigningClient, GroupQueryClient, - MultisigQueryClient, MultisigSigningClient, NameServiceQueryClient, + MultisigQueryClient, MultisigSigningClient, NameServiceQueryClient, VestingQueryClient, }, Fee, }; use nym_validator_client::nyxd::{ hash::{Hash, SHA256_HASH_SIZE}, - AccountId, Coin, DirectSigningNyxdClient, TendermintTime, VestingQueryClient, + AccountId, Coin, DirectSigningNyxdClient, TendermintTime, }; use nym_validator_client::ValidatorClientError; use nym_vesting_contract_common::AccountVestingCoins; @@ -321,8 +321,8 @@ impl crate::coconut::client::Client for Client { &self, tx_hash: &str, ) -> crate::coconut::error::Result { - let tx_hash = tx_hash - .parse::() + let tx_hash: Hash = tx_hash + .parse() .map_err(|_| CoconutError::TxHashParseError)?; Ok(self.0.read().await.nyxd.get_tx(tx_hash).await?) } diff --git a/nym-connect/desktop/CHANGELOG.md b/nym-connect/desktop/CHANGELOG.md index 174b5e938d..1149c3b227 100644 --- a/nym-connect/desktop/CHANGELOG.md +++ b/nym-connect/desktop/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +## [v1.1.15] (2023-07-25) + +- NC Desktop - remove sentry DSN from code ([#3694]) +- NC - Add Alephium wallet in the supported app list ([#3681]) + +[#3694]: https://github.com/nymtech/nym/issues/3694 +[#3681]: https://github.com/nymtech/nym/issues/3681 + ## [v1.1.14] (2023-07-04) - Nym connect fails to start when encountering an old config version ([#3588]) diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index 0cb8b27caf..6287fadf38 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -8,6 +8,15 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -88,6 +97,9 @@ name = "anyhow" version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +dependencies = [ + "backtrace", +] [[package]] name = "arrayref" @@ -193,12 +205,33 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide 0.7.1", + "object", + "rustc-demangle", +] + [[package]] name = "base16ct" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" @@ -228,18 +261,18 @@ dependencies = [ [[package]] name = "bip32" -version = "0.3.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873faa4363bfc54c36a48321da034c92a0645a363eed34d948683ffc1706e37f" +checksum = "7e141fb0f8be1c7b45887af94c88b182472b57c96b56773250ae00cd6a14a164" dependencies = [ - "bs58", - "hmac 0.11.0", - "k256", + "bs58 0.5.0", + "hmac 0.12.1", + "k256 0.13.1", "once_cell", "pbkdf2", "rand_core 0.6.4", - "ripemd160", - "sha2 0.9.9", + "ripemd", + "sha2 0.10.6", "subtle 2.4.1", "zeroize", ] @@ -251,8 +284,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "rand 0.8.5", - "rand_core 0.6.4", + "rand 0.7.3", + "rand_core 0.5.1", "serde", "unicode-normalization", "zeroize", @@ -311,7 +344,7 @@ dependencies = [ "cc", "cfg-if", "constant_time_eq", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -326,7 +359,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", "generic-array 0.14.7", ] @@ -339,12 +371,6 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - [[package]] name = "bls12_381" version = "0.5.0" @@ -399,8 +425,14 @@ name = "bs58" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" dependencies = [ - "sha2 0.9.9", + "sha2 0.10.6", ] [[package]] @@ -442,6 +474,9 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "cairo-rs" @@ -707,9 +742,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.7.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "constant_time_eq" @@ -766,8 +801,9 @@ dependencies = [ [[package]] name = "cosmos-sdk-proto" -version = "0.12.3" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" dependencies = [ "prost", "prost-types", @@ -776,17 +812,16 @@ dependencies = [ [[package]] name = "cosmrs" -version = "0.7.1" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af13955d6f356272e6def9ff5e2450a7650df536d8934f47052a20c76513d2f6" dependencies = [ "bip32", "cosmos-sdk-proto", - "ecdsa", + "ecdsa 0.16.8", "eyre", "getrandom 0.2.10", - "k256", - "prost", - "prost-types", + "k256 0.13.1", "rand_core 0.6.4", "serde", "serde_json", @@ -798,39 +833,66 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.0.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd" +checksum = "75836a10cb9654c54e77ee56da94d592923092a10b369cdb0dbd56acefc16340" dependencies = [ - "digest 0.9.0", + "digest 0.10.7", "ed25519-zebra", - "k256", + "k256 0.11.6", "rand_core 0.6.4", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.0.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4" +checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" dependencies = [ "syn 1.0.109", ] [[package]] -name = "cosmwasm-std" -version = "1.0.0" +name = "cosmwasm-schema" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195" +checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc" +dependencies = [ + "cosmwasm-schema-derive", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "cosmwasm-std" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb" dependencies = [ "base64 0.13.1", "cosmwasm-crypto", "cosmwasm-derive", + "derivative", "forward_ref", + "hex", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.5.1", + "sha2 0.10.6", "thiserror", "uint", ] @@ -944,9 +1006,21 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.3.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", @@ -1063,11 +1137,25 @@ dependencies = [ ] [[package]] -name = "cw-controllers" -version = "0.13.4" +name = "curve25519-dalek-ng" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", +] + +[[package]] +name = "cw-controllers" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071" +dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "cw-utils", @@ -1078,9 +1166,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a" +checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" dependencies = [ "cosmwasm-std", "schemars", @@ -1089,22 +1177,26 @@ dependencies = [ [[package]] name = "cw-utils" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b" +checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw2", "schemars", + "semver 1.0.17", "serde", "thiserror", ] [[package]] name = "cw2" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" +checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", @@ -1112,11 +1204,12 @@ dependencies = [ ] [[package]] -name = "cw3" -version = "0.13.4" +name = "cw20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b" +checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-utils", "schemars", @@ -1124,11 +1217,27 @@ dependencies = [ ] [[package]] -name = "cw4" -version = "0.13.4" +name = "cw3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd" +checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa" dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "cw20", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw4" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b" +dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", @@ -1195,12 +1304,44 @@ dependencies = [ ] [[package]] -name = "der" -version = "0.5.1" +name = "debugid" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid 1.3.0", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1236,11 +1377,12 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle 2.4.1", ] @@ -1321,9 +1463,9 @@ checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" [[package]] name = "dotenvy" -version = "0.15.6" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "dtoa" @@ -1354,14 +1496,28 @@ checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "ecdsa" -version = "0.13.4" +version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der 0.7.7", + "digest 0.10.7", + "elliptic-curve 0.13.5", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki 0.7.2", ] [[package]] @@ -1370,7 +1526,30 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "signature 1.6.4", +] + +[[package]] +name = "ed25519" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.1.0", +] + +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", ] [[package]] @@ -1380,7 +1559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek", - "ed25519", + "ed25519 1.5.3", "rand 0.7.3", "serde", "sha2 0.9.9", @@ -1410,18 +1589,39 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" -version = "0.11.12" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "ff 0.11.1", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", "generic-array 0.14.7", - "group 0.11.0", + "group 0.12.1", + "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.2", + "digest 0.10.7", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", "subtle 2.4.1", "zeroize", ] @@ -1474,6 +1674,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + [[package]] name = "errno" version = "0.2.8" @@ -1550,6 +1756,26 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle 2.4.1", +] + [[package]] name = "field-offset" version = "0.3.5" @@ -1572,6 +1798,18 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "fix-path-env" version = "0.1.0" @@ -1588,7 +1826,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.6.2", ] [[package]] @@ -1877,6 +2115,7 @@ dependencies = [ "serde", "typenum", "version_check", + "zeroize", ] [[package]] @@ -1917,6 +2156,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "gio" version = "0.15.12" @@ -2071,6 +2316,28 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core 0.6.4", + "subtle 2.4.1", +] + [[package]] name = "gtk" version = "0.15.5" @@ -2138,7 +2405,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.2", "slab", "tokio", "tokio-util", @@ -2177,6 +2444,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "hashlink" version = "0.7.0" @@ -2309,7 +2582,29 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", ] [[package]] @@ -2539,6 +2834,16 @@ dependencies = [ "serde", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + [[package]] name = "infer" version = "0.7.0" @@ -2702,25 +3007,28 @@ dependencies = [ [[package]] name = "k256" -version = "0.10.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", - "ecdsa", - "elliptic-curve", - "sec1", - "sha2 0.9.9", - "sha3", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.6", ] [[package]] -name = "keccak" -version = "0.1.3" +name = "k256" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ - "cpufeatures", + "cfg-if", + "ecdsa 0.16.8", + "elliptic-curve 0.13.5", + "once_cell", + "sha2 0.10.6", + "signature 2.1.0", ] [[package]] @@ -2947,6 +3255,12 @@ dependencies = [ "tendril", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -3004,6 +3318,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.6" @@ -3192,7 +3515,7 @@ dependencies = [ name = "nym-api-requests" version = "0.1.0" dependencies = [ - "bs58", + "bs58 0.4.0", "cosmrs", "cosmwasm-std", "getset", @@ -3283,7 +3606,7 @@ name = "nym-coconut" version = "0.5.0" dependencies = [ "bls12_381 0.6.0", - "bs58", + "bs58 0.4.0", "digest 0.9.0", "ff 0.11.1", "getrandom 0.2.10", @@ -3324,7 +3647,7 @@ dependencies = [ name = "nym-coconut-interface" version = "0.1.0" dependencies = [ - "bs58", + "bs58 0.4.0", "getset", "nym-coconut", "serde", @@ -3351,6 +3674,7 @@ dependencies = [ "anyhow", "bip39", "dirs 4.0.0", + "dotenvy", "eyre", "fern", "fix-path-env", @@ -3371,6 +3695,8 @@ dependencies = [ "rand 0.8.5", "reqwest", "rust-embed", + "sentry", + "sentry-log", "serde", "serde_json", "serde_repr", @@ -3380,10 +3706,10 @@ dependencies = [ "tauri-codegen", "tauri-macros", "tempfile", - "tendermint-rpc", "thiserror", "time", "tokio", + "toml 0.7.4", "ts-rs", "url", "yaml-rust", @@ -3393,7 +3719,7 @@ dependencies = [ name = "nym-contracts-common" version = "0.5.0" dependencies = [ - "bs58", + "bs58 0.4.0", "cosmwasm-std", "schemars", "serde", @@ -3430,10 +3756,10 @@ version = "0.4.0" dependencies = [ "aes 0.8.2", "blake3", - "bs58", + "bs58 0.4.0", "cipher 0.4.4", "ctr 0.9.2", - "digest 0.10.6", + "digest 0.10.7", "ed25519-dalek", "generic-array 0.14.7", "hkdf 0.12.3", @@ -3453,7 +3779,7 @@ version = "0.1.0" dependencies = [ "bitvec", "bls12_381 0.6.0", - "bs58", + "bs58 0.4.0", "ff 0.11.1", "group 0.11.0", "lazy_static", @@ -3503,7 +3829,7 @@ dependencies = [ name = "nym-gateway-requests" version = "0.1.0" dependencies = [ - "bs58", + "bs58 0.4.0", "futures", "generic-array 0.14.7", "log", @@ -3524,6 +3850,8 @@ dependencies = [ name = "nym-group-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", + "cw-controllers", "cw4", "schemars", "serde", @@ -3533,14 +3861,14 @@ dependencies = [ name = "nym-mixnet-contract-common" version = "0.6.0" dependencies = [ - "bs58", + "bs58 0.4.0", "cosmwasm-std", "humantime-serde", "log", "nym-contracts-common", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.4.1", "serde_repr", "thiserror", "time", @@ -3550,7 +3878,9 @@ dependencies = [ name = "nym-multisig-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw-storage-plus", "cw-utils", "cw3", "cw4", @@ -3762,7 +4092,7 @@ dependencies = [ name = "nym-sphinx-anonymous-replies" version = "0.1.0" dependencies = [ - "bs58", + "bs58 0.4.0", "nym-crypto", "nym-sphinx-addressing", "nym-sphinx-params", @@ -3871,7 +4201,7 @@ name = "nym-topology" version = "0.1.0" dependencies = [ "async-trait", - "bs58", + "bs58 0.4.0", "log", "nym-bin-common", "nym-crypto", @@ -3920,6 +4250,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.9.9", + "tendermint-rpc", "thiserror", "tokio", "url", @@ -3994,6 +4325,15 @@ dependencies = [ "objc", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -4083,6 +4423,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "os_info" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +dependencies = [ + "log", + "serde", + "winapi", +] + [[package]] name = "os_str_bytes" version = "6.5.0" @@ -4200,11 +4551,12 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pbkdf2" -version = "0.9.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "crypto-mac 0.11.1", + "digest 0.10.7", + "hmac 0.12.1", ] [[package]] @@ -4427,13 +4779,22 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", - "zeroize", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.7", + "spki 0.7.2", ] [[package]] @@ -4449,7 +4810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590" dependencies = [ "base64 0.21.2", - "indexmap", + "indexmap 1.9.2", "line-wrap", "quick-xml 0.28.1", "serde", @@ -4465,7 +4826,7 @@ dependencies = [ "bitflags 1.3.2", "crc32fast", "flate2", - "miniz_oxide", + "miniz_oxide 0.6.2", ] [[package]] @@ -4552,9 +4913,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.10.4" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", "prost-derive", @@ -4562,9 +4923,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.10.1" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", @@ -4575,11 +4936,10 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.10.1" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "bytes", "prost", ] @@ -4833,15 +5193,25 @@ dependencies = [ [[package]] name = "rfc6979" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", - "hmac 0.11.0", + "crypto-bigint 0.4.9", + "hmac 0.12.1", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle 2.4.1", +] + [[package]] name = "rfd" version = "0.10.0" @@ -4882,14 +5252,12 @@ dependencies = [ ] [[package]] -name = "ripemd160" -version = "0.9.1" +name = "ripemd" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug 0.3.0", + "digest 0.10.7", ] [[package]] @@ -4927,6 +5295,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc_version" version = "0.4.0" @@ -5039,7 +5413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" dependencies = [ "dyn-clone", - "indexmap", + "indexmap 1.9.2", "schemars_derive", "serde", "serde_json", @@ -5091,13 +5465,28 @@ dependencies = [ [[package]] name = "sec1" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "der", + "base16ct 0.1.1", + "der 0.6.1", "generic-array 0.14.7", - "pkcs8", + "pkcs8 0.9.0", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.7", + "generic-array 0.14.7", + "pkcs8 0.10.2", "subtle 2.4.1", "zeroize", ] @@ -5172,6 +5561,136 @@ dependencies = [ "pest", ] +[[package]] +name = "sentry" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b0ad16faa5d12372f914ed40d00bda21a6d1bdcc99264c5e5e1c9495cf3654" +dependencies = [ + "httpdate", + "native-tls", + "reqwest", + "sentry-anyhow", + "sentry-backtrace", + "sentry-contexts", + "sentry-core", + "sentry-debug-images", + "sentry-panic", + "sentry-tracing", + "tokio", + "ureq", +] + +[[package]] +name = "sentry-anyhow" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3a571f02f9982af445af829c4837fe4857568a431bd2bed9f7cf88de4a6c44" +dependencies = [ + "anyhow", + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-backtrace" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f2ee8f147bb5f22ac59b5c35754a759b9a6f6722402e2a14750b2a63fc59bd" +dependencies = [ + "backtrace", + "once_cell", + "regex", + "sentry-core", +] + +[[package]] +name = "sentry-contexts" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcd133362c745151eeba0ac61e3ba8350f034e9fe7509877d08059fe1d7720c6" +dependencies = [ + "hostname", + "libc", + "os_info", + "rustc_version", + "sentry-core", + "uname", +] + +[[package]] +name = "sentry-core" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7163491708804a74446642ff2c80b3acd668d4b9e9f497f85621f3d250fd012b" +dependencies = [ + "once_cell", + "rand 0.8.5", + "sentry-types", + "serde", + "serde_json", +] + +[[package]] +name = "sentry-debug-images" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a5003d7ff08aa3b2b76994080b183e8cfa06c083e280737c9cee02ca1c70f5e" +dependencies = [ + "findshlibs", + "once_cell", + "sentry-core", +] + +[[package]] +name = "sentry-log" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2558fc4a85326e6063711b45ce82ed6b18cdacd0732580c1567da914ac1df33e" +dependencies = [ + "log", + "sentry-core", +] + +[[package]] +name = "sentry-panic" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4dfe8371c9b2e126a8b64f6fefa54cef716ff2a50e63b5558a48b899265bccd" +dependencies = [ + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-tracing" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca8b88978677a27ee1a91beafe4052306c474c06f582321fde72d2e2cc2f7f" +dependencies = [ + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sentry-types" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e7a88e0c1922d19b3efee12a8215f6a8a806e442e665ada71cc222cab72985f" +dependencies = [ + "debugid", + "getrandom 0.2.10", + "hex", + "serde", + "serde_json", + "thiserror", + "time", + "url", + "uuid 1.3.0", +] + [[package]] name = "serde" version = "1.0.158" @@ -5190,6 +5709,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-json-wasm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +dependencies = [ + "serde", +] + [[package]] name = "serde_bytes" version = "0.11.9" @@ -5245,9 +5773,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" dependencies = [ "serde", ] @@ -5339,7 +5867,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -5363,19 +5891,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha3" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug 0.3.0", + "digest 0.10.7", ] [[package]] @@ -5398,11 +5914,21 @@ dependencies = [ [[package]] name = "signature" -version = "1.4.0" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.9.0", + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", "rand_core 0.6.4", ] @@ -5474,7 +6000,7 @@ dependencies = [ "aes 0.7.5", "arrayref", "blake2", - "bs58", + "bs58 0.4.0", "byteorder", "chacha", "curve25519-dalek", @@ -5506,12 +6032,22 @@ dependencies = [ [[package]] name = "spki" -version = "0.5.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.7", ] [[package]] @@ -5579,7 +6115,7 @@ dependencies = [ "futures-util", "hashlink 0.7.0", "hex", - "indexmap", + "indexmap 1.9.2", "itoa 1.0.6", "libc", "libsqlite3-sys", @@ -5625,7 +6161,7 @@ dependencies = [ "futures-util", "hashlink 0.8.1", "hex", - "indexmap", + "indexmap 1.9.2", "itoa 1.0.6", "libc", "libsqlite3-sys", @@ -5821,6 +6357,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "syn" version = "1.0.109" @@ -6150,28 +6692,28 @@ dependencies = [ [[package]] name = "tendermint" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca881fa4dedd2b46334f13be7fbc8cc1549ba4be5a833fe4e73d1a1baaf7949" +checksum = "3f0a7d05cf78524782337f8edd55cbc578d159a16ad4affe2135c92f7dbac7f0" dependencies = [ - "async-trait", "bytes", - "ed25519", - "ed25519-dalek", + "digest 0.10.7", + "ed25519 2.2.1", + "ed25519-consensus", "flex-error", "futures", - "k256", + "k256 0.13.1", "num-traits", "once_cell", "prost", "prost-types", - "ripemd160", + "ripemd", "serde", "serde_bytes", "serde_json", "serde_repr", - "sha2 0.9.9", - "signature", + "sha2 0.10.6", + "signature 2.1.0", "subtle 2.4.1", "subtle-encoding", "tendermint-proto", @@ -6181,9 +6723,9 @@ dependencies = [ [[package]] name = "tendermint-config" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c56ee93f4e9b7e7daba86d171f44572e91b741084384d0ae00df7991873dfd" +checksum = "71a72dbbea6dde12045d261f2c70c0de039125675e8a026c8d5ad34522756372" dependencies = [ "flex-error", "serde", @@ -6195,9 +6737,9 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71f925d74903f4abbdc4af0110635a307b3cb05b175fdff4a7247c14a4d0874" +checksum = "c0cec054567d16d85e8c3f6a3139963d1a66d9d3051ed545d31562550e9bcc3d" dependencies = [ "bytes", "flex-error", @@ -6213,9 +6755,9 @@ dependencies = [ [[package]] name = "tendermint-rpc" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63f57ee05a1e927887191c76d1b139de9fa40c180b9f8727ee44377242a6" +checksum = "d119d83a130537fc4a98c3c9eb6899ebe857fea4860400a61675bfb5f0b35129" dependencies = [ "async-trait", "bytes", @@ -6228,9 +6770,11 @@ dependencies = [ "hyper-rustls", "peg", "pin-project", + "semver 1.0.17", "serde", "serde_bytes", "serde_json", + "subtle 2.4.1", "subtle-encoding", "tendermint", "tendermint-config", @@ -6482,20 +7026,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.10" +version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" dependencies = [ - "indexmap", + "indexmap 2.0.0", "serde", "serde_spanned", "toml_datetime", @@ -6652,6 +7196,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -6707,6 +7260,19 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "ureq" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b11c96ac7ee530603dcdf68ed1557050f374ce55a5a07193ebf8cbc9f8927e9" +dependencies = [ + "base64 0.21.2", + "log", + "native-tls", + "once_cell", + "url", +] + [[package]] name = "url" version = "2.3.1" @@ -6744,6 +7310,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" dependencies = [ "getrandom 0.2.10", + "serde", ] [[package]] diff --git a/nym-connect/desktop/package.json b/nym-connect/desktop/package.json index 60ea3b4830..626b415b8f 100644 --- a/nym-connect/desktop/package.json +++ b/nym-connect/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@nym/nym-connect", - "version": "1.1.13", + "version": "1.1.15", "main": "index.js", "license": "MIT", "scripts": { @@ -115,4 +115,4 @@ "webpack-merge": "^5.8.0", "yaml-loader": "^0.8.0" } -} +} \ No newline at end of file diff --git a/nym-connect/desktop/scripts/pre_medium_build.sh b/nym-connect/desktop/scripts/pre_medium_build.sh new file mode 100755 index 0000000000..5a797ffc8f --- /dev/null +++ b/nym-connect/desktop/scripts/pre_medium_build.sh @@ -0,0 +1,17 @@ +#! /bin/bash + +set -e + +if ! [[ "$RELEASE_TAG" =~ ^nym-connect-s-v.*? ]]; then + echo -e " ✗ Invalid release tag $RELEASE_TAG" + exit 1 +fi + +version="${RELEASE_TAG#nym-connect-s-v}" + +sed -i "s/^name = \".*\"/name = \"nym-connect-s\"/" src-tauri/Cargo.toml +sed -i "s/^version = \".*\"/version = \"$version\"/" src-tauri/Cargo.toml +sed -i 's/"productName": "nym-connect"/"productName": "NymConnect S"/' src-tauri/tauri.conf.json +sed -i "s/\"version\": \".*\"/\"version\": \"$version\"/" src-tauri/tauri.conf.json + +echo -e " ✓ bump version $version" diff --git a/nym-connect/desktop/src-tauri/Cargo.toml b/nym-connect/desktop/src-tauri/Cargo.toml index ffd4c43461..e3bd49cb41 100644 --- a/nym-connect/desktop/src-tauri/Cargo.toml +++ b/nym-connect/desktop/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-connect" -version = "1.1.13" +version = "1.1.15" description = "nym-connect" authors = ["Nym Technologies SA"] license = "" @@ -37,13 +37,16 @@ serde_json = "1.0" serde_repr = "0.1" tap = "1.0.1" tauri = { version = "^1.2.2", features = ["clipboard-write-text", "macos-private-api", "notification-all", "shell-open", "system-tray", "updater", "window-close", "window-minimize", "window-start-dragging"] } -tendermint-rpc = "0.23.0" +#tendermint-rpc = "0.23.0" thiserror = "1.0" time = { version = "0.3.17", features = ["local-offset"] } tokio = { version = "1.24.1", features = ["sync", "time"] } url = "2.2" yaml-rust = "0.4" - +toml = "0.7" +sentry = { version = "0.31.5", features = [ "anyhow" ] } +sentry-log = "0.31.5" +dotenvy = "0.15.7" nym-client-core = { path = "../../../common/client-core" } nym-api-requests = { path = "../../../nym-api/nym-api-requests" } diff --git a/nym-connect/desktop/src-tauri/src/build.rs b/nym-connect/desktop/src-tauri/src/build.rs index 261851f6b6..7c65873b94 100644 --- a/nym-connect/desktop/src-tauri/src/build.rs +++ b/nym-connect/desktop/src-tauri/src/build.rs @@ -1,3 +1,16 @@ +use std::env; + +mod constants; + +use constants::{SENTRY_DSN_JS, SENTRY_DSN_RUST}; + fn main() { + // set these env vars at compile time + if let Ok(dsn) = env::var(SENTRY_DSN_RUST) { + println!("cargo:rustc-env={}={}", SENTRY_DSN_RUST, dsn); + } + if let Ok(dsn) = env::var(SENTRY_DSN_JS) { + println!("cargo:rustc-env={}={}", SENTRY_DSN_JS, dsn); + } tauri_build::build(); } diff --git a/nym-connect/desktop/src-tauri/src/config/mod.rs b/nym-connect/desktop/src-tauri/src/config/mod.rs index 3c76500aab..f2cbff0d46 100644 --- a/nym-connect/desktop/src-tauri/src/config/mod.rs +++ b/nym-connect/desktop/src-tauri/src/config/mod.rs @@ -26,6 +26,10 @@ mod old_config_v1_1_20_2; mod persistence; mod template; mod upgrade; +mod user_data; + +pub use user_data::PrivacyLevel; +pub use user_data::UserData; static SOCKS5_CONFIG_ID: &str = "nym-connect"; diff --git a/nym-connect/desktop/src-tauri/src/config/user_data.rs b/nym-connect/desktop/src-tauri/src/config/user_data.rs new file mode 100644 index 0000000000..3e9287f375 --- /dev/null +++ b/nym-connect/desktop/src-tauri/src/config/user_data.rs @@ -0,0 +1,67 @@ +use eyre::{eyre, Context, Result}; +use log::error; +use serde::{Deserialize, Serialize}; +use std::{fs, str}; +use tauri::api::path::data_dir; + +const DATA_DIR: &str = "nym-connect"; +const DATA_FILE: &str = "user-data.toml"; + +#[derive(Serialize, Deserialize, Debug, Copy, Clone, Default)] +pub enum PrivacyLevel { + #[default] + High, + Medium, +} + +// User data is read from and write on disk +// Linux: $XDG_DATA_HOME or $HOME/.local/share/ +// macOS: $HOME/Library/Application Support +// Windows: {FOLDERID_RoamingAppData} +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +pub struct UserData { + pub monitoring: Option, + pub privacy_level: Option, +} + +fn create_directory_path() -> Result<()> { + let mut data_dir = data_dir().ok_or(eyre!("Failed to retrieve data directory"))?; + data_dir.push(DATA_DIR); + + fs::create_dir_all(&data_dir).context(format!( + "Failed to create user data directory path {}", + data_dir.display() + )) +} + +impl UserData { + pub fn read() -> Result { + // create the full directory path if it is missing + create_directory_path()?; + + let mut data_path = data_dir().ok_or(eyre!("Failed to retrieve data directory"))?; + + data_path.push(DATA_DIR); + data_path.push(DATA_FILE); + let content = fs::read(&data_path) + .context(format!("Failed to read user data {}", data_path.display()))?; + + toml::from_str::(str::from_utf8(&content)?).map_err(|e| { + error!("{}", e); + eyre!("{e}") + }) + } + + pub fn write(&self) -> Result<()> { + // create the full directory path if it is missing + create_directory_path()?; + + let mut data_path = data_dir().ok_or(eyre!("Failed to retrieve data directory"))?; + + data_path.push(DATA_DIR); + data_path.push(DATA_FILE); + let toml = toml::to_string(&self)?; + fs::write(data_path, toml)?; + Ok(()) + } +} diff --git a/nym-connect/desktop/src-tauri/src/constants.rs b/nym-connect/desktop/src-tauri/src/constants.rs new file mode 100644 index 0000000000..379fa02be8 --- /dev/null +++ b/nym-connect/desktop/src-tauri/src/constants.rs @@ -0,0 +1,3 @@ +// env var keys +pub const SENTRY_DSN_RUST: &str = "SENTRY_DSN_RUST"; +pub const SENTRY_DSN_JS: &str = "SENTRY_DSN_JS"; diff --git a/nym-connect/desktop/src-tauri/src/error.rs b/nym-connect/desktop/src-tauri/src/error.rs index 0540f01daf..651e741aea 100644 --- a/nym-connect/desktop/src-tauri/src/error.rs +++ b/nym-connect/desktop/src-tauri/src/error.rs @@ -70,6 +70,8 @@ pub enum BackendError { NewWindowError, #[error("unable to parse the specified gateway")] UnableToParseGateway, + #[error("unable to write user data to disk")] + UserDataWriteError, #[error("unable to load keys: {source}")] UnableToLoadKeys { diff --git a/nym-connect/desktop/src-tauri/src/logging.rs b/nym-connect/desktop/src-tauri/src/logging.rs index 3a6ee89e62..b1441a5a02 100644 --- a/nym-connect/desktop/src-tauri/src/logging.rs +++ b/nym-connect/desktop/src-tauri/src/logging.rs @@ -1,4 +1,6 @@ use fern::colors::{Color, ColoredLevelConfig}; +use log::Level; +use sentry::Level as SentryLevel; use serde::Serialize; use serde_repr::{Deserialize_repr, Serialize_repr}; use std::str::FromStr; @@ -22,7 +24,10 @@ fn formatted_time() -> String { _now.format(&format).unwrap() } -pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerError> { +pub fn setup_logging( + app_handle: tauri::AppHandle, + monitoring: bool, +) -> Result<(), log::SetLoggerError> { let colors = ColoredLevelConfig::new() .trace(Color::Magenta) .debug(Color::Blue) @@ -61,6 +66,21 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE level: record.level().into(), }; app_handle.emit_all("log://log", msg).unwrap(); + })) + .chain(fern::Output::call(move |record| { + if !monitoring { + return; + } + let level = match record.level() { + Level::Error => SentryLevel::Error, + Level::Warn => SentryLevel::Warning, + Level::Info => SentryLevel::Info, + _ => SentryLevel::Debug, + }; + // only send error and warn logs to sentry + if let Level::Error | Level::Warn = record.level() { + sentry::capture_message(&record.args().to_string(), level); + }; })); base_config @@ -84,16 +104,17 @@ impl FernExt for fern::Dispatch { } fn filter_lowlevel_external_components(self) -> Self { - self.level_for("hyper", log::LevelFilter::Warn) - .level_for("tokio_reactor", log::LevelFilter::Warn) - .level_for("reqwest", log::LevelFilter::Warn) + self.level_for("handlebars", log::LevelFilter::Warn) + .level_for("hyper", log::LevelFilter::Warn) .level_for("mio", log::LevelFilter::Warn) - .level_for("want", log::LevelFilter::Warn) - .level_for("sled", log::LevelFilter::Warn) - .level_for("tungstenite", log::LevelFilter::Warn) - .level_for("tokio_tungstenite", log::LevelFilter::Warn) + .level_for("reqwest", log::LevelFilter::Warn) .level_for("rustls", log::LevelFilter::Warn) + .level_for("sled", log::LevelFilter::Warn) + .level_for("tokio_reactor", log::LevelFilter::Warn) + .level_for("tokio_tungstenite", log::LevelFilter::Warn) .level_for("tokio_util", log::LevelFilter::Warn) + .level_for("tungstenite", log::LevelFilter::Warn) + .level_for("want", log::LevelFilter::Warn) } } diff --git a/nym-connect/desktop/src-tauri/src/main.rs b/nym-connect/desktop/src-tauri/src/main.rs index fc51403ab2..12c7ad06ac 100644 --- a/nym-connect/desktop/src-tauri/src/main.rs +++ b/nym-connect/desktop/src-tauri/src/main.rs @@ -3,49 +3,75 @@ windows_subsystem = "windows" )] +use std::env; use std::sync::Arc; use nym_config::defaults::setup_env; use tauri::Manager; use tokio::sync::RwLock; +use crate::config::UserData; use crate::menu::{create_tray_menu, tray_menu_event_handler}; use crate::state::State; use crate::window::window_toggle; mod config; +mod constants; mod error; mod events; mod logging; mod menu; mod models; +mod monitoring; mod operations; mod state; mod tasks; mod window; fn main() { - if std::env::var("NYM_CONNECT_ENABLE_MEDIUM").is_ok() { - std::env::set_var("NYM_CONNECT_DISABLE_COVER", "1"); - std::env::set_var("NYM_CONNECT_ENABLE_MIXED_SIZE_PACKETS", "1"); - std::env::set_var("NYM_CONNECT_DISABLE_PER_HOP_DELAYS", "1"); - } + dotenvy::dotenv().ok(); setup_env(None); println!("Starting up..."); // As per breaking change description here // https://github.com/tauri-apps/tauri/blob/feac1d193c6d618e49916ad0707201f43d5cdd36/tooling/bundler/CHANGELOG.md if let Err(error) = fix_path_env::fix() { - log::warn!("Failed to fix PATH: {error}"); + println!("Failed to fix PATH: {error}"); + } + + let user_data = UserData::read().unwrap_or_else(|e| { + println!("{}", e); + println!("Fallback to default"); + UserData::default() + }); + + let monitoring = user_data.monitoring.unwrap_or(false); + let mut _sentry_guard; + + if monitoring { + match monitoring::init() { + Ok(guard) => { + println!("Monitoring and error reporting enabled"); + + // we must keep the sentry guard in scope during app lifetime + _sentry_guard = guard; + } + Err(e) => { + println!("Unable to init monitoring: {e}"); + } + } } let context = tauri::generate_context!(); tauri::Builder::default() - .manage(Arc::new(RwLock::new(State::new()))) + .manage(Arc::new(RwLock::new(State::new(user_data)))) .invoke_handler(tauri::generate_handler![ crate::operations::config::get_config_file_location, crate::operations::config::get_config_id, crate::operations::common::get_env, + crate::operations::common::get_user_data, + crate::operations::common::set_monitoring, + crate::operations::common::set_privacy_level, crate::operations::connection::connect::get_gateway, crate::operations::connection::connect::get_service_provider, crate::operations::connection::connect::set_gateway, @@ -57,7 +83,7 @@ fn main() { crate::operations::connection::status::get_gateway_connection_status, crate::operations::connection::status::start_connection_health_check_task, crate::operations::directory::get_services, - crate::operations::directory::get_gateways_detailed, + crate::operations::directory::get_gateways, crate::operations::export::export_keys, crate::operations::window::hide_window, crate::operations::growth::test_and_earn::growth_tne_get_client_id, @@ -81,7 +107,7 @@ fn main() { ); } }) - .setup(|app| Ok(crate::logging::setup_logging(app.app_handle())?)) + .setup(move |app| Ok(crate::logging::setup_logging(app.app_handle(), monitoring)?)) .system_tray(create_tray_menu()) .on_system_tray_event(tray_menu_event_handler) .run(context) diff --git a/nym-connect/desktop/src-tauri/src/models/mod.rs b/nym-connect/desktop/src-tauri/src/models/mod.rs index fb43877675..1b9ed11e71 100644 --- a/nym-connect/desktop/src-tauri/src/models/mod.rs +++ b/nym-connect/desktop/src-tauri/src/models/mod.rs @@ -86,7 +86,6 @@ pub struct DirectoryService { #[derive(Debug, Clone, Deserialize, Serialize)] pub struct HarbourMasterService { pub service_provider_client_id: String, - pub gateway_identity_key: String, pub ip_address: String, pub last_successful_ping_utc: String, pub last_updated_utc: String, @@ -98,11 +97,9 @@ pub struct HarbourMasterService { pub struct DirectoryServiceProvider { pub id: String, pub description: String, - /// Address of the network requester in the form "." + /// Address of the network requester in the form ".@" /// e.g. DpB3cHAchJiNBQi5FrZx2csXb1mrHkpYh9Wzf8Rjsuko.ANNWrvHqMYuertHGHUrZdBntQhpzfbWekB39qez9U2Vx@2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh pub address: String, - /// Address of the gateway, e.g. 2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh - pub gateway: String, } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -112,3 +109,9 @@ pub struct PagedResult { pub total: i32, pub items: Vec, } + +#[cfg_attr(test, derive(ts_rs::TS))] +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Gateway { + pub identity: String, +} diff --git a/nym-connect/desktop/src-tauri/src/monitoring.rs b/nym-connect/desktop/src-tauri/src/monitoring.rs new file mode 100644 index 0000000000..e4555f3277 --- /dev/null +++ b/nym-connect/desktop/src-tauri/src/monitoring.rs @@ -0,0 +1,38 @@ +use std::env; + +use anyhow::{Context, Result}; +use sentry::ClientInitGuard; + +use crate::constants::{SENTRY_DSN_JS, SENTRY_DSN_RUST}; + +pub fn init() -> Result { + // if these env vars were set at compile time, use their value + if let Some(v) = option_env!("SENTRY_DSN_RUST") { + env::set_var(SENTRY_DSN_RUST, v); + } + if let Some(v) = option_env!("SENTRY_DSN_JS") { + env::set_var(SENTRY_DSN_JS, v); + } + + let dsn = env::var(SENTRY_DSN_RUST).context(format!("{} env var not set", SENTRY_DSN_RUST))?; + println!("using DSN {dsn}"); + let guard = sentry::init(( + dsn, + sentry::ClientOptions { + release: sentry::release_name!(), + sample_rate: 1.0, // TODO lower this in prod + traces_sample_rate: 1.0, + ..Default::default() // TODO add data scrubbing + // see https://docs.sentry.io/platforms/rust/data-management/sensitive-data/ + }, + )); + + sentry::configure_scope(|scope| { + scope.set_user(Some(sentry::User { + id: Some("nym".into()), + ..Default::default() + })); + }); + + Ok(guard) +} diff --git a/nym-connect/desktop/src-tauri/src/operations/common.rs b/nym-connect/desktop/src-tauri/src/operations/common.rs index 707dd19860..a4e9604d60 100644 --- a/nym-connect/desktop/src-tauri/src/operations/common.rs +++ b/nym-connect/desktop/src-tauri/src/operations/common.rs @@ -1,4 +1,9 @@ +use crate::config::PrivacyLevel; +use crate::error::Result; +use crate::{config::UserData, state::State}; use std::env; +use std::sync::Arc; +use tokio::sync::RwLock; #[tauri::command] pub async fn get_env(variable: String) -> Option { @@ -7,3 +12,27 @@ pub async fn get_env(variable: String) -> Option { var } + +#[tauri::command] +pub async fn get_user_data(state: tauri::State<'_, Arc>>) -> Result { + let guard = state.read().await; + Ok(guard.get_user_data().clone()) +} + +#[tauri::command] +pub async fn set_monitoring( + enabled: bool, + state: tauri::State<'_, Arc>>, +) -> Result<()> { + let mut guard = state.write().await; + guard.set_monitoring(enabled) +} + +#[tauri::command] +pub async fn set_privacy_level( + privacy_level: PrivacyLevel, + state: tauri::State<'_, Arc>>, +) -> Result<()> { + let mut guard = state.write().await; + guard.set_privacy_level(privacy_level) +} diff --git a/nym-connect/desktop/src-tauri/src/operations/connection/connect.rs b/nym-connect/desktop/src-tauri/src/operations/connection/connect.rs index 1d83748698..95a125fa30 100644 --- a/nym-connect/desktop/src-tauri/src/operations/connection/connect.rs +++ b/nym-connect/desktop/src-tauri/src/operations/connection/connect.rs @@ -12,6 +12,7 @@ pub async fn start_connecting( window: tauri::Window, ) -> Result { log::trace!("Start connecting"); + let (msg_receiver, exit_status_receiver) = { let mut state_w = state.write().await; state_w.start_connecting(&window).await? diff --git a/nym-connect/desktop/src-tauri/src/operations/connection/health_check.rs b/nym-connect/desktop/src-tauri/src/operations/connection/health_check.rs index 3f401933a1..52b47057b1 100644 --- a/nym-connect/desktop/src-tauri/src/operations/connection/health_check.rs +++ b/nym-connect/desktop/src-tauri/src/operations/connection/health_check.rs @@ -11,15 +11,15 @@ pub async fn run_health_check() -> bool { log::info!("Running network health check"); match crate::operations::http::socks5_get::<_, ConnectionSuccess>(HEALTH_CHECK_URL).await { Ok(res) if res.status == "ok" => { - log::info!("Healthcheck success!"); + log::info!("✅✅✅ Healthcheck success!"); true } Ok(res) => { - log::error!("Healthcheck failed with status: {}", res.status); + log::error!("⛔⛔⛔ Healthcheck failed with status: {}", res.status); false } Err(err) => { - log::error!("Healthcheck failed: {err}"); + log::error!("⛔⛔⛔ Healthcheck failed: {err}"); false } } diff --git a/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs b/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs index 6195d41283..a4f8bf53a9 100644 --- a/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs +++ b/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs @@ -1,11 +1,16 @@ -use itertools::Itertools; - -use crate::error::Result; -use crate::models::{ - DirectoryService, DirectoryServiceProvider, HarbourMasterService, PagedResult, +use crate::{ + config::PrivacyLevel, + error::Result, + models::{ + DirectoryService, DirectoryServiceProvider, Gateway, HarbourMasterService, PagedResult, + }, + state::State, }; +use itertools::Itertools; use nym_api_requests::models::GatewayBondAnnotated; use nym_contracts_common::types::Percent; +use std::sync::Arc; +use tokio::sync::RwLock; static SERVICE_PROVIDER_WELLKNOWN_URL: &str = "https://nymtech.net/.wellknown/connect/service-providers.json"; @@ -14,27 +19,39 @@ static SERVICE_PROVIDER_WELLKNOWN_URL: &str = static SERVICE_PROVIDER_WELLKNOWN_URL_MEDIUM: &str = "https://nymtech.net/.wellknown/connect/service-providers-medium.json"; +// Harbour master is used to periodically keep track of which network-requesters are online static HARBOUR_MASTER_URL: &str = "https://harbourmaster.nymtech.net/v1/services/?size=100"; +// We only consider network requesters with a routing score above this threshold +const SERVICE_ROUTING_SCORE_THRESHOLD: f32 = 0.9; + static GATEWAYS_DETAILED_URL: &str = "https://validator.nymtech.net/api/v1/status/gateways/detailed"; -fn get_services_url() -> &'static str { - std::env::var("NYM_CONNECT_ENABLE_MEDIUM") - .is_ok() - .then(|| SERVICE_PROVIDER_WELLKNOWN_URL_MEDIUM) - .unwrap_or(SERVICE_PROVIDER_WELLKNOWN_URL) -} +// Only use gateways with a performnnce score above this +const GATEWAY_PERFORMANCE_SCORE_THRESHOLD: u64 = 90; #[tauri::command] -pub async fn get_services() -> Result> { +pub async fn get_services( + state: tauri::State<'_, Arc>>, +) -> Result> { + let guard = state.read().await; + let privacy_level = guard.get_user_data().privacy_level.unwrap_or_default(); + log::trace!("Fetching services"); - let all_services = fetch_services().await?; - log::trace!("Received: {:#?}", all_services); + let all_services_with_category = fetch_services(&privacy_level).await?; + log::trace!("Received: {:#?}", all_services_with_category); + + // Flatten all services into a single vector (get rid of categories) + // We currently don't care about categories, but we might in the future... + let all_services = all_services_with_category + .into_iter() + .flat_map(|sp| sp.items) + .collect_vec(); // Early return if we're running with medium toggle enabled - if std::env::var("NYM_CONNECT_ENABLE_MEDIUM").is_ok() { - return Ok(all_services.into_iter().flat_map(|sp| sp.items).collect()); + if let PrivacyLevel::Medium = privacy_level { + return Ok(all_services); } // TODO: get paged @@ -42,65 +59,31 @@ pub async fn get_services() -> Result> { let active_services = fetch_active_services().await?; log::trace!("Active: {:#?}", active_services); - let filtered_services = filter_out_inactive(all_services, active_services); - - log::trace!("Fetching gateways"); - let gateway_res = get_gateways_detailed().await?; - log::trace!("Received: {:#?}", gateway_res); - - // Use only services that are active AND have a performance of >= 90% - let filtered_services_with_good_gateway = - filter_out_poor_gateways(filtered_services, gateway_res); - - Ok(filtered_services_with_good_gateway) -} - -fn filter_out_inactive( - services_res: Vec, - active_services: PagedResult, -) -> Vec { - let mut filtered: Vec = vec![]; - for service_type in &services_res { - let items = service_type - .items - .clone() - .into_iter() - .filter(|sp| { - active_services - .items - .iter() - .any(|active| active.service_provider_client_id == sp.address) - }) - .collect_vec(); - log::trace!("service = {} has {} items", service_type.id, items.len()); - filtered.push(DirectoryService { - id: service_type.id.clone(), - description: service_type.description.clone(), - items, - }) + if active_services.items.is_empty() { + log::warn!("No active services found! Using all services instead as fallback"); + return Ok(all_services); } - filtered + + log::trace!("Filter out inactive"); + let filtered_services = filter_out_inactive_services(&all_services, active_services); + log::trace!("After filtering: {:#?}", filtered_services); + + if filtered_services.is_empty() { + log::warn!( + "After filtering, no active services found! Using all services instead as fallback" + ); + return Ok(all_services); + } + + Ok(filtered_services) } -fn filter_out_poor_gateways( - services: Vec, - gateway_res: Vec, -) -> Vec { - let perf_threshold = Percent::from_percentage_value(90).unwrap(); - services - .into_iter() - .flat_map(|sp| sp.items) - .filter(|sp| { - gateway_res.iter().any(|gateway| { - gateway.gateway_bond.gateway.identity_key == sp.gateway - && gateway.performance >= perf_threshold - }) - }) - .collect() -} +async fn fetch_services(privacy_level: &PrivacyLevel) -> Result> { + let services_url = match privacy_level { + PrivacyLevel::Medium => SERVICE_PROVIDER_WELLKNOWN_URL_MEDIUM, + _ => SERVICE_PROVIDER_WELLKNOWN_URL, + }; -async fn fetch_services() -> Result> { - let services_url = get_services_url(); let services_res = reqwest::get(services_url) .await? .json::>() @@ -116,11 +99,56 @@ async fn fetch_active_services() -> Result> { Ok(active_services) } -#[tauri::command] -pub async fn get_gateways_detailed() -> Result> { - let res = reqwest::get(GATEWAYS_DETAILED_URL) +fn filter_out_inactive_services( + all_services: &[DirectoryServiceProvider], + active_services: PagedResult, +) -> Vec { + all_services + .iter() + .filter(|sp| { + active_services.items.iter().any(|active| { + active.service_provider_client_id == sp.address + && active.routing_score > SERVICE_ROUTING_SCORE_THRESHOLD + }) + }) + .cloned() + .collect() +} + +async fn fetch_gateways() -> Result> { + Ok(reqwest::get(GATEWAYS_DETAILED_URL) .await? .json::>() - .await?; - Ok(res) + .await?) +} + +#[tauri::command] +pub async fn get_gateways() -> Result> { + log::trace!("Fetching gateways"); + let all_gateways = fetch_gateways().await?; + log::trace!("Received: {:#?}", all_gateways); + + let filtered_gateways = all_gateways + .iter() + .filter(|g| { + g.node_performance.most_recent + > Percent::from_percentage_value(GATEWAY_PERFORMANCE_SCORE_THRESHOLD).unwrap() + }) + .map(|g| Gateway { + identity: g.identity().clone(), + }) + .collect_vec(); + log::trace!("Filtered: {:#?}", filtered_gateways); + + if filtered_gateways.is_empty() { + log::warn!("No gateways with high enough performance score found! Using all gateways instead as fallback"); + return Ok(all_gateways + .iter() + .map(|g| Gateway { + identity: g.identity().clone(), + }) + .collect_vec()); + } + + Ok(filtered_gateways) } diff --git a/nym-connect/desktop/src-tauri/src/state.rs b/nym-connect/desktop/src-tauri/src/state.rs index 9e4ba9c10b..044b185b86 100644 --- a/nym-connect/desktop/src-tauri/src/state.rs +++ b/nym-connect/desktop/src-tauri/src/state.rs @@ -1,4 +1,5 @@ use futures::SinkExt; +use log::error; use nym_client_core::error::ClientCoreStatusMessage; use nym_socks5_client_core::{Socks5ControlMessage, Socks5ControlMessageSender}; use std::time::Duration; @@ -7,6 +8,8 @@ use tauri::Manager; use tokio::time::Instant; use crate::config::Config; +use crate::config::PrivacyLevel; +use crate::config::UserData; use crate::{ config::{self, socks5_config_id_appended_with}, error::{BackendError, Result}, @@ -65,10 +68,13 @@ pub struct State { /// The latest end-to-end connectivity test result. The first test is initiated on connection /// established. Additional tests can be triggered. connectivity_test_result: ConnectivityTestResult, + + /// User data saved on disk, like user settings + user_data: UserData, } impl State { - pub fn new() -> Self { + pub fn new(user_data: UserData) -> Self { State { status: ConnectionStatusKind::Disconnected, service_provider: None, @@ -76,6 +82,7 @@ impl State { socks5_client_sender: None, gateway_connectivity: GatewayConnectivity::Good, connectivity_test_result: ConnectivityTestResult::NotAvailable, + user_data, } } @@ -93,6 +100,26 @@ impl State { self.gateway_connectivity } + pub fn get_user_data(&self) -> &UserData { + &self.user_data + } + + pub fn set_monitoring(&mut self, enabled: bool) -> Result<()> { + self.user_data.monitoring = Some(enabled); + self.user_data.write().map_err(|e| { + error!("Failed to write user data to disk {e}"); + BackendError::UserDataWriteError + }) + } + + pub fn set_privacy_level(&mut self, privacy_level: PrivacyLevel) -> Result<()> { + self.user_data.privacy_level = Some(privacy_level); + self.user_data.write().map_err(|e| { + error!("Failed to write user data to disk {e}"); + BackendError::UserDataWriteError + }) + } + pub fn set_gateway_connectivity(&mut self, gateway_connectivity: GatewayConnectivity) { self.gateway_connectivity = gateway_connectivity } @@ -200,8 +227,9 @@ impl State { &mut self, ) -> Result<(nym_task::StatusReceiver, ExitStatusReceiver)> { let id = self.get_config_id()?; + let privacy_level = self.user_data.privacy_level.unwrap_or_default(); let (control_tx, msg_rx, exit_status_rx, used_gateway) = - tasks::start_nym_socks5_client(&id).await?; + tasks::start_nym_socks5_client(&id, &privacy_level).await?; self.socks5_client_sender = Some(control_tx); self.gateway = Some(used_gateway.gateway_id); Ok((msg_rx, exit_status_rx)) diff --git a/nym-connect/desktop/src-tauri/src/tasks.rs b/nym-connect/desktop/src-tauri/src/tasks.rs index d6ac1b0918..8c69b1be7e 100644 --- a/nym-connect/desktop/src-tauri/src/tasks.rs +++ b/nym-connect/desktop/src-tauri/src/tasks.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use tap::TapFallible; use tokio::sync::RwLock; -use crate::config::Config; +use crate::config::{Config, PrivacyLevel}; use crate::{ error::Result, events::{self, emit_event, emit_status_event}, @@ -30,23 +30,20 @@ pub enum Socks5ExitStatusMessage { Failed(Box), } -fn override_config_from_env(config: &mut Config) { +fn override_config_from_env(config: &mut Config, privacy_level: &PrivacyLevel) { // Disable both the loop cover traffic that runs in the background as well as the Poisson // process that injects cover traffic into the traffic stream. - if std::env::var("NYM_CONNECT_DISABLE_COVER").is_ok() { + if let PrivacyLevel::Medium = privacy_level { + log::info!("Running in Medium privacy level"); log::warn!("Disabling cover traffic"); config.core.base.set_no_cover_traffic_with_keepalive(); - } - if std::env::var("NYM_CONNECT_ENABLE_MIXED_SIZE_PACKETS").is_ok() { log::warn!("Enabling mixed size packets"); config .core .base .set_secondary_packet_size(Some(PacketSize::ExtendedPacket16)); - } - if std::env::var("NYM_CONNECT_DISABLE_PER_HOP_DELAYS").is_ok() { log::warn!("Disabling per-hop delay"); config.core.base.set_no_per_hop_delays(); } @@ -55,6 +52,7 @@ fn override_config_from_env(config: &mut Config) { /// The main SOCKS5 client task. It loads the configuration from file determined by the `id`. pub async fn start_nym_socks5_client( id: &str, + privacy_level: &PrivacyLevel, ) -> Result<( Socks5ControlMessageSender, nym_task::StatusReceiver, @@ -65,7 +63,7 @@ pub async fn start_nym_socks5_client( let mut config = Config::read_from_default_path(id) .tap_err(|_| log::warn!("Failed to load configuration file"))?; - override_config_from_env(&mut config); + override_config_from_env(&mut config, privacy_level); log::trace!("Configuration used: {:#?}", config); diff --git a/nym-connect/desktop/src-tauri/tauri.conf.json b/nym-connect/desktop/src-tauri/tauri.conf.json index 9b62f46169..b95859ca53 100644 --- a/nym-connect/desktop/src-tauri/tauri.conf.json +++ b/nym-connect/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "package": { "productName": "nym-connect", - "version": "1.1.14" + "version": "1.1.15" }, "build": { "distDir": "../dist", @@ -86,4 +86,4 @@ "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'" } } -} +} \ No newline at end of file diff --git a/nym-connect/desktop/src/components/ConnectionStatus.tsx b/nym-connect/desktop/src/components/ConnectionStatus.tsx index 0c02ff2585..1a09c8b18e 100644 --- a/nym-connect/desktop/src/components/ConnectionStatus.tsx +++ b/nym-connect/desktop/src/components/ConnectionStatus.tsx @@ -1,10 +1,11 @@ import React from 'react'; -import { Box, CircularProgress, Tooltip, Typography } from '@mui/material'; +import { Box, CircularProgress, Stack, Tooltip, Typography } from '@mui/material'; import { DateTime } from 'luxon'; import { ErrorOutline, InfoOutlined } from '@mui/icons-material'; import { ConnectionStatusKind, GatewayPerformance } from '../types'; -import { ServiceProvider } from '../types/directory'; +import { ServiceProvider, Gateway } from '../types/directory'; import { GatwayWarningInfo, ServiceProviderInfo } from './TooltipInfo'; +import { useClientContext } from '../context/main'; const FONT_SIZE = '14px'; const FONT_WEIGHT = '600'; @@ -13,8 +14,11 @@ const FONT_STYLE = 'normal'; const ConnectionStatusContent: FCWithChildren<{ status: ConnectionStatusKind; serviceProvider?: ServiceProvider; + gateway?: Gateway; gatewayError: boolean; -}> = ({ status, serviceProvider, gatewayError }) => { +}> = ({ status, serviceProvider, gateway, gatewayError }) => { + const { userData } = useClientContext(); + if (gatewayError) { return ( : undefined}> @@ -37,14 +41,29 @@ const ConnectionStatusContent: FCWithChildren<{ switch (status) { case 'connected': return ( - : undefined}> - - - - Connected to Nym Mixnet - - - + <> + + ) : undefined + } + > + + + + Connected to Nym Mixnet + + + + {userData?.privacy_level === 'Medium' && ( + + + Speed boost activated + + + )} + ); case 'disconnected': return ( @@ -81,7 +100,8 @@ export const ConnectionStatus: FCWithChildren<{ gatewayPerformance?: GatewayPerformance; connectedSince?: DateTime; serviceProvider?: ServiceProvider; -}> = ({ status, serviceProvider, gatewayPerformance }) => { + gateway?: Gateway; +}> = ({ status, serviceProvider, gateway, gatewayPerformance }) => { const color = status === 'connected' || status === 'disconnecting' ? '#21D072' : 'white'; return ( @@ -89,6 +109,7 @@ export const ConnectionStatus: FCWithChildren<{ diff --git a/nym-connect/desktop/src/components/ServiceProviderSelector.stories.tsx b/nym-connect/desktop/src/components/ServiceProviderSelector.stories.tsx deleted file mode 100644 index c7df76ea4a..0000000000 --- a/nym-connect/desktop/src/components/ServiceProviderSelector.stories.tsx +++ /dev/null @@ -1,342 +0,0 @@ -import * as React from 'react'; -import { ComponentMeta } from '@storybook/react'; -import { Box } from '@mui/material'; -import { ServiceProviderSelector } from './ServiceProviderSelector'; -import { Services } from '../types/directory'; - -export default { - title: 'Components/Service Provider Selector', - component: ServiceProviderSelector, -} as ComponentMeta; - -const width = 240; - -export const Loading = () => ( - - - -); - -const services: Services = JSON.parse(`[ - { - "id": "keybase", - "description": "Keybase", - "items": [ - { - "id": "nym-keybase", - "description": "Nym Keybase Service Provider", - "address": "Entztfv6Uaz2hpYHQJ6JKoaCTpDL5dja18SuQWVJAmmx.Cvhn9rBJw5Ay9wgHcbgCnVg89MPSV5s2muPV2YF1BXYu@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf", - "gateway": "Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf" - }, - { - "id": "shipyard-keybase-1", - "description": "Nym Keybase Service Provider", - "address": "D55ksecHzY6vAeqk8MCTzCfj2pqwJeKCKZCUUGnwGnn3.FS42vXS5a6GNTb1qk3aVk5mjSiJCAuawbBVyQZZVfhvt@DfNMqQRy6pPkU8Z5rBsxRwzDUzAMXHPFwMhjF16ScZqn", - "gateway": "DfNMqQRy6pPkU8Z5rBsxRwzDUzAMXHPFwMhjF16ScZqn" - }, - { - "id": "shipyard-keybase-2", - "description": "Nym Keybase Service Provider", - "address": "DFdDtW7LNBATxQ4ef3jNbqs3cRE8b9wDZTCctHCQRULa.4AbKiTNVUwYFWHhy98o5pT9dELiUrkXoJQ9wHqPgf6GV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN", - "gateway": "GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN" - }, - { - "id": "shipyard-keybase-3", - "description": "Nym Keybase Service Provider", - "address": "6Y1HE1jJ92P9yoHer11TR4A2NdZePrLGaBNFf65MnYGe.FwXoh217odQDWNmViqzNX28fauYrjB3PYLrVvpqnQrX4@5vC8spDvw5VDQ8Zvd9fVvBhbUDv9jABR4cXzd4Kh5vz", - "gateway": "5vC8spDvw5VDQ8Zvd9fVvBhbUDv9jABR4cXzd4Kh5vz" - }, - { - "id": "shipyard-keybase-4", - "description": "Nym Keybase Service Provider", - "address": "3zzhLtWvaJgn755MkRckG5aRnoTZich8ASn395iSsTgj.J1R5VuxXbh2eNHiaRbrwbKGXrrEQcHKLdzf8eg9HTB6q@3B7PsbXFuqq6rerYFLw5HPbQb4UmBqAhfWURRovMmWoj", - "gateway": "3B7PsbXFuqq6rerYFLw5HPbQb4UmBqAhfWURRovMmWoj" - }, - { - "id": "shipyard-keybase-5", - "description": "Nym Keybase Service Provider", - "address": "CHuXdZJYQ8xH7ekgN9gAuVtQ7ZikjjHEZY5BSN7yc5mN.29dFvqicKQQQvoX1vup44mspmc249RH5xgLibWMwTYGT@CfWcDJq8QBz6cVAPCYSaLbaJEhVTmHEmyYgQ6C5GdDW9", - "gateway": "CfWcDJq8QBz6cVAPCYSaLbaJEhVTmHEmyYgQ6C5GdDW9" - } - ] - }, - { - "id": "electrum", - "description": "Electrum Wallet", - "items": [ - { - "id": "nym-electrum", - "description": "Nym Electrum Service Provider", - "address": "DpB3cHAchJiNBQi5FrZx2csXb1mrHkpYh9Wzf8Rjsuko.ANNWrvHqMYuertHGHUrZdBntQhpzfbWekB39qez9U2Vx@2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh", - "gateway": "2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh" - }, - { - "id": "shipyard-electrum-1", - "description": "Nym Electrum Service Provider", - "address": "8Tb73cFQpXCLpgxEA2VSDru2hHrcZ3KQcyMsGbxcTjBp.4x5tu66k8YkHk4tYac1qwEFbNq5WsKiX5kR51q5KKH88@4WgKhJdmUffz4e1o1ftVAGS3HnG56LiNAxA9dmaekrVd", - "gateway": "4WgKhJdmUffz4e1o1ftVAGS3HnG56LiNAxA9dmaekrVd" - }, - { - "id": "shipyard-electrum-2", - "description": "Nym Electrum Service Provider", - "address": "GR6z31MwCsvxHrnvvVN1Cpasd8aQ1giwQqPTZM9dN7VH.5rEiqakSPDrBtKmvpU8Shnhz6gRM85JLoB7AX4h7PJYr@5Ao1J38frnU9Rx5YVeF5BWExcnDTcW8etNe9W2sRASXD", - "gateway": "5Ao1J38frnU9Rx5YVeF5BWExcnDTcW8etNe9W2sRASXD" - } - ] - }, - { - "id": "telegram", - "description": "Telegram", - "items": [ - { - "id": "shipyard-telegram-2", - "description": "Nym Telegram Service Provider", - "address": "C4w6ewbQtoaZEeoaaNw1xVASChqo4WVjNfuYEUFjZxpc.8F1D7rQXf2jGoj1Ken7PiGDM8HS2Ug79wSoc9nZ1iqh1@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve", - "gateway": "62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve" - }, - { - "id": "shipyard-telegram-3", - "description": "Nym Telegram Service Provider", - "address": "DStL3BEUZuQZfbij1KAY3BvJh8rC5jpr9mc6AQ6aTLUu.Ax9foYaKfFgX6g8y393GoNpKkKrnDGFGRZwxDv9R7X6M@FQon7UwF5knbUr2jf6jHhmNLbJnMreck1eUcVH59kxYE", - "gateway": "FQon7UwF5knbUr2jf6jHhmNLbJnMreck1eUcVH59kxYE" - }, - { - "id": "shipyard-telegram-4", - "description": "Nym Telegram Service Provider", - "address": "8gRdGTzsDxYzpasRQhsRg59MCgNNhnfag2oFfwwZPXnB.DtDrGz7ScVm4o7sN4K3CYUJveYgz7fcXELBVLNDfMS9Y@3ojQD6V7skM1bSXJX7fVQvscjmcgptzdixQEaAha2ixh", - "gateway": "3ojQD6V7skM1bSXJX7fVQvscjmcgptzdixQEaAha2ixh" - }, - { - "id": "shipyard-telegram-5", - "description": "Nym Telegram Service Provider", - "address": "AR3oEM6Uvmfs6fyddwSehoBUKCFxz7MdFi4z7aahuHuY.3ZKapg9A3Py1PXhyLbCJr8ZbJsEV6NZdN1WJaGGut5tj@EEyq16v63aotPBCepxUpCgAojrNasZ6Hk1PjpRyBAdEp", - "gateway": "EEyq16v63aotPBCepxUpCgAojrNasZ6Hk1PjpRyBAdEp" - }, - { - "id": "shipyard-telegram-6", - "description": "Nym Telegram Service Provider", - "address": "7n1BYhsXSwcr8Qim8AqZTAodqFia4QG6T7CRc1ihQHpv.7o4trpGqu2LHMUiXc3dddgfGET1CFFcAK9gKYoHoSn5e@BTZNB3bkkEePsT14GN8ofVtM1SJae4YLWjpBerrKYfr", - "gateway": "BTZNB3bkkEePsT14GN8ofVtM1SJae4YLWjpBerrKYfr" - }, - { - "id": "shipyard-telegram-7", - "description": "Nym Telegram Service Provider", - "address": "Gv4TWhUKrvJfqh1jBRPGEQrikNZvZse2kS3ZgN9Z2nAZ.7KGPaaqUEum2C59jLvw7f8Ug8a48YuZdjjZu3t4JES4U@C7J8SwZQqjWqhBryyjJxLt7FacVuPTwAmR2otGy53ayi", - "gateway": "C7J8SwZQqjWqhBryyjJxLt7FacVuPTwAmR2otGy53ayi" - }, - { - "id": "shipyard-telegram-8", - "description": "Nym Telegram Service Provider", - "address": "8Mqgp12cpF6FSXMeqzxgFgQXvTSapyNqGAi5wy7ub4ge.7z7PDsiJGiGxGz4p77v5L5fZhXBJ5qNZ8CgJwYNr6H6J", - "gateway": "3zd3wrCK8Dz5TXrcvk5dG5s9EEdf4Ck1v9VgBPMMFVkR" - }, - { - "id": "shipyard-telegram-9", - "description": "Nym Telegram Service Provider", - "address": "F3N5eiPDZcGFC985Go4Mpv8p9uxFD1L3jRUdrLCbrZLm.EyTxWwwTwYpPrJBmc97GLd1LpUAphjptS5y1ed182bGk@GAjhJcrd6f1edaqUkfWCff6zdHoqo756qYrc2TfPuCXJ", - "gateway": "GAjhJcrd6f1edaqUkfWCff6zdHoqo756qYrc2TfPuCXJ" - }, - { - "id": "shipyard-telegram-10", - "description": "Nym Telegram Service Provider", - "address": "G7y7e1nVBr8fmQSzdeAxXnCmmmJb5k8N3E8LBV31KE5g.GRRUCj6t6cCUUjakmTWzidMLiYA7EdCedKnup8osaBC6@AJad2R9virYEYXEsTcicN5y5tyPoixrhhAGsxoESZVnc", - "gateway": "AJad2R9virYEYXEsTcicN5y5tyPoixrhhAGsxoESZVnc" - }, - { - "id": "shipyard-telegram-11", - "description": "Nym Telegram Service Provider", - "address": "2kq9Z7RyDZtb8kxXjyP3ZT8VMWHg6JXFDChGuuNBk7Hw.F5XYbBaGSoF8qAo8faPcaNRPHEq3Y25BDcwESeabUS9S@HaLyPQrhBTq75dnGeBUdYWeFVA2BBn39MgkhEt3VTMMM", - "gateway": "HaLyPQrhBTq75dnGeBUdYWeFVA2BBn39MgkhEt3VTMMM" - }, - { - "id": "shipyard-telegram-12", - "description": "Nym Telegram Service Provider", - "address": "GegdtpNzYj4QCgpih9Kxv7ZVZxmVdxYHsDkiPsbT71XG.E8xtE8mrapjzFtyuziZSrsScAKhwZMH5wNpKWtKfzJ5Y@9Byd9VAtyYMnbVAcqdoQxJnq76XEg2dbxbiF5Aa5Jj9J", - "gateway": "9Byd9VAtyYMnbVAcqdoQxJnq76XEg2dbxbiF5Aa5Jj9J" - }, - { - "id": "shipyard-telegram-13", - "description": "Nym Telegram Service Provider", - "address": "4SsrDQeEtG3mpeD9nN5CDdGaCsxFvNeYMhoviDzNNB9f.GyqG6iK5rBvhe3HXLR11m6ULpf13ATgYvkkidLmteDLs@5EpkkrMFYAM3XcaztXnZwBWquURHSKsyc9JxUCengDFS", - "gateway": "5EpkkrMFYAM3XcaztXnZwBWquURHSKsyc9JxUCengDFS" - }, - { - "id": "shipyard-telegram-14", - "description": "Nym Telegram Service Provider", - "address": "9JoHRu2RrSD1fjbj9NSTASgjv9Szep7Nhd9L2PywxbBi.AZhAUDNX6iH8BqXyR5c7TJuzpwMEvDXrabNLGuRukvVf@9xJM74FwwHhEKKJHihD21QSZnHM2QBRMoFx9Wst6qNBS", - "gateway": "9xJM74FwwHhEKKJHihD21QSZnHM2QBRMoFx9Wst6qNBS" - }, - { - "id": "shipyard-telegram-15", - "description": "Nym Telegram Service Provider", - "address": "3K174ijjXqCkhMDT9xLcqjS4MXk2QsqZt4PdgNcuUrnn.BNnHnQmWoj6Uo6kkS1QkPqsdHaXrcwyR9F6MnnzDkZJL@C7J8SwZQqjWqhBryyjJxLt7FacVuPTwAmR2otGy53ayi", - "gateway": "C7J8SwZQqjWqhBryyjJxLt7FacVuPTwAmR2otGy53ayi" - }, - { - "id": "shipyard-telegram-16", - "description": "Nym Telegram Service Provider", - "address": "BqX5Q3MEcbTnM39hUswQchLW68SrqbhL8K5ucrLmtP39.AWrVsFoVC9s6KjdpcasATmZPA3GtMsUxcfHpAkuNdtFG@Emswx6KXyjRfq1c2k4d4uD2e6nBSbH1biorCZUei8UNS", - "gateway": "Emswx6KXyjRfq1c2k4d4uD2e6nBSbH1biorCZUei8UNS" - }, - { - "id": "shipyard-telegram-17", - "description": "Nym Telegram Service Provider", - "address": "2tQxccgcqdkuUvLqgiEkEN4rNRZ5QknygnKAFcS4gfoe.EVrY5q5sqDqBUbS3wHsRRZhk2MAw1S17hNoH1Bicyv7n@DAGQxdxwAkwjaLjTw1B9vndia4YyFD15qRgcTQxrmkom", - "gateway": "DAGQxdxwAkwjaLjTw1B9vndia4YyFD15qRgcTQxrmkom" - }, - { - "id": "shipyard-telegram-18", - "description": "Nym Telegram Service Provider", - "address": "8YG1rcEauJA814Nd7hSxjNe2UrRwrGsrXTm1Cmd3gRrU.FxYaYqpNN8PciNsySs3zYPrTB1J8AYUu9DBsM2vVDDfF@7EfEESLo71GUvx3UEW79LgTegHUBPUocUzGyJVv6LHog", - "gateway": "7EfEESLo71GUvx3UEW79LgTegHUBPUocUzGyJVv6LHog" - }, - { - "id": "shipyard-telegram-19", - "description": "Nym Telegram Service Provider", - "address": "HPiXADVFLwLQPNpPtyYefzvYntC6tp9UJ5fJZGfkqvDt.2EUUxmeT3AiaUzAcE5SyXRAk8a2JXBkRz4B8McSdkrST@9ACTkYraCqE9jMb6zb6ne8EDQGGhZw5ykNiq9YRUdHTD", - "gateway": "9ACTkYraCqE9jMb6zb6ne8EDQGGhZw5ykNiq9YRUdHTD" - }, - { - "id": "shipyard-telegram-20", - "description": "Nym Telegram Service Provider", - "address": "2QLnEEnTmf2NRWtcQPWBeRcg7Hej5WSPWRWwtTpEEZWF.BheS78ozc8ngvhsXNNnshdJzpoYsmEvhfn3WKUYF5dRU@C2uyokSPoxhku9GexRxEo1e8KPZ7q6e8FXmK3gtY8kkF", - "gateway": "C2uyokSPoxhku9GexRxEo1e8KPZ7q6e8FXmK3gtY8kkF" - }, - { - "id": "shipyard-telegram-21", - "description": "Nym Telegram Service Provider", - "address": "FuBbnwiANfaXZnn683jBapK5XVm5ttgZSykU3vqPSHoD.94MFGv1VcBLTkRwzBDQUkWjvqtZYVBrJg2Q8JGbizcib@CTqYPY8htdAQMXCzRW9SjZzZuqYwSt2iUh6HPaNgmTvK", - "gateway": "CTqYPY8htdAQMXCzRW9SjZzZuqYwSt2iUh6HPaNgmTvK" - }, - { - "id": "shipyard-telegram-22", - "description": "Nym Telegram Service Provider", - "address": "9EbQx5jQznSVbftFom7sqUSHAACrsfvMhrzhaFt4A3SZ.D1FQCirL4YKwfcmtMGvB5Rugt5sAzGEhdSjJ3bHVQRZ@7Zh1Sz5dXpA6s53CbtcdqhQhLqwf4cLynL7KqHKcjrG4", - "gateway": "7Zh1Sz5dXpA6s53CbtcdqhQhLqwf4cLynL7KqHKcjrG4" - }, - { - "id": "shipyard-telegram-24", - "description": "Nym Telegram Service Provider", - "address": "6Umawwvf551VyB3Ko46NgKLqJdTFJeToCM67mrTmM3G.3A4sesBac4KGuMTFjvYBwLpksMJvbMbteGJQgmm4PV4Y@AnnYnEtBjB2a5sHmeRCnBq43qxyHDf95Bqd7cwQyKNLR", - "gateway": "AnnYnEtBjB2a5sHmeRCnBq43qxyHDf95Bqd7cwQyKNLR" - }, - { - "id": "shipyard-telegram-25", - "description": "Nym Telegram Service Provider", - "address": "CDtxTeoyqq83JpV9G8cR5HRHRdMMaVspQsCwH3Qnajt3.F5EHK9HFcdGrE2hqA7bK9AUmkbihujYDhtNNqHKxW765@BDkeNx7JQm5NsQakst9s8htogZXhpTQedFAgZpvsGCqH", - "gateway": "BDkeNx7JQm5NsQakst9s8htogZXhpTQedFAgZpvsGCqH" - }, - { - "id": "shipyard-telegram-26", - "description": "Nym Telegram Service Provider", - "address": "HukZkLG2DoarQEqaoDLuqW1GFf2NSHDUMGBZiyJGRYJD.9GyU8wPsyzcvRjcyk8hiNpTJbXCmq5F3VoVhFBZYuHR3@GsGEZiDBz8SWfHGaK5SDmhfbTEM55v37WCYYcT9wTSxN", - "gateway": "GsGEZiDBz8SWfHGaK5SDmhfbTEM55v37WCYYcT9wTSxN" - }, - { - "id": "shipyard-telegram-27", - "description": "Nym Telegram Service Provider", - "address": "773y8iMVJiRk4dRbjQzkJVbrei4TwkePNE5WTEttt77d.3Mw47C9XZj3oAzk1iSqC5Y36tbBsjtaTtdgaHM3Zsdma@7fiZtNL1RACQTwGrKLBT9nbY77bfwZnX9rqcWqc53qgv", - "gateway": "7fiZtNL1RACQTwGrKLBT9nbY77bfwZnX9rqcWqc53qgv" - }, - { - "id": "shipyard-telegram-28", - "description": "Nym Telegram Service Provider", - "address": "6jQJEorCu7YiP9HdDaMeHxcNhxeNmZ1kpd836GnqLZX.HsJqEmNTszGecsKqFB37i84nBXxqf4ETgrKmKmBvMGHC@FYnDMQzT49ZGM23gVqpTxfih14V6wuedNXirekmt37zE", - "gateway": "FYnDMQzT49ZGM23gVqpTxfih14V6wuedNXirekmt37zE" - }, - { - "id": "shipyard-telegram-29", - "description": "Nym Telegram Service Provider", - "address": "BiCSyovpFMuSnTvF2TdiuZwrytXDrd9AH47ZMcCxscVC.G9YpdicA9BBNoVHDgjWjgt17wv5WYKWcbE3vPJJVpSJD@GAjhJcrd6f1edaqUkfWCff6zdHoqo756qYrc2TfPuCXJ", - "gateway":"GAjhJcrd6f1edaqUkfWCff6zdHoqo756qYrc2TfPuCXJ" - }, - { - "id": "shipyard-telegram-30", - "description": "Nym Telegram Service Provider", - "address": "AQRRAs9oc8QWXAFBs44YhCKUny7AyLsfLy91pwmGgxuf.CWUKoKA1afSKyw5BnFJJg19UDgnaVATupsFhQpyTEBHJ@EBT8jTD8o4tKng2NXrrcrzVhJiBnKpT1bJy5CMeArt2w", - "gateway": "EBT8jTD8o4tKng2NXrrcrzVhJiBnKpT1bJy5CMeArt2w" - }, - { - "id": "shipyard-telegram-31", - "description": "Nym Telegram Service Provider", - "address": "6YqjAZK3Pr1ngiBLcDkotboB5WiN6k6NPpbXvShH4pR5.9Ss6VW3Xbyi8LuxduNNwnXEv9njHCQ2PLSP1UK6tsoa5@42XCK9dMS9m5XJLzQd2dBuwimk6ndZnczhZaV5VPFkQD", - "gateway": "42XCK9dMS9m5XJLzQd2dBuwimk6ndZnczhZaV5VPFkQD" - }, - { - "id": "shipyard-telegram-32", - "description": "Nym Telegram Service Provider", - "address": "EmYWLeybmj86Vzr62vxuZ3T15jwMNHggzK7sQwid96yp.GyaF9WprSr56cxUdGf5TpcUvAjb2VbAr8CVBrmBUYAaw@GL5wESoz4oSbpBaTki9qB9213FGUQXCiRjbzDkhWwoBC", - "gateway": "GL5wESoz4oSbpBaTki9qB9213FGUQXCiRjbzDkhWwoBC" - }, - { - "id": "shipyard-telegram-33", - "description": "Nym Telegram Service Provider", - "address": "4PDb96cck5btTj6G7rsomqwHJsp4qu8uPvFCbwHfjFUx.C5dKbaoakH7egsZvAueRbwLFbmxnQaVMeSr6QTMpuBAA@58ceEFaLJh6zAo3cirzT1BDQm7D3L5acnQrxGH1D6TAY", - "gateway": "58ceEFaLJh6zAo3cirzT1BDQm7D3L5acnQrxGH1D6TAY" - }, - { - "id": "shipyard-telegram-34", - "description": "Nym Telegram Service Provider", - "address": "BeZbeMf9vcpUf368qDd85dtLwXLj4Ee5bsHMB2fUD8uX.HELVbppkwU1jmzUAUrCEbHeJfVciSeo8VGAkbJSpwxsb@ADdHkiTfkpsSt31zVToWW9j3KikH24aLAAwDKtCYE5jY", - "gateway":"ADdHkiTfkpsSt31zVToWW9j3KikH24aLAAwDKtCYE5jY" - }, - { - "id": "shipyard-telegram-35", - "description": "Nym Telegram Service Provider", - "address": "Bp4JRFyf7GB9L9J95AqMPnz9zbGmPnViA5fDXKeNraLJ.D6CTdcjJVxDmH2UQvzXuPWg9Se9xXYe76uDMypXvhzd7@6UjGEeQZK14C5K2kenycTkqt7qRjEHGLgaQx3FWySo3N", - "gateway": "6UjGEeQZK14C5K2kenycTkqt7qRjEHGLgaQx3FWySo3N" - }, - { - "id": "shipyard-telegram-36", - "description": "Nym Telegram Service Provider", - "address": "91h7io6BGhVjbtC7dbbRcScyTJcTfnMsTQZ6aWMVsrWR.Epb4hANXCp8cGEY3wSgawux991ti9Z5Y1FHTMzAKFa6E@DF4TE7V8kJkttMvnoSVGnRFFRt6WYGxxiC2w1XyPQnHe", - "gateway": "DF4TE7V8kJkttMvnoSVGnRFFRt6WYGxxiC2w1XyPQnHe" - }, - { - "id": "shipyard-telegram-37", - "description": "Nym Telegram Service Provider", - "address": "Cy2wuwKpWZ3iWzKU3ZWL1qqcVfJ5Cq85dU7UHVWwv2gc.9AhC9b2zVcLnXLGriMdxjpsWJpq6iAdCavDi63udbL89@678qVUJ21uwxZBhp3r56z7GRf6gMh3NYDHruTegPtgMf", - "gateway": "678qVUJ21uwxZBhp3r56z7GRf6gMh3NYDHruTegPtgMf" - }, - { - "id": "shipyard-telegram-38", - "description": "Nym Telegram Service Provider", - "address": "GgUeUWW1NRSuquZYeZf3WkppE92EQUHJrFjNZtYU1jow.CSEjwrRi4f4uyw7N6L2LPKw2tB8spcMbFu99wHZzFZSj@77TSuVU8d1oXKbPzjec2xh4i3Wj5WwUyy9Lr36sm8gZm", - "gateway": "77TSuVU8d1oXKbPzjec2xh4i3Wj5WwUyy9Lr36sm8gZm" - }, - { - "id": "shipyard-telegram-39", - "description": "Nym Telegram Service Provider", - "address": "kz4zWwSkYiQxqxXFPNcGUByTPQWXascD9RfYsmSxY7n.ajp3SjbBVBjrU9nXpSQXAXzbb6EHJJyhbY6cc1ajayx@BTZNB3bkkEePsT14GN8ofVtM1SJae4YLWjpBerrKYf", - "gateway": "HyS2UZtZX3kQXdazbdE99DhCjBXjbG61LC9QsmXwbxrU" - } - ] - }, - { - "id": "blockstream", - "description": "Blockstream Green", - "items": [ - { - "id": "nym-blockstream", - "description": "Nym Blockstream Green Service Provider", - "address": "GiRjFWrMxt58pEMuusm4yT3RxoMD1MMPrR9M2N4VWRJP.3CNZBPq4vg7v7qozjGjdPMXcvDmkbWPCgbGCjQVw9n6Z@2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW", - "gateway": "2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW" - } - ] - } -]`); -export const Loaded = () => ( - - - -); - -export const ServiceAlreadySelected = () => ( - - console.log('New service provider selected: ', serviceProvider)} - /> - -); diff --git a/nym-connect/desktop/src/components/ServiceProviderSelector.tsx b/nym-connect/desktop/src/components/ServiceProviderSelector.tsx deleted file mode 100644 index 741465dac4..0000000000 --- a/nym-connect/desktop/src/components/ServiceProviderSelector.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import React, { useEffect, useMemo } from 'react'; -import { Box, CircularProgress, MenuItem, Stack, TextField, Tooltip, Typography } from '@mui/material'; -import { Service, ServiceProvider, Services } from '../types/directory'; -import { useTauriEvents } from '../utils'; - -type ServiceWithRandomSp = { - id: string; - description: string; - sp: ServiceProvider; -}; - -const defaultServiceValue = { id: '', description: '', items: [] }; - -export const ServiceProviderSelector: FCWithChildren<{ - onChange?: (serviceProvider?: ServiceProvider) => void; - services?: Services; - currentSp?: ServiceProvider; -}> = ({ services, currentSp, onChange }) => { - const [service, setService] = React.useState(defaultServiceValue); - const [serviceProvider, setServiceProvider] = React.useState(currentSp); - const [resetTrigger, setResetTrigger] = React.useState(new Date().toISOString()); - - const handleSelectSp = (newServiceProvider?: ServiceProvider) => { - if (newServiceProvider && newServiceProvider !== currentSp) { - setServiceProvider(newServiceProvider); - onChange?.(newServiceProvider); - } - }; - - // when the user clears local storage, reset the selector - useTauriEvents('help://clear-storage', () => { - setService(defaultServiceValue); - setServiceProvider(undefined); - onChange?.(undefined); - setResetTrigger(new Date().toISOString()); - }); - - useEffect(() => { - if (!serviceProvider && currentSp) { - setServiceProvider(currentSp); - } - }, [currentSp]); - - useEffect(() => { - if (services && serviceProvider) { - // retrieve the service corresponding to this service provider - - const match = services.find((s) => - s.items.some( - ({ id, address, gateway }) => - id === serviceProvider.id && address === serviceProvider.address && gateway === serviceProvider.gateway, - ), - ); - - if (match) { - setService(match); - } - } - }, [serviceProvider, services]); - - if (!services) { - return ( - - theme.palette.common.white}> - - Loading services... - - - ); - } - - const servicesWithRandomSp: ServiceWithRandomSp[] = useMemo( - () => - services.map(({ id, items, description }) => ({ - id, - description, - sp: items[Math.floor(Math.random() * items.length)], - })), - [services, resetTrigger], - ); - - if (!service) return null; - - return ( - - - {servicesWithRandomSp.map(({ id, description, sp }) => ( - handleSelectSp(sp)}> - - - {sp.id} - - - {sp.description} - - - Gateway {sp.gateway.slice(0, 10)}... - - - Provider {sp.address.slice(0, 10)}... - - - } - arrow - placement="top" - > - {description} - - - ))} - - - ); -}; diff --git a/nym-connect/desktop/src/components/ServiceSelector.stories.tsx b/nym-connect/desktop/src/components/ServiceSelector.stories.tsx deleted file mode 100644 index 56be7c19ee..0000000000 --- a/nym-connect/desktop/src/components/ServiceSelector.stories.tsx +++ /dev/null @@ -1,342 +0,0 @@ -import * as React from 'react'; -import { ComponentMeta } from '@storybook/react'; -import { Box } from '@mui/material'; -import { ServiceSelector } from './ServiceSelector'; -import { Services } from '../types/directory'; - -export default { - title: 'Components/Service Selector', - component: ServiceSelector, -} as ComponentMeta; - -const width = 240; - -export const Loading = () => ( - - - -); - -const services: Services = JSON.parse(`[ - { - "id": "keybase", - "description": "Keybase", - "items": [ - { - "id": "nym-keybase", - "description": "Nym Keybase Service Provider", - "address": "Entztfv6Uaz2hpYHQJ6JKoaCTpDL5dja18SuQWVJAmmx.Cvhn9rBJw5Ay9wgHcbgCnVg89MPSV5s2muPV2YF1BXYu@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf", - "gateway": "Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf" - }, - { - "id": "shipyard-keybase-1", - "description": "Nym Keybase Service Provider", - "address": "D55ksecHzY6vAeqk8MCTzCfj2pqwJeKCKZCUUGnwGnn3.FS42vXS5a6GNTb1qk3aVk5mjSiJCAuawbBVyQZZVfhvt@DfNMqQRy6pPkU8Z5rBsxRwzDUzAMXHPFwMhjF16ScZqn", - "gateway": "DfNMqQRy6pPkU8Z5rBsxRwzDUzAMXHPFwMhjF16ScZqn" - }, - { - "id": "shipyard-keybase-2", - "description": "Nym Keybase Service Provider", - "address": "DFdDtW7LNBATxQ4ef3jNbqs3cRE8b9wDZTCctHCQRULa.4AbKiTNVUwYFWHhy98o5pT9dELiUrkXoJQ9wHqPgf6GV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN", - "gateway": "GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN" - }, - { - "id": "shipyard-keybase-3", - "description": "Nym Keybase Service Provider", - "address": "6Y1HE1jJ92P9yoHer11TR4A2NdZePrLGaBNFf65MnYGe.FwXoh217odQDWNmViqzNX28fauYrjB3PYLrVvpqnQrX4@5vC8spDvw5VDQ8Zvd9fVvBhbUDv9jABR4cXzd4Kh5vz", - "gateway": "5vC8spDvw5VDQ8Zvd9fVvBhbUDv9jABR4cXzd4Kh5vz" - }, - { - "id": "shipyard-keybase-4", - "description": "Nym Keybase Service Provider", - "address": "3zzhLtWvaJgn755MkRckG5aRnoTZich8ASn395iSsTgj.J1R5VuxXbh2eNHiaRbrwbKGXrrEQcHKLdzf8eg9HTB6q@3B7PsbXFuqq6rerYFLw5HPbQb4UmBqAhfWURRovMmWoj", - "gateway": "3B7PsbXFuqq6rerYFLw5HPbQb4UmBqAhfWURRovMmWoj" - }, - { - "id": "shipyard-keybase-5", - "description": "Nym Keybase Service Provider", - "address": "CHuXdZJYQ8xH7ekgN9gAuVtQ7ZikjjHEZY5BSN7yc5mN.29dFvqicKQQQvoX1vup44mspmc249RH5xgLibWMwTYGT@CfWcDJq8QBz6cVAPCYSaLbaJEhVTmHEmyYgQ6C5GdDW9", - "gateway": "CfWcDJq8QBz6cVAPCYSaLbaJEhVTmHEmyYgQ6C5GdDW9" - } - ] - }, - { - "id": "electrum", - "description": "Electrum Wallet", - "items": [ - { - "id": "nym-electrum", - "description": "Nym Electrum Service Provider", - "address": "DpB3cHAchJiNBQi5FrZx2csXb1mrHkpYh9Wzf8Rjsuko.ANNWrvHqMYuertHGHUrZdBntQhpzfbWekB39qez9U2Vx@2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh", - "gateway": "2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh" - }, - { - "id": "shipyard-electrum-1", - "description": "Nym Electrum Service Provider", - "address": "8Tb73cFQpXCLpgxEA2VSDru2hHrcZ3KQcyMsGbxcTjBp.4x5tu66k8YkHk4tYac1qwEFbNq5WsKiX5kR51q5KKH88@4WgKhJdmUffz4e1o1ftVAGS3HnG56LiNAxA9dmaekrVd", - "gateway": "4WgKhJdmUffz4e1o1ftVAGS3HnG56LiNAxA9dmaekrVd" - }, - { - "id": "shipyard-electrum-2", - "description": "Nym Electrum Service Provider", - "address": "GR6z31MwCsvxHrnvvVN1Cpasd8aQ1giwQqPTZM9dN7VH.5rEiqakSPDrBtKmvpU8Shnhz6gRM85JLoB7AX4h7PJYr@5Ao1J38frnU9Rx5YVeF5BWExcnDTcW8etNe9W2sRASXD", - "gateway": "5Ao1J38frnU9Rx5YVeF5BWExcnDTcW8etNe9W2sRASXD" - } - ] - }, - { - "id": "telegram", - "description": "Telegram", - "items": [ - { - "id": "shipyard-telegram-2", - "description": "Nym Telegram Service Provider", - "address": "C4w6ewbQtoaZEeoaaNw1xVASChqo4WVjNfuYEUFjZxpc.8F1D7rQXf2jGoj1Ken7PiGDM8HS2Ug79wSoc9nZ1iqh1@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve", - "gateway": "62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve" - }, - { - "id": "shipyard-telegram-3", - "description": "Nym Telegram Service Provider", - "address": "DStL3BEUZuQZfbij1KAY3BvJh8rC5jpr9mc6AQ6aTLUu.Ax9foYaKfFgX6g8y393GoNpKkKrnDGFGRZwxDv9R7X6M@FQon7UwF5knbUr2jf6jHhmNLbJnMreck1eUcVH59kxYE", - "gateway": "FQon7UwF5knbUr2jf6jHhmNLbJnMreck1eUcVH59kxYE" - }, - { - "id": "shipyard-telegram-4", - "description": "Nym Telegram Service Provider", - "address": "8gRdGTzsDxYzpasRQhsRg59MCgNNhnfag2oFfwwZPXnB.DtDrGz7ScVm4o7sN4K3CYUJveYgz7fcXELBVLNDfMS9Y@3ojQD6V7skM1bSXJX7fVQvscjmcgptzdixQEaAha2ixh", - "gateway": "3ojQD6V7skM1bSXJX7fVQvscjmcgptzdixQEaAha2ixh" - }, - { - "id": "shipyard-telegram-5", - "description": "Nym Telegram Service Provider", - "address": "AR3oEM6Uvmfs6fyddwSehoBUKCFxz7MdFi4z7aahuHuY.3ZKapg9A3Py1PXhyLbCJr8ZbJsEV6NZdN1WJaGGut5tj@EEyq16v63aotPBCepxUpCgAojrNasZ6Hk1PjpRyBAdEp", - "gateway": "EEyq16v63aotPBCepxUpCgAojrNasZ6Hk1PjpRyBAdEp" - }, - { - "id": "shipyard-telegram-6", - "description": "Nym Telegram Service Provider", - "address": "7n1BYhsXSwcr8Qim8AqZTAodqFia4QG6T7CRc1ihQHpv.7o4trpGqu2LHMUiXc3dddgfGET1CFFcAK9gKYoHoSn5e@BTZNB3bkkEePsT14GN8ofVtM1SJae4YLWjpBerrKYfr", - "gateway": "BTZNB3bkkEePsT14GN8ofVtM1SJae4YLWjpBerrKYfr" - }, - { - "id": "shipyard-telegram-7", - "description": "Nym Telegram Service Provider", - "address": "Gv4TWhUKrvJfqh1jBRPGEQrikNZvZse2kS3ZgN9Z2nAZ.7KGPaaqUEum2C59jLvw7f8Ug8a48YuZdjjZu3t4JES4U@C7J8SwZQqjWqhBryyjJxLt7FacVuPTwAmR2otGy53ayi", - "gateway": "C7J8SwZQqjWqhBryyjJxLt7FacVuPTwAmR2otGy53ayi" - }, - { - "id": "shipyard-telegram-8", - "description": "Nym Telegram Service Provider", - "address": "8Mqgp12cpF6FSXMeqzxgFgQXvTSapyNqGAi5wy7ub4ge.7z7PDsiJGiGxGz4p77v5L5fZhXBJ5qNZ8CgJwYNr6H6J", - "gateway": "3zd3wrCK8Dz5TXrcvk5dG5s9EEdf4Ck1v9VgBPMMFVkR" - }, - { - "id": "shipyard-telegram-9", - "description": "Nym Telegram Service Provider", - "address": "F3N5eiPDZcGFC985Go4Mpv8p9uxFD1L3jRUdrLCbrZLm.EyTxWwwTwYpPrJBmc97GLd1LpUAphjptS5y1ed182bGk@GAjhJcrd6f1edaqUkfWCff6zdHoqo756qYrc2TfPuCXJ", - "gateway": "GAjhJcrd6f1edaqUkfWCff6zdHoqo756qYrc2TfPuCXJ" - }, - { - "id": "shipyard-telegram-10", - "description": "Nym Telegram Service Provider", - "address": "G7y7e1nVBr8fmQSzdeAxXnCmmmJb5k8N3E8LBV31KE5g.GRRUCj6t6cCUUjakmTWzidMLiYA7EdCedKnup8osaBC6@AJad2R9virYEYXEsTcicN5y5tyPoixrhhAGsxoESZVnc", - "gateway": "AJad2R9virYEYXEsTcicN5y5tyPoixrhhAGsxoESZVnc" - }, - { - "id": "shipyard-telegram-11", - "description": "Nym Telegram Service Provider", - "address": "2kq9Z7RyDZtb8kxXjyP3ZT8VMWHg6JXFDChGuuNBk7Hw.F5XYbBaGSoF8qAo8faPcaNRPHEq3Y25BDcwESeabUS9S@HaLyPQrhBTq75dnGeBUdYWeFVA2BBn39MgkhEt3VTMMM", - "gateway": "HaLyPQrhBTq75dnGeBUdYWeFVA2BBn39MgkhEt3VTMMM" - }, - { - "id": "shipyard-telegram-12", - "description": "Nym Telegram Service Provider", - "address": "GegdtpNzYj4QCgpih9Kxv7ZVZxmVdxYHsDkiPsbT71XG.E8xtE8mrapjzFtyuziZSrsScAKhwZMH5wNpKWtKfzJ5Y@9Byd9VAtyYMnbVAcqdoQxJnq76XEg2dbxbiF5Aa5Jj9J", - "gateway": "9Byd9VAtyYMnbVAcqdoQxJnq76XEg2dbxbiF5Aa5Jj9J" - }, - { - "id": "shipyard-telegram-13", - "description": "Nym Telegram Service Provider", - "address": "4SsrDQeEtG3mpeD9nN5CDdGaCsxFvNeYMhoviDzNNB9f.GyqG6iK5rBvhe3HXLR11m6ULpf13ATgYvkkidLmteDLs@5EpkkrMFYAM3XcaztXnZwBWquURHSKsyc9JxUCengDFS", - "gateway": "5EpkkrMFYAM3XcaztXnZwBWquURHSKsyc9JxUCengDFS" - }, - { - "id": "shipyard-telegram-14", - "description": "Nym Telegram Service Provider", - "address": "9JoHRu2RrSD1fjbj9NSTASgjv9Szep7Nhd9L2PywxbBi.AZhAUDNX6iH8BqXyR5c7TJuzpwMEvDXrabNLGuRukvVf@9xJM74FwwHhEKKJHihD21QSZnHM2QBRMoFx9Wst6qNBS", - "gateway": "9xJM74FwwHhEKKJHihD21QSZnHM2QBRMoFx9Wst6qNBS" - }, - { - "id": "shipyard-telegram-15", - "description": "Nym Telegram Service Provider", - "address": "3K174ijjXqCkhMDT9xLcqjS4MXk2QsqZt4PdgNcuUrnn.BNnHnQmWoj6Uo6kkS1QkPqsdHaXrcwyR9F6MnnzDkZJL@C7J8SwZQqjWqhBryyjJxLt7FacVuPTwAmR2otGy53ayi", - "gateway": "C7J8SwZQqjWqhBryyjJxLt7FacVuPTwAmR2otGy53ayi" - }, - { - "id": "shipyard-telegram-16", - "description": "Nym Telegram Service Provider", - "address": "BqX5Q3MEcbTnM39hUswQchLW68SrqbhL8K5ucrLmtP39.AWrVsFoVC9s6KjdpcasATmZPA3GtMsUxcfHpAkuNdtFG@Emswx6KXyjRfq1c2k4d4uD2e6nBSbH1biorCZUei8UNS", - "gateway": "Emswx6KXyjRfq1c2k4d4uD2e6nBSbH1biorCZUei8UNS" - }, - { - "id": "shipyard-telegram-17", - "description": "Nym Telegram Service Provider", - "address": "2tQxccgcqdkuUvLqgiEkEN4rNRZ5QknygnKAFcS4gfoe.EVrY5q5sqDqBUbS3wHsRRZhk2MAw1S17hNoH1Bicyv7n@DAGQxdxwAkwjaLjTw1B9vndia4YyFD15qRgcTQxrmkom", - "gateway": "DAGQxdxwAkwjaLjTw1B9vndia4YyFD15qRgcTQxrmkom" - }, - { - "id": "shipyard-telegram-18", - "description": "Nym Telegram Service Provider", - "address": "8YG1rcEauJA814Nd7hSxjNe2UrRwrGsrXTm1Cmd3gRrU.FxYaYqpNN8PciNsySs3zYPrTB1J8AYUu9DBsM2vVDDfF@7EfEESLo71GUvx3UEW79LgTegHUBPUocUzGyJVv6LHog", - "gateway": "7EfEESLo71GUvx3UEW79LgTegHUBPUocUzGyJVv6LHog" - }, - { - "id": "shipyard-telegram-19", - "description": "Nym Telegram Service Provider", - "address": "HPiXADVFLwLQPNpPtyYefzvYntC6tp9UJ5fJZGfkqvDt.2EUUxmeT3AiaUzAcE5SyXRAk8a2JXBkRz4B8McSdkrST@9ACTkYraCqE9jMb6zb6ne8EDQGGhZw5ykNiq9YRUdHTD", - "gateway": "9ACTkYraCqE9jMb6zb6ne8EDQGGhZw5ykNiq9YRUdHTD" - }, - { - "id": "shipyard-telegram-20", - "description": "Nym Telegram Service Provider", - "address": "2QLnEEnTmf2NRWtcQPWBeRcg7Hej5WSPWRWwtTpEEZWF.BheS78ozc8ngvhsXNNnshdJzpoYsmEvhfn3WKUYF5dRU@C2uyokSPoxhku9GexRxEo1e8KPZ7q6e8FXmK3gtY8kkF", - "gateway": "C2uyokSPoxhku9GexRxEo1e8KPZ7q6e8FXmK3gtY8kkF" - }, - { - "id": "shipyard-telegram-21", - "description": "Nym Telegram Service Provider", - "address": "FuBbnwiANfaXZnn683jBapK5XVm5ttgZSykU3vqPSHoD.94MFGv1VcBLTkRwzBDQUkWjvqtZYVBrJg2Q8JGbizcib@CTqYPY8htdAQMXCzRW9SjZzZuqYwSt2iUh6HPaNgmTvK", - "gateway": "CTqYPY8htdAQMXCzRW9SjZzZuqYwSt2iUh6HPaNgmTvK" - }, - { - "id": "shipyard-telegram-22", - "description": "Nym Telegram Service Provider", - "address": "9EbQx5jQznSVbftFom7sqUSHAACrsfvMhrzhaFt4A3SZ.D1FQCirL4YKwfcmtMGvB5Rugt5sAzGEhdSjJ3bHVQRZ@7Zh1Sz5dXpA6s53CbtcdqhQhLqwf4cLynL7KqHKcjrG4", - "gateway": "7Zh1Sz5dXpA6s53CbtcdqhQhLqwf4cLynL7KqHKcjrG4" - }, - { - "id": "shipyard-telegram-24", - "description": "Nym Telegram Service Provider", - "address": "6Umawwvf551VyB3Ko46NgKLqJdTFJeToCM67mrTmM3G.3A4sesBac4KGuMTFjvYBwLpksMJvbMbteGJQgmm4PV4Y@AnnYnEtBjB2a5sHmeRCnBq43qxyHDf95Bqd7cwQyKNLR", - "gateway": "AnnYnEtBjB2a5sHmeRCnBq43qxyHDf95Bqd7cwQyKNLR" - }, - { - "id": "shipyard-telegram-25", - "description": "Nym Telegram Service Provider", - "address": "CDtxTeoyqq83JpV9G8cR5HRHRdMMaVspQsCwH3Qnajt3.F5EHK9HFcdGrE2hqA7bK9AUmkbihujYDhtNNqHKxW765@BDkeNx7JQm5NsQakst9s8htogZXhpTQedFAgZpvsGCqH", - "gateway": "BDkeNx7JQm5NsQakst9s8htogZXhpTQedFAgZpvsGCqH" - }, - { - "id": "shipyard-telegram-26", - "description": "Nym Telegram Service Provider", - "address": "HukZkLG2DoarQEqaoDLuqW1GFf2NSHDUMGBZiyJGRYJD.9GyU8wPsyzcvRjcyk8hiNpTJbXCmq5F3VoVhFBZYuHR3@GsGEZiDBz8SWfHGaK5SDmhfbTEM55v37WCYYcT9wTSxN", - "gateway": "GsGEZiDBz8SWfHGaK5SDmhfbTEM55v37WCYYcT9wTSxN" - }, - { - "id": "shipyard-telegram-27", - "description": "Nym Telegram Service Provider", - "address": "773y8iMVJiRk4dRbjQzkJVbrei4TwkePNE5WTEttt77d.3Mw47C9XZj3oAzk1iSqC5Y36tbBsjtaTtdgaHM3Zsdma@7fiZtNL1RACQTwGrKLBT9nbY77bfwZnX9rqcWqc53qgv", - "gateway": "7fiZtNL1RACQTwGrKLBT9nbY77bfwZnX9rqcWqc53qgv" - }, - { - "id": "shipyard-telegram-28", - "description": "Nym Telegram Service Provider", - "address": "6jQJEorCu7YiP9HdDaMeHxcNhxeNmZ1kpd836GnqLZX.HsJqEmNTszGecsKqFB37i84nBXxqf4ETgrKmKmBvMGHC@FYnDMQzT49ZGM23gVqpTxfih14V6wuedNXirekmt37zE", - "gateway": "FYnDMQzT49ZGM23gVqpTxfih14V6wuedNXirekmt37zE" - }, - { - "id": "shipyard-telegram-29", - "description": "Nym Telegram Service Provider", - "address": "BiCSyovpFMuSnTvF2TdiuZwrytXDrd9AH47ZMcCxscVC.G9YpdicA9BBNoVHDgjWjgt17wv5WYKWcbE3vPJJVpSJD@GAjhJcrd6f1edaqUkfWCff6zdHoqo756qYrc2TfPuCXJ", - "gateway":"GAjhJcrd6f1edaqUkfWCff6zdHoqo756qYrc2TfPuCXJ" - }, - { - "id": "shipyard-telegram-30", - "description": "Nym Telegram Service Provider", - "address": "AQRRAs9oc8QWXAFBs44YhCKUny7AyLsfLy91pwmGgxuf.CWUKoKA1afSKyw5BnFJJg19UDgnaVATupsFhQpyTEBHJ@EBT8jTD8o4tKng2NXrrcrzVhJiBnKpT1bJy5CMeArt2w", - "gateway": "EBT8jTD8o4tKng2NXrrcrzVhJiBnKpT1bJy5CMeArt2w" - }, - { - "id": "shipyard-telegram-31", - "description": "Nym Telegram Service Provider", - "address": "6YqjAZK3Pr1ngiBLcDkotboB5WiN6k6NPpbXvShH4pR5.9Ss6VW3Xbyi8LuxduNNwnXEv9njHCQ2PLSP1UK6tsoa5@42XCK9dMS9m5XJLzQd2dBuwimk6ndZnczhZaV5VPFkQD", - "gateway": "42XCK9dMS9m5XJLzQd2dBuwimk6ndZnczhZaV5VPFkQD" - }, - { - "id": "shipyard-telegram-32", - "description": "Nym Telegram Service Provider", - "address": "EmYWLeybmj86Vzr62vxuZ3T15jwMNHggzK7sQwid96yp.GyaF9WprSr56cxUdGf5TpcUvAjb2VbAr8CVBrmBUYAaw@GL5wESoz4oSbpBaTki9qB9213FGUQXCiRjbzDkhWwoBC", - "gateway": "GL5wESoz4oSbpBaTki9qB9213FGUQXCiRjbzDkhWwoBC" - }, - { - "id": "shipyard-telegram-33", - "description": "Nym Telegram Service Provider", - "address": "4PDb96cck5btTj6G7rsomqwHJsp4qu8uPvFCbwHfjFUx.C5dKbaoakH7egsZvAueRbwLFbmxnQaVMeSr6QTMpuBAA@58ceEFaLJh6zAo3cirzT1BDQm7D3L5acnQrxGH1D6TAY", - "gateway": "58ceEFaLJh6zAo3cirzT1BDQm7D3L5acnQrxGH1D6TAY" - }, - { - "id": "shipyard-telegram-34", - "description": "Nym Telegram Service Provider", - "address": "BeZbeMf9vcpUf368qDd85dtLwXLj4Ee5bsHMB2fUD8uX.HELVbppkwU1jmzUAUrCEbHeJfVciSeo8VGAkbJSpwxsb@ADdHkiTfkpsSt31zVToWW9j3KikH24aLAAwDKtCYE5jY", - "gateway":"ADdHkiTfkpsSt31zVToWW9j3KikH24aLAAwDKtCYE5jY" - }, - { - "id": "shipyard-telegram-35", - "description": "Nym Telegram Service Provider", - "address": "Bp4JRFyf7GB9L9J95AqMPnz9zbGmPnViA5fDXKeNraLJ.D6CTdcjJVxDmH2UQvzXuPWg9Se9xXYe76uDMypXvhzd7@6UjGEeQZK14C5K2kenycTkqt7qRjEHGLgaQx3FWySo3N", - "gateway": "6UjGEeQZK14C5K2kenycTkqt7qRjEHGLgaQx3FWySo3N" - }, - { - "id": "shipyard-telegram-36", - "description": "Nym Telegram Service Provider", - "address": "91h7io6BGhVjbtC7dbbRcScyTJcTfnMsTQZ6aWMVsrWR.Epb4hANXCp8cGEY3wSgawux991ti9Z5Y1FHTMzAKFa6E@DF4TE7V8kJkttMvnoSVGnRFFRt6WYGxxiC2w1XyPQnHe", - "gateway": "DF4TE7V8kJkttMvnoSVGnRFFRt6WYGxxiC2w1XyPQnHe" - }, - { - "id": "shipyard-telegram-37", - "description": "Nym Telegram Service Provider", - "address": "Cy2wuwKpWZ3iWzKU3ZWL1qqcVfJ5Cq85dU7UHVWwv2gc.9AhC9b2zVcLnXLGriMdxjpsWJpq6iAdCavDi63udbL89@678qVUJ21uwxZBhp3r56z7GRf6gMh3NYDHruTegPtgMf", - "gateway": "678qVUJ21uwxZBhp3r56z7GRf6gMh3NYDHruTegPtgMf" - }, - { - "id": "shipyard-telegram-38", - "description": "Nym Telegram Service Provider", - "address": "GgUeUWW1NRSuquZYeZf3WkppE92EQUHJrFjNZtYU1jow.CSEjwrRi4f4uyw7N6L2LPKw2tB8spcMbFu99wHZzFZSj@77TSuVU8d1oXKbPzjec2xh4i3Wj5WwUyy9Lr36sm8gZm", - "gateway": "77TSuVU8d1oXKbPzjec2xh4i3Wj5WwUyy9Lr36sm8gZm" - }, - { - "id": "shipyard-telegram-39", - "description": "Nym Telegram Service Provider", - "address": "kz4zWwSkYiQxqxXFPNcGUByTPQWXascD9RfYsmSxY7n.ajp3SjbBVBjrU9nXpSQXAXzbb6EHJJyhbY6cc1ajayx@BTZNB3bkkEePsT14GN8ofVtM1SJae4YLWjpBerrKYf", - "gateway": "HyS2UZtZX3kQXdazbdE99DhCjBXjbG61LC9QsmXwbxrU" - } - ] - }, - { - "id": "blockstream", - "description": "Blockstream Green", - "items": [ - { - "id": "nym-blockstream", - "description": "Nym Blockstream Green Service Provider", - "address": "GiRjFWrMxt58pEMuusm4yT3RxoMD1MMPrR9M2N4VWRJP.3CNZBPq4vg7v7qozjGjdPMXcvDmkbWPCgbGCjQVw9n6Z@2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW", - "gateway": "2xU4CBE6QiiYt6EyBXSALwxkNvM7gqJfjHXaMkjiFmYW" - } - ] - } -]`); -export const Loaded = () => ( - - - -); - -export const ServiceAlreadySelected = () => ( - - console.log('New service provider selected: ', serviceProvider)} - /> - -); diff --git a/nym-connect/desktop/src/components/ServiceSelector.tsx b/nym-connect/desktop/src/components/ServiceSelector.tsx deleted file mode 100644 index 5eb8361d3c..0000000000 --- a/nym-connect/desktop/src/components/ServiceSelector.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import React, { useEffect } from 'react'; -import { - Box, - CircularProgress, - Divider, - FormControl, - InputLabel, - MenuItem, - Select, - Stack, - Typography, -} from '@mui/material'; -import { Service, ServiceProvider, Services } from '../types/directory'; -import { useTauriEvents } from '../utils'; -import { ServiceProviderPopup } from './ServiceProviderPopup'; - -export const ServiceSelector: FCWithChildren<{ - onChange?: (serviceProvider?: ServiceProvider) => void; - services?: Services; - currentSp?: ServiceProvider; -}> = ({ services, currentSp, onChange }) => { - const [service, setService] = React.useState(); - const [serviceProvider, setServiceProvider] = React.useState(currentSp); - const [isPopupVisible, setPopupVisible] = React.useState(false); - - const getService = () => { - if (!services || !currentSp) { - return undefined; - } - return services.find((s) => - s.items.some( - ({ id, address, gateway }) => - id === currentSp.id && address === currentSp.address && gateway === currentSp.gateway, - ), - ); - }; - - useEffect(() => { - if (!service && currentSp) { - setServiceProvider(currentSp); - setService(getService()); - } - }, [currentSp, services]); - - /** - * Gets a random service provider from the currently selected service. - * - * If there is no service selected, or it does not have items, `undefined` is returned. - */ - const getRandomServiceProviderForService = (serviceToUse?: Service): ServiceProvider | undefined => { - if (!serviceToUse?.items.length) { - return undefined; - } - return serviceToUse.items[Math.floor(Math.random() * serviceToUse.items.length)]; - }; - - const handleServiceSelected = React.useCallback( - (newService?: Service) => { - console.log(newService?.id, service?.id); - // if the user has chosen a new service, then pick a random service provider - if (newService?.id !== service?.id) { - const newServiceProvider = getRandomServiceProviderForService(newService); - setServiceProvider(newServiceProvider); - onChange?.(newServiceProvider); - setService(newService); - } - }, - [service], - ); - - // clears the display and fire on change (to trigger upstream storage clearance) - const clearServiceProviderAndFireOnChange = () => { - setService(undefined); - setServiceProvider(undefined); - onChange?.(undefined); - }; - - // when the user clears local storage, reset the selector - useTauriEvents('help://clear-storage', () => { - clearServiceProviderAndFireOnChange(); - }); - - const handleAdvancedSpChange = (newServiceProvider?: ServiceProvider, newService?: Service) => { - setPopupVisible(false); - setService(newService); - setServiceProvider(newServiceProvider); - onChange?.(newServiceProvider); - }; - - const handleNewService = (newServiceId?: string) => { - const newService = (services || []).find((s) => s.id === newServiceId); - setService(newService); - }; - - if (!services) { - return ( - - theme.palette.common.white}> - - Loading services... - - - ); - } - - return ( - - - - Select a service - - - - setPopupVisible(false)} - onServiceProviderChanged={handleAdvancedSpChange} - /> - - ); -}; diff --git a/nym-connect/desktop/src/components/TooltipInfo.tsx b/nym-connect/desktop/src/components/TooltipInfo.tsx index 2d76c6859e..7a78242c08 100644 --- a/nym-connect/desktop/src/components/TooltipInfo.tsx +++ b/nym-connect/desktop/src/components/TooltipInfo.tsx @@ -1,15 +1,21 @@ import React from 'react'; import { Divider, Stack, Typography } from '@mui/material'; -import { ServiceProvider } from 'src/types/directory'; +import { ServiceProvider, Gateway } from 'src/types/directory'; -export const ServiceProviderInfo = ({ serviceProvider }: { serviceProvider: ServiceProvider }) => ( +export const ServiceProviderInfo = ({ + serviceProvider, + gateway, +}: { + serviceProvider: ServiceProvider; + gateway: Gateway; +}) => ( Connection info - Gateway {serviceProvider.gateway} + Gateway {gateway.identity} diff --git a/nym-connect/desktop/src/context/main.tsx b/nym-connect/desktop/src/context/main.tsx index f1dbe5948a..46d3312fac 100644 --- a/nym-connect/desktop/src/context/main.tsx +++ b/nym-connect/desktop/src/context/main.tsx @@ -3,48 +3,56 @@ import { DateTime } from 'luxon'; import { invoke } from '@tauri-apps/api'; import { Error } from 'src/types/error'; import { getVersion } from '@tauri-apps/api/app'; +import * as Sentry from '@sentry/react'; import { useEvents } from 'src/hooks/events'; import { UserDefinedGateway, UserDefinedSPAddress } from 'src/types/service-provider'; import { getItemFromStorage, setItemInStorage } from 'src/utils'; -import { ConnectionStatusKind, GatewayPerformance } from '../types'; +import { ConnectionStatusKind, GatewayPerformance, PrivacyLevel, UserData } from '../types'; import { ConnectionStatsItem } from '../components/ConnectionStats'; -import { ServiceProvider } from '../types/directory'; +import { ServiceProvider, Gateway } from '../types/directory'; import initSentry from '../sentry'; const FORAGE_GATEWAY_KEY = 'nym-connect-user-gateway'; const FORAGE_SP_KEY = 'nym-connect-user-sp'; -const FORAGE_MONITORING_ENABLED = 'nym-connect-monitoring-enabled'; type ModeType = 'light' | 'dark'; export type TClientContext = { mode: ModeType; appVersion?: string; + userData?: UserData; connectionStatus: ConnectionStatusKind; connectionStats?: ConnectionStatsItem[]; connectedSince?: DateTime; error?: Error; gatewayPerformance: GatewayPerformance; selectedProvider?: ServiceProvider; + selectedGateway?: Gateway; showInfoModal: boolean; userDefinedGateway?: UserDefinedGateway; userDefinedSPAddress: UserDefinedSPAddress; serviceProviders?: ServiceProvider[]; + gateways?: Gateway[]; setMode: (mode: ModeType) => void; clearError: () => void; - monitoringEnabled: boolean; setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void; setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void; setConnectedSince: (connectedSince: DateTime | undefined) => void; setShowInfoModal: (show: boolean) => void; - setSerivceProvider: () => void; + setServiceProvider: () => void; + setGateway: () => void; startConnecting: () => Promise; startDisconnecting: () => Promise; setUserDefinedGateway: React.Dispatch>; setUserDefinedSPAddress: React.Dispatch>; setMonitoring: (value: boolean) => Promise; + setPrivacyLevel: (value: PrivacyLevel) => Promise; }; +function getRandomFromList(items: T[]): T { + return items[Math.floor(Math.random() * items.length)]; +} + export const ClientContext = createContext({} as TClientContext); export const ClientContextProvider: FCWithChildren = ({ children }) => { @@ -53,7 +61,9 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { const [connectionStats, setConnectionStats] = useState(); const [connectedSince, setConnectedSince] = useState(); const [selectedProvider, setSelectedProvider] = React.useState(); + const [selectedGateway, setSelectedGateway] = React.useState(); const [serviceProviders, setServiceProviders] = React.useState(); + const [gateways, setGateways] = React.useState(); const [error, setError] = useState(); const [appVersion, setAppVersion] = useState(); const [gatewayPerformance, setGatewayPerformance] = useState('Good'); @@ -66,18 +76,26 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { isActive: false, address: undefined, }); - const [monitoringEnabled, setMonitoringEnabled] = useState(false); + const [userData, setUserData] = useState(); const getAppVersion = async () => { const version = await getVersion(); return version; }; + const getUserData = async () => { + const data = await invoke('get_user_data'); + if (!data.privacy_level) { + data.privacy_level = 'High'; + } + setUserData(data); + return data; + }; + useEffect(() => { const initSentryClient = async () => { - const monitoring = await getItemFromStorage({ key: FORAGE_MONITORING_ENABLED }); - setMonitoringEnabled(Boolean(monitoring)); - if (monitoring === true) { + const data = await getUserData(); + if (data.monitoring) { await initSentry(); } }; @@ -94,13 +112,15 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { }, [userDefinedSPAddress]); const initialiseApp = async () => { - const services = await invoke('get_services'); + const fetchedServices = await invoke('get_services'); + const fetchedGateways = await invoke('get_gateways'); const AppVersion = await getAppVersion(); const storedUserDefinedGateway = await getItemFromStorage({ key: FORAGE_GATEWAY_KEY }); const storedUserDefinedSP = await getItemFromStorage({ key: FORAGE_SP_KEY }); setAppVersion(AppVersion); - setServiceProviders(services as ServiceProvider[]); + setServiceProviders(fetchedServices); + setGateways(fetchedGateways); if (storedUserDefinedGateway) { setUserDefinedGateway(storedUserDefinedGateway); @@ -111,7 +131,10 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { }; useEvents({ - onError: (e) => setError(e), + onError: (e) => { + setError(e); + Sentry.captureException(e); + }, onGatewayPerformanceChange: (performance) => setGatewayPerformance(performance), onStatusChange: (status) => setConnectionStatus(status), }); @@ -134,6 +157,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { } catch (e) { setError({ title: 'Could not connect', message: e as string }); console.log(e); + Sentry.captureException(e); } }, []); @@ -142,50 +166,61 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { await invoke('start_disconnecting'); } catch (e) { console.log(e); + Sentry.captureException(e); } }, []); const shouldUseUserGateway = !!userDefinedGateway.gateway && userDefinedGateway.isActive; const shouldUseUserSP = !!userDefinedSPAddress.address && userDefinedSPAddress.isActive; - const setServiceProvider = async (newServiceProvider: ServiceProvider) => { - await invoke('set_gateway', { - gateway: shouldUseUserGateway ? userDefinedGateway.gateway : newServiceProvider.gateway, - }); - await invoke('set_service_provider', { - serviceProvider: shouldUseUserSP ? userDefinedSPAddress.address : newServiceProvider.address, - }); - }; - - const getRandomSPFromList = (services: ServiceProvider[]) => { - const randomSelection = services[Math.floor(Math.random() * services.length)]; - return randomSelection; - }; - const buildServiceProvider = async (serviceProvider: ServiceProvider) => { const sp = { ...serviceProvider }; - - if (shouldUseUserGateway) sp.gateway = userDefinedGateway.gateway as string; if (shouldUseUserSP) sp.address = userDefinedSPAddress.address as string; - return sp; }; - const setSerivceProvider = async () => { + const buildGateway = async (gateway: Gateway) => { + const gw = { ...gateway }; + if (shouldUseUserGateway) gw.identity = userDefinedGateway.gateway as string; + return gw; + }; + + const setServiceProvider = async () => { if (serviceProviders) { - const randomServiceProvider = getRandomSPFromList(serviceProviders); + const randomServiceProvider = getRandomFromList(serviceProviders); const withUserDefinitions = await buildServiceProvider(randomServiceProvider); - await setServiceProvider(withUserDefinitions); + await invoke('set_service_provider', { + serviceProvider: shouldUseUserSP ? userDefinedSPAddress.address : withUserDefinitions.address, + }); setSelectedProvider(withUserDefinitions); } return undefined; }; + const setGateway = async () => { + if (gateways) { + const randomGateway = getRandomFromList(gateways); + const withUserDefinitions = await buildGateway(randomGateway); + await invoke('set_gateway', { + gateway: shouldUseUserGateway ? userDefinedGateway.gateway : withUserDefinitions.identity, + }); + setSelectedGateway(withUserDefinitions); + } + return undefined; + }; + const clearError = () => setError(undefined); const setMonitoring = async (value: boolean) => { - setMonitoringEnabled(value); - await setItemInStorage({ key: FORAGE_MONITORING_ENABLED, value }); + await invoke('set_monitoring', { enabled: value }); + // refresh user data + await getUserData(); + }; + + const setPrivacyLevel = async (value: PrivacyLevel) => { + await invoke('set_privacy_level', { privacyLevel: value }); + // refresh user data + await getUserData(); }; const contextValue = useMemo( @@ -201,11 +236,13 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { showInfoModal, setConnectionStats, selectedProvider, + selectedGateway, serviceProviders, connectedSince, - monitoringEnabled, + userData, setConnectedSince, - setSerivceProvider, + setServiceProvider, + setGateway, startConnecting, startDisconnecting, gatewayPerformance, @@ -215,6 +252,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { setUserDefinedGateway, setUserDefinedSPAddress, setMonitoring, + setPrivacyLevel, }), [ mode, @@ -228,9 +266,10 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { connectedSince, gatewayPerformance, selectedProvider, + selectedGateway, userDefinedGateway, userDefinedSPAddress, - monitoringEnabled, + userData, ], ); diff --git a/nym-connect/desktop/src/context/mocks/main.tsx b/nym-connect/desktop/src/context/mocks/main.tsx index 8c602596eb..46a5f50298 100644 --- a/nym-connect/desktop/src/context/mocks/main.tsx +++ b/nym-connect/desktop/src/context/mocks/main.tsx @@ -6,12 +6,12 @@ const mockValues: TClientContext = { appVersion: 'v1.x.x', mode: 'dark', connectionStatus: ConnectionStatusKind.disconnected, - selectedProvider: { id: '1', description: 'Keybase service provider', gateway: 'abc123', address: '123abc' }, + selectedProvider: { id: '1', description: 'Keybase service provider', address: '123abc' }, gatewayPerformance: 'Good', showInfoModal: false, userDefinedGateway: { isActive: false, gateway: '' }, userDefinedSPAddress: { isActive: false, address: '' }, - monitoringEnabled: false, + userData: { monitoring: false, privacy_level: 'High' }, setShowInfoModal: () => {}, setMode: () => {}, clearError: () => {}, @@ -20,10 +20,12 @@ const mockValues: TClientContext = { setConnectionStatus: () => {}, startConnecting: async () => {}, startDisconnecting: async () => {}, - setSerivceProvider: () => {}, + setServiceProvider: () => {}, + setGateway: () => {}, setUserDefinedGateway: () => {}, setUserDefinedSPAddress: () => {}, setMonitoring: async () => {}, + setPrivacyLevel: async () => {}, }; export const MockProvider: FCWithChildren<{ diff --git a/nym-connect/desktop/src/hooks/events.ts b/nym-connect/desktop/src/hooks/events.ts index 4b2c4fcc55..0b15e5347d 100644 --- a/nym-connect/desktop/src/hooks/events.ts +++ b/nym-connect/desktop/src/hooks/events.ts @@ -1,5 +1,6 @@ import { useEffect, useRef } from 'react'; import { listen, UnlistenFn } from '@tauri-apps/api/event'; +import * as Sentry from '@sentry/react'; import { ConnectionStatusKind, GatewayPerformance } from 'src/types'; import { Error } from 'src/types/error'; import { TauriEvent } from 'src/types/event'; @@ -29,7 +30,10 @@ export const useEvents = ({ .then((result) => { unlisten.push(result); }) - .catch((e) => console.log(e)); + .catch((e) => { + console.log(e); + Sentry.captureException(e); + }); listen('socks5-event', (e: TauriEvent) => { console.log(e); diff --git a/nym-connect/desktop/src/pages/connection/Connected.tsx b/nym-connect/desktop/src/pages/connection/Connected.tsx index 5e06b8828e..1408b8644f 100644 --- a/nym-connect/desktop/src/pages/connection/Connected.tsx +++ b/nym-connect/desktop/src/pages/connection/Connected.tsx @@ -7,7 +7,7 @@ import { ConnectionStatus } from 'src/components/ConnectionStatus'; import { ConnectionStatusKind, GatewayPerformance } from 'src/types'; import { ConnectionStatsItem } from 'src/components/ConnectionStats'; import { IpAddressAndPort } from 'src/components/IpAddressAndPort'; -import { ServiceProvider } from 'src/types/directory'; +import { ServiceProvider, Gateway } from 'src/types/directory'; import { ExperimentalWarning } from 'src/components/ExperimentalWarning'; import { ConnectionLayout } from 'src/layouts/ConnectionLayout'; import { PowerButton } from 'src/components/PowerButton/PowerButton'; @@ -26,6 +26,7 @@ export const Connected: FCWithChildren<{ busy?: boolean; isError?: boolean; serviceProvider?: ServiceProvider; + gateway?: Gateway; clearError: () => void; onConnectClick: (status: ConnectionStatusKind) => void; closeInfoModal: () => void; @@ -40,6 +41,7 @@ export const Connected: FCWithChildren<{ busy, isError, serviceProvider, + gateway, clearError, onConnectClick, closeInfoModal, @@ -54,6 +56,7 @@ export const Connected: FCWithChildren<{ status={ConnectionStatusKind.connected} gatewayPerformance={gatewayPerformance} serviceProvider={serviceProvider} + gateway={gateway} /> diff --git a/nym-connect/desktop/src/pages/connection/index.tsx b/nym-connect/desktop/src/pages/connection/index.tsx index 2ba081331f..2f7ebc5899 100644 --- a/nym-connect/desktop/src/pages/connection/index.tsx +++ b/nym-connect/desktop/src/pages/connection/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { forage } from '@tauri-apps/tauri-forage'; +import * as Sentry from '@sentry/react'; import { DateTime } from 'luxon'; import { useClientContext } from 'src/context/main'; import { useTauriEvents } from 'src/utils'; @@ -28,12 +29,15 @@ export const ConnectionPage = () => { // eslint-disable-next-line default-case switch (currentStatus) { case 'disconnected': - await context.setSerivceProvider(); + Sentry.captureMessage('start connect', 'info'); + await context.setServiceProvider(); + await context.setGateway(); await context.startConnecting(); context.setConnectedSince(DateTime.now()); context.setShowInfoModal(true); break; case 'connected': + Sentry.captureMessage('start disconnect', 'info'); await context.startDisconnecting(); context.setConnectedSince(undefined); break; @@ -58,6 +62,7 @@ export const ConnectionPage = () => { gatewayPerformance={context.gatewayPerformance} connectedSince={context.connectedSince} serviceProvider={context.selectedProvider} + gateway={context.selectedGateway} closeInfoModal={closeInfoModal} stats={[ { diff --git a/nym-connect/desktop/src/pages/menu/Apps.tsx b/nym-connect/desktop/src/pages/menu/Apps.tsx index 5de95cc9c3..dc6584a859 100644 --- a/nym-connect/desktop/src/pages/menu/Apps.tsx +++ b/nym-connect/desktop/src/pages/menu/Apps.tsx @@ -4,7 +4,7 @@ import { Box } from '@mui/system'; const appsSchema = { messagingApps: ['Matrix', 'Telegram', 'Keybase'], - wallets: ['Monero', 'Blockstream', 'Electrum'], + wallets: ['Monero', 'Blockstream', 'Electrum', 'Alephium'], }; export const CompatibleApps = () => ( diff --git a/nym-connect/desktop/src/pages/menu/index.tsx b/nym-connect/desktop/src/pages/menu/index.tsx index da4f2be73d..67576dc88c 100644 --- a/nym-connect/desktop/src/pages/menu/index.tsx +++ b/nym-connect/desktop/src/pages/menu/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Apps, HelpOutline, Settings, BugReport } from '@mui/icons-material'; +import { Apps, HelpOutline, Settings, BugReport, PrivacyTip } from '@mui/icons-material'; import { Stack, Link, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material'; import { Link as RouterLink } from 'react-router-dom'; import { AppVersion } from 'src/components/AppVersion'; @@ -7,6 +7,7 @@ import { AppVersion } from 'src/components/AppVersion'; const menuSchema = [ { title: 'Supported apps', icon: Apps, path: 'apps' }, { title: 'How to connect guide', icon: HelpOutline, path: 'guide' }, + { title: 'Privacy level', icon: PrivacyTip, path: 'privacy-level' }, { title: 'Please help us improve the app', icon: BugReport, path: 'monitoring' }, { title: 'Settings', icon: Settings, path: 'settings' }, ]; diff --git a/nym-connect/desktop/src/pages/menu/settings/MonitoringSettings.tsx b/nym-connect/desktop/src/pages/menu/settings/MonitoringSettings.tsx index 92e15a155d..8b899cbe72 100644 --- a/nym-connect/desktop/src/pages/menu/settings/MonitoringSettings.tsx +++ b/nym-connect/desktop/src/pages/menu/settings/MonitoringSettings.tsx @@ -4,8 +4,8 @@ import { Box, FormControl, FormControlLabel, FormHelperText, Stack, Switch, Typo import { useClientContext } from 'src/context/main'; export const MonitoringSettings = () => { - const { monitoringEnabled, setMonitoring } = useClientContext(); - const [enabled, setEnabled] = useState(monitoringEnabled); + const { userData, setMonitoring } = useClientContext(); + const [enabled, setEnabled] = useState(userData?.monitoring || false); const [loading, setLoading] = useState(false); const handleChange = async (e: ChangeEvent) => { @@ -25,7 +25,13 @@ export const MonitoringSettings = () => { + } label="Enable" /> diff --git a/nym-connect/desktop/src/pages/menu/settings/PrivacyLevelSettings.tsx b/nym-connect/desktop/src/pages/menu/settings/PrivacyLevelSettings.tsx new file mode 100644 index 0000000000..5c2241b4d0 --- /dev/null +++ b/nym-connect/desktop/src/pages/menu/settings/PrivacyLevelSettings.tsx @@ -0,0 +1,48 @@ +import React, { ChangeEvent, useState } from 'react'; +import * as Sentry from '@sentry/react'; +import { Box, FormControl, FormControlLabel, FormHelperText, Stack, Switch, Typography } from '@mui/material'; +import { useClientContext } from 'src/context/main'; + +export const PrivacyLevelSettings = () => { + const { userData, setPrivacyLevel } = useClientContext(); + const [speedBoost, setSpeedBoost] = useState(userData?.privacy_level !== 'High'); + const [loading, setLoading] = useState(false); + + const handleChange = async (e: ChangeEvent) => { + setLoading(true); + setSpeedBoost(e.target.checked); + Sentry.captureMessage(`privacy level switched to ${e.target.checked ? 'Medium' : 'High'}`, 'info'); + await setPrivacyLevel(e.target.checked ? 'Medium' : 'High'); + setLoading(false); + }; + + return ( + + + + + Speed boost + + + + } + label="Enable" + /> + + By activating this option, the connection speed will be relatively faster in exchange of relaxing some + protections + + + + + + ); +}; diff --git a/nym-connect/desktop/src/routes/index.tsx b/nym-connect/desktop/src/routes/index.tsx index 8af5a703e3..f1e4b1da67 100644 --- a/nym-connect/desktop/src/routes/index.tsx +++ b/nym-connect/desktop/src/routes/index.tsx @@ -9,14 +9,15 @@ import { SettingsMenu } from 'src/pages/menu/settings'; import { GatewaySettings } from 'src/pages/menu/settings/GatewaySettings'; import { ServiceProviderSettings } from 'src/pages/menu/settings/ServiceProviderSettings'; import { MonitoringSettings } from '../pages/menu/settings/MonitoringSettings'; +import { PrivacyLevelSettings } from '../pages/menu/settings/PrivacyLevelSettings'; import { useClientContext } from '../context/main'; const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes); export const AppRoutes = () => { - const { monitoringEnabled } = useClientContext(); + const { userData } = useClientContext(); - const RoutesContainer = monitoringEnabled ? SentryRoutes : Routes; + const RoutesContainer = userData?.monitoring ? SentryRoutes : Routes; return ( @@ -25,6 +26,7 @@ export const AppRoutes = () => { } /> } /> } /> + } /> } /> } /> diff --git a/nym-connect/desktop/src/sentry.ts b/nym-connect/desktop/src/sentry.ts index 13f7529c30..5127c5dc0c 100644 --- a/nym-connect/desktop/src/sentry.ts +++ b/nym-connect/desktop/src/sentry.ts @@ -1,17 +1,25 @@ import React from 'react'; import { createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from 'react-router-dom'; +import { invoke } from '@tauri-apps/api'; import * as Sentry from '@sentry/react'; import { CaptureConsole } from '@sentry/integrations'; import { getVersion } from '@tauri-apps/api/app'; -const SENTRY_DSN = 'https://625e2658da4945a7a253f3ee04413a31@o967446.ingest.sentry.io/4505306292289536'; +const SENTRY_DSN = 'SENTRY_DSN_JS'; async function initSentry() { console.log('⚠ performance monitoring and error reporting enabled'); console.log('initializing sentry'); + const dsn = await invoke('get_env', { variable: SENTRY_DSN }); + + if (!dsn) { + console.warn(`unable to initialize sentry, ${SENTRY_DSN} env var not set`); + return; + } + Sentry.init({ - dsn: SENTRY_DSN, + dsn, integrations: [ new Sentry.BrowserTracing({ // Set `tracePropagationTargets` to control for which URLs distributed tracing should be enabled diff --git a/nym-connect/desktop/src/stories/AppFlow.stories.tsx b/nym-connect/desktop/src/stories/AppFlow.stories.tsx index c00cb2aa9e..c88f32bae2 100644 --- a/nym-connect/desktop/src/stories/AppFlow.stories.tsx +++ b/nym-connect/desktop/src/stories/AppFlow.stories.tsx @@ -29,7 +29,6 @@ export const Mock: ComponentStory = () => { id: 'nym-keybase', description: 'Nym Keybase Service Provider', address: '1234.5678', - gateway: 'abcedf', }, ], }, diff --git a/nym-connect/desktop/src/stories/ConnectedLayout.stories.tsx b/nym-connect/desktop/src/stories/ConnectedLayout.stories.tsx index 426252dc63..b0e72587c2 100644 --- a/nym-connect/desktop/src/stories/ConnectedLayout.stories.tsx +++ b/nym-connect/desktop/src/stories/ConnectedLayout.stories.tsx @@ -22,7 +22,7 @@ export const Default: ComponentStory = () => ( status={ConnectionStatusKind.connected} connectedSince={DateTime.now()} ipAddress="127.0.0.1" - serviceProvider={{ id: 'service 1', description: 'good services', address: 'abc123', gateway: '8910xyz' }} + serviceProvider={{ id: 'service 1', description: 'good services', address: 'abc123' }} port={1080} stats={[ { diff --git a/nym-connect/desktop/src/stories/DefaultLayout.stories.tsx b/nym-connect/desktop/src/stories/DefaultLayout.stories.tsx index 4d1b869a10..00a2c5b684 100644 --- a/nym-connect/desktop/src/stories/DefaultLayout.stories.tsx +++ b/nym-connect/desktop/src/stories/DefaultLayout.stories.tsx @@ -30,7 +30,7 @@ export const WithServices: ComponentStory = () => ( { id: '1', description: 'Keybase service', - items: [{ id: '1', description: 'Keybase service 1', gateway: 'abc123', address: '123abc' }], + items: [{ id: '1', description: 'Keybase service 1', address: '123abc' }], }, ]} clearError={() => {}} diff --git a/nym-connect/desktop/src/types/directory.ts b/nym-connect/desktop/src/types/directory.ts index 55395192a1..3883b5ce5c 100644 --- a/nym-connect/desktop/src/types/directory.ts +++ b/nym-connect/desktop/src/types/directory.ts @@ -2,7 +2,6 @@ export interface ServiceProvider { id: string; description: string; address: string; - gateway: string; } export interface Service { @@ -12,3 +11,7 @@ export interface Service { } export type Services = Service[]; + +export interface Gateway { + identity: string; +} diff --git a/nym-connect/desktop/src/types/index.ts b/nym-connect/desktop/src/types/index.ts index 6bc150d410..7ebac88588 100644 --- a/nym-connect/desktop/src/types/index.ts +++ b/nym-connect/desktop/src/types/index.ts @@ -1,2 +1,3 @@ export * from './rust'; export * from './connection'; +export * from './user-data'; diff --git a/nym-connect/desktop/src/types/user-data.ts b/nym-connect/desktop/src/types/user-data.ts new file mode 100644 index 0000000000..8bd43a941e --- /dev/null +++ b/nym-connect/desktop/src/types/user-data.ts @@ -0,0 +1,6 @@ +export type PrivacyLevel = 'High' | 'Medium'; + +export type UserData = { + monitoring?: boolean; + privacy_level?: PrivacyLevel; +}; diff --git a/nym-connect/mobile/src-tauri/Cargo.toml b/nym-connect/mobile/src-tauri/Cargo.toml index 0817f11d62..28ea437cdc 100644 --- a/nym-connect/mobile/src-tauri/Cargo.toml +++ b/nym-connect/mobile/src-tauri/Cargo.toml @@ -43,7 +43,7 @@ serde_repr = "0.1" tap = "1.0.1" # tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open"] } tauri = { version = "2.0.0-alpha.3", features = ["clipboard-write-text", "native-tls-vendored", "notification-all", "shell-open"] } -tendermint-rpc = "0.23.0" +#tendermint-rpc = "0.23.0" thiserror = "1.0" time = { version = "0.3.17", features = ["local-offset"] } tokio = { version = "1.24.1", features = ["sync", "time"] } diff --git a/nym-connect/native/android/app/build.gradle b/nym-connect/native/android/app/build.gradle index 5cd2701c53..72ba890287 100644 --- a/nym-connect/native/android/app/build.gradle +++ b/nym-connect/native/android/app/build.gradle @@ -2,7 +2,7 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.21' - id "io.sentry.android.gradle" version "3.11.0" + id 'io.sentry.android.gradle' version '3.11.0' } android { @@ -122,6 +122,6 @@ dependencies { androidTestImplementation 'androidx.compose.ui:ui-test-junit4' debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' - implementation 'com.github.kittinunf.fuel:fuel:3.0.0-alpha1' + implementation 'com.github.kittinunf.fuel:fuel:2.3.1' implementation 'io.sentry:sentry-android:6.24.0' } diff --git a/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ProxyWorker.kt b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ProxyWorker.kt index c2f71c3118..6f798eff9a 100644 --- a/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ProxyWorker.kt +++ b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ProxyWorker.kt @@ -15,8 +15,7 @@ import androidx.work.ForegroundInfo import androidx.work.WorkManager import androidx.work.WorkerParameters import androidx.work.workDataOf -import fuel.Fuel -import fuel.get +import com.github.kittinunf.fuel.Fuel import io.sentry.Sentry import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString @@ -110,20 +109,23 @@ class ProxyWorker( var serviceProvider: String? = null // fetch the SP list and select a random one try { - val res = Fuel.get(spUrl) - if (res.statusCode == 200) { - val spJson = json.decodeFromString(res.body) - serviceProvider = - Random.nextInt(until = spJson.items.size) - .let { spJson.items[it].service_provider_client_id } - Log.d(tag, "selected service provider: $serviceProvider") - } else { - Log.w( - tag, - "failed to fetch the service providers list: $res.statusCode" - ) - Log.w(tag, "using a default service provider $defaultSp") - } + val (_, response, result) = Fuel.get(spUrl).responseString() + result.fold( + { data -> + val spJson = json.decodeFromString(data) + serviceProvider = + Random.nextInt(until = spJson.items.size) + .let { spJson.items[it].service_provider_client_id } + Log.d(tag, "selected service provider: $serviceProvider") + }, + { error -> + Log.w( + tag, + "failed to fetch the service provider list: ${response.statusCode} ${error.message}" + ) + Log.w(tag, "using a default service provider $defaultSp") + } + ) } catch (e: Throwable) { Log.e( tag, diff --git a/nym-wallet/CHANGELOG.md b/nym-wallet/CHANGELOG.md index 82a2dd9da7..f0d62aec2d 100644 --- a/nym-wallet/CHANGELOG.md +++ b/nym-wallet/CHANGELOG.md @@ -2,6 +2,12 @@ ## [Unreleased] +## [v1.2.6] (2023-07-18) + +- [wallet] bugfix: don't send funds for pledge decrease simulation ([#3676]) + +[#3676]: https://github.com/nymtech/nym/pull/3676 + ## [v1.2.5] (2023-07-04) - Wallet - add "Test my node" in the Node Settings and show its results ([#2314]) diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index fa2722e281..2da872c4fc 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -122,9 +122,9 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-trait" @@ -200,6 +200,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" @@ -220,18 +226,18 @@ checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" [[package]] name = "bip32" -version = "0.3.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873faa4363bfc54c36a48321da034c92a0645a363eed34d948683ffc1706e37f" +checksum = "7e141fb0f8be1c7b45887af94c88b182472b57c96b56773250ae00cd6a14a164" dependencies = [ - "bs58", - "hmac", - "k256", + "bs58 0.5.0", + "hmac 0.12.1", + "k256 0.13.1", "once_cell", "pbkdf2", "rand_core 0.6.4", - "ripemd160", - "sha2 0.9.9", + "ripemd", + "sha2 0.10.6", "subtle 2.4.1", "zeroize", ] @@ -292,21 +298,21 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] name = "blake3" -version = "1.3.3" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" +checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -321,7 +327,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", "generic-array 0.14.7", ] @@ -334,20 +339,14 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - [[package]] name = "bls12_381" version = "0.6.0" source = "git+https://github.com/jstuczyn/bls12_381?branch=gt-serialisation#10fb6f700bfda17c8475af3bfd31e3fec15f2278" dependencies = [ "digest 0.9.0", - "ff", - "group", + "ff 0.11.1", + "group 0.11.0", "pairing", "rand_core 0.6.4", "subtle 2.4.1", @@ -380,8 +379,14 @@ name = "bs58" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" dependencies = [ - "sha2 0.9.9", + "sha2 0.10.6", ] [[package]] @@ -423,6 +428,9 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "cairo-rs" @@ -688,15 +696,15 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.7.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" [[package]] name = "constant_time_eq" -version = "0.2.5" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "convert_case" @@ -747,8 +755,9 @@ dependencies = [ [[package]] name = "cosmos-sdk-proto" -version = "0.12.3" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" dependencies = [ "prost", "prost-types", @@ -757,17 +766,16 @@ dependencies = [ [[package]] name = "cosmrs" -version = "0.7.1" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af13955d6f356272e6def9ff5e2450a7650df536d8934f47052a20c76513d2f6" dependencies = [ "bip32", "cosmos-sdk-proto", - "ecdsa", + "ecdsa 0.16.8", "eyre", "getrandom 0.2.10", - "k256", - "prost", - "prost-types", + "k256 0.13.1", "rand_core 0.6.4", "serde", "serde_json", @@ -779,39 +787,66 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.0.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd" +checksum = "75836a10cb9654c54e77ee56da94d592923092a10b369cdb0dbd56acefc16340" dependencies = [ - "digest 0.9.0", + "digest 0.10.7", "ed25519-zebra", - "k256", + "k256 0.11.6", "rand_core 0.6.4", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.0.0" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4" +checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" dependencies = [ "syn 1.0.107", ] [[package]] -name = "cosmwasm-std" -version = "1.0.0" +name = "cosmwasm-schema" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195" +checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc" +dependencies = [ + "cosmwasm-schema-derive", + "schemars", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cosmwasm-schema-derive" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.107", +] + +[[package]] +name = "cosmwasm-std" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb" dependencies = [ "base64 0.13.1", "cosmwasm-crypto", "cosmwasm-derive", + "derivative", "forward_ref", + "hex", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.5.1", + "sha2 0.10.6", "thiserror", "uint", ] @@ -857,14 +892,14 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.8.0", + "memoffset 0.9.0", "scopeguard", ] @@ -885,9 +920,21 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.3.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", @@ -1010,11 +1057,25 @@ dependencies = [ ] [[package]] -name = "cw-controllers" -version = "0.13.4" +name = "curve25519-dalek-ng" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0bc6019b4d3d81e11f5c384bcce7173e2210bd654d75c6c9668e12cca05dfa" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", +] + +[[package]] +name = "cw-controllers" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071" +dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "cw-utils", @@ -1025,9 +1086,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a" +checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" dependencies = [ "cosmwasm-std", "schemars", @@ -1036,22 +1097,26 @@ dependencies = [ [[package]] name = "cw-utils" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b" +checksum = "c80e93d1deccb8588db03945016a292c3c631e6325d349ebb35d2db6f4f946f7" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw2", "schemars", + "semver 1.0.16", "serde", "thiserror", ] [[package]] name = "cw2" -version = "0.13.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" +checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", @@ -1059,11 +1124,12 @@ dependencies = [ ] [[package]] -name = "cw3" -version = "0.13.4" +name = "cw20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b" +checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-utils", "schemars", @@ -1071,11 +1137,27 @@ dependencies = [ ] [[package]] -name = "cw4" -version = "0.13.4" +name = "cw3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd" +checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa" dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "cw20", + "schemars", + "serde", + "thiserror", +] + +[[package]] +name = "cw4" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b" +dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", @@ -1119,11 +1201,33 @@ dependencies = [ [[package]] name = "der" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.107", ] [[package]] @@ -1159,11 +1263,12 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.3", + "const-oid", "crypto-common", "subtle 2.4.1", ] @@ -1271,14 +1376,28 @@ checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" [[package]] name = "ecdsa" -version = "0.13.4" +version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der 0.7.7", + "digest 0.10.7", + "elliptic-curve 0.13.5", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki 0.7.2", ] [[package]] @@ -1287,7 +1406,30 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "signature 1.6.4", +] + +[[package]] +name = "ed25519" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +dependencies = [ + "pkcs8 0.10.2", + "signature 2.1.0", +] + +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", ] [[package]] @@ -1297,7 +1439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek", - "ed25519", + "ed25519 1.5.3", "rand 0.7.3", "serde", "sha2 0.9.9", @@ -1327,18 +1469,39 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" -version = "0.11.12" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "ff", + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", "generic-array 0.14.7", - "group", + "group 0.12.1", + "pkcs8 0.9.0", "rand_core 0.6.4", - "sec1", + "sec1 0.3.0", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.2", + "digest 0.10.7", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", "subtle 2.4.1", "zeroize", ] @@ -1451,6 +1614,26 @@ dependencies = [ "subtle 2.4.1", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle 2.4.1", +] + [[package]] name = "field-offset" version = "0.3.4" @@ -1921,7 +2104,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" dependencies = [ "byteorder", - "ff", + "ff 0.11.1", + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle 2.4.1", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", "rand_core 0.6.4", "subtle 2.4.1", ] @@ -2106,7 +2311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" dependencies = [ "digest 0.9.0", - "hmac", + "hmac 0.11.0", ] [[package]] @@ -2119,6 +2324,15 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "html5ever" version = "0.25.2" @@ -2496,25 +2710,28 @@ dependencies = [ [[package]] name = "k256" -version = "0.10.4" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ "cfg-if", - "ecdsa", - "elliptic-curve", - "sec1", - "sha2 0.9.9", - "sha3", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.6", ] [[package]] -name = "keccak" -version = "0.1.3" +name = "k256" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ - "cpufeatures", + "cfg-if", + "ecdsa 0.16.8", + "elliptic-curve 0.13.5", + "once_cell", + "sha2 0.10.6", + "signature 2.1.0", ] [[package]] @@ -2700,9 +2917,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] @@ -2903,7 +3120,7 @@ dependencies = [ name = "nym-api-requests" version = "0.1.0" dependencies = [ - "bs58", + "bs58 0.4.0", "cosmrs", "cosmwasm-std", "getset", @@ -2933,11 +3150,11 @@ name = "nym-coconut" version = "0.5.0" dependencies = [ "bls12_381", - "bs58", + "bs58 0.4.0", "digest 0.9.0", - "ff", + "ff 0.11.1", "getrandom 0.2.10", - "group", + "group 0.11.0", "itertools", "nym-dkg", "nym-pemstore", @@ -2974,7 +3191,7 @@ dependencies = [ name = "nym-coconut-interface" version = "0.1.0" dependencies = [ - "bs58", + "bs58 0.4.0", "getset", "nym-coconut", "serde", @@ -2998,7 +3215,7 @@ dependencies = [ name = "nym-contracts-common" version = "0.5.0" dependencies = [ - "bs58", + "bs58 0.4.0", "cosmwasm-std", "schemars", "serde", @@ -3009,7 +3226,7 @@ dependencies = [ name = "nym-crypto" version = "0.4.0" dependencies = [ - "bs58", + "bs58 0.4.0", "ed25519-dalek", "nym-pemstore", "nym-sphinx-types", @@ -3026,9 +3243,9 @@ version = "0.1.0" dependencies = [ "bitvec", "bls12_381", - "bs58", - "ff", - "group", + "bs58 0.4.0", + "ff 0.11.1", + "group 0.11.0", "lazy_static", "nym-pemstore", "rand 0.8.5", @@ -3045,6 +3262,8 @@ dependencies = [ name = "nym-group-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", + "cw-controllers", "cw4", "schemars", "serde", @@ -3054,14 +3273,14 @@ dependencies = [ name = "nym-mixnet-contract-common" version = "0.6.0" dependencies = [ - "bs58", + "bs58 0.4.0", "cosmwasm-std", "humantime-serde", "log", "nym-contracts-common", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.4.1", "serde_repr", "thiserror", "time", @@ -3072,7 +3291,9 @@ dependencies = [ name = "nym-multisig-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw-storage-plus", "cw-utils", "cw3", "cw4", @@ -3230,6 +3451,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.9.9", + "tendermint-rpc", "thiserror", "tokio", "url", @@ -3302,7 +3524,7 @@ dependencies = [ [[package]] name = "nym_wallet" -version = "1.2.5" +version = "1.2.6" dependencies = [ "async-trait", "base64 0.13.1", @@ -3317,7 +3539,7 @@ dependencies = [ "fern", "futures", "itertools", - "k256", + "k256 0.13.1", "log", "nym-coconut-interface", "nym-config", @@ -3343,7 +3565,6 @@ dependencies = [ "tauri-codegen", "tauri-macros", "tempfile", - "tendermint-rpc", "thiserror", "time", "tokio", @@ -3499,7 +3720,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2e415e349a3006dd7d9482cdab1c980a845bed1377777d768cb693a44540b42" dependencies = [ - "group", + "group 0.11.0", ] [[package]] @@ -3575,11 +3796,12 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pbkdf2" -version = "0.9.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "crypto-mac 0.11.1", + "digest 0.10.7", + "hmac 0.12.1", ] [[package]] @@ -3802,13 +4024,22 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkcs8" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" dependencies = [ - "der", - "spki", - "zeroize", + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.7", + "spki 0.7.2", ] [[package]] @@ -3939,9 +4170,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.10.4" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", "prost-derive", @@ -3949,9 +4180,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.10.1" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", @@ -3962,11 +4193,10 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.10.1" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "bytes", "prost", ] @@ -4222,15 +4452,25 @@ dependencies = [ [[package]] name = "rfc6979" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" dependencies = [ - "crypto-bigint", - "hmac", + "crypto-bigint 0.4.9", + "hmac 0.12.1", "zeroize", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle 2.4.1", +] + [[package]] name = "rfd" version = "0.10.0" @@ -4271,14 +4511,12 @@ dependencies = [ ] [[package]] -name = "ripemd160" -version = "0.9.1" +name = "ripemd" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug 0.3.0", + "digest 0.10.7", ] [[package]] @@ -4423,13 +4661,28 @@ dependencies = [ [[package]] name = "sec1" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ - "der", + "base16ct 0.1.1", + "der 0.6.1", "generic-array 0.14.7", - "pkcs8", + "pkcs8 0.9.0", + "subtle 2.4.1", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.7", + "generic-array 0.14.7", + "pkcs8 0.10.2", "subtle 2.4.1", "zeroize", ] @@ -4522,6 +4775,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde-json-wasm" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +dependencies = [ + "serde", +] + [[package]] name = "serde_bytes" version = "0.11.9" @@ -4658,7 +4920,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -4682,19 +4944,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha3" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug 0.3.0", + "digest 0.10.7", ] [[package]] @@ -4717,11 +4967,21 @@ dependencies = [ [[package]] name = "signature" -version = "1.4.0" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" dependencies = [ - "digest 0.9.0", + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", "rand_core 0.6.4", ] @@ -4793,13 +5053,13 @@ dependencies = [ "aes 0.7.5", "arrayref", "blake2 0.8.1", - "bs58", + "bs58 0.4.0", "byteorder", "chacha", "curve25519-dalek", "digest 0.9.0", "hkdf", - "hmac", + "hmac 0.11.0", "lioness", "log", "rand 0.7.3", @@ -4816,12 +5076,22 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spki" -version = "0.5.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" dependencies = [ "base64ct", - "der", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.7", ] [[package]] @@ -4920,6 +5190,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "syn" version = "1.0.107" @@ -5236,28 +5512,28 @@ dependencies = [ [[package]] name = "tendermint" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca881fa4dedd2b46334f13be7fbc8cc1549ba4be5a833fe4e73d1a1baaf7949" +checksum = "3f0a7d05cf78524782337f8edd55cbc578d159a16ad4affe2135c92f7dbac7f0" dependencies = [ - "async-trait", "bytes", - "ed25519", - "ed25519-dalek", + "digest 0.10.7", + "ed25519 2.2.1", + "ed25519-consensus", "flex-error", "futures", - "k256", + "k256 0.13.1", "num-traits", "once_cell", "prost", "prost-types", - "ripemd160", + "ripemd", "serde", "serde_bytes", "serde_json", "serde_repr", - "sha2 0.9.9", - "signature", + "sha2 0.10.6", + "signature 2.1.0", "subtle 2.4.1", "subtle-encoding", "tendermint-proto", @@ -5267,9 +5543,9 @@ dependencies = [ [[package]] name = "tendermint-config" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c56ee93f4e9b7e7daba86d171f44572e91b741084384d0ae00df7991873dfd" +checksum = "71a72dbbea6dde12045d261f2c70c0de039125675e8a026c8d5ad34522756372" dependencies = [ "flex-error", "serde", @@ -5281,9 +5557,9 @@ dependencies = [ [[package]] name = "tendermint-proto" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71f925d74903f4abbdc4af0110635a307b3cb05b175fdff4a7247c14a4d0874" +checksum = "c0cec054567d16d85e8c3f6a3139963d1a66d9d3051ed545d31562550e9bcc3d" dependencies = [ "bytes", "flex-error", @@ -5299,9 +5575,9 @@ dependencies = [ [[package]] name = "tendermint-rpc" -version = "0.23.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63f57ee05a1e927887191c76d1b139de9fa40c180b9f8727ee44377242a6" +checksum = "d119d83a130537fc4a98c3c9eb6899ebe857fea4860400a61675bfb5f0b35129" dependencies = [ "async-trait", "bytes", @@ -5314,9 +5590,11 @@ dependencies = [ "hyper-rustls", "peg", "pin-project", + "semver 1.0.16", "serde", "serde_bytes", "serde_json", + "subtle 2.4.1", "subtle-encoding", "tendermint", "tendermint-config", diff --git a/nym-wallet/Cargo.toml b/nym-wallet/Cargo.toml index 80e269fba7..de5e6faf93 100644 --- a/nym-wallet/Cargo.toml +++ b/nym-wallet/Cargo.toml @@ -5,4 +5,4 @@ members = [ "src-tauri", "nym-wallet-recovery-cli", "nym-wallet-types" -] +] \ No newline at end of file diff --git a/nym-wallet/nym-wallet-types/Cargo.toml b/nym-wallet/nym-wallet-types/Cargo.toml index 8d47e3d70d..63a4aa5daf 100644 --- a/nym-wallet/nym-wallet-types/Cargo.toml +++ b/nym-wallet/nym-wallet-types/Cargo.toml @@ -11,15 +11,13 @@ serde_json = "1.0" strum = { version = "0.23", features = ["derive"] } ts-rs = "6.1.2" -cosmwasm-std = "1.0.0" -cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } +cosmwasm-std = "1.2.0" +cosmrs = "=0.14.0" nym-config = { path = "../../common/config" } nym-network-defaults = { path = "../../common/network-defaults" } nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } -nym-validator-client = { path = "../../common/client-libs/validator-client", features = [ - "nyxd-client", -] } +nym-validator-client = { path = "../../common/client-libs/validator-client" } nym-vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } # Used for Type conversion, can be extracted but its a lot of work nym-vesting-contract = { path = "../../contracts/vesting" } diff --git a/nym-wallet/package.json b/nym-wallet/package.json index f14d6dd227..e93b887fcf 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -1,6 +1,6 @@ { "name": "@nymproject/nym-wallet-app", - "version": "1.2.5", + "version": "1.2.6", "main": "index.js", "license": "MIT", "scripts": { @@ -123,4 +123,4 @@ "webpack-favicons": "^1.3.8", "webpack-merge": "^5.8.0" } -} +} \ No newline at end of file diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 06757de614..19b5f0e3c9 100644 --- a/nym-wallet/src-tauri/Cargo.toml +++ b/nym-wallet/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym_wallet" -version = "1.2.5" +version = "1.2.6" description = "Nym Native Wallet" authors = ["Nym Technologies SA"] license = "" @@ -38,21 +38,21 @@ serde_json = "1.0" serde_repr = "0.1" strum = { version = "0.23", features = ["derive"] } tauri = { version = "=1.2.3", features = ["clipboard-all", "shell-open", "updater", "window-maximize", "window-print"] } -tendermint-rpc = "0.23.0" +#tendermint-rpc = "0.23.0" time = { version = "0.3.17", features = ["local-offset"] } thiserror = "1.0" tokio = { version = "1.10", features = ["full"] } toml = "0.5.8" url = "2.2" -k256 = { version = "0.10", features = ["ecdsa", "sha256"] } +k256 = { version = "0.13", features = ["ecdsa", "sha256"] } base64 = "0.13" zeroize = { version = "1.5", features = ["zeroize_derive", "serde"] } -cosmwasm-std = "1.0.0" -cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" } +cosmwasm-std = "1.2.0" +cosmrs = "=0.14.0" nym-validator-client = { path = "../../common/client-libs/validator-client", features = [ - "nyxd-client", + "signing", "http-client" ] } nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] } nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" } diff --git a/nym-wallet/src-tauri/src/error.rs b/nym-wallet/src-tauri/src/error.rs index 8cee3018d5..3d9374c22a 100644 --- a/nym-wallet/src-tauri/src/error.rs +++ b/nym-wallet/src-tauri/src/error.rs @@ -1,4 +1,5 @@ use nym_contracts_common::signing::SigningAlgorithm; +use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity::Ed25519RecoveryError; use nym_types::error::TypesError; use nym_validator_client::nym_api::error::NymAPIError; @@ -25,7 +26,7 @@ pub enum BackendError { #[error("{source}")] TendermintError { #[from] - source: tendermint_rpc::Error, + source: cosmrs::rpc::Error, }, #[error("{pretty_error}")] NyxdError { @@ -149,6 +150,9 @@ pub enum BackendError { #[error(transparent)] Ed25519Recovery(#[from] Ed25519RecoveryError), + #[error("failed to verify ed25519 signature: {0}")] + Ed25519SignatureError(#[from] identity::SignatureError), + #[error("This command ({name}) has been removed. Please try to use {alternative} instead.")] RemovedCommand { name: String, alternative: String }, } diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index 87c961b38e..dd7cb4558c 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -103,7 +103,6 @@ fn main() { utils::owns_gateway, utils::owns_mixnode, utils::get_env, - utils::get_old_and_incorrect_hardcoded_fee, utils::try_convert_pubkey_to_mix_id, utils::default_mixnode_cost_params, nym_api::status::compute_mixnode_reward_estimation, diff --git a/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs index 0f12ff80c4..332f0eeb43 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs @@ -124,11 +124,9 @@ pub async fn simulate_update_pledge( ); simulate_mixnet_operation( ExecuteMsg::DecreasePledge { - decrease_by: guard - .attempt_convert_to_base_coin(dec_delta.clone())? - .into(), + decrease_by: guard.attempt_convert_to_base_coin(dec_delta)?.into(), }, - Some(dec_delta), + None, &state, ) .await diff --git a/nym-wallet/src-tauri/src/operations/simulate/mod.rs b/nym-wallet/src-tauri/src/operations/simulate/mod.rs index b3e032d000..5662f56fda 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/mod.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use cosmrs::tx; -use cosmrs::tx::Gas; +use cosmrs::Gas; use nym_types::fees::FeeDetails; use nym_validator_client::nyxd::cosmwasm_client::types::GasInfo; use nym_validator_client::nyxd::{CosmosCoin, Fee, GasAdjustable, GasAdjustment, GasPrice}; diff --git a/nym-wallet/src-tauri/src/operations/vesting/bond.rs b/nym-wallet/src-tauri/src/operations/vesting/bond.rs index 38583a877b..f2077e5694 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/bond.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/bond.rs @@ -1,19 +1,17 @@ -use std::cmp::Ordering; - use crate::error::BackendError; use crate::nyxd_client; +use crate::operations::helpers::{ + verify_gateway_bonding_sign_payload, verify_mixnode_bonding_sign_payload, +}; use crate::state::WalletState; use crate::{Gateway, MixNode}; use nym_contracts_common::signing::MessageSignature; use nym_mixnet_contract_common::{GatewayConfigUpdate, MixNodeConfigUpdate}; - -use crate::operations::helpers::{ - verify_gateway_bonding_sign_payload, verify_mixnode_bonding_sign_payload, -}; use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::{Fee, VestingSigningClient}; +use nym_validator_client::nyxd::{traits::VestingSigningClient, Fee}; +use std::cmp::Ordering; #[tauri::command] pub async fn vesting_bond_gateway( diff --git a/nym-wallet/src-tauri/src/operations/vesting/delegate.rs b/nym-wallet/src-tauri/src/operations/vesting/delegate.rs index 2cc873499a..ff7c33710f 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/delegate.rs @@ -6,7 +6,7 @@ use crate::state::WalletState; use nym_mixnet_contract_common::MixId; use nym_types::currency::DecCoin; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::{Fee, VestingSigningClient}; +use nym_validator_client::nyxd::{traits::VestingSigningClient, Fee}; #[tauri::command] pub async fn vesting_delegate_to_mixnode( diff --git a/nym-wallet/src-tauri/src/operations/vesting/queries.rs b/nym-wallet/src-tauri/src/operations/vesting/queries.rs index b8ed343554..a24e26d001 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/queries.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/queries.rs @@ -8,7 +8,7 @@ use cosmwasm_std::Timestamp; use nym_types::currency::DecCoin; use nym_types::vesting::VestingAccountInfo; use nym_types::vesting::{OriginalVestingResponse, PledgeData}; -use nym_validator_client::nyxd::VestingQueryClient; +use nym_validator_client::nyxd::traits::VestingQueryClient; use nym_vesting_contract_common::Period; #[tauri::command] diff --git a/nym-wallet/src-tauri/src/operations/vesting/rewards.rs b/nym-wallet/src-tauri/src/operations/vesting/rewards.rs index 7d64fafed4..dbedb302e3 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/rewards.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/rewards.rs @@ -5,8 +5,8 @@ use crate::error::BackendError; use crate::state::WalletState; use nym_mixnet_contract_common::MixId; use nym_types::transaction::TransactionExecuteResult; +use nym_validator_client::nyxd::traits::VestingSigningClient; use nym_validator_client::nyxd::Fee; -use nym_validator_client::nyxd::VestingSigningClient; #[tauri::command] pub async fn vesting_claim_operator_reward( diff --git a/nym-wallet/src-tauri/src/utils.rs b/nym-wallet/src-tauri/src/utils.rs index 9fe353302c..e5aed4e092 100644 --- a/nym-wallet/src-tauri/src/utils.rs +++ b/nym-wallet/src-tauri/src/utils.rs @@ -9,9 +9,7 @@ use nym_mixnet_contract_common::{IdentityKey, MixId, Percent}; use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::{tx, Coin, CosmosCoin, Gas, GasPrice}; use nym_wallet_types::app::AppEnv; -use serde::{Deserialize, Serialize}; fn get_env_as_option(key: &str) -> Option { match ::std::env::var(key) { @@ -79,116 +77,3 @@ pub async fn default_mixnode_cost_params( }, }) } - -#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub enum Operation { - Upload, - Init, - Migrate, - ChangeAdmin, - Send, - - BondMixnode, - BondMixnodeOnBehalf, - UnbondMixnode, - UnbondMixnodeOnBehalf, - UpdateMixnodeConfig, - DelegateToMixnode, - DelegateToMixnodeOnBehalf, - UndelegateFromMixnode, - UndelegateFromMixnodeOnBehalf, - - BondGateway, - BondGatewayOnBehalf, - UnbondGateway, - UnbondGatewayOnBehalf, - - UpdateContractSettings, - - BeginMixnodeRewarding, - FinishMixnodeRewarding, - - TrackUnbondGateway, - TrackUnbondMixnode, - WithdrawVestedCoins, - TrackUndelegation, - CreatePeriodicVestingAccount, - - AdvanceCurrentInterval, - AdvanceCurrentEpoch, - WriteRewardedSet, - ClearRewardedSet, - UpdateMixnetAddress, - CheckpointMixnodes, - ReconcileDelegations, -} - -impl Operation { - fn default_gas_limit(&self) -> Gas { - match self { - Operation::Upload => 3_000_000u64.into(), - Operation::Init => 500_000u64.into(), - Operation::Migrate => 200_000u64.into(), - Operation::ChangeAdmin => 80_000u64.into(), - Operation::Send => 80_000u64.into(), - - Operation::BondMixnode => 175_000u64.into(), - Operation::BondMixnodeOnBehalf => 200_000u64.into(), - Operation::UnbondMixnode => 175_000u64.into(), - Operation::UnbondMixnodeOnBehalf => 175_000u64.into(), - Operation::UpdateMixnodeConfig => 175_000u64.into(), - Operation::DelegateToMixnode => 175_000u64.into(), - Operation::DelegateToMixnodeOnBehalf => 175_000u64.into(), - Operation::UndelegateFromMixnode => 175_000u64.into(), - Operation::UndelegateFromMixnodeOnBehalf => 175_000u64.into(), - - Operation::BondGateway => 175_000u64.into(), - Operation::BondGatewayOnBehalf => 200_000u64.into(), - Operation::UnbondGateway => 175_000u64.into(), - Operation::UnbondGatewayOnBehalf => 200_000u64.into(), - - Operation::UpdateContractSettings => 175_000u64.into(), - Operation::BeginMixnodeRewarding => 175_000u64.into(), - Operation::FinishMixnodeRewarding => 175_000u64.into(), - Operation::TrackUnbondGateway => 175_000u64.into(), - Operation::TrackUnbondMixnode => 175_000u64.into(), - Operation::WithdrawVestedCoins => 175_000u64.into(), - Operation::TrackUndelegation => 175_000u64.into(), - Operation::CreatePeriodicVestingAccount => 175_000u64.into(), - Operation::AdvanceCurrentInterval => 175_000u64.into(), - Operation::WriteRewardedSet => 175_000u64.into(), - Operation::ClearRewardedSet => 175_000u64.into(), - Operation::UpdateMixnetAddress => 80_000u64.into(), - Operation::CheckpointMixnodes => 175_000u64.into(), - Operation::ReconcileDelegations => 500_000u64.into(), - Operation::AdvanceCurrentEpoch => 175_000u64.into(), - } - } - - fn calculate_fee(gas_price: &GasPrice, gas_limit: Gas) -> CosmosCoin { - gas_price * gas_limit - } - - fn determine_custom_fee(gas_price: &GasPrice, gas_limit: Gas) -> tx::Fee { - let fee = Self::calculate_fee(gas_price, gas_limit); - tx::Fee::from_amount_and_gas(fee, gas_limit) - } - - fn default_fee(&self, gas_price: &GasPrice) -> tx::Fee { - Self::determine_custom_fee(gas_price, self.default_gas_limit()) - } -} - -#[tauri::command] -pub async fn get_old_and_incorrect_hardcoded_fee( - state: tauri::State<'_, WalletState>, - operation: Operation, -) -> Result { - let guard = state.read().await; - let mut approximate_fee = operation.default_fee(guard.current_client()?.nyxd.gas_price()); - // on all our chains it should only ever contain a single type of currency - assert_eq!(approximate_fee.amount.len(), 1); - let coin: Coin = approximate_fee.amount.pop().unwrap().into(); - log::info!("hardcoded fee for {:?} is {:?}", operation, coin); - guard.attempt_convert_to_display_dec_coin(coin) -} diff --git a/nym-wallet/src-tauri/tauri.conf.json b/nym-wallet/src-tauri/tauri.conf.json index 45ba45f846..2986866ccb 100644 --- a/nym-wallet/src-tauri/tauri.conf.json +++ b/nym-wallet/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "package": { "productName": "nym-wallet", - "version": "1.2.5" + "version": "1.2.6" }, "build": { "distDir": "../dist", @@ -14,7 +14,13 @@ "active": true, "targets": "all", "identifier": "net.nymtech.wallet", - "icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"], + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], "resources": [], "externalBin": [], "copyright": "Copyright © 2021-2023 Nym Technologies SA", @@ -39,7 +45,9 @@ }, "updater": { "active": true, - "endpoints": ["https://nymtech.net/.wellknown/wallet/updater.json"], + "endpoints": [ + "https://nymtech.net/.wellknown/wallet/updater.json" + ], "dialog": true, "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo=" }, @@ -67,4 +75,4 @@ "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'" } } -} +} \ No newline at end of file diff --git a/nym-wallet/src/components/Bonding/modals/BondGatewayModal.tsx b/nym-wallet/src/components/Bonding/modals/BondGatewayModal.tsx index 0a3cd718a5..defdac3f95 100644 --- a/nym-wallet/src/components/Bonding/modals/BondGatewayModal.tsx +++ b/nym-wallet/src/components/Bonding/modals/BondGatewayModal.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { Box } from '@mui/material'; import { CurrencyDenom, TNodeType } from '@nymproject/types'; import { ConfirmTx } from 'src/components/ConfirmTX'; @@ -9,6 +9,8 @@ import { useGetFee } from 'src/hooks/useGetFee'; import { GatewayAmount, GatewayData, Signature } from 'src/pages/bonding/types'; import { simulateBondGateway, simulateVestingBondGateway } from 'src/requests'; import { TBondGatewayArgs } from 'src/types'; +import { BalanceWarning } from 'src/components/FeeWarning'; +import { AppContext } from 'src/context'; import { BondGatewayForm } from '../forms/BondGatewayForm'; import { gatewayToTauri } from '../utils'; @@ -50,6 +52,7 @@ export const BondGatewayModal = ({ const [signature, setSignature] = useState(); const { fee, getFee, resetFeeState, feeError } = useGetFee(); + const { userBalance } = useContext(AppContext); useEffect(() => { if (feeError) { @@ -123,6 +126,9 @@ export const BondGatewayModal = ({ value={`${amountData.amount.amount} ${amountData.amount.denom.toUpperCase()}`} divider /> + {fee.amount?.amount && userBalance.balance && ( + + )} ); } diff --git a/nym-wallet/src/components/Bonding/modals/BondMixnodeModal.tsx b/nym-wallet/src/components/Bonding/modals/BondMixnodeModal.tsx index 6cad10f9ab..4e4c39f9cf 100644 --- a/nym-wallet/src/components/Bonding/modals/BondMixnodeModal.tsx +++ b/nym-wallet/src/components/Bonding/modals/BondMixnodeModal.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { CurrencyDenom, TNodeType } from '@nymproject/types'; import { ConfirmTx } from 'src/components/ConfirmTX'; import { ModalListItem } from 'src/components/Modals/ModalListItem'; @@ -8,6 +8,8 @@ import { useGetFee } from 'src/hooks/useGetFee'; import { MixnodeAmount, MixnodeData, Signature } from 'src/pages/bonding/types'; import { simulateBondMixnode, simulateVestingBondMixnode } from 'src/requests'; import { TBondMixNodeArgs } from 'src/types'; +import { BalanceWarning } from 'src/components/FeeWarning'; +import { AppContext } from 'src/context'; import { BondMixnodeForm } from '../forms/BondMixnodeForm'; import { costParamsToTauri, mixnodeToTauri } from '../utils'; @@ -50,6 +52,7 @@ export const BondMixnodeModal = ({ const [signature, setSignature] = useState(); const { fee, getFee, resetFeeState, feeError } = useGetFee(); + const { userBalance } = useContext(AppContext); useEffect(() => { if (feeError) { @@ -125,6 +128,9 @@ export const BondMixnodeModal = ({ value={`${amountData.amount.amount} ${amountData.amount.denom.toUpperCase()}`} divider /> + {fee.amount?.amount && userBalance.balance && ( + + )} ); } diff --git a/nym-wallet/src/components/Bonding/modals/RedeemRewardsModal.tsx b/nym-wallet/src/components/Bonding/modals/RedeemRewardsModal.tsx index 76b9192df9..ba802967d8 100644 --- a/nym-wallet/src/components/Bonding/modals/RedeemRewardsModal.tsx +++ b/nym-wallet/src/components/Bonding/modals/RedeemRewardsModal.tsx @@ -1,11 +1,13 @@ -import React, { useEffect } from 'react'; +import React, { useContext, useEffect } from 'react'; import { FeeDetails } from '@nymproject/types'; import { ModalListItem } from 'src/components/Modals/ModalListItem'; import { SimpleModal } from 'src/components/Modals/SimpleModal'; import { ModalFee } from 'src/components/Modals/ModalFee'; import { useGetFee } from 'src/hooks/useGetFee'; import { simulateClaimOperatorReward, simulateVestingClaimOperatorReward } from 'src/requests'; -import { TBondedMixnode } from 'src/context'; +import { AppContext, TBondedMixnode } from 'src/context'; +import { BalanceWarning } from 'src/components/FeeWarning'; +import { Box } from '@mui/material'; export const RedeemRewardsModal = ({ node, @@ -19,6 +21,7 @@ export const RedeemRewardsModal = ({ onClose: () => void; }) => { const { fee, getFee, isFeeLoading, feeError } = useGetFee(); + const { userBalance } = useContext(AppContext); useEffect(() => { if (feeError) onError(feeError); @@ -49,7 +52,13 @@ export const RedeemRewardsModal = ({ divider /> + + {userBalance.balance?.amount.amount && fee?.amount?.amount && ( + + + + )} ); }; diff --git a/nym-wallet/src/components/Bonding/modals/UpdateBondAmountModal.tsx b/nym-wallet/src/components/Bonding/modals/UpdateBondAmountModal.tsx index 552f2b1f4c..2148313b70 100644 --- a/nym-wallet/src/components/Bonding/modals/UpdateBondAmountModal.tsx +++ b/nym-wallet/src/components/Bonding/modals/UpdateBondAmountModal.tsx @@ -11,6 +11,7 @@ import { decCoinToDisplay, validateAmount } from 'src/utils'; import { simulateUpdateBond, simulateVestingUpdateBond } from 'src/requests'; import { TSimulateUpdateBondArgs, TUpdateBondArgs } from 'src/types'; import { AppContext, TBondedMixnode } from 'src/context'; +import { BalanceWarning } from 'src/components/FeeWarning'; import { TPoolOption } from '../../TokenPoolSelector'; export const UpdateBondAmountModal = ({ @@ -30,7 +31,7 @@ export const UpdateBondAmountModal = ({ const [newBond, setNewBond] = useState(); const [errorAmount, setErrorAmount] = useState(false); - const { printBalance, printVestedBalance } = useContext(AppContext); + const { printBalance, printVestedBalance, userBalance } = useContext(AppContext); useEffect(() => { if (feeError) { @@ -104,6 +105,11 @@ export const UpdateBondAmountModal = ({ > + {userBalance.balance?.amount.amount && fee?.amount?.amount && ( + + + + )} ); diff --git a/nym-wallet/src/components/Delegation/DelegateModal.tsx b/nym-wallet/src/components/Delegation/DelegateModal.tsx index 61762357de..211b4ae2b9 100644 --- a/nym-wallet/src/components/Delegation/DelegateModal.tsx +++ b/nym-wallet/src/components/Delegation/DelegateModal.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useContext, useState } from 'react'; import { Box, Typography, SxProps } from '@mui/material'; import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField'; @@ -7,6 +7,7 @@ import { Console } from 'src/utils/console'; import { useGetFee } from 'src/hooks/useGetFee'; import { simulateDelegateToMixnode, simulateVestingDelegateToMixnode, tryConvertIdentityToMixId } from 'src/requests'; import { debounce } from 'lodash'; +import { AppContext } from 'src/context'; import { SimpleModal } from '../Modals/SimpleModal'; import { ModalListItem } from '../Modals/ModalListItem'; import { checkTokenBalance, validateAmount, validateKey } from '../../utils'; @@ -15,6 +16,7 @@ import { ConfirmTx } from '../ConfirmTX'; import { getMixnodeStakeSaturation } from '../../requests'; import { ErrorModal } from '../Modals/ErrorModal'; +import { BalanceWarning } from '../FeeWarning'; const MIN_AMOUNT_TO_DELEGATE = 10; @@ -73,6 +75,7 @@ export const DelegateModal: FCWithChildren<{ const [mixIdError, setMixIdError] = useState(); const { fee, getFee, resetFeeState, feeError } = useGetFee(); + const { userBalance } = useContext(AppContext); const handleCheckStakeSaturation = async (newMixId: number) => { try { @@ -213,6 +216,11 @@ export const DelegateModal: FCWithChildren<{ onPrev={resetFeeState} onConfirm={handleOk} > + {userBalance.balance?.amount.amount && fee?.amount?.amount && ( + + + + )} diff --git a/nym-wallet/src/components/Delegation/UndelegateModal.tsx b/nym-wallet/src/components/Delegation/UndelegateModal.tsx index fe518d37ff..65820eb2bb 100644 --- a/nym-wallet/src/components/Delegation/UndelegateModal.tsx +++ b/nym-wallet/src/components/Delegation/UndelegateModal.tsx @@ -1,11 +1,13 @@ +import React, { useContext, useEffect } from 'react'; import { Box, SxProps } from '@mui/material'; -import React, { useEffect } from 'react'; import { FeeDetails } from '@nymproject/types'; import { useGetFee } from 'src/hooks/useGetFee'; import { simulateUndelegateFromMixnode, simulateVestingUndelegateFromMixnode } from 'src/requests'; +import { AppContext } from 'src/context'; import { ModalFee } from '../Modals/ModalFee'; import { ModalListItem } from '../Modals/ModalListItem'; import { SimpleModal } from '../Modals/SimpleModal'; +import { BalanceWarning } from '../FeeWarning'; export const UndelegateModal: FCWithChildren<{ open: boolean; @@ -20,6 +22,7 @@ export const UndelegateModal: FCWithChildren<{ backdropProps?: object; }> = ({ mixId, identityKey, open, onClose, onOk, amount, currency, usesVestingContractTokens, sx, backdropProps }) => { const { fee, isFeeLoading, feeError, getFee } = useGetFee(); + const { userBalance } = useContext(AppContext); useEffect(() => { if (usesVestingContractTokens) getFee(simulateVestingUndelegateFromMixnode, { mixId }); @@ -50,6 +53,11 @@ export const UndelegateModal: FCWithChildren<{ + {userBalance.balance?.amount.amount && fee?.amount?.amount && ( + + + + )} ); diff --git a/nym-wallet/src/components/FeeWarning.stories.tsx b/nym-wallet/src/components/FeeWarning.stories.tsx new file mode 100644 index 0000000000..7a553aae79 --- /dev/null +++ b/nym-wallet/src/components/FeeWarning.stories.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Box } from '@mui/material'; +import { BalanceWarning } from './FeeWarning'; + +export default { + title: 'Wallet / Balance warning', + component: BalanceWarning, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( + + + +); + +export const WithWarning = Template.bind({}); +WithWarning.args = { + fee: '200', +}; diff --git a/nym-wallet/src/components/FeeWarning.tsx b/nym-wallet/src/components/FeeWarning.tsx index b26a7f8369..745353b2c6 100644 --- a/nym-wallet/src/components/FeeWarning.tsx +++ b/nym-wallet/src/components/FeeWarning.tsx @@ -1,16 +1,35 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { Warning } from '@mui/icons-material'; import { FeeDetails } from '@nymproject/types'; -import { Alert, AlertTitle } from '@mui/material'; +import { Alert, AlertTitle, Box } from '@mui/material'; +import { isBalanceEnough } from 'src/utils'; +import { AppContext } from 'src/context'; export const FeeWarning = ({ fee, amount }: { fee: FeeDetails; amount: number }) => { if (fee.amount && +fee.amount.amount > amount) { return ( }> - Warning: fees are greater than the reward + Warning: Fees are greater than the reward The fees for redeeming rewards will cost more than the rewards. Are you sure you want to continue? ); } return null; }; + +export const BalanceWarning = ({ tx, fee }: { fee: string; tx?: string }) => { + const { userBalance } = useContext(AppContext); + + const hasEnoughBalanace = isBalanceEnough(fee, tx, userBalance.balance?.amount.amount); + + if (hasEnoughBalanace) return null; + + return ( + }> + Warning: Transaction amount is greater than your balance + The transaction amount (inc fees) is greater than your current balance, which could cause this transaction to + fail. + Do you want to continue? + + ); +}; diff --git a/nym-wallet/src/components/Rewards/RedeemModal.tsx b/nym-wallet/src/components/Rewards/RedeemModal.tsx index 9d80a5d048..b9c774cb2e 100644 --- a/nym-wallet/src/components/Rewards/RedeemModal.tsx +++ b/nym-wallet/src/components/Rewards/RedeemModal.tsx @@ -1,12 +1,13 @@ -import React, { useEffect } from 'react'; +import React, { useContext, useEffect } from 'react'; import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; import { CurrencyDenom, FeeDetails } from '@nymproject/types'; -import { SxProps } from '@mui/material'; +import { Box, SxProps } from '@mui/material'; import { useGetFee } from 'src/hooks/useGetFee'; import { simulateClaimDelegatorReward, simulateVestingClaimDelegatorReward } from 'src/requests'; +import { AppContext } from 'src/context'; import { ModalFee } from '../Modals/ModalFee'; import { SimpleModal } from '../Modals/SimpleModal'; -import { FeeWarning } from '../FeeWarning'; +import { BalanceWarning, FeeWarning } from '../FeeWarning'; import { ModalListItem } from '../Modals/ModalListItem'; export const RedeemModal: FCWithChildren<{ @@ -23,6 +24,7 @@ export const RedeemModal: FCWithChildren<{ usesVestingTokens: boolean; }> = ({ open, onClose, onOk, mixId, identityKey, amount, denom, message, usesVestingTokens, sx, backdropProps }) => { const { fee, isFeeLoading, feeError, getFee } = useGetFee(); + const { userBalance } = useContext(AppContext); const handleOk = async () => { if (onOk) { @@ -56,6 +58,11 @@ export const RedeemModal: FCWithChildren<{ {fee && } + {userBalance.balance?.amount.amount && fee?.amount?.amount && ( + + + + )} ); }; diff --git a/nym-wallet/src/components/Send/SendDetailsModal.tsx b/nym-wallet/src/components/Send/SendDetailsModal.tsx index 366bf7ba9c..061345c68b 100644 --- a/nym-wallet/src/components/Send/SendDetailsModal.tsx +++ b/nym-wallet/src/components/Send/SendDetailsModal.tsx @@ -1,9 +1,11 @@ -import React from 'react'; -import { Stack, SxProps } from '@mui/material'; +import React, { useContext } from 'react'; +import { Box, Stack, SxProps } from '@mui/material'; import { FeeDetails, DecCoin, CurrencyDenom } from '@nymproject/types'; +import { AppContext } from 'src/context'; import { SimpleModal } from '../Modals/SimpleModal'; import { ModalListItem } from '../Modals/ModalListItem'; import { ModalFee, ModalTotalAmount } from '../Modals/ModalFee'; +import { BalanceWarning } from '../FeeWarning'; export const SendDetailsModal = ({ amount, @@ -29,37 +31,45 @@ export const SendDetailsModal = ({ sx?: SxProps; backdropProps?: object; memo?: string; -}) => ( - amount && onSend({ val: amount, to: toAddress })} - onBack={onPrev} - sx={sx} - backdropProps={backdropProps} - > - - - - - - {memo && ( - +}) => { + const { userBalance } = useContext(AppContext); + return ( + amount && onSend({ val: amount, to: toAddress })} + onBack={onPrev} + sx={sx} + backdropProps={backdropProps} + > + + + + + + {memo && ( + + )} + + + {userBalance.balance?.amount.amount && fee?.amount?.amount && ( + + + )} - - - -); + + ); +}; diff --git a/nym-wallet/src/hooks/useGetBalance.ts b/nym-wallet/src/hooks/useGetBalance.ts index 240910e493..d43eeb1c04 100644 --- a/nym-wallet/src/hooks/useGetBalance.ts +++ b/nym-wallet/src/hooks/useGetBalance.ts @@ -1,5 +1,4 @@ import { useCallback, useEffect, useState } from 'react'; -import { invoke } from '@tauri-apps/api'; import { Account, Balance, DecCoin, OriginalVestingResponse, Period, VestingAccountInfo } from '@nymproject/types'; import { getVestingCoins, @@ -11,6 +10,7 @@ import { getVestingAccountInfo, getSpendableRewardCoins, getSpendableVestedCoins, + userBalance, } from '../requests'; import { Console } from '../utils/console'; @@ -101,8 +101,8 @@ export const useGetBalance = (clientDetails?: Account): TUseuserBalance => { setIsLoading(true); setError(undefined); try { - const bal = await invoke('get_balance'); - setBalance(bal as Balance); + const bal = await userBalance(); + setBalance(bal); } catch (err) { setError(err as string); } finally { diff --git a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralGatewaySettings.tsx b/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralGatewaySettings.tsx index f024fcc149..892c1e245c 100644 --- a/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralGatewaySettings.tsx +++ b/nym-wallet/src/pages/bonding/node-settings/settings-pages/general-settings/GeneralGatewaySettings.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { useForm } from 'react-hook-form'; import { clean } from 'semver'; import { yupResolver } from '@hookform/resolvers/yup'; @@ -18,11 +18,14 @@ import { ConfirmTx } from 'src/components/ConfirmTX'; import { useGetFee } from 'src/hooks/useGetFee'; import { LoadingModal } from 'src/components/Modals/LoadingModal'; import { updateGatewayValidationSchema } from 'src/components/Bonding/forms/gatewayValidationSchema'; +import { BalanceWarning } from 'src/components/FeeWarning'; +import { AppContext } from 'src/context'; export const GeneralGatewaySettings = ({ bondedNode }: { bondedNode: TBondedGateway }) => { const [openConfirmationModal, setOpenConfirmationModal] = useState(false); const { getFee, fee, resetFeeState } = useGetFee(); const { refresh } = useBondingContext(); + const { userBalance } = useContext(AppContext); const theme = useTheme(); @@ -78,7 +81,13 @@ export const GeneralGatewaySettings = ({ bondedNode }: { bondedNode: TBondedGate onConfirm={handleSubmit((d) => onSubmit(d))} onPrev={resetFeeState} onClose={resetFeeState} - /> + > + {fee.amount?.amount && userBalance?.balance?.amount.amount && ( + + + + )} + )} {isSubmitting && } { const [openConfirmationModal, setOpenConfirmationModal] = useState(false); const { getFee, fee, resetFeeState } = useGetFee(); + const { userBalance } = useContext(AppContext); const theme = useTheme(); @@ -72,7 +75,13 @@ export const GeneralMixnodeSettings = ({ bondedNode }: { bondedNode: TBondedMixn onConfirm={handleSubmit((d) => onSubmit(d))} onPrev={resetFeeState} onClose={resetFeeState} - /> + > + {fee.amount?.amount && userBalance?.balance?.amount.amount && ( + + + + )} + )} {isSubmitting && } = ({ isStorybook }) => { tx = await undelegate(mixId, fee?.fee); } - // const txs = await undelegate(mixId, usesVestingContractTokens, fee); const balances = await getAllBalances(); setConfirmationModalProps({ diff --git a/nym-wallet/src/utils/common.ts b/nym-wallet/src/utils/common.ts new file mode 100644 index 0000000000..f4ddba7275 --- /dev/null +++ b/nym-wallet/src/utils/common.ts @@ -0,0 +1,250 @@ +import { appWindow } from '@tauri-apps/api/window'; +import bs58 from 'bs58'; +import Big from 'big.js'; +import { valid } from 'semver'; +import { add, format, fromUnixTime } from 'date-fns'; +import { DecCoin, isValidRawCoin, MixNodeCostParams } from '@nymproject/types'; +import { TPoolOption } from 'src/components'; +import { + getCurrentInterval, + getDefaultMixnodeCostParams, + getLockedCoins, + getSpendableCoins, + userBalance, +} from '../requests'; +import { Console } from './console'; + +export const validateKey = (key: string, bytesLength: number): boolean => { + // it must be a valid base58 key + try { + const bytes = bs58.decode(key); + // of length 32 + return bytes.length === bytesLength; + } catch (e) { + Console.error(e as string); + return false; + } +}; + +export const validateAmount = async ( + majorAmountAsString: DecCoin['amount'], + minimumAmountAsString: DecCoin['amount'], +): Promise => { + // tests basic coin value requirements, like no more than 6 decimal places, value lower than total supply, etc + if (!Number(majorAmountAsString)) { + return false; + } + + if (!isValidRawCoin(majorAmountAsString)) { + return false; + } + + const majorValueFloat = parseInt(majorAmountAsString, Number(10)); + + return majorValueFloat >= parseInt(minimumAmountAsString, Number(10)); + + // this conversion seems really iffy but I'm not sure how to better approach it +}; + +export const isValidHostname = (value: string) => { + // regex for ipv4 and ipv6 and hhostname- source http://jsfiddle.net/DanielD/8S4nq/ + const hostnameRegex = + /((^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))|(^\s*((?=.{1,255}$)(?=.*[A-Za-z].*)[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?)*)\s*$)/; + + return hostnameRegex.test(value); +}; + +export const validateVersion = (version: string): boolean => { + try { + return valid(version) !== null; + } catch (e) { + return false; + } +}; + +export const validateLocation = (location: string): boolean => { + const locationRegex = /^[a-z]+$/i; + return locationRegex.test(location); +}; + +export const validateRawPort = (rawPort: number): boolean => !Number.isNaN(rawPort) && rawPort >= 1 && rawPort <= 65535; + +export const truncate = (text: string, trim: number) => `${text.substring(0, trim)}...`; + +export const isGreaterThan = (a: number, b: number) => a > b; + +export const isLessThan = (a: number, b: number) => a < b; + +export const checkHasEnoughFunds = async (allocationValue: string): Promise => { + try { + const walletValue = await userBalance(); + + const remainingBalance = +walletValue.amount.amount - +allocationValue; + return remainingBalance >= 0; + } catch (e) { + Console.log(e as string); + return false; + } +}; + +export const checkHasEnoughLockedTokens = async (allocationValue: string) => { + try { + const lockedTokens = await getLockedCoins(); + const spendableTokens = await getSpendableCoins(); + const remainingBalance = +lockedTokens.amount + +spendableTokens.amount - +allocationValue; + return remainingBalance >= 0; + } catch (e) { + Console.error(e as string); + } + return false; +}; + +export const randomNumberBetween = (min: number, max: number) => { + const minCeil = Math.ceil(min); + const maxFloor = Math.floor(max); + return Math.floor(Math.random() * (maxFloor - minCeil + 1) + minCeil); +}; + +export const splice = (size: number, address?: string): string => { + if (address) { + return `${address.slice(0, size)}...${address.slice(-size)}`; + } + return ''; +}; + +export const maximizeWindow = async () => { + await appWindow.maximize(); +}; + +export function removeObjectDuplicates(arr: T[], id: K) { + return arr.filter((v, i, a) => a.findIndex((v2) => v2[id] === v[id]) === i); +} + +export const checkTokenBalance = async (tokenPool: TPoolOption, amount: string) => { + let hasEnoughFunds = false; + if (tokenPool === 'locked') { + hasEnoughFunds = await checkHasEnoughLockedTokens(amount); + } + + if (tokenPool === 'balance') { + hasEnoughFunds = await checkHasEnoughFunds(amount); + } + + return hasEnoughFunds; +}; + +export const isDecimal = (value: number) => value - Math.floor(value) !== 0; + +export const attachDefaultOperatingCost = async (profitMarginPercent: string): Promise => + getDefaultMixnodeCostParams(profitMarginPercent); + +/** + * Converts a stringified percentage integer (0-100) to a stringified float (0.0-1.0). + * + * @param value - the percentage to convert + * @returns A stringified float + */ +export const toPercentFloatString = (value: string) => (Number(value) / 100).toString(); + +/** + * Converts a stringified percentage float (0.0-1.0) to a stringified integer (0-100). + * + * @param value - the percentage to convert + * @returns A stringified integer + */ +export const toPercentIntegerString = (value: string) => Math.round(Number(value) * 100).toString(); + +/** + * Converts a decimal number to a pretty representation + * with fixed decimal places. + * + * @param val - a decimal number of string form + * @param dp - number of decimal places (4 by default ie. 0.0000) + * @returns A prettified decimal number + */ +export const toDisplay = (val: string | number | Big, dp = 4) => { + let displayValue; + try { + displayValue = Big(val).toFixed(dp); + } catch (e: any) { + Console.warn(`${displayValue} not a valid decimal number: ${e}`); + } + return displayValue; +}; + +/** + * Takes a DecCoin and prettify its amount to a representation + * with fixed decimal places. + * + * @param coin - a DecCoin + * @param dp - number of decimal places to apply to amount (4 by default ie. 0.0000) + * @returns A DecCoin with prettified amount + */ +export const decCoinToDisplay = (coin: DecCoin, dp = 4) => { + const displayCoin = { ...coin }; + try { + displayCoin.amount = Big(coin.amount).toFixed(dp); + } catch (e: any) { + Console.warn(`${coin.amount} not a valid decimal number: ${e}`); + } + return displayCoin; +}; + +/** + * Converts a decimal number of μNYM (micro NYM) to NYM. + * + * @param unym - string representation of a decimal number of μNYM + * @param dp - number of decimal places (4 by default ie. 0.0000) + * @returns The corresponding decimal number in NYM + */ +export const unymToNym = (unym: string | Big, dp = 4) => { + let nym; + try { + nym = Big(unym).div(1_000_000).toFixed(dp); + } catch (e: any) { + Console.warn(`${unym} not a valid decimal number: ${e}`); + } + return nym; +}; + +/** + * + * Checks if the user's balance is enough to pay the fee + * @param balance - The user's current balance + * @param fee - The fee for the tx + * @param tx - The amount of the tx + * @returns boolean + * + */ + +export const isBalanceEnough = (fee: string, tx: string = '0', balance: string = '0') => { + console.log('balance', balance, fee, tx); + try { + return Big(balance).gte(Big(fee).plus(Big(tx))); + } catch (e) { + console.log(e); + return false; + } +}; + +export const getIntervalAsDate = async () => { + const interval = await getCurrentInterval(); + const secondsToNextInterval = + Number(interval.epochs_in_interval - interval.current_epoch_id) * Number(interval.epoch_length_seconds); + + const nextInterval = format( + add(new Date(), { + seconds: secondsToNextInterval, + }), + 'dd/MM/yyyy, HH:mm', + ); + + const nextEpoch = format( + add(fromUnixTime(Number(interval.current_epoch_start_unix)), { + seconds: Number(interval.epoch_length_seconds), + }), + 'HH:mm', + ); + + return { nextEpoch, nextInterval }; +}; diff --git a/nym-wallet/src/utils/index.ts b/nym-wallet/src/utils/index.ts index 1f713a7b1d..b1a2ade95a 100644 --- a/nym-wallet/src/utils/index.ts +++ b/nym-wallet/src/utils/index.ts @@ -1,233 +1,4 @@ -import { appWindow } from '@tauri-apps/api/window'; -import bs58 from 'bs58'; -import Big from 'big.js'; -import { valid } from 'semver'; -import { add, format, fromUnixTime } from 'date-fns'; -import { DecCoin, isValidRawCoin, MixNodeCostParams } from '@nymproject/types'; -import { TPoolOption } from 'src/components'; -import { - getCurrentInterval, - getDefaultMixnodeCostParams, - getLockedCoins, - getSpendableCoins, - userBalance, -} from '../requests'; -import { Console } from './console'; - -export const validateKey = (key: string, bytesLength: number): boolean => { - // it must be a valid base58 key - try { - const bytes = bs58.decode(key); - // of length 32 - return bytes.length === bytesLength; - } catch (e) { - Console.error(e as string); - return false; - } -}; - -export const validateAmount = async ( - majorAmountAsString: DecCoin['amount'], - minimumAmountAsString: DecCoin['amount'], -): Promise => { - // tests basic coin value requirements, like no more than 6 decimal places, value lower than total supply, etc - if (!Number(majorAmountAsString)) { - return false; - } - - if (!isValidRawCoin(majorAmountAsString)) { - return false; - } - - const majorValueFloat = parseInt(majorAmountAsString, Number(10)); - - return majorValueFloat >= parseInt(minimumAmountAsString, Number(10)); - - // this conversion seems really iffy but I'm not sure how to better approach it -}; - -export const isValidHostname = (value: string) => { - // regex for ipv4 and ipv6 and hhostname- source http://jsfiddle.net/DanielD/8S4nq/ - const hostnameRegex = - /((^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))|(^\s*((?=.{1,255}$)(?=.*[A-Za-z].*)[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?)*)\s*$)/; - - return hostnameRegex.test(value); -}; - -export const validateVersion = (version: string): boolean => { - try { - return valid(version) !== null; - } catch (e) { - return false; - } -}; - -export const validateLocation = (location: string): boolean => { - const locationRegex = /^[a-z]+$/i; - return locationRegex.test(location); -}; - -export const validateRawPort = (rawPort: number): boolean => !Number.isNaN(rawPort) && rawPort >= 1 && rawPort <= 65535; - -export const truncate = (text: string, trim: number) => `${text.substring(0, trim)}...`; - -export const isGreaterThan = (a: number, b: number) => a > b; - -export const isLessThan = (a: number, b: number) => a < b; - -export const checkHasEnoughFunds = async (allocationValue: string): Promise => { - try { - const walletValue = await userBalance(); - - const remainingBalance = +walletValue.amount.amount - +allocationValue; - return remainingBalance >= 0; - } catch (e) { - Console.log(e as string); - return false; - } -}; - -export const checkHasEnoughLockedTokens = async (allocationValue: string) => { - try { - const lockedTokens = await getLockedCoins(); - const spendableTokens = await getSpendableCoins(); - const remainingBalance = +lockedTokens.amount + +spendableTokens.amount - +allocationValue; - return remainingBalance >= 0; - } catch (e) { - Console.error(e as string); - } - return false; -}; - -export const randomNumberBetween = (min: number, max: number) => { - const minCeil = Math.ceil(min); - const maxFloor = Math.floor(max); - return Math.floor(Math.random() * (maxFloor - minCeil + 1) + minCeil); -}; - -export const splice = (size: number, address?: string): string => { - if (address) { - return `${address.slice(0, size)}...${address.slice(-size)}`; - } - return ''; -}; - -export const maximizeWindow = async () => { - await appWindow.maximize(); -}; - -export function removeObjectDuplicates(arr: T[], id: K) { - return arr.filter((v, i, a) => a.findIndex((v2) => v2[id] === v[id]) === i); -} - -export const checkTokenBalance = async (tokenPool: TPoolOption, amount: string) => { - let hasEnoughFunds = false; - if (tokenPool === 'locked') { - hasEnoughFunds = await checkHasEnoughLockedTokens(amount); - } - - if (tokenPool === 'balance') { - hasEnoughFunds = await checkHasEnoughFunds(amount); - } - - return hasEnoughFunds; -}; - -export const isDecimal = (value: number) => value - Math.floor(value) !== 0; - -export const attachDefaultOperatingCost = async (profitMarginPercent: string): Promise => - getDefaultMixnodeCostParams(profitMarginPercent); - -/** - * Converts a stringified percentage integer (0-100) to a stringified float (0.0-1.0). - * - * @param value - the percentage to convert - * @returns A stringified float - */ -export const toPercentFloatString = (value: string) => (Number(value) / 100).toString(); - -/** - * Converts a stringified percentage float (0.0-1.0) to a stringified integer (0-100). - * - * @param value - the percentage to convert - * @returns A stringified integer - */ -export const toPercentIntegerString = (value: string) => Math.round(Number(value) * 100).toString(); - -/** - * Converts a decimal number to a pretty representation - * with fixed decimal places. - * - * @param val - a decimal number of string form - * @param dp - number of decimal places (4 by default ie. 0.0000) - * @returns A prettified decimal number - */ -export const toDisplay = (val: string | number | Big, dp = 4) => { - let displayValue; - try { - displayValue = Big(val).toFixed(dp); - } catch (e: any) { - Console.warn(`${displayValue} not a valid decimal number: ${e}`); - } - return displayValue; -}; - -/** - * Takes a DecCoin and prettify its amount to a representation - * with fixed decimal places. - * - * @param coin - a DecCoin - * @param dp - number of decimal places to apply to amount (4 by default ie. 0.0000) - * @returns A DecCoin with prettified amount - */ -export const decCoinToDisplay = (coin: DecCoin, dp = 4) => { - const displayCoin = { ...coin }; - try { - displayCoin.amount = Big(coin.amount).toFixed(dp); - } catch (e: any) { - Console.warn(`${coin.amount} not a valid decimal number: ${e}`); - } - return displayCoin; -}; - -/** - * Converts a decimal number of μNYM (micro NYM) to NYM. - * - * @param unym - string representation of a decimal number of μNYM - * @param dp - number of decimal places (4 by default ie. 0.0000) - * @returns The corresponding decimal number in NYM - */ -export const unymToNym = (unym: string | Big, dp = 4) => { - let nym; - try { - nym = Big(unym).div(1_000_000).toFixed(dp); - } catch (e: any) { - Console.warn(`${unym} not a valid decimal number: ${e}`); - } - return nym; -}; - -export const getIntervalAsDate = async () => { - const interval = await getCurrentInterval(); - const secondsToNextInterval = - Number(interval.epochs_in_interval - interval.current_epoch_id) * Number(interval.epoch_length_seconds); - - const nextInterval = format( - add(new Date(), { - seconds: secondsToNextInterval, - }), - 'dd/MM/yyyy, HH:mm', - ); - - const nextEpoch = format( - add(fromUnixTime(Number(interval.current_epoch_start_unix)), { - seconds: Number(interval.epoch_length_seconds), - }), - 'HH:mm', - ); - - return { nextEpoch, nextInterval }; -}; - +export * from './common'; export * from './fireRequests'; +export * from './console'; export { default as fireRequests } from './fireRequests'; diff --git a/sdk/rust/nym-sdk/Cargo.toml b/sdk/rust/nym-sdk/Cargo.toml index 94f6656df5..7aaced99aa 100644 --- a/sdk/rust/nym-sdk/Cargo.toml +++ b/sdk/rust/nym-sdk/Cargo.toml @@ -18,7 +18,7 @@ nym-sphinx = { path = "../../../common/nymsphinx" } nym-task = { path = "../../../common/task" } nym-topology = { path = "../../../common/topology" } nym-socks5-client-core = { path = "../../../common/socks5-client-core" } -nym-validator-client = { path = "../../../common/client-libs/validator-client", features = ["nyxd-client"] } +nym-validator-client = { path = "../../../common/client-libs/validator-client", features = ["http-client"] } futures = "0.3" log = { workspace = true } diff --git a/sdk/rust/nym-sdk/examples/surb-reply.rs b/sdk/rust/nym-sdk/examples/surb-reply.rs index 52ae2ca78a..38c7e8257a 100644 --- a/sdk/rust/nym-sdk/examples/surb-reply.rs +++ b/sdk/rust/nym-sdk/examples/surb-reply.rs @@ -1,14 +1,14 @@ use nym_sdk::mixnet::{ AnonymousSenderTag, MixnetClientBuilder, ReconstructedMessage, StoragePaths, }; -use std::path::PathBuf; +use std::path::PathBuf; #[tokio::main] async fn main() { nym_bin_common::logging::setup_logging(); // Specify some config options - let config_dir = PathBuf::from("/tmp/surb-example-client"); + let config_dir = PathBuf::from("/tmp/surb-example"); let storage_paths = StoragePaths::new_from_dir(&config_dir).unwrap(); // Create the client with a storage backend, and enable it by giving it some paths. If keys @@ -25,13 +25,13 @@ async fn main() { // Be able to get our client address let our_address = client.nym_address(); - println!("\nOur client nym address is: {our_address}"); + println!("\nOur client nym address is: {our_address}"); // Send a message through the mixnet to ourselves using our nym address client.send_str(*our_address, "hello there").await; - // we're going to parse the sender_tag (AnonymousSenderTag) from the incoming message and use it to 'reply' to ourselves instead of our Nym address. - // we know there will be a sender_tag since the sdk sends SURBs along with messages by default. + // we're going to parse the sender_tag (AnonymousSenderTag) from the incoming message and use it to 'reply' to ourselves instead of our Nym address. + // we know there will be a sender_tag since the sdk sends SURBs along with messages by default. println!("Waiting for message\n"); // get the actual message - discard the empty vec sent along with a potential SURB topup request @@ -45,20 +45,22 @@ async fn main() { } let mut parsed = String::new(); - if let Some(r) = message.iter().next() { + if let Some(r) = message.first() { parsed = String::from_utf8(r.message.clone()).unwrap(); } - // parse sender_tag: we will use this to reply to sender without needing their Nym address + // parse sender_tag: we will use this to reply to sender without needing their Nym address let return_recipient: AnonymousSenderTag = message[0].sender_tag.unwrap(); - println!("\nReceived the following message: {} \nfrom sender with surb bucket {}", parsed, return_recipient); + println!( + "\nReceived the following message: {} \nfrom sender with surb bucket {}", + parsed, return_recipient + ); // reply to self with it: note we use `send_str_reply` instead of `send_str` - println!("Replying with using SURBs"); - client.send_str_reply(return_recipient, "hi an0n!").await; + println!("Replying with using SURBs"); + client.send_str_reply(return_recipient, "hi an0n!").await; println!("Waiting for message (once you see it, ctrl-c to exit)\n"); client .on_messages(|msg| println!("\nReceived: {}", String::from_utf8_lossy(&msg.message))) .await; - -} \ No newline at end of file +} diff --git a/sdk/typescript/examples/chrome-extension/README.md b/sdk/typescript/examples/chrome-extension/README.md new file mode 100644 index 0000000000..8d7359e8c3 --- /dev/null +++ b/sdk/typescript/examples/chrome-extension/README.md @@ -0,0 +1,22 @@ +# Nym Chrome Extension Example + +This is an example of how Nym can be used within the context of a Chrome extension. + +## Running the example + +1. Copy a build of the Nym TypeScript SDK (ESM version) into `./sdk`. +2. Navigate to `chrome://extensions` in Google Chrome. +3. Enable "Developer mode" (top right of the page). +4. Click on "Load unpacked" (top left of the page). +5. Load this extension folder. + +## How does it work? + +The Nym Mixnet Client runs a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) that wraps +a WASM library that builds and encrypts Sphinx packets in the browser to send over the Nym mixnet: + +![Sphinx packet](../docs/worker.svg) + +The WASM code encrypts each layer of the Sphinx packet in the browser, before sending the Sphinx packet over a websocket to the ingress gateway: + +![Sphinx packet](../docs/sphinx.svg) diff --git a/sdk/typescript/examples/chrome-extension/manifest.json b/sdk/typescript/examples/chrome-extension/manifest.json new file mode 100644 index 0000000000..5f83d5c014 --- /dev/null +++ b/sdk/typescript/examples/chrome-extension/manifest.json @@ -0,0 +1,17 @@ +{ + "name": "Nym Chrome Extension Example", + "description": "An example demonstrating how to integrate the Nym TypeScript SDK in the context of a Google Chrome browser extension.", + "version": "1.0", + "manifest_version": 3, + "icons": { + "48": "icon.png" + }, + "content_security_policy": { + "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';" + }, + "action": { + "default_title": "Nym Chrome Extension Example", + "default_icon": "icon.png", + "default_popup": "popup.html" + } +} diff --git a/sdk/typescript/examples/chrome-extension/package.json b/sdk/typescript/examples/chrome-extension/package.json new file mode 100644 index 0000000000..cf9d9927d0 --- /dev/null +++ b/sdk/typescript/examples/chrome-extension/package.json @@ -0,0 +1,20 @@ +{ + "name": "chrome-extension", + "version": "1.0.0", + "description": "This is an example of how Nym can be used within the context of a Chrome extension.", + "main": "index.js", + "scripts": { + "build": "webpack" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "clean-webpack-plugin": "^4.0.0", + "copy-webpack-plugin": "^11.0.0", + "webpack": "^5.88.1", + "webpack-cli": "^5.1.4" + }, + "dependencies": { + "@nymproject/sdk": "^1.1.8" + } +} diff --git a/sdk/typescript/examples/chrome-extension/popup.css b/sdk/typescript/examples/chrome-extension/popup.css new file mode 100644 index 0000000000..491c7dc3c7 --- /dev/null +++ b/sdk/typescript/examples/chrome-extension/popup.css @@ -0,0 +1,8 @@ +body { + width: 800px; + min-height: 400px; +} + +#editdialog input { + width: 100%; +} \ No newline at end of file diff --git a/sdk/typescript/examples/chrome-extension/popup.html b/sdk/typescript/examples/chrome-extension/popup.html new file mode 100644 index 0000000000..9245988b60 --- /dev/null +++ b/sdk/typescript/examples/chrome-extension/popup.html @@ -0,0 +1,23 @@ + + + + + + + + +

+

+

+

+

Send messages from your browser, through the mixnet, and to the recipient using the "send" button.

+

+ Sent messages show in blue, received messages + show in green. +

+
+

+
+

+ + diff --git a/sdk/typescript/examples/chrome-extension/src/dom-utils.js b/sdk/typescript/examples/chrome-extension/src/dom-utils.js new file mode 100644 index 0000000000..322606c241 --- /dev/null +++ b/sdk/typescript/examples/chrome-extension/src/dom-utils.js @@ -0,0 +1,66 @@ +// dom-utils.js +// Contains utility functionality to help manipulate the DOM elements necessary to demonstrate the Nym example. + +/** + * Create a Sphinx packet and send it to the mixnet through the gateway node. + * + * Message and recipient are taken from the values in the user interface. + * + * @param {Client} nymClient the nym client to use for message sending + */ +async function sendMessageTo(nym) { + const message = document.getElementById('message').value; + const recipient = document.getElementById('recipient').value; + await nym.client.send({ + payload: { + message, + mimeType: 'text/plain' + }, + recipient + }); + displaySend(message); +} + +/** + * Display messages that have been sent up the websocket. Colours them blue. + * + * @param {string} message + */ +function displaySend(message) { + const timestamp = new Date().toISOString().substr(11, 12); + const sendDiv = document.createElement('div'); + const paragraph = document.createElement('p'); + paragraph.setAttribute('style', 'color: blue'); + const paragraphContent = document.createTextNode(`${timestamp} sent >>> ${message}`); + paragraph.appendChild(paragraphContent); + sendDiv.appendChild(paragraph); + document.getElementById('output')?.appendChild(sendDiv); +} + +/** + * Display received text messages in the browser. Colour them green. + * + * @param {string} message + */ +function displayReceived(message) { + const content = message; + const timestamp = new Date().toLocaleTimeString(); + const receivedDiv = document.createElement('div'); + const paragraph = document.createElement('p'); + paragraph.setAttribute('style', 'color: green'); + const paragraphContent = document.createTextNode(`${timestamp} received >>> ${content}`); + paragraph.appendChild(paragraphContent); + receivedDiv.appendChild(paragraph); + document.getElementById('output')?.appendChild(receivedDiv); +} + +/** + * Display the nymClient's sender address in the user interface + * + * @param {Client} nymClient + */ +function displaySenderAddress(address) { + document.getElementById('sender').value = address; +} + +export { sendMessageTo, displaySend, displayReceived , displaySenderAddress } \ No newline at end of file diff --git a/sdk/typescript/examples/chrome-extension/src/main.js b/sdk/typescript/examples/chrome-extension/src/main.js new file mode 100644 index 0000000000..5ead4f6c67 --- /dev/null +++ b/sdk/typescript/examples/chrome-extension/src/main.js @@ -0,0 +1,53 @@ +// main.js +// Simple example of how to load Nym's TypeScript SDK and bind it to a DOM. +// Look at dom-utils.js for the DOM utility functionality referenced here. + +// Import the Nym mixnet ESM module. +import { createNymMixnetClient } from '@nymproject/sdk'; + +// Import the DOM utility functionality. +import { displaySenderAddress, displayReceived, sendMessageTo } from './dom-utils.js'; + +async function main() { + // Initialize the Nym mixnet client. + let nymClient = await createNymMixnetClient(); + if (!nymClient) { + console.error('Oh no! Could not create client'); + return; + } + + const nymApiUrl = 'https://validator.nymtech.net/api'; + const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM'; + + // subscribe to connect event, so that we can show the client's address + nymClient.events.subscribeToConnected((e) => { + if (e.args.address) { + displaySenderAddress(e.args.address); + } + }); + + // subscribe to message received events and show any string messages received + nymClient.events.subscribeToTextMessageReceivedEvent((e) => { + displayReceived(e.args.payload); + }); + + const sendButton = document.querySelector('#send-button'); + if (sendButton) { + sendButton.onclick = function () { + if (nymClient) { + sendMessageTo(nymClient); + } + }; + } + + nymClient.events.subscribeToRawMessageReceivedEvent((e) => console.log('Received: ', e.args.payload)); + await nymClient.client.start({ + clientId: 'My awesome client', + nymApiUrl, + preferredGatewayIdentityKey, + }); +} + +window.addEventListener('DOMContentLoaded', () => { + main(); +}); diff --git a/sdk/typescript/examples/chrome-extension/webpack.config.js b/sdk/typescript/examples/chrome-extension/webpack.config.js new file mode 100644 index 0000000000..d1c7c473c7 --- /dev/null +++ b/sdk/typescript/examples/chrome-extension/webpack.config.js @@ -0,0 +1,25 @@ +// Webpack configuration for the Chrome extension example + +const path = require('path'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const { CleanWebpackPlugin } = require('clean-webpack-plugin'); + +module.exports = { + mode: 'production', + entry: { + main: './src/main.js', + }, + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + new CleanWebpackPlugin(), + new CopyWebpackPlugin({ + patterns: [ + 'manifest.json', + 'popup.html', + { from: path.resolve(__dirname, '../../../../assets/favicon/favicon.png'), to: 'icon.png' }, + ], + }), + ], +}; diff --git a/sdk/typescript/examples/firefox-extension/.gitignore b/sdk/typescript/examples/firefox-extension/.gitignore new file mode 100644 index 0000000000..6e8096e78b --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/.gitignore @@ -0,0 +1 @@ +sdk/index.js \ No newline at end of file diff --git a/sdk/typescript/examples/firefox-extension/README.md b/sdk/typescript/examples/firefox-extension/README.md new file mode 100644 index 0000000000..27678813c6 --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/README.md @@ -0,0 +1,35 @@ +# Nym Firefox Extension Example + +This is an example of how Nym can be used within the context of a Mozilla Firefox extension. + +## Running the example + +First, build the Nym SDK: + +From the SDK directory `sdk/typescript/packages/sdk` run: + +```js +npm run build:local +``` + +Then, from the example directory `sdk/typescript/examples/firefox-extension` run: + +```js +npm install +npm run build +``` + +## Workers + +Firefox browser extensions cannot run inline web workers. In order to overcome this limitation, the Nym Firefox Extension Example imports workers from the SDK and uses Webpack's `worker-loader` to allow the worker's to be bundled inline into the extension. In order for webpack to include the workers in the build, they are imported as modules in the `src/index.js` file: + +## How does it work? + +The Nym Mixnet Client runs a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) that wraps +a WASM library that builds and encrypts Sphinx packets in the browser to send over the Nym mixnet: + +![Sphinx packet](../docs/worker.svg) + +The WASM code encrypts each layer of the Sphinx packet in the browser, before sending the Sphinx packet over a websocket to the ingress gateway: + +![Sphinx packet](../docs/sphinx.svg) diff --git a/sdk/typescript/examples/firefox-extension/manifest.json b/sdk/typescript/examples/firefox-extension/manifest.json new file mode 100644 index 0000000000..5829265a88 --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/manifest.json @@ -0,0 +1,23 @@ +{ + "manifest_version": 3, + "name": "Nym Firefox Extension Example", + "version": "1.0", + "description": "An example demonstrating how to integrate the Nym TypeScript SDK in the context of a Mozilla Firefox browser extension.", + "icons": { + "48": "icon.png" + }, + "permissions": [], + "content_security_policy": { + "extension_pages": "script-src 'self' 'wasm-unsafe-eval';" + }, + "background": { + "scripts": ["background.js"] + }, + "action": { + "default_icon": { + "32": "icon.png" + }, + "default_title": "Nym Firefox Extension Example", + "default_popup": "popup.html" + } +} diff --git a/sdk/typescript/examples/firefox-extension/package.json b/sdk/typescript/examples/firefox-extension/package.json new file mode 100644 index 0000000000..70160f5150 --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/package.json @@ -0,0 +1,19 @@ +{ + "name": "firefox-extension", + "version": "1.0.0", + "description": "This is an example of how Nym can be used within the context of a Firefox extension.", + "main": "index.js", + "author": "", + "license": "ISC", + "devDependencies": { + "copy-webpack-plugin": "^11.0.0", + "webpack": "^5.88.1", + "webpack-cli": "^5.1.4" + }, + "scripts": { + "build": "yarn webpack" + }, + "dependencies": { + "worker-loader": "^3.0.8" + } +} diff --git a/sdk/typescript/examples/firefox-extension/popup.css b/sdk/typescript/examples/firefox-extension/popup.css new file mode 100644 index 0000000000..491c7dc3c7 --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/popup.css @@ -0,0 +1,8 @@ +body { + width: 800px; + min-height: 400px; +} + +#editdialog input { + width: 100%; +} \ No newline at end of file diff --git a/sdk/typescript/examples/firefox-extension/popup.html b/sdk/typescript/examples/firefox-extension/popup.html new file mode 100644 index 0000000000..976e0c54f4 --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/popup.html @@ -0,0 +1,23 @@ + + + + + + + + +

+

+

+

+

Send messages from your browser, through the mixnet, and to the recipient using the "send" button.

+

+ Sent messages show in blue, received messages + show in green. +

+
+

+
+

+ + diff --git a/sdk/typescript/examples/firefox-extension/src/background.js b/sdk/typescript/examples/firefox-extension/src/background.js new file mode 100644 index 0000000000..c0a6ce8a35 --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/src/background.js @@ -0,0 +1,107 @@ +// main.js +// Simple example of how to load Nym's TypeScript SDK and bind it to a DOM. +// Look at dom-utils.js for the DOM utility functionality referenced here. + +// Import the Nym mixnet ESM module. +// Import The web workers for the Nym mixnet ESM module.These are required for to run the Nym mixnet client. + +import { createNymMixnetClient } from '../../../packages/sdk/dist/full-fat/index.js'; +import '../../../packages/sdk/dist/full-fat/web-worker-0.js'; +import '../../../packages/sdk/dist/full-fat/web-worker-1.js'; + +const backgroundState = { + isReady: false, + address: '', + recipient: '', + messageLog: [], +}; + +async function initBackground() { + // Initialize the Nym mixnet client. + let nymClient = await createNymMixnetClient().catch((err) => { + console.log(err); + }); + if (!nymClient) { + console.error('Oh no! Could not create client'); + return; + } + const nymApiUrl = 'https://validator.nymtech.net/api'; + const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM'; + + // subscribe to connect event, so that we can show the client's address + nymClient.events.subscribeToConnected((e) => { + if (e.args.address) { + backgroundState.address = e.args.address; + browser.runtime.sendMessage({ + type: 'displaySenderAddress', + message: backgroundState.address, + }); + } + }); + + // subscribe to message received events and show any string messages received + nymClient.events.subscribeToTextMessageReceivedEvent((e) => { + backgroundState.messageLog.push({ + type: 'received', + message: e.args.payload, + }); + browser.runtime.sendMessage({ + type: 'displayReceived', + message: e.args.payload, + }); + }); + + nymClient.events.subscribeToRawMessageReceivedEvent((e) => console.log('Received: ', e.args.payload)); + await nymClient.client.start({ + clientId: 'My awesome client', + nymApiUrl, + preferredGatewayIdentityKey, + }); + browser.runtime.onMessage.addListener(async (data) => { + switch (data.type) { + case 'nymClientSendMessage': + if (nymClient) { + await nymClient.client.send({ + payload: { + message: data.message, + mimeType: 'text/plain', + }, + recipient: data.recipient, + }); + backgroundState.messageLog.push({ + type: 'sent', + message: data.message, + }); + break; + } + } + }); + backgroundState.isReady = true; +} + +window.addEventListener('DOMContentLoaded', () => { + browser.runtime.onMessage.addListener((data) => { + switch (data.type) { + case 'popupReady': + if (backgroundState.isReady) { + browser.runtime.sendMessage({ + type: 'displaySenderAddress', + message: backgroundState.address, + }); + browser.runtime.sendMessage({ + type: 'displayMessageLog', + message: backgroundState.messageLog, + }); + browser.runtime.sendMessage({ + type: 'updateRecipient', + message: backgroundState.recipient, + }); + } else { + initBackground(); + } + break; + case 'updateRecipient': + backgroundState.recipient = data.message; + } + }); +}); diff --git a/sdk/typescript/examples/firefox-extension/src/dom-utils.js b/sdk/typescript/examples/firefox-extension/src/dom-utils.js new file mode 100644 index 0000000000..859dcdac2c --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/src/dom-utils.js @@ -0,0 +1,74 @@ +// dom-utils.js +// Contains utility functionality to help manipulate the DOM elements necessary to demonstrate the Nym example. + +/** + * Create a Sphinx packet and send it to the mixnet through the gateway node. + * + * Message and recipient are taken from the values in the user interface. + * + * @param {Client} nymClient the nym client to use for message sending + */ +async function sendMessageTo() { + const message = document.getElementById('message').value; + const recipient = document.getElementById('recipient').value; + browser.runtime.sendMessage({ + type: 'nymClientSendMessage', + message, + recipient, + }); + displaySend(message); +} + +/** + * Display messages that have been sent up the websocket. Colours them blue. + * + * @param {string} message + */ +function displaySend(message) { + const timestamp = new Date().toISOString().substr(11, 12); + const sendDiv = document.createElement('div'); + const paragraph = document.createElement('p'); + paragraph.setAttribute('style', 'color: blue'); + const paragraphContent = document.createTextNode(`${timestamp} sent >>> ${message}`); + paragraph.appendChild(paragraphContent); + sendDiv.appendChild(paragraph); + document.getElementById('output')?.appendChild(sendDiv); +} + +/** + * Display received text messages in the browser. Colour them green. + * + * @param {string} message + */ +function displayReceived(message) { + const content = message; + const timestamp = new Date().toLocaleTimeString(); + const receivedDiv = document.createElement('div'); + const paragraph = document.createElement('p'); + paragraph.setAttribute('style', 'color: green'); + const paragraphContent = document.createTextNode(`${timestamp} received >>> ${content}`); + paragraph.appendChild(paragraphContent); + receivedDiv.appendChild(paragraph); + document.getElementById('output')?.appendChild(receivedDiv); +} + +/** + * Display the nymClient's sender address in the user interface + * + * @param {Client} nymClient + */ +function displaySenderAddress(address) { + document.getElementById('sender').value = address; +} + +function displayMessageLog(messageLog) { + for (let i = 0; i < messageLog.length; i++) { + if (messageLog[i].type === 'sent') { + displaySend(messageLog[i].message); + } else if (messageLog[i].type === 'received') { + displayReceived(messageLog[i].message); + } + } +} + +export { sendMessageTo, displaySend, displayReceived, displaySenderAddress, displayMessageLog }; diff --git a/sdk/typescript/examples/firefox-extension/src/popup.js b/sdk/typescript/examples/firefox-extension/src/popup.js new file mode 100644 index 0000000000..8126f21827 --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/src/popup.js @@ -0,0 +1,40 @@ +// Import the DOM utility functionality. +import { displaySenderAddress, displayReceived, sendMessageTo, displayMessageLog } from './dom-utils.js'; + +window.addEventListener('DOMContentLoaded', () => { + const sendButton = document.querySelector('#send-button'); + if (sendButton) { + sendButton.onclick = function () { + sendMessageTo(); + }; + } + const recipient = document.getElementById('recipient'); + recipient.onchange = () => { + browser.runtime.sendMessage({ + type: 'updateRecipient', + message: recipient.value, + }); + }; + browser.runtime.onMessage.addListener((data) => { + switch (data.type) { + case 'displaySenderAddress': + displaySenderAddress(data.message); + break; + case 'displayReceived': + displayReceived(data.message); + break; + case 'sendMessageTo': + sendMessageTo(data.message); + break; + case 'displayMessageLog': + displayMessageLog(data.message); + break; + case 'updateRecipient': + recipient.value = data.message; + } + }); + browser.runtime.sendMessage({ + type: 'popupReady', + message: '', + }); +}); diff --git a/sdk/typescript/examples/firefox-extension/webpack.config.js b/sdk/typescript/examples/firefox-extension/webpack.config.js new file mode 100644 index 0000000000..26bd5d679c --- /dev/null +++ b/sdk/typescript/examples/firefox-extension/webpack.config.js @@ -0,0 +1,38 @@ +// Webpack configuration for the Firefox extension example + +const path = require('path'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const { CleanWebpackPlugin } = require('clean-webpack-plugin'); + +module.exports = { + mode: 'production', + entry: { + background: './src/background.js', + popup: './src/popup.js', + }, + output: { + path: path.resolve(__dirname, 'dist'), + }, + plugins: [ + new CleanWebpackPlugin(), + new CopyWebpackPlugin({ + patterns: [ + 'manifest.json', + 'popup.html', + { from: path.resolve(__dirname, '../../../../assets/favicon/favicon.png'), to: 'icon.png' }, + ], + }), + ], + module: { + rules: [ + { + test: /web-worker.*\.js$/, + loader: 'worker-loader', + options: { + filename: '[name].js', + inline: 'fallback', + }, + }, + ], + }, +}; diff --git a/sdk/typescript/examples/node-tester/parcel/src/index.html b/sdk/typescript/examples/node-tester/parcel/src/index.html index a7bd3057e7..acd8174db6 100644 --- a/sdk/typescript/examples/node-tester/parcel/src/index.html +++ b/sdk/typescript/examples/node-tester/parcel/src/index.html @@ -4,7 +4,7 @@ - Nym WebAssembly Demo + Nym Node Tester Demo diff --git a/sdk/typescript/examples/node-tester/plain-html/.babelrc b/sdk/typescript/examples/node-tester/plain-html/.babelrc deleted file mode 100644 index 85e1f1ba26..0000000000 --- a/sdk/typescript/examples/node-tester/plain-html/.babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "presets": ["@babel/env", "@babel/react"] -} \ No newline at end of file diff --git a/sdk/typescript/examples/node-tester/plain-html/jest.config.js b/sdk/typescript/examples/node-tester/plain-html/jest.config.js deleted file mode 100644 index e86e13bab9..0000000000 --- a/sdk/typescript/examples/node-tester/plain-html/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', -}; diff --git a/sdk/typescript/examples/node-tester/plain-html/src/index.html b/sdk/typescript/examples/node-tester/plain-html/src/index.html index ccac6e2365..fc536c636c 100644 --- a/sdk/typescript/examples/node-tester/plain-html/src/index.html +++ b/sdk/typescript/examples/node-tester/plain-html/src/index.html @@ -4,7 +4,7 @@ - Nym WebAssembly Demo + Nym Node Tester Demo diff --git a/sdk/typescript/examples/node-tester/react/README.md b/sdk/typescript/examples/node-tester/react/README.md new file mode 100644 index 0000000000..13d4a79db3 --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/README.md @@ -0,0 +1,14 @@ +# Nym Node Tester - React + +This is an example of using the Nym Mixnet node tester. + +You can use this example as a seed for a new project. + +## Running the example + +Try out the node tester app by running: + +``` +npm install +npm start +``` diff --git a/sdk/typescript/examples/node-tester/react/index.html b/sdk/typescript/examples/node-tester/react/index.html new file mode 100644 index 0000000000..193ccdcbd1 --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/index.html @@ -0,0 +1,13 @@ + + + + + + + Nym Node Tester Example + + +
+ + + diff --git a/sdk/typescript/examples/node-tester/react/package.json b/sdk/typescript/examples/node-tester/react/package.json new file mode 100644 index 0000000000..d453a0c291 --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/package.json @@ -0,0 +1,19 @@ +{ + "name": "@nymproject/sdk-example-node-tester-react", + "description": "An example project that uses WASM node tester and React", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@emotion/react": "^11.11.1", + "@emotion/styled": "^11.11.0", + "@mui/icons-material": "^5.14.0", + "@mui/material": "^5.14.0", + "@nymproject/sdk": "1", + "parcel": "^2.9.3", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "scripts": { + "start": "parcel index.html" + } +} diff --git a/sdk/typescript/examples/node-tester/react/src/App.tsx b/sdk/typescript/examples/node-tester/react/src/App.tsx new file mode 100644 index 0000000000..eb0bd1927a --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/src/App.tsx @@ -0,0 +1,127 @@ +import React, { useState } from 'react'; +import { + Button, + Card, + CardActions, + CardContent, + CardHeader, + CircularProgress, + Grid, + List, + ListItem, + ListItemText, + TextField, + Typography, +} from '@mui/material'; +import { NodeTestResultResponse } from '@nymproject/sdk'; +import { ScoreIndicator } from 'src/components/ScoreIndicator'; +import { useNodeTesterClient } from 'src/hooks/useNodeTesterClient'; +import { BasicPageLayout } from 'src/layouts'; +import { TestStatusLabel } from 'src/components/TestStatusLabel'; +import Icon from '../../../../../../assets/appicon/appicon.png'; + +export const App = () => { + const { testState, error, testNode, disconnectFromGateway, reconnectToGateway } = useNodeTesterClient(); + const [mixnodeIdentity, setMixnodeIdentity] = useState(''); + const [results, setResults] = React.useState(); + + console.log({ testState, error, testNode }); + + const handleTestNode = async () => { + setResults(undefined); + try { + const result = await testNode(mixnodeIdentity); + setResults(result); + } catch (e) { + console.error(e); + } + }; + + return ( + + + Nym Mixnode Testnet Node Tester
} + action={} + avatar={} + /> + + + + + + + + + + + + + + + + + + + + + + + + { + setMixnodeIdentity(e.target.value); + }} + fullWidth + /> + + + + + + + + + + + + + + + ); +}; diff --git a/sdk/typescript/examples/node-tester/react/src/components/ScoreIndicator.tsx b/sdk/typescript/examples/node-tester/react/src/components/ScoreIndicator.tsx new file mode 100644 index 0000000000..6527edeb48 --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/src/components/ScoreIndicator.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { Box, CircularProgress, CircularProgressProps, Stack, Typography } from '@mui/material'; + +const getPerformanceDescriptionAndColor = (score: number) => { + const res: { description: string; color: CircularProgressProps['color'] } = { description: '', color: 'warning' }; + + if (score >= 90) { + res.description = 'Reliable node'; + res.color = 'success'; + } + + if (score >= 75 && score < 90) { + res.description = 'Average node'; + res.color = 'warning'; + } + + if (score > 0 && score < 75) { + res.description = 'Unreliable node'; + res.color = 'error'; + } + + return res; +}; + +export const ScoreIndicator = ({ score }) => { + const { color } = getPerformanceDescriptionAndColor(score); + return ( + + + + + + {Math.round(score)}% + + Performance Score + + + ); +}; diff --git a/sdk/typescript/examples/node-tester/react/src/components/TestStatusLabel.tsx b/sdk/typescript/examples/node-tester/react/src/components/TestStatusLabel.tsx new file mode 100644 index 0000000000..558677fce7 --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/src/components/TestStatusLabel.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { Chip } from '@mui/material'; +import { HourglassTop, ErrorOutline, CheckCircleOutline, WarningAmber } from '@mui/icons-material'; +import { TestState } from 'src/hooks/useNodeTesterClient'; + +const getColor = (state: TestState) => { + switch (state) { + case 'Connecting': + return 'warning'; + case 'Error': + return 'error'; + case 'Ready': + return 'success'; + default: + return 'warning'; + } +}; + +const getIcon = (state: TestState) => { + switch (state) { + case 'Connecting': + return ; + case 'Error': + return ; + case 'Ready': + return ; + default: + return ; + } +}; + +export const TestStatusLabel = ({ state }: { state: TestState }) => ( + +); diff --git a/sdk/typescript/examples/node-tester/react/src/hooks/useNodeTesterClient.ts b/sdk/typescript/examples/node-tester/react/src/hooks/useNodeTesterClient.ts new file mode 100644 index 0000000000..fcd6f92937 --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/src/hooks/useNodeTesterClient.ts @@ -0,0 +1,71 @@ +import { useState, useEffect } from 'react'; +import { createNodeTesterClient, NodeTester } from '@nymproject/sdk'; + +export type TestState = 'Ready' | 'Connecting' | 'Disconnected' | 'Disconnecting' | 'Error' | 'Testing' | 'Stopped'; + +export const useNodeTesterClient = () => { + const [client, setClient] = useState(); + const [error, setError] = useState(); + const [testState, setTestState] = useState('Disconnected'); + + const createClient = async () => { + setTestState('Connecting'); + try { + const validator = 'https://validator.nymtech.net/api'; + const nodeTesterClient = await createNodeTesterClient(); + + await nodeTesterClient.tester.init(validator); + setClient(nodeTesterClient); + } catch (e) { + console.log(e); + setError('Failed to load node tester client, please try again'); + } finally { + setTestState('Ready'); + } + }; + + useEffect(() => { + createClient(); + }, []); + + const testNode = !client + ? undefined + : async (mixnodeIdentity: string) => { + try { + setTestState('Testing'); + const result = await client.tester.startTest(mixnodeIdentity); + setTestState('Ready'); + return result; + } catch (e) { + console.log(e); + setError('Failed to test node, please try again'); + setTestState('Error'); + } + }; + + const disconnectFromGateway = !client + ? undefined + : async () => { + setTestState('Disconnecting'); + await client.tester.disconnectFromGateway(); + setTestState('Disconnected'); + }; + + const reconnectToGateway = !client + ? undefined + : async () => { + setTestState('Connecting'); + await client.tester.reconnectToGateway(); + setTestState('Ready'); + }; + + const terminateWorker = !client + ? undefined + : async () => { + setTestState('Disconnecting'); + await client.terminate(); + setTestState('Disconnected'); + }; + + return { testNode, disconnectFromGateway, reconnectToGateway, terminateWorker, testState, error }; +}; diff --git a/sdk/typescript/examples/node-tester/react/src/index.tsx b/sdk/typescript/examples/node-tester/react/src/index.tsx new file mode 100644 index 0000000000..77d3da3be1 --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/src/index.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { App } from './App'; +import { NymThemeProvider } from './theme/theme'; + +const rootDOMElem = document.getElementById('root'); +if (!rootDOMElem) throw new Error('Root element not found'); + +const root = createRoot(rootDOMElem); +root.render( + + + , +); diff --git a/sdk/typescript/examples/node-tester/react/src/layouts/index.tsx b/sdk/typescript/examples/node-tester/react/src/layouts/index.tsx new file mode 100644 index 0000000000..1a38d6a350 --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/src/layouts/index.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { Container } from '@mui/material'; + +export const BasicPageLayout = ({ children }: { children: React.ReactNode }) => ( + {children} +); diff --git a/sdk/typescript/examples/node-tester/react/src/theme/theme.tsx b/sdk/typescript/examples/node-tester/react/src/theme/theme.tsx new file mode 100644 index 0000000000..3f5d35fdf9 --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/src/theme/theme.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; +import { createTheme, ThemeProvider } from '@mui/material/styles'; +import { CssBaseline } from '@mui/material'; + +export const NymThemeProvider = ({ children }: { children: React.ReactNode }) => { + const theme = createTheme({ + palette: { + mode: 'dark', + primary: { + main: '#FB6E4E', + }, + success: { + main: '#21D073', + }, + background: { + default: '#1D2125', + paper: '#292E34', + }, + }, + }); + + return ( + + + {children} + + ); +}; diff --git a/sdk/typescript/examples/node-tester/react/src/typesDefs.ts b/sdk/typescript/examples/node-tester/react/src/typesDefs.ts new file mode 100644 index 0000000000..dd84df40a4 --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/src/typesDefs.ts @@ -0,0 +1,4 @@ +declare module '*.png' { + const content: any; + export default content; +} diff --git a/sdk/typescript/examples/node-tester/react/tsconfig.json b/sdk/typescript/examples/node-tester/react/tsconfig.json new file mode 100644 index 0000000000..272e623d99 --- /dev/null +++ b/sdk/typescript/examples/node-tester/react/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "jsx": "react", + "baseUrl": ".", + "paths": { + "react": ["node_modules/react"], + "react-dom": ["node_modules/react-dom"] + }, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + }, + "include": ["src"], + "paths": { + "@assets/*": ["../../../../../../assets"] + } +} diff --git a/sdk/typescript/packages/sdk/.babelrc b/sdk/typescript/packages/sdk/.babelrc new file mode 100644 index 0000000000..0ef5ffc214 --- /dev/null +++ b/sdk/typescript/packages/sdk/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/env"] +} diff --git a/sdk/typescript/packages/sdk/jest.config.mjs b/sdk/typescript/packages/sdk/jest.config.mjs new file mode 100644 index 0000000000..ed5b112c72 --- /dev/null +++ b/sdk/typescript/packages/sdk/jest.config.mjs @@ -0,0 +1,15 @@ +import preset from 'ts-jest/presets/index.js' + +/** @type {import('ts-jest').JestConfigWithTsJest} */ +export default { + ...preset.defaults, + transform: { + '^.+\\.(ts|tsx)$': [ + 'ts-jest', + { + tsconfig: 'tsconfig.jest.json', + useESM: true, + }, + ], + }, +} diff --git a/sdk/typescript/packages/sdk/package.json b/sdk/typescript/packages/sdk/package.json index de6d16554d..c156bd5876 100644 --- a/sdk/typescript/packages/sdk/package.json +++ b/sdk/typescript/packages/sdk/package.json @@ -27,16 +27,22 @@ "prebuild:dev": "yarn build:dependencies", "build:dev": "yarn build:dev:only-this", "build:dev:only-this": "scripts/build.sh", - "build:local": "run-s build:dependencies:nym-client-wasm build:dev:only-this" + "build:local": "run-s build:dependencies:nym-client-wasm build:dev:only-this", + "test": "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js -c=jest.config.mjs --no-cache" }, "dependencies": { "@npmcli/node-gyp": "^3.0.0", - "@nymproject/nym-client-wasm": "1", + "@nymproject/nym-client-wasm": "1.0.0", "comlink": "^4.3.1", "lerna": "^6.6.2", "node-gyp": "^9.3.1" }, "devDependencies": { + "@babel/core": "^7.15.0", + "@babel/plugin-transform-async-to-generator": "^7.14.5", + "@babel/preset-env": "^7.15.0", + "@babel/preset-react": "^7.14.5", + "@babel/preset-typescript": "^7.15.0", "@nymproject/eslint-config-react-typescript": "^1.0.0", "@rollup/plugin-commonjs": "^24.0.1", "@rollup/plugin-inject": "^5.0.3", @@ -48,6 +54,8 @@ "@rollup/plugin-wasm": "^6.1.1", "@typescript-eslint/eslint-plugin": "^5.13.0", "@typescript-eslint/parser": "^5.13.0", + "@types/jest": "^27.0.1", + "@types/node": "^16.7.13", "eslint": "^8.10.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-typescript": "^16.1.0", @@ -63,6 +71,9 @@ "rollup": "^3.9.1", "rollup-plugin-base64": "^1.0.1", "rollup-plugin-web-worker-loader": "^1.6.1", - "typescript": "^4.8.4" + "typescript": "^4.8.4", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "ts-loader": "^9.4.2" } } diff --git a/sdk/typescript/packages/sdk/rollup-full-fat.config.mjs b/sdk/typescript/packages/sdk/rollup-full-fat.config.mjs new file mode 100644 index 0000000000..f9d3739df2 --- /dev/null +++ b/sdk/typescript/packages/sdk/rollup-full-fat.config.mjs @@ -0,0 +1,28 @@ +// This is the rollup config for the full-fat SDK package. +// The config is similar to the esm config, but exports web workers as separate files. +// This can be necessary for implentations that do not support inline web workers. + +import typescript from '@rollup/plugin-typescript'; +import resolve from '@rollup/plugin-node-resolve'; +import webWorkerLoader from 'rollup-plugin-web-worker-loader'; + +const extensions = ['.js', '.jsx', '.ts', '.tsx']; + +export default { + input: 'src/index.ts', + output: { + dir: 'dist/full-fat', + format: 'es', + }, + plugins: [ + webWorkerLoader({ + targetPlatform: 'browser', + inline: false, + }), + resolve({ extensions }), + typescript({ + exclude: ['mixnet/wasm/worker.ts', 'mixnet/node-tester/worker.ts'], + compilerOptions: { outDir: 'dist/full-fat' }, + }), + ], +}; diff --git a/sdk/typescript/packages/sdk/scripts/build.sh b/sdk/typescript/packages/sdk/scripts/build.sh index d2763969e7..05800b5c9d 100755 --- a/sdk/typescript/packages/sdk/scripts/build.sh +++ b/sdk/typescript/packages/sdk/scripts/build.sh @@ -48,6 +48,13 @@ rollup -c rollup-esm.config.mjs # build the SDK as a CommonJS bundle rollup -c rollup-cjs.config.mjs +#------------------------------------------------------- +# FULL FAT +#------------------------------------------------------- + +# build the SDK as a ESM bundle +rollup -c rollup-full-fat.config.mjs + #------------------------------------------------------- # CLEAN UP #------------------------------------------------------- diff --git a/sdk/typescript/packages/sdk/src/mixnet/wasm/index.ts b/sdk/typescript/packages/sdk/src/mixnet/wasm/index.ts index b60550aee0..1b80d844e8 100644 --- a/sdk/typescript/packages/sdk/src/mixnet/wasm/index.ts +++ b/sdk/typescript/packages/sdk/src/mixnet/wasm/index.ts @@ -3,16 +3,16 @@ import InlineWasmWebWorker from 'web-worker:./worker'; import { BinaryMessageReceivedEvent, ConnectedEvent, - EventHandlerFn, EventKinds, IWebWorker, IWebWorkerAsync, IWebWorkerEvents, LoadedEvent, MimeTypes, - StringMessageReceivedEvent, RawMessageReceivedEvent, + StringMessageReceivedEvent, } from './types'; +import { createSubscriptions } from './subscriptions'; /** * Client for the Nym mixnet. @@ -33,25 +33,13 @@ export const createNymMixnetClient = async (options?: { // eslint-disable-next-line @typescript-eslint/no-use-before-define const worker = await createWorker(); - // stores the subscriptions for events - const subscriptions: { - [key: string]: Array>; - } = {}; - - /** - * Helper method to get typed subscriptions - */ - const getSubscriptions = (key: EventKinds): Array> => { - if (!subscriptions[key]) { - subscriptions[key] = []; - } - return subscriptions[key] as Array>; - }; + const subscriptions = createSubscriptions(); + const { getSubscriptions, addSubscription } = subscriptions; // listen to messages from the worker, parse them and let the subscribers handle them, catching any unhandled exceptions worker.addEventListener('message', (msg) => { if (msg.data && msg.data.kind) { - const subscribers = subscriptions[msg.data.kind]; + const subscribers = getSubscriptions(msg.data.kind); (subscribers || []).forEach((s) => { try { // let the subscriber handle the message @@ -66,36 +54,14 @@ export const createNymMixnetClient = async (options?: { // manage the subscribers, returning self-unsubscribe methods const events: IWebWorkerEvents = { - subscribeToConnected: (handler) => { - getSubscriptions(EventKinds.Connected).push(handler); - return () => { - getSubscriptions(EventKinds.Connected).unshift(handler); - }; - }, - subscribeToLoaded: (handler) => { - getSubscriptions(EventKinds.Loaded).push(handler); - return () => { - getSubscriptions(EventKinds.Loaded).unshift(handler); - }; - }, - subscribeToTextMessageReceivedEvent: (handler) => { - getSubscriptions(EventKinds.StringMessageReceived).push(handler); - return () => { - getSubscriptions(EventKinds.StringMessageReceived).unshift(handler); - }; - }, - subscribeToBinaryMessageReceivedEvent: (handler) => { - getSubscriptions(EventKinds.BinaryMessageReceived).push(handler); - return () => { - getSubscriptions(EventKinds.BinaryMessageReceived).unshift(handler); - }; - }, - subscribeToRawMessageReceivedEvent: (handler) => { - getSubscriptions(EventKinds.RawMessageReceived).push(handler); - return () => { - getSubscriptions(EventKinds.RawMessageReceived).unshift(handler); - }; - }, + subscribeToConnected: (handler) => addSubscription(EventKinds.Connected, handler), + subscribeToLoaded: (handler) => addSubscription(EventKinds.Loaded, handler), + subscribeToTextMessageReceivedEvent: (handler) => + addSubscription(EventKinds.StringMessageReceived, handler), + subscribeToBinaryMessageReceivedEvent: (handler) => + addSubscription(EventKinds.BinaryMessageReceived, handler), + subscribeToRawMessageReceivedEvent: (handler) => + addSubscription(EventKinds.RawMessageReceived, handler), }; // let comlink handle interop with the web worker diff --git a/sdk/typescript/packages/sdk/src/mixnet/wasm/subscriptions.test.ts b/sdk/typescript/packages/sdk/src/mixnet/wasm/subscriptions.test.ts new file mode 100644 index 0000000000..49e72ed988 --- /dev/null +++ b/sdk/typescript/packages/sdk/src/mixnet/wasm/subscriptions.test.ts @@ -0,0 +1,98 @@ +import { createSubscriptions } from './subscriptions'; +import { EventKinds, MimeTypes, StringMessageReceivedEvent } from './types'; + +describe('wasm subscription manager', () => { + test('works with default values', () => { + const { getSubscriptions, fireEvent, addSubscription } = createSubscriptions(); + + expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(0); + + // the event should fire and not fail + fireEvent(EventKinds.StringMessageReceived, {}); + + // mock a handler, fire events and check that it was called + const mockHandler = jest.fn(); + addSubscription(EventKinds.StringMessageReceived, mockHandler); + fireEvent(EventKinds.StringMessageReceived, {}); + expect(mockHandler).toHaveBeenCalled(); + }); + + test('adding and removing subscriptions works as expected', () => { + const { addSubscription, getSubscriptions, fireEvent } = createSubscriptions(); + + expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(0); + + const callStats: number[] = [0, 0, 0]; + + const showDebug = false; + + const handler1 = (e: StringMessageReceivedEvent) => { + if (showDebug) { + console.log('handler1', e); + } + callStats[0] += 1; + }; + const handler2 = (e: StringMessageReceivedEvent) => { + if (showDebug) { + console.log('handler2', e); + } + callStats[1] += 1; + }; + const handler3 = (e: StringMessageReceivedEvent) => { + if (showDebug) { + console.log('handler3', e); + } + callStats[2] += 1; + }; + + const unsubcribeFn1 = addSubscription(EventKinds.StringMessageReceived, handler1); + const unsubcribeFn2 = addSubscription(EventKinds.StringMessageReceived, handler2); + const unsubcribeFn3 = addSubscription(EventKinds.StringMessageReceived, handler3); + + const event: StringMessageReceivedEvent = { + kind: EventKinds.StringMessageReceived, + args: { + payload: 'Testing', + mimeType: MimeTypes.TextPlain, + payloadRaw: new Uint8Array(), + }, + }; + + // fire and expect all handlers to get message + fireEvent(EventKinds.StringMessageReceived, event); + expect(callStats[0]).toBe(1); + expect(callStats[1]).toBe(1); + expect(callStats[2]).toBe(1); + expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(3); + + // unscribe and fire again + unsubcribeFn2(); + fireEvent(EventKinds.StringMessageReceived, event); + expect(callStats[0]).toBe(2); + expect(callStats[1]).toBe(1); + expect(callStats[2]).toBe(2); + expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(2); + + // unscribe and fire again + unsubcribeFn3(); + fireEvent(EventKinds.StringMessageReceived, event); + expect(callStats[0]).toBe(3); + expect(callStats[1]).toBe(1); + expect(callStats[2]).toBe(2); + expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(1); + + // unscribe and fire again + unsubcribeFn1(); + fireEvent(EventKinds.StringMessageReceived, event); + expect(callStats[0]).toBe(3); + expect(callStats[1]).toBe(1); + expect(callStats[2]).toBe(2); + expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(0); + + // nothing is subscribed, so fire again and check + fireEvent(EventKinds.StringMessageReceived, event); + expect(callStats[0]).toBe(3); + expect(callStats[1]).toBe(1); + expect(callStats[2]).toBe(2); + }); +}); diff --git a/sdk/typescript/packages/sdk/src/mixnet/wasm/subscriptions.ts b/sdk/typescript/packages/sdk/src/mixnet/wasm/subscriptions.ts new file mode 100644 index 0000000000..d819591f8b --- /dev/null +++ b/sdk/typescript/packages/sdk/src/mixnet/wasm/subscriptions.ts @@ -0,0 +1,69 @@ +import type { EventHandlerFn } from './types'; +import { EventKinds } from './types'; + +type ISubscriptions = { + [key: string]: Array>; +}; + +/** + * Creates a subscription manager. + */ +export const createSubscriptions = () => { + // stores the subscriptions for events + const subscriptions: ISubscriptions = {}; + + /** + * Helper method to get typed subscriptions. + */ + const getSubscriptions = (key: EventKinds): Array> => { + if (!subscriptions[key]) { + subscriptions[key] = []; + } + return subscriptions[key] as Array>; + }; + + /** + * Remove a subscription. + */ + const removeSubscription = (key: EventKinds, handler: EventHandlerFn) => { + if (!subscriptions[key]) { + subscriptions[key] = []; + } + const items: Array> = (subscriptions[key] as Array>).filter( + (h) => h !== handler, + ); + subscriptions[key] = items; + }; + + /** + * Add typed subscription. + */ + const addSubscription = (key: EventKinds, handler: EventHandlerFn) => { + getSubscriptions(key).push(handler as EventHandlerFn); + + return () => { + removeSubscription(key, handler); + }; + }; + + /** + * Fires an event. + */ + const fireEvent = (key: EventKinds, event: E) => { + getSubscriptions(key).forEach((handler) => { + try { + handler(event); + } catch (e: any) { + console.error(`Unhandled exception in handler for ${key}: `, e); + } + }); + }; + + return { + getSubscriptions, + addSubscription, + removeSubscription, + fireEvent, + subscriptions, + }; +}; diff --git a/sdk/typescript/packages/sdk/tsconfig.jest.json b/sdk/typescript/packages/sdk/tsconfig.jest.json new file mode 100644 index 0000000000..16e94d82b5 --- /dev/null +++ b/sdk/typescript/packages/sdk/tsconfig.jest.json @@ -0,0 +1,42 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "lib": [ + "es2021", + "dom", + "dom.iterable", + "esnext", + "webworker" + ], + "module": "CommonJS", + "target": "es5", + "strict": true, + "moduleResolution": "node", + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "declaration": true, + "baseUrl": ".", + "esModuleInterop": true, + "allowJs": true + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "jest.*", + "webpack.config.js", + "webpack.prod.js", + "webpack.common.js", + "node_modules", + "**/node_modules", + "dist", + "**/dist", + "scripts", + "jest", + "__tests__", + "**/__tests__", + "__jest__", + "**/__jest__", + "config/*" + ] +} diff --git a/tools/nym-cli/Cargo.toml b/tools/nym-cli/Cargo.toml index 4dd1d9197f..ea5cad9e65 100644 --- a/tools/nym-cli/Cargo.toml +++ b/tools/nym-cli/Cargo.toml @@ -22,5 +22,5 @@ tap = "1" nym-cli-commands = { path = "../../common/commands" } nym-bin-common = { path = "../../common/bin-common"} -nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] } +nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] } nym-network-defaults = { path = "../../common/network-defaults" } diff --git a/tools/ts-rs-cli/Cargo.toml b/tools/ts-rs-cli/Cargo.toml index 366bbe3ef4..41f9ead98d 100644 --- a/tools/ts-rs-cli/Cargo.toml +++ b/tools/ts-rs-cli/Cargo.toml @@ -11,7 +11,7 @@ ts-rs = "6.1.2" walkdir = "2" nym-validator-client = { path = "../../common/client-libs/validator-client", features = [ - "nyxd-client", "generate-ts" + "generate-ts" ] } nym-api-requests = { path = "../../nym-api/nym-api-requests", features = ["generate-ts"] } nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract", features = [ "generate-ts" ]} diff --git a/yarn.lock b/yarn.lock index c223bce5af..8c6092b23f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39,6 +39,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" integrity sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA== +"@babel/compat-data@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.6.tgz#15606a20341de59ba02cd2fcc5086fcbe73bf544" + integrity sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg== + "@babel/core@7.12.9": version "7.12.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" @@ -82,6 +87,27 @@ json5 "^2.2.2" semver "^6.3.0" +"@babel/core@^7.11.6": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.8.tgz#386470abe884302db9c82e8e5e87be9e46c86785" + integrity sha512-75+KxFB4CZqYRXjx4NlR4J7yGvKumBuZTmV4NV6v09dVXXkuYVYLT68N6HCzLvfJ+fWCxQsntNzKwwIXL4bHnw== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.7" + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helpers" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.8" + "@babel/types" "^7.22.5" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + "@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.22.5", "@babel/generator@^7.7.2": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" @@ -92,6 +118,16 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.7.tgz#a6b8152d5a621893f2c9dacf9a4e286d520633d5" + integrity sha512-p+jPjMG+SI8yvIaxGgeW24u7q9+5+TGpZh8/CuB7RhBKd7RCy8FayNEFNNKrNK/eUcY/4ExQqLmyrvBXKsIcwQ== + dependencies: + "@babel/types" "^7.22.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" @@ -117,6 +153,17 @@ lru-cache "^5.1.1" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.6.tgz#e30d61abe9480aa5a83232eb31c111be922d2e52" + integrity sha512-534sYEqWD9VfUm3IPn2SLcH4Q3P86XL+QvqdC7ZsFrzyyPF3T4XGiVghF6PTYNdWg6pXuoqXxNQAhbYeEInTzA== + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-validator-option" "^7.22.5" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + browserslist "^4.21.9" + lru-cache "^5.1.1" + "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz#2192a1970ece4685fbff85b48da2c32fcb130b7c" @@ -275,6 +322,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-string-parser@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" @@ -309,6 +363,15 @@ "@babel/traverse" "^7.22.5" "@babel/types" "^7.22.5" +"@babel/helpers@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" + integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== + dependencies: + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.6" + "@babel/types" "^7.22.5" + "@babel/highlight@^7.10.4", "@babel/highlight@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" @@ -323,6 +386,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== +"@babel/parser@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" @@ -532,7 +600,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-jsx@^7.22.5": +"@babel/plugin-syntax-jsx@^7.22.5", "@babel/plugin-syntax-jsx@^7.7.2": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== @@ -1247,6 +1315,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" + integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.7" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/types" "^7.22.5" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.2.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" @@ -1941,6 +2025,18 @@ jest-util "^27.5.1" slash "^3.0.0" +"@jest/console@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.6.1.tgz#b48ba7b9c34b51483e6d590f46e5837f1ab5f639" + integrity sha512-Aj772AYgwTSr5w8qnyoJ0eDYvN6bMsH3ORH1ivMotrInHLKdUz6BDlaEXHdM6kODaBIkNIyQGzsMvRdOv7VG7Q== + dependencies: + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + slash "^3.0.0" + "@jest/core@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" @@ -1975,6 +2071,40 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/core@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.6.1.tgz#fac0d9ddf320490c93356ba201451825231e95f6" + integrity sha512-CcowHypRSm5oYQ1obz1wfvkjZZ2qoQlrKKvlfPwh5jUXVU12TWr2qMeH8chLMuTFzHh5a1g2yaqlqDICbr+ukQ== + dependencies: + "@jest/console" "^29.6.1" + "@jest/reporters" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.5.0" + jest-config "^29.6.1" + jest-haste-map "^29.6.1" + jest-message-util "^29.6.1" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.1" + jest-resolve-dependencies "^29.6.1" + jest-runner "^29.6.1" + jest-runtime "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" + jest-watcher "^29.6.1" + micromatch "^4.0.4" + pretty-format "^29.6.1" + slash "^3.0.0" + strip-ansi "^6.0.0" + "@jest/environment@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" @@ -1985,6 +2115,16 @@ "@types/node" "*" jest-mock "^27.5.1" +"@jest/environment@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.6.1.tgz#ee358fff2f68168394b4a50f18c68278a21fe82f" + integrity sha512-RMMXx4ws+Gbvw3DfLSuo2cfQlK7IwGbpuEWXCqyYDcqYTI+9Ju3a5hDnXaxjNsa6uKh9PQF2v+qg+RLe63tz5A== + dependencies: + "@jest/fake-timers" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + jest-mock "^29.6.1" + "@jest/expect-utils@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" @@ -1999,6 +2139,21 @@ dependencies: jest-get-type "^29.4.3" +"@jest/expect-utils@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.1.tgz#ab83b27a15cdd203fe5f68230ea22767d5c3acc5" + integrity sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw== + dependencies: + jest-get-type "^29.4.3" + +"@jest/expect@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.6.1.tgz#fef18265188f6a97601f1ea0a2912d81a85b4657" + integrity sha512-N5xlPrAYaRNyFgVf2s9Uyyvr795jnB6rObuPx4QFvNJz8aAjpZUDfO4bh5G/xuplMID8PrnuF1+SfSyDxhsgYg== + dependencies: + expect "^29.6.1" + jest-snapshot "^29.6.1" + "@jest/fake-timers@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" @@ -2011,6 +2166,18 @@ jest-mock "^27.5.1" jest-util "^27.5.1" +"@jest/fake-timers@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.6.1.tgz#c773efddbc61e1d2efcccac008139f621de57c69" + integrity sha512-RdgHgbXyosCDMVYmj7lLpUwXA4c69vcNzhrt69dJJdf8azUrpRh3ckFCaTPNjsEeRi27Cig0oKDGxy5j7hOgHg== + dependencies: + "@jest/types" "^29.6.1" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.6.1" + jest-mock "^29.6.1" + jest-util "^29.6.1" + "@jest/globals@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" @@ -2020,6 +2187,16 @@ "@jest/types" "^27.5.1" expect "^27.5.1" +"@jest/globals@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.6.1.tgz#c8a8923e05efd757308082cc22893d82b8aa138f" + integrity sha512-2VjpaGy78JY9n9370H8zGRCFbYVWwjY6RdDMhoJHa1sYfwe6XM/azGN0SjY8kk7BOZApIejQ1BFPyH7FPG0w3A== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/expect" "^29.6.1" + "@jest/types" "^29.6.1" + jest-mock "^29.6.1" + "@jest/reporters@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" @@ -2051,6 +2228,36 @@ terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" +"@jest/reporters@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.6.1.tgz#3325a89c9ead3cf97ad93df3a427549d16179863" + integrity sha512-9zuaI9QKr9JnoZtFQlw4GREQbxgmNYXU6QuWtmuODvk5nvPUeBYapVR/VYMyi2WSx3jXTLJTJji8rN6+Cm4+FA== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + jest-worker "^29.6.1" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + "@jest/schemas@^28.1.3": version "28.1.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" @@ -2065,6 +2272,13 @@ dependencies: "@sinclair/typebox" "^0.25.16" +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" + integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== + dependencies: + "@sinclair/typebox" "^0.27.8" + "@jest/source-map@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" @@ -2074,6 +2288,15 @@ graceful-fs "^4.2.9" source-map "^0.6.0" +"@jest/source-map@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.0.tgz#bd34a05b5737cb1a99d43e1957020ac8e5b9ddb1" + integrity sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + "@jest/test-result@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" @@ -2084,6 +2307,16 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" +"@jest/test-result@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.6.1.tgz#850e565a3f58ee8ca6ec424db00cb0f2d83c36ba" + integrity sha512-Ynr13ZRcpX6INak0TPUukU8GWRfm/vAytE3JbJNGAvINySWYdfE7dGZMbk36oVuK4CigpbhMn8eg1dixZ7ZJOw== + dependencies: + "@jest/console" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + "@jest/test-sequencer@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" @@ -2094,6 +2327,16 @@ jest-haste-map "^27.5.1" jest-runtime "^27.5.1" +"@jest/test-sequencer@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.6.1.tgz#e3e582ee074dd24ea9687d7d1aaf05ee3a9b068e" + integrity sha512-oBkC36PCDf/wb6dWeQIhaviU0l5u6VCsXa119yqdUosYAt7/FbQU2M2UoziO3igj/HBDEgp57ONQ3fm0v9uyyg== + dependencies: + "@jest/test-result" "^29.6.1" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + slash "^3.0.0" + "@jest/transform@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" @@ -2136,6 +2379,27 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/transform@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.6.1.tgz#acb5606019a197cb99beda3c05404b851f441c92" + integrity sha512-URnTneIU3ZjRSaf906cvf6Hpox3hIeJXRnz3VDSw5/X93gR8ycdfSIEy19FlVx8NFmpN7fe3Gb1xF+NjXaQLWg== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + jest-regex-util "^29.4.3" + jest-util "^29.6.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -2182,6 +2446,18 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jest/types@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" + integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== + dependencies: + "@jest/schemas" "^29.6.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.3" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" @@ -2232,7 +2508,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": version "0.3.18" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== @@ -2639,6 +2915,11 @@ prop-types "^15.8.1" reselect "^4.1.6" +"@nicolo-ribaudo/semver-v6@^6.3.3": + version "6.3.3" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz#ea6d23ade78a325f7a52750aab1526b02b628c29" + integrity sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg== + "@noble/hashes@^1", "@noble/hashes@^1.0.0", "@noble/hashes@^1.2.0": version "1.3.1" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" @@ -3372,6 +3653,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + "@sinonjs/commons@^1.7.0": version "1.8.6" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" @@ -3379,6 +3665,20 @@ dependencies: type-detect "4.0.8" +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers@^8.0.1": version "8.1.0" resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" @@ -5009,7 +5309,7 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/graceful-fs@^4.1.2": +"@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== @@ -6517,6 +6817,19 @@ babel-jest@^27.5.1: graceful-fs "^4.2.9" slash "^3.0.0" +babel-jest@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.1.tgz#a7141ad1ed5ec50238f3cd36127636823111233a" + integrity sha512-qu+3bdPEQC6KZSPz+4Fyjbga5OODNcp49j6GKzG1EKbkfyJBxEYGVUmVGpwCSeGouG52R4EgYMLb6p9YeEEQ4A== + dependencies: + "@jest/transform" "^29.6.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.5.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + babel-loader@^8.0.0, babel-loader@^8.2.3, babel-loader@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8" @@ -6568,6 +6881,16 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + babel-plugin-macros@^3.0.1, babel-plugin-macros@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" @@ -6663,6 +6986,14 @@ babel-preset-jest@^27.5.1: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== + dependencies: + babel-plugin-jest-hoist "^29.5.0" + babel-preset-current-node-syntax "^1.0.0" + bail@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" @@ -6971,7 +7302,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5: +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5, browserslist@^4.21.9: version "4.21.9" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== @@ -7967,6 +8298,11 @@ convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -9129,6 +9465,11 @@ elliptic@^6.5.3, elliptic@^6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + emittery@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" @@ -9935,6 +10276,18 @@ expect@^29.0.0: jest-message-util "^29.5.0" jest-util "^29.5.0" +expect@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.1.tgz#64dd1c8f75e2c0b209418f2b8d36a07921adfdf1" + integrity sha512-XEdDLonERCU1n9uR56/Stx9OqojaLAQtZf9PrCHH9Hl8YXiEIka3H4NXJ3NOIBmQJTg7+j7buh34PMHfJujc8g== + dependencies: + "@jest/expect-utils" "^29.6.1" + "@types/node" "*" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + exponential-backoff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" @@ -10085,7 +10438,7 @@ fast-json-parse@^1.0.3: resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw== -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -12367,6 +12720,14 @@ jest-changed-files@^27.5.1: execa "^5.0.0" throat "^6.0.1" +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== + dependencies: + execa "^5.0.0" + p-limit "^3.1.0" + jest-circus@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" @@ -12392,6 +12753,32 @@ jest-circus@^27.5.1: stack-utils "^2.0.3" throat "^6.0.1" +jest-circus@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.6.1.tgz#861dab37e71a89907d1c0fabc54a0019738ed824" + integrity sha512-tPbYLEiBU4MYAL2XoZme/bgfUeotpDBd81lgHLCbDZZFaGmECk0b+/xejPFtmiBP87GgP/y4jplcRpbH+fgCzQ== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/expect" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + is-generator-fn "^2.0.0" + jest-each "^29.6.1" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-runtime "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" + p-limit "^3.1.0" + pretty-format "^29.6.1" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-cli@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" @@ -12410,6 +12797,24 @@ jest-cli@^27.5.1: prompts "^2.0.1" yargs "^16.2.0" +jest-cli@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.6.1.tgz#99d9afa7449538221c71f358f0fdd3e9c6e89f72" + integrity sha512-607dSgTA4ODIN6go9w6xY3EYkyPFGicx51a69H7yfvt7lN53xNswEVLovq+E77VsTRi5fWprLH0yl4DJgE8Ing== + dependencies: + "@jest/core" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" + prompts "^2.0.1" + yargs "^17.3.1" + jest-config@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" @@ -12440,6 +12845,34 @@ jest-config@^27.5.1: slash "^3.0.0" strip-json-comments "^3.1.1" +jest-config@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.6.1.tgz#d785344509065d53a238224c6cdc0ed8e2f2f0dd" + integrity sha512-XdjYV2fy2xYixUiV2Wc54t3Z4oxYPAELUzWnV6+mcbq0rh742X2p52pii5A3oeRzYjLnQxCsZmp0qpI6klE2cQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.6.1" + "@jest/types" "^29.6.1" + babel-jest "^29.6.1" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.6.1" + jest-environment-node "^29.6.1" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.1" + jest-runner "^29.6.1" + jest-util "^29.6.1" + jest-validate "^29.6.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.6.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" + jest-diff@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" @@ -12470,6 +12903,16 @@ jest-diff@^29.5.0: jest-get-type "^29.4.3" pretty-format "^29.5.0" +jest-diff@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.1.tgz#13df6db0a89ee6ad93c747c75c85c70ba941e545" + integrity sha512-FsNCvinvl8oVxpNLttNQX7FAq7vR+gMDGj90tiP7siWw1UdakWUGqrylpsYrpvj908IYckm5Y0Q7azNAozU1Kg== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.6.1" + jest-docblock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" @@ -12477,6 +12920,13 @@ jest-docblock@^27.5.1: dependencies: detect-newline "^3.0.0" +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== + dependencies: + detect-newline "^3.0.0" + jest-each@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" @@ -12488,6 +12938,17 @@ jest-each@^27.5.1: jest-util "^27.5.1" pretty-format "^27.5.1" +jest-each@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.6.1.tgz#975058e5b8f55c6780beab8b6ab214921815c89c" + integrity sha512-n5eoj5eiTHpKQCAVcNTT7DRqeUmJ01hsAL0Q1SMiBHcBcvTKDELixQOGMCpqhbIuTcfC4kMfSnpmDqRgRJcLNQ== + dependencies: + "@jest/types" "^29.6.1" + chalk "^4.0.0" + jest-get-type "^29.4.3" + jest-util "^29.6.1" + pretty-format "^29.6.1" + jest-environment-jsdom@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" @@ -12513,6 +12974,18 @@ jest-environment-node@^27.5.1: jest-mock "^27.5.1" jest-util "^27.5.1" +jest-environment-node@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.6.1.tgz#08a122dece39e58bc388da815a2166c58b4abec6" + integrity sha512-ZNIfAiE+foBog24W+2caIldl4Irh8Lx1PUhg/GZ0odM1d/h2qORAsejiFc7zb+SEmYPn1yDZzEDSU5PmDkmVLQ== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/fake-timers" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + jest-mock "^29.6.1" + jest-util "^29.6.1" + jest-get-type@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" @@ -12569,6 +13042,25 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" +jest-haste-map@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.6.1.tgz#62655c7a1c1b349a3206441330fb2dbdb4b63803" + integrity sha512-0m7f9PZXxOCk1gRACiVgX85knUKPKLPg4oRCjLoqIm9brTHXaorMA0JpmtmVkQiT8nmXyIVoZd/nnH1cfC33ig== + dependencies: + "@jest/types" "^29.6.1" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.4.3" + jest-util "^29.6.1" + jest-worker "^29.6.1" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + jest-jasmine2@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" @@ -12600,6 +13092,14 @@ jest-leak-detector@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-leak-detector@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.6.1.tgz#66a902c81318e66e694df7d096a95466cb962f8e" + integrity sha512-OrxMNyZirpOEwkF3UHnIkAiZbtkBWiye+hhBweCHkVbCgyEy71Mwbb5zgeTNYWJBi1qgDVfPC1IwO9dVEeTLwQ== + dependencies: + jest-get-type "^29.4.3" + pretty-format "^29.6.1" + jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" @@ -12630,6 +13130,16 @@ jest-matcher-utils@^29.5.0: jest-get-type "^29.4.3" pretty-format "^29.5.0" +jest-matcher-utils@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.1.tgz#6c60075d84655d6300c5d5128f46531848160b53" + integrity sha512-SLaztw9d2mfQQKHmJXKM0HCbl2PPVld/t9Xa6P9sgiExijviSp7TnZZpw2Fpt+OI3nwUO/slJbOfzfUMKKC5QA== + dependencies: + chalk "^4.0.0" + jest-diff "^29.6.1" + jest-get-type "^29.4.3" + pretty-format "^29.6.1" + jest-message-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" @@ -12675,6 +13185,21 @@ jest-message-util@^29.5.0: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.1.tgz#d0b21d87f117e1b9e165e24f245befd2ff34ff8d" + integrity sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.6.1" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^27.0.6, jest-mock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" @@ -12683,6 +13208,15 @@ jest-mock@^27.0.6, jest-mock@^27.5.1: "@jest/types" "^27.5.1" "@types/node" "*" +jest-mock@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.6.1.tgz#049ee26aea8cbf54c764af649070910607316517" + integrity sha512-brovyV9HBkjXAEdRooaTQK42n8usKoSRR3gihzUpYeV/vwqgSoNfrksO7UfSACnPmxasO/8TmHM3w9Hp3G1dgw== + dependencies: + "@jest/types" "^29.6.1" + "@types/node" "*" + jest-util "^29.6.1" + jest-pnp-resolver@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" @@ -12698,6 +13232,11 @@ jest-regex-util@^27.5.1: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== + jest-resolve-dependencies@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" @@ -12707,6 +13246,14 @@ jest-resolve-dependencies@^27.5.1: jest-regex-util "^27.5.1" jest-snapshot "^27.5.1" +jest-resolve-dependencies@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.1.tgz#b85b06670f987a62515bbf625d54a499e3d708f5" + integrity sha512-BbFvxLXtcldaFOhNMXmHRWx1nXQO5LoXiKSGQcA1LxxirYceZT6ch8KTE1bK3X31TNG/JbkI7OkS/ABexVahiw== + dependencies: + jest-regex-util "^29.4.3" + jest-snapshot "^29.6.1" + jest-resolve@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" @@ -12723,6 +13270,21 @@ jest-resolve@^27.5.1: resolve.exports "^1.1.0" slash "^3.0.0" +jest-resolve@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.6.1.tgz#4c3324b993a85e300add2f8609f51b80ddea39ee" + integrity sha512-AeRkyS8g37UyJiP9w3mmI/VXU/q8l/IH52vj/cDAyScDcemRbSBhfX/NMYIGilQgSVwsjxrCHf3XJu4f+lxCMg== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + jest-pnp-resolver "^1.2.2" + jest-util "^29.6.1" + jest-validate "^29.6.1" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + jest-runner@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" @@ -12750,6 +13312,33 @@ jest-runner@^27.5.1: source-map-support "^0.5.6" throat "^6.0.1" +jest-runner@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.6.1.tgz#54557087e7972d345540d622ab5bfc3d8f34688c" + integrity sha512-tw0wb2Q9yhjAQ2w8rHRDxteryyIck7gIzQE4Reu3JuOBpGp96xWgF0nY8MDdejzrLCZKDcp8JlZrBN/EtkQvPQ== + dependencies: + "@jest/console" "^29.6.1" + "@jest/environment" "^29.6.1" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.4.3" + jest-environment-node "^29.6.1" + jest-haste-map "^29.6.1" + jest-leak-detector "^29.6.1" + jest-message-util "^29.6.1" + jest-resolve "^29.6.1" + jest-runtime "^29.6.1" + jest-util "^29.6.1" + jest-watcher "^29.6.1" + jest-worker "^29.6.1" + p-limit "^3.1.0" + source-map-support "0.5.13" + jest-runtime@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" @@ -12778,6 +13367,34 @@ jest-runtime@^27.5.1: slash "^3.0.0" strip-bom "^4.0.0" +jest-runtime@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.6.1.tgz#8a0fc9274ef277f3d70ba19d238e64334958a0dc" + integrity sha512-D6/AYOA+Lhs5e5il8+5pSLemjtJezUr+8zx+Sn8xlmOux3XOqx4d8l/2udBea8CRPqqrzhsKUsN/gBDE/IcaPQ== + dependencies: + "@jest/environment" "^29.6.1" + "@jest/fake-timers" "^29.6.1" + "@jest/globals" "^29.6.1" + "@jest/source-map" "^29.6.0" + "@jest/test-result" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + jest-message-util "^29.6.1" + jest-mock "^29.6.1" + jest-regex-util "^29.4.3" + jest-resolve "^29.6.1" + jest-snapshot "^29.6.1" + jest-util "^29.6.1" + slash "^3.0.0" + strip-bom "^4.0.0" + jest-serializer@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" @@ -12822,6 +13439,33 @@ jest-snapshot@^27.5.1: pretty-format "^27.5.1" semver "^7.3.2" +jest-snapshot@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.6.1.tgz#0d083cb7de716d5d5cdbe80d598ed2fbafac0239" + integrity sha512-G4UQE1QQ6OaCgfY+A0uR1W2AY0tGXUPQpoUClhWHq1Xdnx1H6JOrC2nH5lqnOEqaDgbHFgIwZ7bNq24HpB180A== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.6.1" + "@jest/transform" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.6.1" + graceful-fs "^4.2.9" + jest-diff "^29.6.1" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + natural-compare "^1.4.0" + pretty-format "^29.6.1" + semver "^7.5.3" + jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" @@ -12858,6 +13502,18 @@ jest-util@^28.1.3: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.0.0, jest-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.1.tgz#c9e29a87a6edbf1e39e6dee2b4689b8a146679cb" + integrity sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg== + dependencies: + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-util@^29.5.0: version "29.5.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" @@ -12882,6 +13538,18 @@ jest-validate@^27.5.1: leven "^3.1.0" pretty-format "^27.5.1" +jest-validate@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.6.1.tgz#765e684af6e2c86dce950aebefbbcd4546d69f7b" + integrity sha512-r3Ds69/0KCN4vx4sYAbGL1EVpZ7MSS0vLmd3gV78O+NAx3PDQQukRU5hNHPXlyqCgFY8XUk7EuTMLugh0KzahA== + dependencies: + "@jest/types" "^29.6.1" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.4.3" + leven "^3.1.0" + pretty-format "^29.6.1" + jest-watcher@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" @@ -12895,6 +13563,20 @@ jest-watcher@^27.5.1: jest-util "^27.5.1" string-length "^4.0.1" +jest-watcher@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.6.1.tgz#7c0c43ddd52418af134c551c92c9ea31e5ec942e" + integrity sha512-d4wpjWTS7HEZPaaj8m36QiaP856JthRZkrgcIY/7ISoUWPIillrXM23WPboZVLbiwZBt4/qn2Jke84Sla6JhFA== + dependencies: + "@jest/test-result" "^29.6.1" + "@jest/types" "^29.6.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.6.1" + string-length "^4.0.1" + jest-worker@^26.5.0, jest-worker@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" @@ -12913,6 +13595,16 @@ jest-worker@^27.0.2, jest-worker@^27.4.5, jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" +jest-worker@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.6.1.tgz#64b015f0e985ef3a8ad049b61fe92b3db74a5319" + integrity sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA== + dependencies: + "@types/node" "*" + jest-util "^29.6.1" + merge-stream "^2.0.0" + supports-color "^8.0.0" + jest@^27.1.0: version "27.5.1" resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" @@ -12922,6 +13614,16 @@ jest@^27.1.0: import-local "^3.0.2" jest-cli "^27.5.1" +jest@^29.5.0: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.6.1.tgz#74be1cb719c3abe439f2d94aeb18e6540a5b02ad" + integrity sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw== + dependencies: + "@jest/core" "^29.6.1" + "@jest/types" "^29.6.1" + import-local "^3.0.2" + jest-cli "^29.6.1" + js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -15549,7 +16251,7 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -16477,6 +17179,15 @@ pretty-format@^29.0.0, pretty-format@^29.5.0: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-format@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.1.tgz#ec838c288850b7c4f9090b867c2d4f4edbfb0f3e" + integrity sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog== + dependencies: + "@jest/schemas" "^29.6.0" + ansi-styles "^5.0.0" + react-is "^18.0.0" + pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -16715,6 +17426,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +pure-rand@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" + integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== + q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -17588,6 +18304,11 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.3.2, resolve@^1.9.0: version "1.22.2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" @@ -17926,6 +18647,13 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + semver@~7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" @@ -18308,6 +19036,14 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -19282,6 +20018,20 @@ ts-jest@^27.0.5: semver "7.x" yargs-parser "20.x" +ts-jest@^29.1.0: + version "29.1.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" + integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "4.x" + make-error "1.x" + semver "^7.5.3" + yargs-parser "^21.0.1" + ts-loader@^9.4.2: version "9.4.4" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.4.tgz#6ceaf4d58dcc6979f84125335904920884b7cee4" @@ -19979,7 +20729,7 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" -v8-to-istanbul@^9.0.0: +v8-to-istanbul@^9.0.0, v8-to-istanbul@^9.0.1: version "9.1.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== @@ -20129,7 +20879,7 @@ walk-up-path@^1.0.0: resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== -walker@^1.0.7, walker@~1.0.5: +walker@^1.0.7, walker@^1.0.8, walker@~1.0.5: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== @@ -20619,6 +21369,14 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + write-file-atomic@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" @@ -20758,7 +21516,7 @@ yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3, yargs-parser@^20. resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs-parser@21.1.1, yargs-parser@^21.1.1: +yargs-parser@21.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== @@ -20786,7 +21544,7 @@ yargs@16.2.0, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.6.2: +yargs@^17.3.1, yargs@^17.6.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==