diff --git a/.github/workflows/build-and-upload-binaries-ci.yml b/.github/workflows/build-and-upload-binaries-ci.yml index 5429baf2b4..57b897d9c2 100644 --- a/.github/workflows/build-and-upload-binaries-ci.yml +++ b/.github/workflows/build-and-upload-binaries-ci.yml @@ -57,7 +57,7 @@ jobs: echo $OUTPUT_DIR - name: Install Dependencies (Linux) - run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools + run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler continue-on-error: true - name: Install Rust stable @@ -98,7 +98,6 @@ jobs: cp target/release/nym-network-requester $OUTPUT_DIR cp target/release/nym-network-statistics $OUTPUT_DIR cp target/release/nym-cli $OUTPUT_DIR - cp target/release/nym-credential-client $OUTPUT_DIR cp target/release/explorer-api $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR @@ -109,6 +108,7 @@ jobs: cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/nym_name_service.wasm $OUTPUT_DIR + cp contracts/target/wasm32-unknown-unknown/release/nym_ephemera.wasm $OUTPUT_DIR - name: Deploy branch to CI www continue-on-error: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d346df6296..df3ad85309 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,7 @@ on: - 'clients/**' - 'common/**' - 'explorer-api/**' + - 'ephemera/**' - 'gateway/**' - 'integrations/**' - 'mixnode/**' @@ -15,6 +16,7 @@ on: - 'nym-api/**' - 'nym-outfox/**' - 'tools/nym-cli/**' + - 'tools/nym-nr-query/**' - 'tools/ts-rs-cli/**' - 'Cargo.toml' pull_request: @@ -22,6 +24,7 @@ on: - 'clients/**' - 'common/**' - 'explorer-api/**' + - 'ephemera/**' - 'gateway/**' - 'integrations/**' - 'mixnode/**' @@ -31,6 +34,7 @@ on: - 'nym-api/**' - 'nym-outfox/**' - 'tools/nym-cli/**' + - 'tools/nym-nr-query/**' - 'tools/ts-rs-cli/**' - 'Cargo.toml' @@ -42,7 +46,7 @@ jobs: RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache steps: - name: Install Dependencies (Linux) - run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools + run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler continue-on-error: true - name: Check out repository code diff --git a/.github/workflows/cd-dev.yml b/.github/workflows/cd-dev.yml deleted file mode 100644 index 0ca463345f..0000000000 --- a/.github/workflows/cd-dev.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: CD dev-portal - -on: - push: - branches: master - paths: - - 'documentation/dev-portal/**' - -jobs: - build: - runs-on: custom-runner-linux - steps: - - uses: actions/checkout@v3 - - name: Install rsync - run: sudo apt-get install rsync - - uses: rlespinasse/github-slug-action@v3.x - - uses: actions/setup-node@v3 - with: - node-version: 18 - - name: Install Rust stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - - name: Install mdbook - run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook) - - name: Install mdbook plugins - run: | - cargo install --vers "^0.2.0" mdbook-variables && cargo install \ - --vers "^1.8.0" mdbook-admonish && cargo install --vers \ - "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" \ - mdbook-theme && cargo install --vers "^0.7.7" mdbook-linkcheck - - name: Clean website - run: cd documentation/dev-portal && mdbook clean - - name: Build website - run: cd documentation/dev-portal && mdbook build - - name: Deploy branch master to dev - continue-on-error: true - uses: easingthemes/ssh-deploy@main - env: - SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }} - ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/dev-portal/book/html/" - REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_DEV }} - REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }} - TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET_DEVP }}/ - EXCLUDE: "/dist/, /node_modules/" - - name: Deploy branch master to prod - uses: easingthemes/ssh-deploy@main - env: - SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }} - ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/dev-portal/book/html/" - REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_PROD }} - REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }} - TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET_DEVP }}/ - EXCLUDE: "/dist/, /node_modules/" - - name: Matrix - Node Install - run: npm install - working-directory: .github/workflows/support-files - - name: Matrix - Send Notification - env: - NYM_NOTIFICATION_KIND: cd-dev - NYM_PROJECT_NAME: "Dev portal CD" - NYM_CI_WWW_BASE: "${{ secrets.NYM_CD_WWW_BASE }}" - NYM_CI_WWW_LOCATION: "${{ env.GITHUB_REF_SLUG }}" - GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}" - GIT_BRANCH: "${GITHUB_REF##*/}" - MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}" - MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_DEVP }}" - MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}" - MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}" - MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}" - IS_SUCCESS: "${{ job.status == 'success' }}" - uses: docker://keybaseio/client:stable-node - with: - args: .github/workflows/support-files/notifications/entry_point.sh diff --git a/.github/workflows/cd-docs.yml b/.github/workflows/cd-docs.yml index 782a2c196d..a1ba0c104a 100644 --- a/.github/workflows/cd-docs.yml +++ b/.github/workflows/cd-docs.yml @@ -1,6 +1,7 @@ name: CD docs on: + workflow_dispatch: push: branches: master paths: @@ -27,39 +28,38 @@ jobs: command: build args: --workspace --release --all - name: Install mdbook - run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook) + run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook) - name: Install mdbook plugins run: | - cargo install --vers "^0.2.0" mdbook-variables && cargo install \ + cargo install --vers "=0.2.2" mdbook-variables && cargo install \ --vers "^1.8.0" mdbook-admonish && cargo install --vers \ - "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" \ - mdbook-theme && cargo install --vers "^0.7.7" mdbook-linkcheck && \ - cargo install --vers "^0.5.0" mdbook-cmdrun - - name: Clean website - run: cd documentation/docs && mdbook clean - - name: Build website - run: cd documentation/docs && mdbook build + "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \ + && cargo install --vers "^0.7.7" mdbook-linkcheck + - name: Build all projects in documentation/ & move to ~/dist/docs/ + run: cd documentation && ./build_all_to_dist.sh + continue-on-error: false - name: Deploy branch master to dev continue-on-error: true uses: easingthemes/ssh-deploy@main env: SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }} ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/docs/book/" + SOURCE: "dist/docs/" REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_DEV }} REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }} TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/ - EXCLUDE: "/dist/, /node_modules/" + EXCLUDE: "/node_modules/" - name: Deploy branch master to prod + if: github.ref == 'refs/heads/master' uses: easingthemes/ssh-deploy@main env: SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }} ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/docs/book/" + SOURCE: "dist/docs/" REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_PROD }} REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }} TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/ - EXCLUDE: "/dist/, /node_modules/" + EXCLUDE: "/node_modules/" - name: Matrix - Node Install run: npm install working-directory: .github/workflows/support-files diff --git a/.github/workflows/check-contract-schema.yml b/.github/workflows/check-contract-schema.yml new file mode 100644 index 0000000000..6de645dfa5 --- /dev/null +++ b/.github/workflows/check-contract-schema.yml @@ -0,0 +1,31 @@ +name: Check Contract Schema + +on: + push: + paths: + - 'contracts/**' + - 'common/**' + pull_request: + paths: + - 'contracts/**' + - 'common/**' + +jobs: + check-schema: + name: Generate and check schema + runs-on: custom-runner-linux + steps: + - name: Check out repository code + uses: actions/checkout@v2 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + + + - name: Generate the schema + run: make contract-schema + + - name: Check for diff + run: git diff --exit-code -- contracts/*/schema diff --git a/.github/workflows/ci-dev.yml b/.github/workflows/ci-dev.yml deleted file mode 100644 index 82068fce76..0000000000 --- a/.github/workflows/ci-dev.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: CI dev-portal - -on: - push: - branches-ignore: master - paths: - - 'documentation/dev-portal/**' - -jobs: - build: - runs-on: custom-runner-linux - steps: - - uses: actions/checkout@v3 - - name: Install rsync - run: sudo apt-get install rsync - - uses: rlespinasse/github-slug-action@v3.x - - uses: actions/setup-node@v3 - with: - node-version: 18 - - name: Install Rust stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - - name: Install mdbook - run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook) - - name: Install mdbook plugins - run: | - cargo install --vers "^0.2.0" mdbook-variables && cargo install \ - --vers "^1.8.0" mdbook-admonish && cargo install --vers \ - "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \ - && cargo install --vers "^0.7.7" mdbook-linkcheck - - name: Clean website - run: cd documentation/dev-portal && mdbook clean - - name: Build website - run: cd documentation/dev-portal && mdbook build - - name: Deploy branch to CI www - continue-on-error: true - uses: easingthemes/ssh-deploy@main - env: - SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }} - ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/dev-portal/book/html/" - REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }} - REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} - TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/dev-portal-${{ env.GITHUB_REF_SLUG }} - EXCLUDE: "/dist/, /node_modules/" - - name: Matrix - Node Install - run: npm install - working-directory: .github/workflows/support-files - - name: Matrix - Send Notification - env: - NYM_NOTIFICATION_KIND: ci-dev - NYM_PROJECT_NAME: "Dev portal CI" - NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}" - NYM_CI_WWW_LOCATION: "dev-portal-${{ env.GITHUB_REF_SLUG }}" - GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}" - GIT_BRANCH: "${GITHUB_REF##*/}" - MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}" - MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_DEVP }}" - MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}" - MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}" - MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}" - IS_SUCCESS: "${{ job.status == 'success' }}" - uses: docker://keybaseio/client:stable-node - with: - args: .github/workflows/support-files/notifications/entry_point.sh diff --git a/.github/workflows/ci-docs.yml b/.github/workflows/ci-docs.yml index 2523013204..2ce019272a 100644 --- a/.github/workflows/ci-docs.yml +++ b/.github/workflows/ci-docs.yml @@ -1,6 +1,7 @@ name: CI docs on: + workflow_dispatch: push: branches-ignore: master paths: @@ -27,29 +28,27 @@ jobs: command: build args: --workspace --release --all - name: Install mdbook - run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook) + run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook) - name: Install mdbook plugins run: | - cargo install --vers "^0.2.0" mdbook-variables && cargo install \ + cargo install --vers "=0.2.2" mdbook-variables && cargo install \ --vers "^1.8.0" mdbook-admonish && cargo install --vers \ - "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" \ - mdbook-theme && cargo install --vers "^0.7.7" mdbook-linkcheck && \ - cargo install --vers "^0.5.0" mdbook-cmdrun - - name: Clean website - run: cd documentation/docs && mdbook clean - - name: Build website - run: cd documentation/docs && mdbook build + "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \ + && cargo install --vers "^0.7.7" mdbook-linkcheck + - name: Build all projects in documentation/ & move to ~/dist/docs/ + run: cd documentation && ./build_all_to_dist.sh + continue-on-error: false - name: Deploy branch to CI www continue-on-error: true uses: easingthemes/ssh-deploy@main env: SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }} ARGS: "-rltgoDzvO --delete" - SOURCE: "documentation/docs/book/" + SOURCE: "dist/docs/" REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }} REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/docs-${{ env.GITHUB_REF_SLUG }} - EXCLUDE: "/dist/, /node_modules/" + EXCLUDE: "/node_modules/" - name: Matrix - Node Install run: npm install working-directory: .github/workflows/support-files diff --git a/.github/workflows/connect-mobile.yml b/.github/workflows/connect-mobile.yml deleted file mode 100644 index 05f0add6e2..0000000000 --- a/.github/workflows/connect-mobile.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Nym Connect - mobile (Rust) - -on: - push: - paths: - - "nym-connect/mobile/src-tauri/**" - - "nym-connect/mobile/src-tauri/Cargo.toml" - - "!nym-connect/mobile/src-tauri/gen/**" - - "clients/client-core/**" - - "clients/socks5/**" - - "common/**" - - "gateway/gateway-requests/**" - - "contracts/vesting/**" - - "nym-api/nym-api-requests/**" - pull_request: - paths: - - "nym-connect/mobile/src-tauri/**" - - "nym-connect/mobile/src-tauri/Cargo.toml" - - "!nym-connect/mobile/src-tauri/gen/**" - - "clients/client-core/**" - - "clients/socks5/**" - - "common/**" - - "gateway/gateway-requests/**" - - "contracts/vesting/**" - - "nym-api/nym-api-requests/**" - -jobs: - build: - #runs-on: [self-hosted, custom-linux] - runs-on: ubuntu-22.04 - #env: - #RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache - #defaults: - #run: - #working-directory: nym-connect/mobile/src-tauri/ - steps: - - name: Install Dependencies (Linux) - run: | - sudo apt-get update - sudo apt-get -y install \ - libwebkit2gtk-4.1-dev \ - build-essential \ - curl \ - wget \ - libssl-dev \ - libgtk-3-dev \ - squashfs-tools \ - libayatana-appindicator3-dev \ - librsvg2-dev \ - libsoup-3.0-dev \ - libjavascriptcoregtk-4.1-dev - - - name: Checkout - uses: actions/checkout@v3 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - components: clippy, rustfmt - - - name: Check formatting - run: cargo fmt --manifest-path nym-connect/mobile/src-tauri/Cargo.toml -- --check - - - name: Build all binaries - run: cargo build --manifest-path nym-connect/mobile/src-tauri/Cargo.toml - - - name: Run all tests - run: cargo test --manifest-path nym-connect/mobile/src-tauri/Cargo.toml - - - name: Clippy - run: cargo clippy --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all-targets -- -D warnings diff --git a/.github/workflows/contracts-build.yml b/.github/workflows/contracts-build.yml index f44f291f1b..8a0a5d4122 100644 --- a/.github/workflows/contracts-build.yml +++ b/.github/workflows/contracts-build.yml @@ -14,7 +14,7 @@ jobs: - name: Install Rust stable uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: 1.69.0 target: wasm32-unknown-unknown override: true components: rustfmt, clippy diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml index 9de2cab348..64aeabb5d7 100644 --- a/.github/workflows/contracts.yml +++ b/.github/workflows/contracts.yml @@ -48,12 +48,12 @@ jobs: RUSTFLAGS: '-C link-arg=-s' with: command: build - args: --manifest-path contracts/Cargo.toml --workspace --target wasm32-unknown-unknown + args: --manifest-path contracts/Cargo.toml --workspace --lib --target wasm32-unknown-unknown - uses: actions-rs/cargo@v1 with: command: test - args: --manifest-path contracts/Cargo.toml + args: --lib --manifest-path contracts/Cargo.toml - uses: actions-rs/cargo@v1 with: @@ -64,4 +64,4 @@ jobs: if: ${{ matrix.rust != 'nightly' }} with: command: clippy - args: --manifest-path contracts/Cargo.toml --workspace --all-targets -- -D warnings + args: --lib --manifest-path contracts/Cargo.toml --workspace --all-targets -- -D warnings diff --git a/.github/workflows/nightly_build.yml b/.github/workflows/nightly_build.yml index 1ee391d6a7..84ea9fabec 100644 --- a/.github/workflows/nightly_build.yml +++ b/.github/workflows/nightly_build.yml @@ -24,7 +24,7 @@ jobs: continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }} steps: - name: Install Dependencies (Linux) - run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools + run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler continue-on-error: true if: matrix.os == 'ubuntu-20.04' diff --git a/.github/workflows/nightly_build_release.yml b/.github/workflows/nightly_build_release.yml index fa1c465c97..6ddee762e1 100644 --- a/.github/workflows/nightly_build_release.yml +++ b/.github/workflows/nightly_build_release.yml @@ -37,7 +37,7 @@ jobs: continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }} steps: - name: Install Dependencies (Linux) - run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools + run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler continue-on-error: true if: matrix.os == 'ubuntu-20.04' diff --git a/.github/workflows/nym-connect-publish-macos.yml b/.github/workflows/nym-connect-publish-macos.yml index 45dac7e87a..a5a0a65a8c 100644 --- a/.github/workflows/nym-connect-publish-macos.yml +++ b/.github/workflows/nym-connect-publish-macos.yml @@ -118,7 +118,7 @@ jobs: 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 "filename=nym-connect_${semver}_x64.dmg " >> "$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 cc5e1f3f00..2e27f3044c 100644 --- a/.github/workflows/nym-connect-publish-ubuntu.yml +++ b/.github/workflows/nym-connect-publish-ubuntu.yml @@ -85,8 +85,9 @@ jobs: 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 "filename=nym-connect_${semver}_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 1b4b1be2cc..f90ce1697d 100644 --- a/.github/workflows/nym-connect-publish-windows10.yml +++ b/.github/workflows/nym-connect-publish-windows10.yml @@ -100,11 +100,12 @@ jobs: nym-connect/desktop/target/release/bundle/msi/*.msi.zip* - id: release-info name: Prepare release info + shell: bash run: | 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 "filename=nym-connect_${semver}_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 408a3b7406..f8f342d291 100644 --- a/.github/workflows/nym-wallet-publish-macos.yml +++ b/.github/workflows/nym-wallet-publish-macos.yml @@ -106,7 +106,7 @@ jobs: 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 "filename=nym-wallet_${semver}_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 9e9ee904cd..c21824eacf 100644 --- a/.github/workflows/nym-wallet-publish-ubuntu.yml +++ b/.github/workflows/nym-wallet-publish-ubuntu.yml @@ -83,7 +83,7 @@ jobs: 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 "filename=nym-wallet_${semver}_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 56a332a75d..b2f710100c 100644 --- a/.github/workflows/nym-wallet-publish-windows10.yml +++ b/.github/workflows/nym-wallet-publish-windows10.yml @@ -103,7 +103,7 @@ jobs: 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 "filename=nym-wallet_${semver}_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/.github/workflows/run-api-tests.yml b/.github/workflows/run-api-tests.yml index 1d2afec2bd..fbc3d63eea 100644 --- a/.github/workflows/run-api-tests.yml +++ b/.github/workflows/run-api-tests.yml @@ -32,5 +32,5 @@ jobs: run: yarn - name: Run tests - run: yarn test:qa + run: yarn test:sandbox working-directory: nym-api/tests diff --git a/.github/workflows/sdk-docs-typescript.yml b/.github/workflows/sdk-docs-typescript.yml new file mode 100644 index 0000000000..2df60dcc0b --- /dev/null +++ b/.github/workflows/sdk-docs-typescript.yml @@ -0,0 +1,50 @@ +name: Typescript SDK docs + +on: + push: + paths: + - "sdk/typescript/**" + pull_request: + paths: + - "sdk/typescript/**" + +jobs: + build: + runs-on: custom-runner-linux + steps: + - uses: actions/checkout@v2 + - name: Install rsync + run: sudo apt-get install rsync + continue-on-error: true + - uses: rlespinasse/github-slug-action@v3.x + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install Rust stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: Setup yarn + run: npm install -g yarn + - name: Install + run: yarn + - name: Matrix - Node Install + run: npm install + working-directory: .github/workflows/support-files + - name: Matrix - Send Notification + env: + NYM_NOTIFICATION_KIND: ts-packages + NYM_PROJECT_NAME: "ts-packages" + NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}" + NYM_CI_WWW_LOCATION: "ts-${{ env.GITHUB_REF_SLUG }}" + GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}" + GIT_BRANCH: "${GITHUB_REF##*/}" + IS_SUCCESS: "${{ job.status == 'success' }}" + MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}" + MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}" + MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}" + MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}" + MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}" + uses: docker://keybaseio/client:stable-node + with: + args: .github/workflows/support-files/notifications/entry_point.sh diff --git a/.github/workflows/typescript-lint.yml b/.github/workflows/typescript-lint.yml index dcaa905f5d..a8318364a4 100644 --- a/.github/workflows/typescript-lint.yml +++ b/.github/workflows/typescript-lint.yml @@ -7,8 +7,6 @@ on: - "sdk/typescript/**" - "nym-connect/desktop/src/**" - "nym-connect/desktop/package.json" - - "nym-connect/mobile/src/**" - - "nym-connect/mobile/package.json" - "nym-wallet/src/**" - "nym-wallet/package.json" - "explorer/**" @@ -18,8 +16,6 @@ on: - "sdk/typescript/**" - "nym-connect/desktop/src/**" - "nym-connect/desktop/package.json" - - "nym-connect/mobile/src/**" - - "nym-connect/mobile/package.json" - "nym-wallet/src/**" - "nym-wallet/package.json" - "explorer/**" diff --git a/.github/workflows/upload-nyxd-ci.yml b/.github/workflows/upload-nyxd-ci.yml deleted file mode 100644 index 1668bf3184..0000000000 --- a/.github/workflows/upload-nyxd-ci.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: Upload nyxd to CI - -on: - workflow_dispatch: - -jobs: - publish-nyxd: - strategy: - fail-fast: false - matrix: - platform: [ubuntu-20.04] - - runs-on: ${{ matrix.platform }} - steps: - - uses: actions/checkout@v3 - - - name: Prepare build output directory - shell: bash - env: - OUTPUT_DIR: ci-builds/nyxd - run: | - rm -rf ci-builds || true - mkdir -p $OUTPUT_DIR - echo $OUTPUT_DIR - - - name: Install Dependencies (Linux) - run: sudo apt-get update && sudo apt-get -y install build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools git - continue-on-error: true - - - name: Update env variables to include go - run: | - sudo rm -rf /usr/local/go - curl https://dl.google.com/go/go1.19.2.linux-amd64.tar.gz | sudo tar -C/usr/local -zxvf - - cat <<'EOF' >>$HOME/.profile - export GOROOT=/usr/local/go - export GOPATH=$HOME/go - export GO111MODULE=on - export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin - EOF - source $HOME/.profile - - - name: Verify Go is installed - run: go version - - - name: Clone nyxd repo - run: | - git clone https://github.com/tommyv1987/nyxd - cd nyxd - git checkout release/v0.30.2 - - - name: Run nyxd - run: | - pwd - cd nyxd && make build - sleep 10 - ls /home/runner/work/nym/nym/nyxd/build - - - name: Prepare build output - shell: bash - env: - OUTPUT_DIR: ci-builds/nyxd - run: | - cp /home/runner/work/nym/nym/nyxd/build/nyxd $OUTPUT_DIR - WASMVM_SO=$(ldd /home/runner/work/nym/nym/nyxd/build/nyxd | grep "libwasm*" | awk '{ print $3 }') - ls $WASMVM_SO - sleep 3 - cp $(echo $WASMVM_SO) $OUTPUT_DIR - - - name: Deploy nyxd to CI www - continue-on-error: true - uses: easingthemes/ssh-deploy@main - env: - SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }} - ARGS: "-avzr" - SOURCE: "ci-builds/" - REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }} - REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} - TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/ - EXCLUDE: "/dist/, /node_modules/" diff --git a/.github/workflows/wallet.yml b/.github/workflows/wallet.yml index d77fb9d6fb..34f202d25f 100644 --- a/.github/workflows/wallet.yml +++ b/.github/workflows/wallet.yml @@ -31,7 +31,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: 1.71.0 override: true components: rustfmt, clippy diff --git a/CHANGELOG.md b/CHANGELOG.md index f938c81fe3..2df4f7e732 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,75 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https:// ## [Unreleased] +## [v1.1.29-snickers] (2023-08-29) + +- Add EXPLORER_API configurable url ([#3810]) +- Bugfix/use correct tendermint dialect ([#3802]) +- Explorer - look up gateways based on geo-location ([#3776]) +- Speedy mode - select the mixnodes based on the location of the NR ([#3775]) +- NR - reduce response time by removing poisson delay ([#3774]) +- [demo] libp2p example with nym-sdk ([#3763]) +- introduced /network/details endpoint to nym-api to return used network information ([#3758]) +- Feature/issue credentials ([#3691]) + +[#3810]: https://github.com/nymtech/nym/pull/3810 +[#3802]: https://github.com/nymtech/nym/pull/3802 +[#3776]: https://github.com/nymtech/nym/issues/3776 +[#3775]: https://github.com/nymtech/nym/issues/3775 +[#3774]: https://github.com/nymtech/nym/issues/3774 +[#3763]: https://github.com/nymtech/nym/pull/3763 +[#3758]: https://github.com/nymtech/nym/pull/3758 +[#3691]: https://github.com/nymtech/nym/pull/3691 + +## [v1.1.28] (2023-08-22) + +- [final step3]: add [rust] support to nyxd client in wasm ([#3743]) +- Feature/ephemera upgrade ([#3791]) +- [rust-sdk] feat: make it more convenient to send and receive messages in different tasks ([#3756]) +- feat: validator client refactoring + wasm compatible nyxd client ([#3726]) +- feat: retain connection between client init and run ([#3767]) + +[#3743]: https://github.com/nymtech/nym/issues/3743 +[#3791]: https://github.com/nymtech/nym/pull/3791 +[#3756]: https://github.com/nymtech/nym/pull/3756 +[#3726]: https://github.com/nymtech/nym/pull/3726 +[#3767]: https://github.com/nymtech/nym/pull/3767 + + +## [v1.1.27] (2023-08-16) + +- fix serialisation of contract types ([#3752]) +- Investigate spending credentials from the main API (coconut enabled to a gateway) from feature/ephemera branch ([#3741]) +- NymConnect UI stuck in showing "Gateway has issues" ([#3594]) +- [UPDATE] Update MiniBolt community-applications-and-guides dev docs ([#3754]) + +[#3752]: https://github.com/nymtech/nym/issues/3752 +[#3741]: https://github.com/nymtech/nym/issues/3741 +[#3594]: https://github.com/nymtech/nym/issues/3594 +[#3754]: https://github.com/nymtech/nym/pull/3754 + +## [v1.1.24] (2023-08-08) + +- Latency based gateway selection is serial and slow ([#3710]) +- Network-requester: strip comments from allow lists ([#3625]) +- Remove (or start maintaining) `upgrade` commands from all binaries ([#3600]) +- Set sphinx as default packet type ([#3748]) +- Apply fix from feature/ephemera to develop too (#3698) ([#3742]) +- Feature/coco demos ([#3732]) +- Add updates to community list projects ([#3722]) +- Add geo-aware mixnet topology provider ([#3713]) +- Add updates to community list projects ([#3711]) + +[#3710]: https://github.com/nymtech/nym/issues/3710 +[#3625]: https://github.com/nymtech/nym/issues/3625 +[#3600]: https://github.com/nymtech/nym/issues/3600 +[#3748]: https://github.com/nymtech/nym/pull/3748 +[#3742]: https://github.com/nymtech/nym/pull/3742 +[#3732]: https://github.com/nymtech/nym/pull/3732 +[#3722]: https://github.com/nymtech/nym/pull/3722 +[#3713]: https://github.com/nymtech/nym/pull/3713 +[#3711]: https://github.com/nymtech/nym/pull/3711 + ## [v1.1.23] (2023-07-04) - nym-cli: add client identity key signing support ([#3576]) diff --git a/Cargo.lock b/Cargo.lock index f93b037760..348e7ff99a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,12 +8,221 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +[[package]] +name = "actix-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" +dependencies = [ + "bitflags 1.3.2", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite 0.2.12", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash 0.8.3", + "base64 0.21.2", + "bitflags 1.3.2", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite 0.2.12", + "rand 0.8.5", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "actix-router" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" +dependencies = [ + "bytestring", + "http", + "regex", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "num_cpus", + "socket2 0.4.9", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite 0.2.12", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite 0.2.12", +] + +[[package]] +name = "actix-web" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash 0.7.6", + "bytes", + "bytestring", + "cfg-if", + "cookie 0.16.2", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite 0.2.12", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.4.9", + "time 0.3.25", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2262160a7ae29e3415554a3f1fc04c764b1540c116aa524683208078b7a75bc9" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", +] + [[package]] name = "aead" version = "0.5.2" @@ -24,6 +233,17 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher 0.2.5", +] + [[package]] name = "aes" version = "0.7.5" @@ -39,9 +259,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher 0.4.4", @@ -50,18 +270,52 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.10.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" dependencies = [ - "aead", - "aes 0.8.2", + "aead 0.4.3", + "aes 0.7.5", + "cipher 0.3.0", + "ctr 0.8.0", + "ghash 0.4.4", + "subtle 2.4.1", +] + +[[package]] +name = "aes-gcm" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +dependencies = [ + "aead 0.5.2", + "aes 0.8.3", "cipher 0.4.4", "ctr 0.9.2", - "ghash", + "ghash 0.5.0", "subtle 2.4.1", ] +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + [[package]] name = "ahash" version = "0.7.6" @@ -80,30 +334,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", + "getrandom 0.2.10", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" dependencies = [ "memchr", ] [[package]] -name = "android_log-sys" -version = "0.3.0" +name = "alloc-no-stdlib" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f0fc03f560e1aebde41c2398b691cb98b5ea5996a6184a7a67bbbb77448969" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_log-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" [[package]] name = "android_logger" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa490e751f3878eb9accb9f18988eca52c2337ce000a8bf31ef50d4c723ca9e" +checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" dependencies = [ "android_log-sys", "env_logger 0.10.0", @@ -143,15 +425,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] @@ -167,9 +449,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -177,21 +459,37 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +dependencies = [ + "backtrace", +] + +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "argon2" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95c2fcf79ad1932ac6269a738109997a83c227c09b75842ae564dc8ede6a861c" +checksum = "b2e554a8638bdc1e4eae9984845306cc95f8a9208ba8d49c3859fd958b46774d" dependencies = [ "base64ct", "blake2 0.10.6", + "cpufeatures", "password-hash", ] +[[package]] +name = "array-bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd" + [[package]] name = "arrayref" version = "0.3.7" @@ -200,15 +498,94 @@ 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 = "asn1-rs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33" +dependencies = [ + "asn1-rs-derive 0.1.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.25", +] + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive 0.4.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time 0.3.25", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "asn1_der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-channel" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", "event-listener", @@ -225,6 +602,35 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg 1.1.0", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix 0.37.23", + "slab", + "socket2 0.4.9", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -233,7 +639,7 @@ checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", - "pin-project-lite", + "pin-project-lite 0.2.12", ] [[package]] @@ -244,18 +650,31 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", +] + +[[package]] +name = "asynchronous-codec" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite 0.2.12", ] [[package]] @@ -282,6 +701,12 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + [[package]] name = "atty" version = "0.2.14" @@ -310,13 +735,13 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-util", "http", @@ -327,7 +752,7 @@ dependencies = [ "memchr", "mime", "percent-encoding", - "pin-project-lite", + "pin-project-lite 0.2.12", "rustversion", "serde", "sync_wrapper", @@ -353,6 +778,27 @@ dependencies = [ "tower-service", ] +[[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", + "object", + "rustc-demangle", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base16ct" version = "0.1.1" @@ -371,12 +817,6 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" -[[package]] -name = "base64" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" - [[package]] name = "base64" version = "0.21.2" @@ -404,6 +844,27 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "prettyplease 0.2.12", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.28", +] + [[package]] name = "bip32" version = "0.5.1" @@ -417,7 +878,7 @@ dependencies = [ "pbkdf2", "rand_core 0.6.4", "ripemd", - "sha2 0.10.6", + "sha2 0.10.7", "subtle 2.4.1", "zeroize", ] @@ -448,6 +909,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "bitvec" version = "1.0.1" @@ -483,9 +950,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", @@ -513,6 +980,22 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "block-modes" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" +dependencies = [ + "block-padding", + "cipher 0.2.5", +] + +[[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" @@ -541,6 +1024,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bs58" version = "0.4.0" @@ -553,7 +1063,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" dependencies = [ - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -567,9 +1077,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.2" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byte-tools" @@ -592,6 +1102,26 @@ dependencies = [ "serde", ] +[[package]] +name = "bytestring" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" +dependencies = [ + "bytes", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "cast" version = "0.3.0" @@ -606,11 +1136,23 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" dependencies = [ "jobserver", + "libc", +] + +[[package]] +name = "ccm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca1a8fbc20b50ac9673ff014abfb2b5f4085ee1a850d408f14a159c5853ac7" +dependencies = [ + "aead 0.3.2", + "cipher 0.2.5", + "subtle 2.4.1", ] [[package]] @@ -619,6 +1161,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -635,6 +1186,18 @@ dependencies = [ "keystream", ] +[[package]] +name = "chacha20" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "zeroize", +] + [[package]] name = "chacha20" version = "0.9.1" @@ -646,28 +1209,41 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "chacha20poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +dependencies = [ + "aead 0.4.3", + "chacha20 0.8.2", + "cipher 0.3.0", + "poly1305 0.7.2", + "zeroize", +] + [[package]] name = "chacha20poly1305" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ - "aead", - "chacha20", + "aead 0.5.2", + "chacha20 0.9.1", "cipher 0.4.4", - "poly1305", + "poly1305 0.8.0", "zeroize", ] [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", "num-traits", "serde", "time 0.1.45", @@ -702,6 +1278,15 @@ dependencies = [ "half", ] +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "cipher" version = "0.3.0" @@ -722,23 +1307,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ - "bitflags", + "bitflags 1.3.2", "clap_lex 0.2.4", - "indexmap", + "indexmap 1.9.3", "textwrap", ] [[package]] name = "clap" -version = "4.2.7" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" dependencies = [ "clap_builder", "clap_derive", @@ -747,47 +1343,46 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.7" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" dependencies = [ "anstream", "anstyle", - "bitflags", - "clap_lex 0.4.1", + "clap_lex 0.5.0", "once_cell", "strsim", ] [[package]] name = "clap_complete" -version = "4.2.3" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1594fe2312ec4abf402076e407628f5c313e54c32ade058521df4ee34ecac8a8" +checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce" dependencies = [ - "clap 4.2.7", + "clap 4.3.21", ] [[package]] name = "clap_complete_fig" -version = "4.2.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3af28956330989baa428ed4d3471b853715d445c62de21b67292e22cf8a41fa" +checksum = "99fee1d30a51305a6c2ed3fc5709be3c8af626c9c958e04dd9ae94e27bcbce9f" dependencies = [ - "clap 4.2.7", + "clap 4.3.21", "clap_complete", ] [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -801,9 +1396,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "cloudabi" @@ -811,7 +1406,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -822,13 +1417,13 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ - "atty", + "is-terminal", "lazy_static", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -843,9 +1438,9 @@ dependencies = [ [[package]] name = "comfy-table" -version = "6.1.4" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e7b787b0dc42e8111badfdbe4c3059158ccb2db8780352fa1b01e8ccf45cc4d" +checksum = "7e959d788268e3bf9d35ace83e81b124190378e4c91c9067524675e33394b8ba" dependencies = [ "crossterm", "strum 0.24.1", @@ -862,6 +1457,20 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "config" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" +dependencies = [ + "async-trait", + "lazy_static", + "nom", + "pathdiff", + "serde", + "toml 0.5.11", +] + [[package]] name = "console-api" version = "0.5.0" @@ -876,9 +1485,9 @@ dependencies = [ [[package]] name = "console-subscriber" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ab2224a0311582eb03adba4caaf18644f7b1f10a760803a803b9b605187fc7" +checksum = "d4cf42660ac07fcebed809cfe561dd8730bcd35b075215e6479c516bcd0d11cb" dependencies = [ "console-api", "crossbeam-channel", @@ -900,15 +1509,21 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[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" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "cookie" @@ -916,15 +1531,19 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ - "aes-gcm", - "base64 0.20.0", - "hkdf 0.12.3", - "hmac 0.12.1", "percent-encoding", - "rand 0.8.5", - "sha2 0.10.6", - "subtle 2.4.1", - "time 0.3.21", + "time 0.3.25", + "version_check", +] + +[[package]] +name = "cookie" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" +dependencies = [ + "percent-encoding", + "time 0.3.25", "version_check", ] @@ -944,6 +1563,15 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cosmos-sdk-proto" version = "0.19.0" @@ -978,9 +1606,9 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.5" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75836a10cb9654c54e77ee56da94d592923092a10b369cdb0dbd56acefc16340" +checksum = "7e272708a9745dad8b591ef8a718571512130f2b39b33e3d7a27c558e3069394" dependencies = [ "digest 0.10.7", "ed25519-zebra", @@ -991,18 +1619,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.5" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" +checksum = "296db6a3caca5283425ae0cf347f4e46999ba3f6620dbea8939a0e00347831ce" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc" +checksum = "bb6b2fb76758ef59cddc77f2e2ae91c22f77da49037e9f182e9c2833f0e959b1" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -1013,9 +1641,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c" +checksum = "2bfa39422f0d9f1c9a6fd3711573258495314dfa3aae738ea825ecd9964bc659" dependencies = [ "proc-macro2", "quote", @@ -1024,11 +1652,12 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb" +checksum = "1f6dc2ee23313add5ecacc3ccac217b9967ad9d2d11bd56e5da6aa65a9da6138" dependencies = [ "base64 0.13.1", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -1036,10 +1665,9 @@ dependencies = [ "hex", "schemars", "serde", - "serde-json-wasm 0.5.1", - "sha2 0.10.6", + "serde-json-wasm", + "sha2 0.10.7", "thiserror", - "uint", ] [[package]] @@ -1052,9 +1680,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -1157,14 +1785,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 1.1.0", "cfg-if", "crossbeam-utils", - "memoffset", + "memoffset 0.9.0", "scopeguard", ] @@ -1180,20 +1808,20 @@ 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", ] [[package]] name = "crossterm" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm_winapi", "libc", "mio", @@ -1205,9 +1833,9 @@ dependencies = [ [[package]] name = "crossterm_winapi" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" dependencies = [ "winapi", ] @@ -1331,15 +1959,15 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "socket2", + "socket2 0.4.9", "winapi", ] [[package]] name = "curl-sys" -version = "0.4.63+curl-8.1.2" +version = "0.4.65+curl-8.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc" +checksum = "961ba061c9ef2fe34bbd12b807152d96f0badd2bebe7b90ce6c8c8b7572a0986" dependencies = [ "cc", "libc", @@ -1364,6 +1992,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" +dependencies = [ + "cfg-if", + "fiat-crypto", + "packed_simd_2", + "platforms", + "subtle 2.4.1", + "zeroize", +] + [[package]] name = "curve25519-dalek-ng" version = "4.1.1" @@ -1379,9 +2021,9 @@ dependencies = [ [[package]] name = "cw-controllers" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071" +checksum = "d5d8edce4b78785f36413f67387e4be7d0cb7d032b5d4164bcc024f9c3f3f2ea" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1394,9 +2036,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" +checksum = "3f0e92a069d62067f3472c62e30adedb4cab1754725c0f2a682b3128d2bf3c79" dependencies = [ "cosmwasm-std", "schemars", @@ -1413,29 +2055,30 @@ dependencies = [ "cosmwasm-std", "cw2", "schemars", - "semver 1.0.17", + "semver 1.0.18", "serde", "thiserror", ] [[package]] name = "cw2" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" +checksum = "29ac2dc7a55ad64173ca1e0a46697c31b7a5c51342f55a1e84a724da4eb99908" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", "serde", + "thiserror", ] [[package]] name = "cw20" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" +checksum = "011c45920f8200bd5d32d4fe52502506f64f2f75651ab408054d4cfc75ca3a9b" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1446,9 +2089,9 @@ dependencies = [ [[package]] name = "cw3" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa" +checksum = "171af3d9127de6805a7dd819fb070c7d2f6c3ea85f4193f42cef259f0a7f33d5" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1461,9 +2104,9 @@ dependencies = [ [[package]] name = "cw4" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b" +checksum = "a398696307efadaaa2d0850076f865fa706c959d493cb4203314f72be6b77a64" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1478,8 +2121,18 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", ] [[package]] @@ -1496,13 +2149,38 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core", + "darling_core 0.13.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", "quote", "syn 1.0.109", ] @@ -1519,15 +2197,41 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.4.0" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" dependencies = [ "cfg-if", - "hashbrown 0.12.3", + "hashbrown 0.14.0", "lock_api", "once_cell", - "parking_lot_core 0.9.7", + "parking_lot_core 0.9.8", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "data-encoding-macro" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +dependencies = [ + "data-encoding", + "syn 1.0.109", ] [[package]] @@ -1537,19 +2241,57 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] [[package]] name = "der" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", "zeroize", ] +[[package]] +name = "der-parser" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82" +dependencies = [ + "asn1-rs 0.3.1", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs 0.5.2", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" +dependencies = [ + "serde", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1562,10 +2304,54 @@ dependencies = [ ] [[package]] -name = "devise" -version = "0.3.1" +name = "derive_builder" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c7580b072f1c8476148f16e0a0d5dedddab787da98d86c5082c5e9ed8ab595" +checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn 1.0.109", +] + +[[package]] +name = "devise" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6eacefd3f541c66fc61433d65e54e0e46e0a029a819a7dbbc7a7b489e8a85f8" dependencies = [ "devise_codegen", "devise_core", @@ -1573,9 +2359,9 @@ dependencies = [ [[package]] name = "devise_codegen" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123c73e7a6e51b05c75fe1a1b2f4e241399ea5740ed810b0e3e6cacd9db5e7b2" +checksum = "9c8cf4b8dd484ede80fd5c547592c46c3745a617c8af278e2b72bea86b2dfed6" dependencies = [ "devise_core", "quote", @@ -1583,15 +2369,15 @@ dependencies = [ [[package]] name = "devise_core" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841ef46f4787d9097405cac4e70fb8644fc037b526e8c14054247c0263c400d0" +checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a" dependencies = [ - "bitflags", + "bitflags 2.4.0", "proc-macro2", - "proc-macro2-diagnostics 0.9.1", + "proc-macro2-diagnostics", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -1665,6 +2451,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -1684,10 +2481,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] -name = "dyn-clone" -version = "1.0.11" +name = "dtoa" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "65d09067bfacaa79114679b279d7f5885b53295b1e2cfb4e79c8e4bd3d633169" + +[[package]] +name = "dyn-clone" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "ecdsa" @@ -1707,7 +2510,7 @@ version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ - "der 0.7.7", + "der 0.7.8", "digest 0.10.7", "elliptic-curve 0.13.5", "rfc6979 0.4.0", @@ -1727,9 +2530,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" dependencies = [ "pkcs8 0.10.2", "signature 2.1.0", @@ -1754,7 +2557,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "ed25519 1.5.3", "rand 0.7.3", "serde", @@ -1769,7 +2572,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "hashbrown 0.12.3", "hex", "rand_core 0.6.4", @@ -1780,9 +2583,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -1797,6 +2600,8 @@ dependencies = [ "ff 0.12.1", "generic-array 0.14.7", "group 0.12.1", + "hkdf 0.12.3", + "pem-rfc7468", "pkcs8 0.9.0", "rand_core 0.6.4", "sec1 0.3.0", @@ -1832,6 +2637,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-as-inner" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "enum-iterator" version = "1.1.3" @@ -1849,7 +2666,7 @@ checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -1876,10 +2693,66 @@ dependencies = [ ] [[package]] -name = "errno" -version = "0.3.1" +name = "ephemera" +version = "0.1.0" +dependencies = [ + "actix-web", + "anyhow", + "array-bytes", + "assert_matches", + "async-trait", + "asynchronous-codec", + "blake2 0.10.6", + "bs58 0.4.0", + "bytes", + "cfg-if", + "chrono", + "clap 4.3.21", + "config", + "digest 0.10.7", + "dirs 5.0.1", + "enum-as-inner", + "futures", + "futures-util", + "lazy_static", + "libp2p 0.51.3", + "libp2p-identity", + "log", + "lru 0.10.0", + "nym-config", + "nym-ephemera-common", + "nym-task", + "pretty_env_logger", + "rand 0.8.5", + "refinery", + "reqwest", + "rocksdb", + "rusqlite", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "tokio", + "tokio-tungstenite 0.18.0", + "tokio-util", + "toml 0.7.6", + "unsigned-varint", + "utoipa", + "utoipa-swagger-ui", + "uuid 1.4.1", +] + +[[package]] +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", @@ -1904,18 +2777,20 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "explorer-api" -version = "1.1.23" +version = "1.1.27" dependencies = [ "chrono", - "clap 4.2.7", + "clap 4.3.21", "dotenvy", "humantime-serde", "isocountry", "itertools", + "lazy_static", "log", "maxminddb", "nym-bin-common", "nym-contracts-common", + "nym-explorer-api-requests", "nym-mixnet-contract-common", "nym-network-defaults", "nym-task", @@ -1975,6 +2850,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fastrand" version = "1.9.0" @@ -1984,6 +2871,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "ff" version = "0.10.1" @@ -2025,31 +2918,43 @@ dependencies = [ ] [[package]] -name = "figment" -version = "0.10.8" +name = "fiat-crypto" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e56602b469b2201400dec66a66aec5a9b8761ee97cd1b8c96ab2483fcc16cc9" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + +[[package]] +name = "figment" +version = "0.10.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4547e226f4c9ab860571e070a9034192b3175580ecea38da34fcdb53a018c9a5" dependencies = [ "atomic", "pear", "serde", - "toml 0.5.11", + "toml 0.7.6", "uncased", "version_check", ] [[package]] name = "filetime" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "windows-sys 0.48.0", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.26" @@ -2057,6 +2962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", + "libz-sys", "miniz_oxide", ] @@ -2105,9 +3011,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -2179,6 +3085,7 @@ dependencies = [ "futures-core", "futures-task", "futures-util", + "num_cpus", ] [[package]] @@ -2204,12 +3111,12 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand", + "fastrand 1.9.0", "futures-core", "futures-io", "memchr", "parking", - "pin-project-lite", + "pin-project-lite 0.2.12", "waker-fn", ] @@ -2221,7 +3128,18 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", +] + +[[package]] +name = "futures-rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" +dependencies = [ + "futures-io", + "rustls 0.20.8", + "webpki 0.22.0", ] [[package]] @@ -2236,6 +3154,12 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + [[package]] name = "futures-util" version = "0.3.28" @@ -2249,7 +3173,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite", + "pin-project-lite 0.2.12", "pin-utils", "slab", ] @@ -2262,15 +3186,15 @@ checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" [[package]] name = "generator" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e123d9ae7c02966b4d892e550bdc32164f05853cd40ab570650ad600596a8a" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" dependencies = [ "cc", "libc", "log", "rustversion", - "windows", + "windows 0.48.0", ] [[package]] @@ -2332,6 +3256,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug 0.3.0", + "polyval 0.5.3", +] + [[package]] name = "ghash" version = "0.5.0" @@ -2339,27 +3273,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" dependencies = [ "opaque-debug 0.3.0", - "polyval", + "polyval 0.6.1", ] [[package]] name = "ghost" -version = "0.1.9" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77ac7b51b8e6313251737fcef4b1c01a2ea102bde68415b62c0ee9268fec357" +checksum = "ba330b70a5341d3bc730b8e205aaee97ddab5d9c448c4f51a7c2d924266fa8f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "git2" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "libgit2-sys", "log", @@ -2432,9 +3372,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -2442,7 +3382,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -2496,6 +3436,16 @@ dependencies = [ "ahash 0.8.3", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash 0.8.3", + "allocator-api2", +] + [[package]] name = "hashlink" version = "0.7.0" @@ -2507,11 +3457,11 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0761a1b9491c4f2e3d66aa0f62d0fba0af9a0e2852e4d48ea506632a4b56e6aa" +checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" dependencies = [ - "hashbrown 0.13.2", + "hashbrown 0.14.0", ] [[package]] @@ -2534,7 +3484,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", - "bitflags", + "bitflags 1.3.2", "bytes", "headers-core", "http", @@ -2581,18 +3531,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -2606,6 +3547,12 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "hex_fmt" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" + [[package]] name = "hidapi" version = "1.5.0" @@ -2656,6 +3603,17 @@ dependencies = [ "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 = "http" version = "0.2.9" @@ -2675,7 +3633,7 @@ checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", - "pin-project-lite", + "pin-project-lite 0.2.12", ] [[package]] @@ -2686,9 +3644,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -2717,9 +3675,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -2731,8 +3689,8 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project-lite", - "socket2", + "pin-project-lite 0.2.12", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -2783,7 +3741,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ "hyper", - "pin-project-lite", + "pin-project-lite 0.2.12", "tokio", "tokio-io-timeout", ] @@ -2803,16 +3761,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows 0.48.0", ] [[package]] @@ -2843,14 +3801,43 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", ] +[[package]] +name = "if-addrs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "if-watch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9465340214b296cd17a0009acdb890d6160010b8adf8f78a00d0d7ab270f79f" +dependencies = [ + "async-io", + "core-foundation", + "fnv", + "futures", + "if-addrs", + "ipnet", + "log", + "rtnetlink", + "system-configuration", + "tokio", + "windows 0.34.0", +] + [[package]] name = "indenter" version = "0.3.3" @@ -2865,7 +3852,7 @@ checksum = "bfbcff6ae46750b15cc594bfd277b188cbddcfdc1817848f97f03f26f8625b9e" dependencies = [ "cfg-if", "js-sys", - "uuid 1.3.3", + "uuid 1.4.1", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -2882,6 +3869,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 = "inlinable_string" version = "0.1.15" @@ -2894,7 +3891,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" dependencies = [ - "bitflags", + "bitflags 1.3.2", "inotify-sys", "libc", ] @@ -2933,9 +3930,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", ] [[package]] @@ -2944,6 +3938,25 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" +[[package]] +name = "interceptor" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8a11ae2da61704edada656798b61c94b35ecac2c58eb955156987d5e6be90b" +dependencies = [ + "async-trait", + "bytes", + "log", + "rand 0.8.5", + "rtcp", + "rtp", + "thiserror", + "tokio", + "waitgroup", + "webrtc-srtp", + "webrtc-util", +] + [[package]] name = "inventory" version = "0.1.11" @@ -2972,16 +3985,28 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi 0.3.2", "libc", "windows-sys 0.48.0", ] [[package]] -name = "ipnet" -version = "2.7.2" +name = "ipconfig" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" +dependencies = [ + "socket2 0.4.9", + "widestring", + "winapi", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "ipnetwork" @@ -3003,13 +4028,12 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", + "hermit-abi 0.3.2", + "rustix 0.38.8", "windows-sys 0.48.0", ] @@ -3059,9 +4083,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jni" @@ -3112,7 +4136,7 @@ dependencies = [ "cfg-if", "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -3125,7 +4149,7 @@ dependencies = [ "ecdsa 0.16.8", "elliptic-curve 0.13.5", "once_cell", - "sha2 0.10.6", + "sha2 0.10.7", "signature 2.1.0", ] @@ -3137,9 +4161,9 @@ checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28" [[package]] name = "kqueue" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" dependencies = [ "kqueue-sys", "libc", @@ -3147,20 +4171,32 @@ dependencies = [ [[package]] name = "kqueue-sys" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", ] +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "ledger" version = "0.1.0" @@ -3211,9 +4247,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libgit2-sys" @@ -3227,12 +4263,782 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + [[package]] name = "libm" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +[[package]] +name = "libp2p" +version = "0.51.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "getrandom 0.2.10", + "instant", + "libp2p-core 0.39.0", + "libp2p-dns 0.39.0 (git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6)", + "libp2p-gossipsub 0.44.0", + "libp2p-identify", + "libp2p-mdns 0.43.0", + "libp2p-metrics 0.12.0 (git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6)", + "libp2p-mplex", + "libp2p-noise 0.42.0", + "libp2p-ping", + "libp2p-quic 0.7.0-alpha.2", + "libp2p-swarm 0.42.0", + "libp2p-tcp 0.39.0 (git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6)", + "libp2p-webrtc 0.4.0-alpha.2", + "libp2p-websocket", + "libp2p-yamux 0.43.0", + "multiaddr 0.17.0", + "pin-project", +] + +[[package]] +name = "libp2p" +version = "0.51.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f210d259724eae82005b5c48078619b7745edb7b76de370b03f8ba59ea103097" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "getrandom 0.2.10", + "instant", + "libp2p-allow-block-list", + "libp2p-connection-limits", + "libp2p-core 0.39.2", + "libp2p-dns 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-gossipsub 0.44.4", + "libp2p-identity", + "libp2p-kad", + "libp2p-mdns 0.43.1", + "libp2p-metrics 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-noise 0.42.2", + "libp2p-quic 0.7.0-alpha.3", + "libp2p-request-response", + "libp2p-swarm 0.42.2", + "libp2p-tcp 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libp2p-webrtc 0.4.0-alpha.4", + "libp2p-yamux 0.43.1", + "multiaddr 0.17.1", + "pin-project", +] + +[[package]] +name = "libp2p-allow-block-list" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510daa05efbc25184458db837f6f9a5143888f1caa742426d92e1833ddd38a50" +dependencies = [ + "libp2p-core 0.39.2", + "libp2p-identity", + "libp2p-swarm 0.42.2", + "void", +] + +[[package]] +name = "libp2p-connection-limits" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4caa33f1d26ed664c4fe2cca81a08c8e07d4c1c04f2f4ac7655c2dd85467fda0" +dependencies = [ + "libp2p-core 0.39.2", + "libp2p-identity", + "libp2p-swarm 0.42.2", + "void", +] + +[[package]] +name = "libp2p-core" +version = "0.39.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "asn1_der", + "bs58 0.4.0", + "ed25519-dalek", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "log", + "multiaddr 0.17.0", + "multihash", + "multistream-select 0.12.1 (git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6)", + "once_cell", + "parking_lot 0.12.1", + "pin-project", + "prost", + "prost-build", + "rand 0.8.5", + "rw-stream-sink 0.3.0 (git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6)", + "sec1 0.3.0", + "sha2 0.10.7", + "smallvec", + "thiserror", + "unsigned-varint", + "void", + "zeroize", +] + +[[package]] +name = "libp2p-core" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1df63c0b582aa434fb09b2d86897fa2b419ffeccf934b36f87fcedc8e835c2" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-identity", + "log", + "multiaddr 0.17.1", + "multihash", + "multistream-select 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell", + "parking_lot 0.12.1", + "pin-project", + "quick-protobuf", + "rand 0.8.5", + "rw-stream-sink 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "smallvec", + "thiserror", + "unsigned-varint", + "void", +] + +[[package]] +name = "libp2p-dns" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146ff7034daae62077c415c2376b8057368042df6ab95f5432ad5e88568b1554" +dependencies = [ + "futures", + "libp2p-core 0.39.2", + "log", + "parking_lot 0.12.1", + "smallvec", + "trust-dns-resolver", +] + +[[package]] +name = "libp2p-dns" +version = "0.39.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "futures", + "libp2p-core 0.39.0", + "log", + "parking_lot 0.12.1", + "smallvec", + "trust-dns-resolver", +] + +[[package]] +name = "libp2p-gossipsub" +version = "0.44.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "asynchronous-codec", + "base64 0.21.2", + "byteorder", + "bytes", + "fnv", + "futures", + "hex_fmt", + "instant", + "libp2p-core 0.39.0", + "libp2p-swarm 0.42.0", + "log", + "prometheus-client", + "prost", + "prost-build", + "prost-codec", + "rand 0.8.5", + "regex", + "sha2 0.10.7", + "smallvec", + "thiserror", + "unsigned-varint", + "wasm-timer", +] + +[[package]] +name = "libp2p-gossipsub" +version = "0.44.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70b34b6da8165c0bde35c82db8efda39b824776537e73973549e76cadb3a77c5" +dependencies = [ + "asynchronous-codec", + "base64 0.21.2", + "byteorder", + "bytes", + "either", + "fnv", + "futures", + "hex_fmt", + "instant", + "libp2p-core 0.39.2", + "libp2p-identity", + "libp2p-swarm 0.42.2", + "log", + "prometheus-client", + "quick-protobuf", + "quick-protobuf-codec", + "rand 0.8.5", + "regex", + "serde", + "sha2 0.10.7", + "smallvec", + "thiserror", + "unsigned-varint", + "void", + "wasm-timer", +] + +[[package]] +name = "libp2p-identify" +version = "0.42.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "asynchronous-codec", + "either", + "futures", + "futures-timer", + "libp2p-core 0.39.0", + "libp2p-swarm 0.42.0", + "log", + "lru 0.9.0", + "prost", + "prost-build", + "prost-codec", + "smallvec", + "thiserror", + "void", +] + +[[package]] +name = "libp2p-identity" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1" +dependencies = [ + "bs58 0.4.0", + "ed25519-dalek", + "log", + "multiaddr 0.17.1", + "multihash", + "quick-protobuf", + "rand 0.8.5", + "serde", + "sha2 0.10.7", + "thiserror", + "zeroize", +] + +[[package]] +name = "libp2p-kad" +version = "0.43.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39d5ef876a2b2323d63c258e63c2f8e36f205fe5a11f0b3095d59635650790ff" +dependencies = [ + "arrayvec", + "asynchronous-codec", + "bytes", + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core 0.39.2", + "libp2p-identity", + "libp2p-swarm 0.42.2", + "log", + "quick-protobuf", + "rand 0.8.5", + "serde", + "sha2 0.10.7", + "smallvec", + "thiserror", + "uint", + "unsigned-varint", + "void", +] + +[[package]] +name = "libp2p-mdns" +version = "0.43.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "data-encoding", + "futures", + "if-watch", + "libp2p-core 0.39.0", + "libp2p-swarm 0.42.0", + "log", + "rand 0.8.5", + "smallvec", + "socket2 0.4.9", + "tokio", + "trust-dns-proto", + "void", +] + +[[package]] +name = "libp2p-mdns" +version = "0.43.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19983e1f949f979a928f2c603de1cf180cc0dc23e4ac93a62651ccb18341460b" +dependencies = [ + "data-encoding", + "futures", + "if-watch", + "libp2p-core 0.39.2", + "libp2p-identity", + "libp2p-swarm 0.42.2", + "log", + "rand 0.8.5", + "smallvec", + "socket2 0.4.9", + "tokio", + "trust-dns-proto", + "void", +] + +[[package]] +name = "libp2p-metrics" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a42ec91e227d7d0dafa4ce88b333cdf5f277253873ab087555c92798db2ddd46" +dependencies = [ + "libp2p-core 0.39.2", + "libp2p-gossipsub 0.44.4", + "libp2p-kad", + "libp2p-swarm 0.42.2", + "prometheus-client", +] + +[[package]] +name = "libp2p-metrics" +version = "0.12.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "libp2p-core 0.39.0", + "libp2p-gossipsub 0.44.0", + "libp2p-identify", + "libp2p-ping", + "libp2p-swarm 0.42.0", + "prometheus-client", +] + +[[package]] +name = "libp2p-mplex" +version = "0.39.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "asynchronous-codec", + "bytes", + "futures", + "libp2p-core 0.39.0", + "log", + "nohash-hasher", + "parking_lot 0.12.1", + "rand 0.8.5", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "libp2p-noise" +version = "0.42.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "bytes", + "curve25519-dalek 3.2.0", + "futures", + "libp2p-core 0.39.0", + "log", + "once_cell", + "prost", + "prost-build", + "rand 0.8.5", + "sha2 0.10.7", + "snow", + "static_assertions", + "thiserror", + "x25519-dalek 1.1.1", + "zeroize", +] + +[[package]] +name = "libp2p-noise" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3673da89d29936bc6435bafc638e2f184180d554ce844db65915113f86ec5e" +dependencies = [ + "bytes", + "curve25519-dalek 3.2.0", + "futures", + "libp2p-core 0.39.2", + "libp2p-identity", + "log", + "once_cell", + "quick-protobuf", + "rand 0.8.5", + "sha2 0.10.7", + "snow", + "static_assertions", + "thiserror", + "x25519-dalek 1.1.1", + "zeroize", +] + +[[package]] +name = "libp2p-ping" +version = "0.42.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "either", + "futures", + "futures-timer", + "instant", + "libp2p-core 0.39.0", + "libp2p-swarm 0.42.0", + "log", + "rand 0.8.5", + "void", +] + +[[package]] +name = "libp2p-quic" +version = "0.7.0-alpha.2" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "if-watch", + "libp2p-core 0.39.0", + "libp2p-tls 0.1.0-alpha.2", + "log", + "parking_lot 0.12.1", + "quinn-proto", + "rand 0.8.5", + "rustls 0.20.8", + "thiserror", + "tokio", +] + +[[package]] +name = "libp2p-quic" +version = "0.7.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b26abd81cd2398382a1edfe739b539775be8a90fa6914f39b2ab49571ec735" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "if-watch", + "libp2p-core 0.39.2", + "libp2p-identity", + "libp2p-tls 0.1.0", + "log", + "parking_lot 0.12.1", + "quinn-proto", + "rand 0.8.5", + "rustls 0.20.8", + "thiserror", + "tokio", +] + +[[package]] +name = "libp2p-request-response" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffdb374267d42dc5ed5bc53f6e601d4a64ac5964779c6e40bb9e4f14c1e30d5" +dependencies = [ + "async-trait", + "futures", + "instant", + "libp2p-core 0.39.2", + "libp2p-identity", + "libp2p-swarm 0.42.2", + "rand 0.8.5", + "smallvec", +] + +[[package]] +name = "libp2p-swarm" +version = "0.42.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core 0.39.0", + "libp2p-swarm-derive 0.32.0 (git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6)", + "log", + "rand 0.8.5", + "smallvec", + "tokio", + "void", +] + +[[package]] +name = "libp2p-swarm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "903b3d592d7694e56204d211f29d31bc004be99386644ba8731fc3e3ef27b296" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core 0.39.2", + "libp2p-identity", + "libp2p-swarm-derive 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "rand 0.8.5", + "smallvec", + "tokio", + "void", +] + +[[package]] +name = "libp2p-swarm-derive" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fba456131824ab6acd4c7bf61e9c0f0a3014b5fc9868ccb8e10d344594cdc4f" +dependencies = [ + "heck 0.4.1", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "libp2p-swarm-derive" +version = "0.32.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "heck 0.4.1", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "libp2p-tcp" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d33698596d7722d85d3ab0c86c2c322254fce1241e91208e3679b4eb3026cf" +dependencies = [ + "futures", + "futures-timer", + "if-watch", + "libc", + "libp2p-core 0.39.2", + "log", + "socket2 0.4.9", + "tokio", +] + +[[package]] +name = "libp2p-tcp" +version = "0.39.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "futures", + "futures-timer", + "if-watch", + "libc", + "libp2p-core 0.39.0", + "log", + "socket2 0.4.9", + "tokio", +] + +[[package]] +name = "libp2p-tls" +version = "0.1.0-alpha.2" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "futures", + "futures-rustls", + "libp2p-core 0.39.0", + "rcgen 0.10.0", + "ring", + "rustls 0.20.8", + "thiserror", + "webpki 0.22.0", + "x509-parser 0.14.0", + "yasna", +] + +[[package]] +name = "libp2p-tls" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" +dependencies = [ + "futures", + "futures-rustls", + "libp2p-core 0.39.2", + "libp2p-identity", + "rcgen 0.10.0", + "ring", + "rustls 0.20.8", + "thiserror", + "webpki 0.22.0", + "x509-parser 0.14.0", + "yasna", +] + +[[package]] +name = "libp2p-webrtc" +version = "0.4.0-alpha.2" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "async-trait", + "asynchronous-codec", + "bytes", + "futures", + "futures-timer", + "hex", + "if-watch", + "libp2p-core 0.39.0", + "libp2p-noise 0.42.0", + "log", + "multihash", + "prost", + "prost-build", + "prost-codec", + "rand 0.8.5", + "rcgen 0.9.3", + "serde", + "stun", + "thiserror", + "tinytemplate", + "tokio", + "tokio-util", + "webrtc", +] + +[[package]] +name = "libp2p-webrtc" +version = "0.4.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba48592edbc2f60b4bc7c10d65445b0c3964c07df26fdf493b6880d33be36f8" +dependencies = [ + "async-trait", + "asynchronous-codec", + "bytes", + "futures", + "futures-timer", + "hex", + "if-watch", + "libp2p-core 0.39.2", + "libp2p-identity", + "libp2p-noise 0.42.2", + "log", + "multihash", + "quick-protobuf", + "quick-protobuf-codec", + "rand 0.8.5", + "rcgen 0.9.3", + "serde", + "stun", + "thiserror", + "tinytemplate", + "tokio", + "tokio-util", + "webrtc", +] + +[[package]] +name = "libp2p-websocket" +version = "0.41.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "either", + "futures", + "futures-rustls", + "libp2p-core 0.39.0", + "log", + "parking_lot 0.12.1", + "quicksink", + "rw-stream-sink 0.3.0 (git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6)", + "soketto", + "url", + "webpki-roots 0.22.6", +] + +[[package]] +name = "libp2p-yamux" +version = "0.43.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "futures", + "libp2p-core 0.39.0", + "log", + "thiserror", + "yamux", +] + +[[package]] +name = "libp2p-yamux" +version = "0.43.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd21d950662700a385d4c6d68e2f5f54d778e97068cdd718522222ef513bda" +dependencies = [ + "futures", + "libp2p-core 0.39.2", + "log", + "thiserror", + "yamux", +] + +[[package]] +name = "librocksdb-sys" +version = "0.11.0+8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "lz4-sys", + "zstd-sys", +] + [[package]] name = "libsqlite3-sys" version = "0.24.2" @@ -3246,9 +5052,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.9" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "libc", @@ -3257,10 +5063,22 @@ dependencies = [ ] [[package]] -name = "linux-raw-sys" -version = "0.3.7" +name = "linked-hash-map" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "lioness" @@ -3275,10 +5093,28 @@ dependencies = [ ] [[package]] -name = "lock_api" -version = "0.4.9" +name = "local-channel" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c" +dependencies = [ + "futures-core", + "futures-sink", + "futures-util", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg 1.1.0", "scopeguard", @@ -3286,12 +5122,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "loom" @@ -3308,6 +5141,43 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "lru" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17" +dependencies = [ + "hashbrown 0.13.2", +] + +[[package]] +name = "lru" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e" +dependencies = [ + "hashbrown 0.13.2", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "lz4-sys" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "macro_rules_attribute" version = "0.1.3" @@ -3324,13 +5194,19 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58093314a45e00c77d5c508f76e77c3396afbbc0d01506e7fae47b018bac2b1d" +[[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -3341,9 +5217,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matchit" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" +checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef" [[package]] name = "maxminddb" @@ -3357,6 +5233,15 @@ dependencies = [ "serde", ] +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "memchr" version = "2.5.0" @@ -3365,9 +5250,18 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.8.0" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg 1.1.0", ] @@ -3378,6 +5272,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -3395,14 +5299,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -3425,6 +5329,115 @@ dependencies = [ "version_check", ] +[[package]] +name = "multiaddr" +version = "0.17.0" +source = "git+https://github.com/ChainSafe/rust-multiaddr.git?rev=2d21365f880622adf1c8e594b72e671dbac9dd64#2d21365f880622adf1c8e594b72e671dbac9dd64" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multiaddr" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "log", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +dependencies = [ + "core2", + "digest 0.10.7", + "multihash-derive", + "serde", + "serde-big-array", + "sha2 0.10.7", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "multistream-select" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" +dependencies = [ + "bytes", + "futures", + "log", + "pin-project", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "multistream-select" +version = "0.12.1" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "bytes", + "futures", + "log", + "pin-project", + "smallvec", + "unsigned-varint", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -3443,12 +5456,96 @@ dependencies = [ "tempfile", ] +[[package]] +name = "netlink-packet-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror", +] + +[[package]] +name = "netlink-proto" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" +dependencies = [ + "bytes", + "futures", + "libc", + "log", + "tokio", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + [[package]] name = "no-std-compat" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "nom" version = "7.1.3" @@ -3465,7 +5562,7 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossbeam-channel", "filetime", "fsevent-sys", @@ -3496,6 +5593,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -3519,41 +5627,47 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg 1.1.0", - "libm", + "libm 0.2.7", ] [[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 0.3.2", "libc", ] [[package]] name = "nym-api" -version = "1.1.24" +version = "1.1.28" dependencies = [ + "actix-web", "anyhow", + "array-bytes", "async-trait", "bip39", "bs58 0.4.0", "cfg-if", - "clap 4.2.7", + "chrono", + "clap 4.3.21", "console-subscriber", "cosmwasm-std", "cw-utils", + "cw2", "cw3", "cw4", "dirs 4.0.0", + "ephemera", "futures", + "futures-util", "getset", "humantime-serde", "lazy_static", @@ -3571,6 +5685,7 @@ dependencies = [ "nym-credentials", "nym-crypto", "nym-dkg", + "nym-ephemera-common", "nym-gateway-client", "nym-inclusion-probability", "nym-mixnet-contract-common", @@ -3595,15 +5710,18 @@ dependencies = [ "rocket_okapi", "schemars", "serde", + "serde_derive", "serde_json", "sqlx 0.6.3", "tap", + "tempfile", "thiserror", - "time 0.3.21", + "time 0.3.25", "tokio", "tokio-stream", "ts-rs", "url", + "uuid 1.4.1", "zeroize", ] @@ -3643,7 +5761,7 @@ name = "nym-bin-common" version = "0.6.0" dependencies = [ "atty", - "clap 4.2.7", + "clap 4.3.21", "clap_complete", "clap_complete_fig", "log", @@ -3676,13 +5794,13 @@ dependencies = [ [[package]] name = "nym-cli" -version = "1.1.23" +version = "1.1.27" dependencies = [ "anyhow", "base64 0.13.1", "bip39", "bs58 0.4.0", - "clap 4.2.7", + "clap 4.3.21", "clap_complete", "clap_complete_fig", "dotenvy", @@ -3702,11 +5820,12 @@ dependencies = [ name = "nym-cli-commands" version = "1.0.0" dependencies = [ + "anyhow", "base64 0.13.1", "bip39", "bs58 0.4.0", "cfg-if", - "clap 4.2.7", + "clap 4.3.21", "comfy-table", "cosmrs", "cosmwasm-std", @@ -3715,10 +5834,16 @@ dependencies = [ "humantime-serde", "k256 0.13.1", "log", + "nym-bandwidth-controller", "nym-bin-common", + "nym-client-core", "nym-coconut-bandwidth-contract-common", "nym-coconut-dkg-common", + "nym-config", "nym-contracts-common", + "nym-credential-storage", + "nym-credential-utils", + "nym-credentials", "nym-crypto", "nym-mixnet-contract-common", "nym-multisig-contract-common", @@ -3735,16 +5860,16 @@ dependencies = [ "serde_json", "tap", "thiserror", - "time 0.3.21", + "time 0.3.25", "toml 0.5.11", "url", ] [[package]] name = "nym-client" -version = "1.1.23" +version = "1.1.27" dependencies = [ - "clap 4.2.7", + "clap 4.3.21", "dirs 4.0.0", "futures", "lazy_static", @@ -3782,7 +5907,8 @@ version = "1.1.15" dependencies = [ "async-trait", "base64 0.21.2", - "dashmap 5.4.0", + "cfg-if", + "dashmap 5.5.0", "dirs 4.0.0", "futures", "gloo-timers", @@ -3792,6 +5918,7 @@ dependencies = [ "nym-config", "nym-credential-storage", "nym-crypto", + "nym-explorer-api-requests", "nym-gateway-client", "nym-gateway-requests", "nym-network-defaults", @@ -3802,14 +5929,15 @@ dependencies = [ "nym-topology", "nym-validator-client", "rand 0.7.3", + "reqwest", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "sqlx 0.6.3", "tap", "tempfile", "thiserror", - "time 0.3.21", + "time 0.3.25", "tokio", "tokio-stream", "tokio-tungstenite 0.14.0", @@ -3817,8 +5945,8 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-timer", "wasm-utils", + "wasmtimer", "zeroize", ] @@ -3858,22 +5986,21 @@ dependencies = [ name = "nym-coconut-bandwidth-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw2", "nym-multisig-contract-common", - "schemars", - "serde", ] [[package]] name = "nym-coconut-dkg-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-utils", "nym-contracts-common", "nym-multisig-contract-common", - "schemars", - "serde", ] [[package]] @@ -3896,7 +6023,7 @@ dependencies = [ "log", "nym-network-defaults", "serde", - "toml 0.7.4", + "toml 0.7.6", "url", ] @@ -3905,6 +6032,7 @@ name = "nym-contracts-common" version = "0.5.0" dependencies = [ "bs58 0.4.0", + "cosmwasm-schema", "cosmwasm-std", "schemars", "serde", @@ -3912,33 +6040,28 @@ dependencies = [ "thiserror", ] -[[package]] -name = "nym-credential-client" -version = "0.1.0" -dependencies = [ - "clap 4.2.7", - "log", - "nym-bandwidth-controller", - "nym-bin-common", - "nym-client-core", - "nym-config", - "nym-credential-storage", - "nym-credentials", - "nym-network-defaults", - "nym-pemstore", - "nym-validator-client", - "serde", - "thiserror", - "tokio", -] - [[package]] name = "nym-credential-storage" version = "0.1.0" dependencies = [ "async-trait", "log", - "sqlx 0.5.13", + "sqlx 0.5.11", + "thiserror", + "tokio", +] + +[[package]] +name = "nym-credential-utils" +version = "0.1.0" +dependencies = [ + "log", + "nym-bandwidth-controller", + "nym-client-core", + "nym-config", + "nym-credential-storage", + "nym-credentials", + "nym-validator-client", "thiserror", "tokio", ] @@ -3949,6 +6072,7 @@ version = "0.1.0" dependencies = [ "bls12_381 0.5.0", "cosmrs", + "log", "nym-api-requests", "nym-coconut-interface", "nym-crypto", @@ -3961,7 +6085,7 @@ dependencies = [ name = "nym-crypto" version = "0.4.0" dependencies = [ - "aes 0.8.2", + "aes 0.8.3", "blake3", "bs58 0.4.0", "cipher 0.4.4", @@ -3979,7 +6103,7 @@ dependencies = [ "serde_bytes", "subtle-encoding", "thiserror", - "x25519-dalek", + "x25519-dalek 1.1.1", "zeroize", ] @@ -4006,6 +6130,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nym-ephemera-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "nym-contracts-common", +] + [[package]] name = "nym-execute" version = "0.1.0" @@ -4014,16 +6148,28 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "nym-explorer-api-requests" +version = "0.1.0" +dependencies = [ + "nym-api-requests", + "nym-contracts-common", + "nym-mixnet-contract-common", + "schemars", + "serde", + "ts-rs", +] + [[package]] name = "nym-gateway" -version = "1.1.23" +version = "1.1.27" dependencies = [ "anyhow", "async-trait", "atty", "bip39", "bs58 0.4.0", - "clap 4.2.7", + "clap 4.3.21", "colored", "dashmap 4.0.2", "dirs 4.0.0", @@ -4053,7 +6199,7 @@ dependencies = [ "rand 0.7.3", "serde", "serde_json", - "sqlx 0.5.13", + "sqlx 0.5.11", "subtle-encoding", "thiserror", "tokio", @@ -4091,8 +6237,8 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-timer", "wasm-utils", + "wasmtimer", ] [[package]] @@ -4153,28 +6299,30 @@ name = "nym-mixnet-contract-common" version = "0.6.0" dependencies = [ "bs58 0.4.0", + "cosmwasm-schema", "cosmwasm-std", + "cw2", "humantime-serde", "log", "nym-contracts-common", "rand_chacha 0.3.1", "schemars", "serde", - "serde-json-wasm 0.4.1", + "serde-json-wasm", "serde_repr", "thiserror", - "time 0.3.21", + "time 0.3.25", "ts-rs", ] [[package]] name = "nym-mixnode" -version = "1.1.24" +version = "1.1.28" dependencies = [ "anyhow", "bs58 0.4.0", "cfg-if", - "clap 4.2.7", + "clap 4.3.21", "colored", "cpu-cycles", "cupid", @@ -4261,11 +6409,12 @@ dependencies = [ name = "nym-name-service-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-utils", + "cw2", "nym-contracts-common", - "schemars", "serde", "thiserror", ] @@ -4278,6 +6427,7 @@ dependencies = [ "dotenvy", "hex-literal", "once_cell", + "schemars", "serde", "thiserror", "url", @@ -4285,13 +6435,13 @@ dependencies = [ [[package]] name = "nym-network-requester" -version = "1.1.23" +version = "1.1.27" dependencies = [ "anyhow", "async-file-watcher", "async-trait", "bs58 0.4.0", - "clap 4.2.7", + "clap 4.3.21", "dirs 4.0.0", "futures", "humantime-serde", @@ -4332,7 +6482,7 @@ dependencies = [ [[package]] name = "nym-network-statistics" -version = "1.1.23" +version = "1.1.27" dependencies = [ "dirs 4.0.0", "log", @@ -4342,7 +6492,7 @@ dependencies = [ "pretty_env_logger", "rocket", "serde", - "sqlx 0.5.13", + "sqlx 0.5.11", "thiserror", "tokio", ] @@ -4370,12 +6520,10 @@ dependencies = [ name = "nym-nonexhaustive-delayqueue" version = "0.1.0" dependencies = [ - "futures-core", - "slab", "tokio", "tokio-stream", "tokio-util", - "wasm-timer", + "wasmtimer", ] [[package]] @@ -4383,7 +6531,7 @@ name = "nym-nr-query" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.2.7", + "clap 4.3.21", "log", "nym-bin-common", "nym-network-defaults", @@ -4407,11 +6555,11 @@ name = "nym-outfox" version = "0.1.0" dependencies = [ "blake3", - "chacha20", - "chacha20poly1305", + "chacha20 0.9.1", + "chacha20poly1305 0.10.1", "criterion", - "curve25519-dalek", - "fastrand", + "curve25519-dalek 3.2.0", + "fastrand 1.9.0", "getrandom 0.2.10", "log", "rand 0.7.3", @@ -4425,21 +6573,26 @@ dependencies = [ name = "nym-pemstore" version = "0.3.0" dependencies = [ - "pem", + "pem 0.8.3", ] [[package]] name = "nym-sdk" version = "0.1.0" dependencies = [ + "anyhow", + "async-trait", "bip39", "dotenvy", "futures", + "hex", + "libp2p 0.51.0", "log", "nym-bandwidth-controller", "nym-bin-common", "nym-client-core", "nym-credential-storage", + "nym-credential-utils", "nym-credentials", "nym-crypto", "nym-gateway-requests", @@ -4449,12 +6602,15 @@ dependencies = [ "nym-task", "nym-topology", "nym-validator-client", + "parking_lot 0.12.1", "pretty_env_logger", "rand 0.7.3", "reqwest", "tap", "thiserror", "tokio", + "tokio-stream", + "tokio-util", "toml 0.5.11", "url", ] @@ -4463,12 +6619,12 @@ dependencies = [ name = "nym-service-provider-directory-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-utils", + "cw2", "nym-contracts-common", - "schemars", - "serde", "thiserror", ] @@ -4491,9 +6647,9 @@ dependencies = [ [[package]] name = "nym-socks5-client" -version = "1.1.23" +version = "1.1.27" dependencies = [ - "clap 4.2.7", + "clap 4.3.21", "lazy_static", "log", "nym-bin-common", @@ -4529,7 +6685,9 @@ dependencies = [ "nym-bandwidth-controller", "nym-client-core", "nym-config", + "nym-contracts-common", "nym-credential-storage", + "nym-mixnet-contract-common", "nym-network-defaults", "nym-service-providers-common", "nym-socks5-proxy-helpers", @@ -4539,10 +6697,13 @@ dependencies = [ "nym-validator-client", "pin-project", "rand 0.7.3", + "reqwest", + "schemars", "serde", "tap", "thiserror", "tokio", + "url", ] [[package]] @@ -4756,7 +6917,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "sqlx 0.5.13", + "sqlx 0.5.11", "thiserror", "tokio", ] @@ -4765,7 +6926,7 @@ dependencies = [ name = "nym-store-cipher" version = "0.1.0" dependencies = [ - "aes-gcm", + "aes-gcm 0.10.2", "argon2", "generic-array 0.14.7", "getrandom 0.2.10", @@ -4818,7 +6979,6 @@ dependencies = [ "nym-config", "nym-mixnet-contract-common", "nym-validator-client", - "nym-vesting-contract", "nym-vesting-contract-common", "reqwest", "schemars", @@ -4841,6 +7001,9 @@ dependencies = [ "colored", "cosmrs", "cosmwasm-std", + "cw-controllers", + "cw-utils", + "cw2", "cw3", "cw4", "eyre", @@ -4854,13 +7017,13 @@ dependencies = [ "nym-coconut-interface", "nym-config", "nym-contracts-common", + "nym-ephemera-common", "nym-group-contract-common", "nym-mixnet-contract-common", "nym-multisig-contract-common", "nym-name-service-common", "nym-network-defaults", "nym-service-provider-directory-common", - "nym-vesting-contract", "nym-vesting-contract-common", "openssl", "prost", @@ -4873,36 +7036,21 @@ dependencies = [ "tokio", "ts-rs", "url", + "wasmtimer", "zeroize", ] -[[package]] -name = "nym-vesting-contract" -version = "1.4.0" -dependencies = [ - "cosmwasm-derive", - "cosmwasm-std", - "cw-storage-plus", - "cw2", - "nym-contracts-common", - "nym-mixnet-contract-common", - "nym-vesting-contract-common", - "schemars", - "semver 1.0.17", - "serde", - "thiserror", - "vergen", -] - [[package]] name = "nym-vesting-contract-common" version = "0.7.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw2", "nym-contracts-common", "nym-mixnet-contract-common", - "schemars", "serde", + "thiserror", "ts-rs", ] @@ -4918,7 +7066,6 @@ dependencies = [ "nym-network-defaults", "nym-types", "nym-validator-client", - "nym-vesting-contract", "nym-vesting-contract-common", "serde", "serde_json", @@ -4926,6 +7073,33 @@ dependencies = [ "ts-rs", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a" +dependencies = [ + "asn1-rs 0.3.1", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs 0.5.2", +] + [[package]] name = "okapi" version = "0.7.0-rc.1" @@ -4940,9 +7114,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oorandom" @@ -4964,11 +7138,11 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -4985,7 +7159,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -4996,18 +7170,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.25.3+1.1.1t" +version = "111.27.0+1.1.1v" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924757a6a226bf60da5f7dd0311a34d2b52283dd82ddeb103208ddc66362f80c" +checksum = "06e8f197c82d7511c5b014030c9b1efeda40d7d5f99d23b4ceed3524a5e63f02" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" dependencies = [ "cc", "libc", @@ -5077,9 +7251,9 @@ dependencies = [ "fnv", "futures-channel", "futures-util", - "indexmap", + "indexmap 1.9.3", "once_cell", - "pin-project-lite", + "pin-project-lite 0.2.12", "thiserror", "urlencoding", ] @@ -5092,7 +7266,7 @@ checksum = "8b3a2a91fdbfdd4d212c0dcc2ab540de2c2bcbbd90be17de7a7daf8822d010c1" dependencies = [ "async-trait", "crossbeam-channel", - "dashmap 5.4.0", + "dashmap 5.5.0", "fnv", "futures-channel", "futures-executor", @@ -5123,9 +7297,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "overload" @@ -5133,6 +7307,38 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.7", +] + +[[package]] +name = "p384" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" +dependencies = [ + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.7", +] + +[[package]] +name = "packed_simd_2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +dependencies = [ + "cfg-if", + "libm 0.1.4", +] + [[package]] name = "pairing" version = "0.20.0" @@ -5175,7 +7381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", + "parking_lot_core 0.9.8", ] [[package]] @@ -5194,15 +7400,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.45.0", + "windows-targets 0.48.1", ] [[package]] @@ -5218,9 +7424,15 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pbkdf2" @@ -5234,27 +7446,33 @@ dependencies = [ [[package]] name = "pear" -version = "0.2.4" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec95680a7087503575284e5063e14b694b7a9c0b065e5dceec661e0497127e8" +checksum = "61a386cd715229d399604b50d1361683fe687066f42d56f54be995bc6868f71c" dependencies = [ "inlinable_string", "pear_codegen", - "yansi", + "yansi 1.0.0-rc.1", ] [[package]] name = "pear_codegen" -version = "0.2.4" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9661a3a53f93f09f2ea882018e4d7c88f6ff2956d809a276060476fd8c879d3c" +checksum = "da9f0f13dac8069c139e8300a6510e3f4143ecf5259c60b116a9b271b4ca0d54" dependencies = [ "proc-macro2", - "proc-macro2-diagnostics 0.10.0", + "proc-macro2-diagnostics", "quote", - "syn 2.0.16", + "syn 2.0.28", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "peg" version = "0.7.0" @@ -5294,16 +7512,34 @@ dependencies = [ ] [[package]] -name = "percent-encoding" -version = "2.2.0" +name = "pem" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.6.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" +checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" dependencies = [ "thiserror", "ucd-trie", @@ -5311,9 +7547,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.6.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" +checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" dependencies = [ "pest", "pest_generator", @@ -5321,53 +7557,69 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.6.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" +checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] name = "pest_meta" -version = "2.6.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" +checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" dependencies = [ "once_cell", "pest", - "sha2 0.10.6", + "sha2 0.10.7", +] + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.0.0", ] [[package]] name = "pin-project" -version = "1.1.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + +[[package]] +name = "pin-project-lite" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -5391,7 +7643,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.7", + "der 0.7.8", "spki 0.7.2", ] @@ -5402,10 +7654,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] -name = "plotters" -version = "0.3.4" +name = "platforms" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", @@ -5416,15 +7674,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] @@ -5436,15 +7694,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg 1.1.0", - "bitflags", + "bitflags 1.3.2", "cfg-if", "concurrent-queue", "libc", "log", - "pin-project-lite", + "pin-project-lite 0.2.12", "windows-sys 0.48.0", ] +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.4.1", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -5453,19 +7722,31 @@ checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.5.1", ] [[package]] name = "polyval" -version = "0.6.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ "cfg-if", "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.4.1", +] + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.5.1", ] [[package]] @@ -5494,6 +7775,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prettyplease" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" +dependencies = [ + "proc-macro2", + "syn 2.0.28", +] + +[[package]] +name = "proc-macro-crate" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +dependencies = [ + "thiserror", + "toml 0.5.11", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -5520,37 +7821,47 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "proc-macro2-diagnostics" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", + "version_check", + "yansi 1.0.0-rc.1", +] + +[[package]] +name = "prometheus-client" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6fa99d535dd930d1249e6c79cb3c2915f9172a540fe2b02a4c8f9ca954721e" +dependencies = [ + "dtoa", + "itoa", + "parking_lot 0.12.1", + "prometheus-client-derive-encode", +] + +[[package]] +name = "prometheus-client-derive-encode" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b6a5217beb0ad503ee7fa752d451c905113d70721b937126158f3106a48cc1" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "version_check", - "yansi", -] - -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "606c4ba35817e2922a308af55ad51bab3645b59eae5c570d4a6cf07e36bd493b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", - "version_check", - "yansi", ] [[package]] @@ -5563,6 +7874,40 @@ dependencies = [ "prost-derive", ] +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes", + "heck 0.4.1", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease 0.1.25", + "prost", + "prost-types", + "regex", + "syn 1.0.109", + "tempfile", + "which", +] + +[[package]] +name = "prost-codec" +version = "0.3.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "asynchronous-codec", + "bytes", + "prost", + "thiserror", + "unsigned-varint", +] + [[package]] name = "prost-derive" version = "0.11.9" @@ -5609,10 +7954,61 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] -name = "quote" -version = "1.0.27" +name = "quick-protobuf" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + +[[package]] +name = "quick-protobuf-codec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1693116345026436eb2f10b677806169c1a1260c1c60eaaffe3fb5a29ae23d8b" +dependencies = [ + "asynchronous-codec", + "bytes", + "quick-protobuf", + "thiserror", + "unsigned-varint", +] + +[[package]] +name = "quicksink" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77de3c815e5a160b1539c6592796801df2043ae35e123b46d73380cfa57af858" +dependencies = [ + "futures-core", + "futures-sink", + "pin-project-lite 0.1.12", +] + +[[package]] +name = "quinn-proto" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" +dependencies = [ + "bytes", + "rand 0.8.5", + "ring", + "rustc-hash", + "rustls 0.20.8", + "slab", + "thiserror", + "tinyvec", + "tracing", + "webpki 0.22.0", +] + +[[package]] +name = "quote" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -5850,6 +8246,31 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "rcgen" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" +dependencies = [ + "pem 1.1.1", + "ring", + "time 0.3.25", + "x509-parser 0.13.2", + "yasna", +] + +[[package]] +name = "rcgen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem 1.1.1", + "ring", + "time 0.3.25", + "yasna", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -5865,7 +8286,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -5874,7 +8295,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -5890,33 +8311,78 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.16" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43faa91b1c8b36841ee70e97188a869d37ae21759da6846d4be66de5bf7b12c" +checksum = "61ef7e18e8841942ddb1cf845054f8008410030a3997875d9e49b7a363063df1" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.16" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d2275aab483050ab2a7364c1a46604865ee7d6906684e08db0f090acf74f9e7" +checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", +] + +[[package]] +name = "refinery" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb0436d0dd7bd8d4fce1e828751fa79742b08e35f27cfea7546f8a322b5ef24" +dependencies = [ + "refinery-core", + "refinery-macros", +] + +[[package]] +name = "refinery-core" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19206547cd047e8f4dfa6b20c30d3ecaf24be05841b6aa0aa926a47a3d0662bb" +dependencies = [ + "async-trait", + "cfg-if", + "lazy_static", + "log", + "regex", + "rusqlite", + "serde", + "siphasher", + "thiserror", + "time 0.3.25", + "toml 0.7.6", + "url", + "walkdir", +] + +[[package]] +name = "refinery-macros" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d94d4b9241859ba19eaa5c04c86e782eb3aa0aae2c5868e0cfa90c856e58a174" +dependencies = [ + "proc-macro2", + "quote", + "refinery-core", + "regex", + "syn 2.0.28", ] [[package]] name = "regex" -version = "1.8.4" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.2", + "regex-automata 0.3.6", + "regex-syntax 0.7.4", ] [[package]] @@ -5928,6 +8394,17 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -5936,9 +8413,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" @@ -5963,7 +8440,7 @@ dependencies = [ "native-tls", "once_cell", "percent-encoding", - "pin-project-lite", + "pin-project-lite 0.2.12", "serde", "serde_json", "serde_urlencoded", @@ -5978,6 +8455,16 @@ dependencies = [ "winreg", ] +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error 1.2.3", +] + [[package]] name = "rfc6979" version = "0.3.1" @@ -6025,26 +8512,26 @@ dependencies = [ [[package]] name = "rocket" -version = "0.5.0-rc.2" +version = "0.5.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ead083fce4a405feb349cf09abdf64471c6077f14e0ce59364aa90d4b99317" +checksum = "58734f7401ae5cfd129685b48f61182331745b357b96f2367f01aebaf1cc9cc9" dependencies = [ "async-stream", "async-trait", "atomic", - "atty", "binascii", "bytes", "either", "figment", "futures", - "indexmap", + "indexmap 1.9.3", + "is-terminal", "log", "memchr", "multer", "num_cpus", "parking_lot 0.12.1", - "pin-project-lite", + "pin-project-lite 0.2.12", "rand 0.8.5", "ref-cast", "rocket_codegen", @@ -6053,28 +8540,28 @@ dependencies = [ "serde_json", "state", "tempfile", - "time 0.3.21", + "time 0.3.25", "tokio", "tokio-stream", "tokio-util", "ubyte", "version_check", - "yansi", + "yansi 0.5.1", ] [[package]] name = "rocket_codegen" -version = "0.5.0-rc.2" +version = "0.5.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6aeb6bb9c61e9cd2c00d70ea267bf36f76a4cc615e5908b349c2f9d93999b47" +checksum = "7093353f14228c744982e409259fb54878ba9563d08214f2d880d59ff2fc508b" dependencies = [ "devise", "glob", - "indexmap", + "indexmap 1.9.3", "proc-macro2", "quote", "rocket_http", - "syn 1.0.109", + "syn 2.0.28", "unicode-xid", ] @@ -6095,36 +8582,36 @@ dependencies = [ [[package]] name = "rocket_http" -version = "0.5.0-rc.2" +version = "0.5.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ded65d127954de3c12471630bf4b81a2792f065984461e65b91d0fdaafc17a2" +checksum = "936012c99162a03a67f37f9836d5f938f662e26f2717809761a9ac46432090f4" dependencies = [ - "cookie", + "cookie 0.17.0", "either", "futures", "http", "hyper", - "indexmap", + "indexmap 1.9.3", "log", "memchr", "pear", "percent-encoding", - "pin-project-lite", + "pin-project-lite 0.2.12", "ref-cast", "serde", "smallvec", "stable-pattern", "state", - "time 0.3.21", + "time 0.3.25", "tokio", "uncased", ] [[package]] name = "rocket_okapi" -version = "0.8.0-rc.2" +version = "0.8.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489f4f5b120762f7974e65b919fc462d0660fd8b839026d8985b850fe5acccb0" +checksum = "742098674565c8f0c35c77444f90344aafedebb71cfee9cdbf0185acc6b9cdb7" dependencies = [ "either", "log", @@ -6138,11 +8625,11 @@ dependencies = [ [[package]] name = "rocket_okapi_codegen" -version = "0.8.0-rc.2" +version = "0.8.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54f94d1ffe41472e08463d7a2674f1db04dc4df745285e8369b33d3cfd6b0308" +checksum = "8c43f8edc57d88750a220b0ec1870a36c1106204ec99cc35131b49de3b954a4a" dependencies = [ - "darling", + "darling 0.13.4", "proc-macro2", "quote", "rocket_http", @@ -6165,10 +8652,125 @@ dependencies = [ "nym-validator-client", "serde", "serde_json", +] + +[[package]] +name = "rocksdb" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6f170a4041d50a0ce04b0d2e14916d6ca863ea2e422689a5b694395d299ffe" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rtcp" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1919efd6d4a6a85d13388f9487549bb8e359f17198cc03ffd72f79b553873691" +dependencies = [ + "bytes", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "rtnetlink" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" +dependencies = [ + "futures", + "log", + "netlink-packet-route", + "netlink-proto", + "nix", + "thiserror", +>>>>>>> 5a8bad45036735f9d088a96490435721179465e7 "tokio", ] [[package]] +<<<<<<< HEAD +======= +name = "rtp" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a095411ff00eed7b12e4c6a118ba984d113e1079582570d56a5ee723f11f80" +dependencies = [ + "async-trait", + "bytes", + "rand 0.8.5", + "serde", + "thiserror", + "webrtc-util", +] + +[[package]] +name = "rusqlite" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a" +dependencies = [ + "bitflags 1.3.2", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink 0.7.0", + "libsqlite3-sys", + "memchr", + "smallvec", +] + +[[package]] +name = "rust-embed" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b68543d5527e158213414a92832d2aab11a84d2571a5eb021ebe22c43aab066" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "6.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4e0f0ced47ded9a68374ac145edd65a6c1fa13a96447b873660b2a568a0fd7" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "shellexpand", + "syn 1.0.109", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512b0ab6853f7e14e3c8754acb43d6f748bb9ced66aa5915a6553ac8213f7731" +dependencies = [ + "sha2 0.10.7", + "walkdir", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +>>>>>>> 5a8bad45036735f9d088a96490435721179465e7 name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6183,20 +8785,42 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.17", + "semver 1.0.18", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", ] [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys 0.4.5", "windows-sys 0.48.0", ] @@ -6239,30 +8863,51 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64 0.21.2", ] [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "rw-stream-sink" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" +dependencies = [ + "futures", + "pin-project", + "static_assertions", +] + +[[package]] +name = "rw-stream-sink" +version = "0.3.0" +source = "git+https://github.com/ChainSafe/rust-libp2p.git?rev=e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6#e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6" +dependencies = [ + "futures", + "pin-project", + "static_assertions", +] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "safer-ffi" -version = "0.1.0-rc1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7270b08a2af50909969cfc28a239cfdda9ab1b3df74749b265c46956183e5bf7" +checksum = "4f47f1d2f33598dab2baa9517fffa1cf722f2e3a30633f2a230f20f9da67c564" dependencies = [ "inventory", "libc", @@ -6277,12 +8922,12 @@ dependencies = [ [[package]] name = "safer_ffi-proc_macros" -version = "0.1.0-rc1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1438fb328c52f5148ea02a317603efd802f2cd6eb9e721adfd51b94bb55d9ddb" +checksum = "b08f58cf71a58bda5734758eb20051cdb66c06c9243badbc45092ced1be834df" dependencies = [ "macro_rules_attribute", - "prettyplease", + "prettyplease 0.1.25", "proc-macro2", "quote", "syn 1.0.109", @@ -6299,11 +8944,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -6313,7 +8958,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" dependencies = [ "dyn-clone", - "indexmap", + "indexmap 1.9.3", "schemars_derive", "serde", "serde_json", @@ -6339,9 +8984,9 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -6363,6 +9008,18 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sdp" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d22a5ef407871893fd72b4562ee15e4742269b173959db4b8df6f538c414e13" +dependencies = [ + "rand 0.8.5", + "substring", + "thiserror", + "url", +] + [[package]] name = "sec1" version = "0.3.0" @@ -6384,7 +9041,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", - "der 0.7.7", + "der 0.7.8", "generic-array 0.14.7", "pkcs8 0.10.2", "subtle 2.4.1", @@ -6393,11 +9050,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.0" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2855b3715770894e67cbfa3df957790aa0c9edc3bf06efa1a84d77fa0839d1" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -6406,9 +9063,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -6434,9 +9091,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "semver-parser" @@ -6455,27 +9112,27 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.163" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] -name = "serde-json-wasm" -version = "0.4.1" +name = "serde-big-array" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" +checksum = "cd31f59f6fe2b0c055371bb2f16d7f0aa7d8881676c04a55b1596d1a17cd10a4" dependencies = [ "serde", ] [[package]] name = "serde-json-wasm" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" dependencies = [ "serde", ] @@ -6493,22 +9150,22 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.9" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -6524,9 +9181,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -6535,20 +9192,20 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[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", ] @@ -6615,9 +9272,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -6634,10 +9291,25 @@ dependencies = [ ] [[package]] -name = "signal-hook" -version = "0.3.15" +name = "shellexpand" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +dependencies = [ + "dirs 4.0.0", +] + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", @@ -6683,6 +9355,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + [[package]] name = "slab" version = "0.4.8" @@ -6705,15 +9383,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "snafu" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0656e7e3ffb70f6c39b3c2a86332bb74aa3c679da781642590f3c1118c5045" +checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" dependencies = [ "doc-comment", "snafu-derive", @@ -6721,9 +9399,9 @@ dependencies = [ [[package]] name = "snafu-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475b3bbe5245c26f2d8a6f62d67c1f30eb9fffeccee721c45d162c3ebbdf81b2" +checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -6731,6 +9409,23 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "snow" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ccba027ba85743e09d15c03296797cad56395089b832b48b5a5217880f57733" +dependencies = [ + "aes-gcm 0.9.4", + "blake2 0.10.6", + "chacha20poly1305 0.9.1", + "curve25519-dalek 4.0.0-rc.1", + "rand_core 0.6.4", + "ring", + "rustc_version 0.4.0", + "sha2 0.10.7", + "subtle 2.4.1", +] + [[package]] name = "socket2" version = "0.4.9" @@ -6741,6 +9436,32 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64 0.13.1", + "bytes", + "flate2", + "futures", + "httparse", + "log", + "rand 0.8.5", + "sha-1 0.9.8", +] + [[package]] name = "sphinx-packet" version = "0.1.0" @@ -6753,7 +9474,7 @@ dependencies = [ "bs58 0.4.0", "byteorder", "chacha", - "curve25519-dalek", + "curve25519-dalek 3.2.0", "digest 0.9.0", "hkdf 0.11.0", "hmac 0.11.0", @@ -6797,7 +9518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der 0.7.7", + "der 0.7.8", ] [[package]] @@ -6824,9 +9545,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.5.13" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b" +checksum = "fc15591eb44ffb5816a4a70a7efd5dd87bfd3aa84c4c200401c4396140525826" dependencies = [ "sqlx-core 0.5.13", "sqlx-macros 0.5.13", @@ -6850,7 +9571,7 @@ checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" dependencies = [ "ahash 0.7.6", "atoi 0.4.0", - "bitflags", + "bitflags 1.3.2", "byteorder", "bytes", "chrono", @@ -6866,7 +9587,7 @@ dependencies = [ "futures-util", "hashlink 0.7.0", "hex", - "indexmap", + "indexmap 1.9.3", "itoa", "libc", "libsqlite3-sys", @@ -6876,7 +9597,7 @@ dependencies = [ "paste", "percent-encoding", "rustls 0.19.1", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "sqlformat 0.1.8", "sqlx-rt 0.5.13", @@ -6896,7 +9617,7 @@ checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" dependencies = [ "ahash 0.7.6", "atoi 1.0.0", - "bitflags", + "bitflags 1.3.2", "byteorder", "bytes", "chrono", @@ -6911,9 +9632,9 @@ dependencies = [ "futures-executor", "futures-intrusive", "futures-util", - "hashlink 0.8.2", + "hashlink 0.8.3", "hex", - "indexmap", + "indexmap 1.9.3", "itoa", "libc", "libsqlite3-sys", @@ -6924,7 +9645,7 @@ dependencies = [ "percent-encoding", "rustls 0.20.8", "rustls-pemfile", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "sqlformat 0.2.1", "sqlx-rt 0.6.3", @@ -6947,7 +9668,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "sha2 0.10.6", + "sha2 0.10.7", "sqlx-core 0.5.13", "sqlx-rt 0.5.13", "syn 1.0.109", @@ -6966,7 +9687,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "sha2 0.10.6", + "sha2 0.10.7", "sqlx-core 0.6.3", "sqlx-rt 0.6.3", "syn 1.0.109", @@ -7021,9 +9742,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stringprep" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +checksum = "db3737bde7edce97102e0e2b15365bf7a20bfdb5f60f4f9e8d7004258a51a8da" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -7076,6 +9797,34 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "stun" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" +dependencies = [ + "base64 0.13.1", + "crc 3.0.1", + "lazy_static", + "md-5", + "rand 0.8.5", + "ring", + "subtle 2.4.1", + "thiserror", + "tokio", + "url", + "webrtc-util", +] + +[[package]] +name = "substring" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +dependencies = [ + "autocfg 1.1.0", +] + [[package]] name = "subtle" version = "1.0.0" @@ -7116,9 +9865,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.16" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -7131,6 +9880,18 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + [[package]] name = "sysinfo" version = "0.27.8" @@ -7146,6 +9907,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -7154,15 +9936,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.5.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" dependencies = [ "cfg-if", - "fastrand", + "fastrand 2.0.0", "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", + "rustix 0.38.8", + "windows-sys 0.48.0", ] [[package]] @@ -7173,7 +9955,7 @@ checksum = "3f0a7d05cf78524782337f8edd55cbc578d159a16ad4affe2135c92f7dbac7f0" dependencies = [ "bytes", "digest 0.10.7", - "ed25519 2.2.1", + "ed25519 2.2.2", "ed25519-consensus", "flex-error", "futures", @@ -7187,12 +9969,12 @@ dependencies = [ "serde_bytes", "serde_json", "serde_repr", - "sha2 0.10.6", + "sha2 0.10.7", "signature 2.1.0", "subtle 2.4.1", "subtle-encoding", "tendermint-proto", - "time 0.3.21", + "time 0.3.25", "zeroize", ] @@ -7225,14 +10007,14 @@ dependencies = [ "serde", "serde_bytes", "subtle-encoding", - "time 0.3.21", + "time 0.3.25", ] [[package]] name = "tendermint-rpc" -version = "0.32.0" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd2cc789170db5a35d4e0bb2490035c03ef96df08f119bee25fd8dab5a09aa25" +checksum = "d119d83a130537fc4a98c3c9eb6899ebe857fea4860400a61675bfb5f0b35129" dependencies = [ "async-trait", "bytes", @@ -7245,7 +10027,7 @@ dependencies = [ "hyper-rustls", "peg", "pin-project", - "semver 1.0.17", + "semver 1.0.18", "serde", "serde_bytes", "serde_json", @@ -7255,7 +10037,7 @@ dependencies = [ "tendermint-config", "tendermint-proto", "thiserror", - "time 0.3.21", + "time 0.3.25", "tokio", "tracing", "url", @@ -7280,22 +10062,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -7343,10 +10125,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" dependencies = [ + "deranged", "itoa", "js-sys", "serde", @@ -7362,9 +10145,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" dependencies = [ "time-core", ] @@ -7396,19 +10179,19 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.1" +version = "1.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "40de3a2ba249dcb097e01be5e67a5ff53cf250397715a071a81543e8a832a920" dependencies = [ - "autocfg 1.1.0", + "backtrace", "bytes", "libc", "mio", "num_cpus", "parking_lot 0.12.1", - "pin-project-lite", + "pin-project-lite 0.2.12", "signal-hook-registry", - "socket2", + "socket2 0.5.3", "tokio-macros", "tracing", "windows-sys 0.48.0", @@ -7420,7 +10203,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ - "pin-project-lite", + "pin-project-lite 0.2.12", "tokio", ] @@ -7432,7 +10215,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -7486,7 +10269,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", - "pin-project-lite", + "pin-project-lite 0.2.12", "tokio", "tokio-util", ] @@ -7529,6 +10312,18 @@ dependencies = [ "tungstenite 0.17.3", ] +[[package]] +name = "tokio-tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.18.0", +] + [[package]] name = "tokio-util" version = "0.7.8" @@ -7537,8 +10332,11 @@ checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", - "pin-project-lite", + "futures-util", + "hashbrown 0.12.3", + "pin-project-lite 0.2.12", "slab", "tokio", "tracing", @@ -7555,9 +10353,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ "serde", "serde_spanned", @@ -7567,20 +10365,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.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "indexmap", + "indexmap 2.0.0", "serde", "serde_spanned", "toml_datetime", @@ -7623,9 +10421,9 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "indexmap", + "indexmap 1.9.3", "pin-project", - "pin-project-lite", + "pin-project-lite 0.2.12", "rand 0.8.5", "slab", "tokio", @@ -7655,20 +10453,20 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "log", - "pin-project-lite", + "pin-project-lite 0.2.12", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -7736,17 +10534,63 @@ dependencies = [ [[package]] name = "tracing-tree" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9742d8df709837409dbb22aa25dd7769c260406f20ff48a2320b80a4a6aed0" +checksum = "92d6b63348fad3ae0439b8bebf8d38fb5bda0b115d7a8a7e6f165f12790c58c3" dependencies = [ - "atty", + "is-terminal", "nu-ansi-term", "tracing-core", "tracing-log", "tracing-subscriber", ] +[[package]] +name = "trust-dns-proto" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.2.3", + "ipnet", + "lazy_static", + "rand 0.8.5", + "smallvec", + "socket2 0.4.9", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" +dependencies = [ + "cfg-if", + "futures-util", + "ipconfig", + "lazy_static", + "lru-cache", + "parking_lot 0.12.1", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", + "trust-dns-proto", +] + [[package]] name = "try-lock" version = "0.2.4" @@ -7755,9 +10599,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "ts-rs" -version = "6.2.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4added4070a4fdf9df03457206cd2e4b12417c8560a2954d91ffcbe60177a56a" +checksum = "e1ff1f8c90369bc172200013ac17ae86e7b5def580687df4e6127883454ff2b0" dependencies = [ "thiserror", "ts-rs-macros", @@ -7780,14 +10624,14 @@ dependencies = [ [[package]] name = "ts-rs-macros" -version = "6.2.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f807fdb3151fee75df7485b901a89624358cd07a67a8fb1a5831bf5a07681ff" +checksum = "a6f41cc0aeb7a4a55730188e147d3795a7349b501f8334697fd37629b896cdc2" dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", "termcolor", ] @@ -7830,6 +10674,44 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +dependencies = [ + "base64 0.13.1", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "turn" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" +dependencies = [ + "async-trait", + "base64 0.13.1", + "futures", + "log", + "md-5", + "rand 0.8.5", + "ring", + "stun", + "thiserror", + "tokio", + "webrtc-util", +] + [[package]] name = "typenum" version = "1.16.0" @@ -7847,9 +10729,9 @@ dependencies = [ [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "uint" @@ -7900,9 +10782,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -7948,14 +10830,34 @@ dependencies = [ [[package]] name = "universal-hash" -version = "0.5.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array 0.14.7", + "subtle 2.4.1", +] + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", "subtle 2.4.1", ] +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +dependencies = [ + "asynchronous-codec", + "bytes", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -7970,21 +10872,21 @@ checksum = "0976c77def3f1f75c4ef892a292c31c0bbe9e3d0702c63044d7c76db298171a3" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", - "idna 0.3.0", + "idna 0.4.0", "percent-encoding", "serde", ] [[package]] name = "urlencoding" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "utf-8" @@ -7998,6 +10900,48 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "utoipa" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ae74ef183fae36d650f063ae7bde1cacbe1cd7e72b617cbe1e985551878b98" +dependencies = [ + "indexmap 1.9.3", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ea8ac818da7e746a63285594cce8a96f5e00ee31994e655bd827569cb8b137b" +dependencies = [ + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn 2.0.28", +] + +[[package]] +name = "utoipa-swagger-ui" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062bba5a3568e126ac72049a63254f4cb1da2eb713db0c1ab2a4c76be191db8c" +dependencies = [ + "actix-web", + "mime_guess", + "regex", + "rust-embed", + "serde", + "serde_json", + "utoipa", + "zip", +] + [[package]] name = "uuid" version = "0.8.2" @@ -8006,11 +10950,12 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.3.3" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "getrandom 0.2.10", + "serde", "wasm-bindgen", ] @@ -8040,7 +10985,7 @@ dependencies = [ "rustc_version 0.4.0", "rustversion", "thiserror", - "time 0.3.21", + "time 0.3.25", ] [[package]] @@ -8049,6 +10994,21 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "waitgroup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" +dependencies = [ + "atomic-waker", +] + [[package]] name = "waker-fn" version = "1.1.0" @@ -8067,11 +11027,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -8162,7 +11121,8 @@ checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "wasm-timer" version = "0.2.5" -source = "git+https://github.com/mmsinclair/wasm-timer?rev=b9d1a54ad514c2f230a026afe0dde341e98cd7b6#b9d1a54ad514c2f230a026afe0dde341e98cd7b6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ "futures", "js-sys", @@ -8191,6 +11151,20 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasmtimer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.12.1", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "web-sys" version = "0.3.60" @@ -8239,6 +11213,232 @@ dependencies = [ "webpki 0.22.0", ] +[[package]] +name = "webrtc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3bc9049bdb2cea52f5fd4f6f728184225bdb867ed0dc2410eab6df5bdd67bb" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "hex", + "interceptor", + "lazy_static", + "log", + "rand 0.8.5", + "rcgen 0.9.3", + "regex", + "ring", + "rtcp", + "rtp", + "rustls 0.19.1", + "sdp", + "serde", + "serde_json", + "sha2 0.10.7", + "stun", + "thiserror", + "time 0.3.25", + "tokio", + "turn", + "url", + "waitgroup", + "webrtc-data", + "webrtc-dtls", + "webrtc-ice", + "webrtc-mdns", + "webrtc-media", + "webrtc-sctp", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "webrtc-data" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef36a4d12baa6e842582fe9ec16a57184ba35e1a09308307b67d43ec8883100" +dependencies = [ + "bytes", + "derive_builder", + "log", + "thiserror", + "tokio", + "webrtc-sctp", + "webrtc-util", +] + +[[package]] +name = "webrtc-dtls" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942be5bd85f072c3128396f6e5a9bfb93ca8c1939ded735d177b7bcba9a13d05" +dependencies = [ + "aes 0.6.0", + "aes-gcm 0.10.2", + "async-trait", + "bincode", + "block-modes", + "byteorder", + "ccm", + "curve25519-dalek 3.2.0", + "der-parser 8.2.0", + "elliptic-curve 0.12.3", + "hkdf 0.12.3", + "hmac 0.12.1", + "log", + "oid-registry 0.6.1", + "p256", + "p384", + "rand 0.8.5", + "rand_core 0.6.4", + "rcgen 0.9.3", + "ring", + "rustls 0.19.1", + "sec1 0.3.0", + "serde", + "sha1", + "sha2 0.10.7", + "signature 1.6.4", + "subtle 2.4.1", + "thiserror", + "tokio", + "webpki 0.21.4", + "webrtc-util", + "x25519-dalek 2.0.0-pre.1", + "x509-parser 0.13.2", +] + +[[package]] +name = "webrtc-ice" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80" +dependencies = [ + "arc-swap", + "async-trait", + "crc 3.0.1", + "log", + "rand 0.8.5", + "serde", + "serde_json", + "stun", + "thiserror", + "tokio", + "turn", + "url", + "uuid 1.4.1", + "waitgroup", + "webrtc-mdns", + "webrtc-util", +] + +[[package]] +name = "webrtc-mdns" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f08dfd7a6e3987e255c4dbe710dde5d94d0f0574f8a21afa95d171376c143106" +dependencies = [ + "log", + "socket2 0.4.9", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-media" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f72e1650a8ae006017d1a5280efb49e2610c19ccc3c0905b03b648aee9554991" +dependencies = [ + "byteorder", + "bytes", + "rand 0.8.5", + "rtp", + "thiserror", +] + +[[package]] +name = "webrtc-sctp" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d47adcd9427eb3ede33d5a7f3424038f63c965491beafcc20bc650a2f6679c0" +dependencies = [ + "arc-swap", + "async-trait", + "bytes", + "crc 3.0.1", + "log", + "rand 0.8.5", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-srtp" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6183edc4c1c6c0175f8812eefdce84dfa0aea9c3ece71c2bf6ddd3c964de3da5" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "aes-gcm 0.9.4", + "async-trait", + "byteorder", + "bytes", + "ctr 0.8.0", + "hmac 0.11.0", + "log", + "rtcp", + "rtp", + "sha-1 0.9.8", + "subtle 2.4.1", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-util" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" +dependencies = [ + "async-trait", + "bitflags 1.3.2", + "bytes", + "cc", + "ipnet", + "lazy_static", + "libc", + "log", + "nix", + "rand 0.8.5", + "thiserror", + "tokio", + "winapi", +] + +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + +[[package]] +name = "widestring" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" + [[package]] name = "winapi" version = "0.3.9" @@ -8270,28 +11470,26 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" +dependencies = [ + "windows_aarch64_msvc 0.34.0", + "windows_i686_gnu 0.34.0", + "windows_i686_msvc 0.34.0", + "windows_x86_64_gnu 0.34.0", + "windows_x86_64_msvc 0.34.0", +] + [[package]] name = "windows" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.48.1", ] [[package]] @@ -8309,7 +11507,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.1", ] [[package]] @@ -8329,9 +11527,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", @@ -8354,6 +11552,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -8366,6 +11570,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -8378,6 +11588,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -8390,6 +11606,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -8414,6 +11636,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -8428,9 +11656,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d" dependencies = [ "memchr", ] @@ -8479,18 +11707,95 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "rand_core 0.5.1", "serde", "zeroize", ] +[[package]] +name = "x25519-dalek" +version = "2.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" +dependencies = [ + "asn1-rs 0.3.1", + "base64 0.13.1", + "data-encoding", + "der-parser 7.0.0", + "lazy_static", + "nom", + "oid-registry 0.4.0", + "ring", + "rusticata-macros", + "thiserror", + "time 0.3.25", +] + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs 0.5.2", + "base64 0.13.1", + "data-encoding", + "der-parser 8.2.0", + "lazy_static", + "nom", + "oid-registry 0.6.1", + "rusticata-macros", + "thiserror", + "time 0.3.25", +] + +[[package]] +name = "yamux" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d9ba232399af1783a58d8eb26f6b5006fbefe2dc9ef36bd283324792d03ea5" +dependencies = [ + "futures", + "log", + "nohash-hasher", + "parking_lot 0.12.1", + "rand 0.8.5", + "static_assertions", +] + [[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yansi" +version = "1.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1367295b8f788d371ce2dbc842c7b709c73ee1364d30351dd300ec2203b12377" + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time 0.3.25", +] + [[package]] name = "zeroize" version = "1.6.0" @@ -8508,5 +11813,47 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "byteorder", + "crc32fast", + "crossbeam-utils", + "flate2", +] + +[[package]] +name = "zstd" +version = "0.12.3+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "6.0.5+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.8+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +dependencies = [ + "cc", + "libc", + "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index 934c5b2c75..16276ee67e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ opt-level = 3 resolver = "2" members = [ - "clients/credential", "clients/native", "clients/native/websocket-requests", "clients/socks5", @@ -34,6 +33,7 @@ members = [ "common/cosmwasm-smart-contracts/coconut-bandwidth-contract", "common/cosmwasm-smart-contracts/coconut-dkg", "common/cosmwasm-smart-contracts/contracts-common", + "common/cosmwasm-smart-contracts/ephemera", "common/cosmwasm-smart-contracts/group-contract", "common/cosmwasm-smart-contracts/mixnet-contract", "common/cosmwasm-smart-contracts/multisig-contract", @@ -42,6 +42,7 @@ members = [ "common/cosmwasm-smart-contracts/vesting-contract", "common/credential-storage", "common/credentials", + "common/credential-utils", "common/crypto", "common/dkg", "common/execute", @@ -75,6 +76,7 @@ members = [ "common/wasm-utils", "demos/rust-cosmos-broadcaster", "explorer-api", + "explorer-api/explorer-api-requests", "gateway", "gateway/gateway-requests", "integrations/bity", @@ -118,32 +120,40 @@ anyhow = "1.0.71" async-trait = "0.1.64" bip39 = { version = "2.0.0", features = ["zeroize"] } cfg-if = "1.0.0" -cosmwasm-derive = "=1.2.5" -cosmwasm-schema = "=1.2.5" -cosmwasm-std = "=1.2.5" -cosmwasm-storage = "=1.2.5" +cosmwasm-derive = "=1.3.0" +cosmwasm-schema = "=1.3.0" +cosmwasm-std = "=1.3.0" +# use 0.5.0 as that's the version used by cosmwasm-std 1.3.0 +# (and ideally we don't want to pull the same dependency twice) +serde-json-wasm = "=0.5.0" +cosmwasm-storage = "=1.3.0" 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" } +cw-storage-plus = "=1.1.0" +cw2 = { version = "=1.1.0" } +cw3 = { version = "=1.1.0" } +cw4 = { version = "=1.1.0" } +cw-controllers = { version = "=1.1.0" } dotenvy = "0.15.6" +futures = "0.3.28" generic-array = "0.14.7" -k256 = "0.13" getrandom = "0.2.10" +k256 = "0.13" lazy_static = "1.4.0" log = "0.4" once_cell = "1.7.2" rand = "0.8.5" +reqwest = "0.11.18" serde = "1.0.152" serde_json = "1.0.91" tap = "1.0.1" +tendermint-rpc = "0.32" # same version as used by cosmrs thiserror = "1.0.38" tokio = "1.24.1" -url = "2.2" +ts-rs = "7.0.0" +url = "2.4" zeroize = "1.6.0" + +# wasm-related dependencies +wasmtimer = "0.2.0" diff --git a/Makefile b/Makefile index ad812f11b8..6fc92e55f8 100644 --- a/Makefile +++ b/Makefile @@ -73,13 +73,10 @@ endef # Generate targets for the various cargo workspaces $(eval $(call add_cargo_workspace,main,.)) -$(eval $(call add_cargo_workspace,contracts,contracts,--target wasm32-unknown-unknown)) +$(eval $(call add_cargo_workspace,contracts,contracts,--lib --target wasm32-unknown-unknown)) $(eval $(call add_cargo_workspace,wasm-client,clients/webassembly,--target wasm32-unknown-unknown)) $(eval $(call add_cargo_workspace,wallet,nym-wallet,)) $(eval $(call add_cargo_workspace,connect,nym-connect/desktop)) -ifdef NYM_MOBILE -$(eval $(call add_cargo_workspace,connect-mobile,nym-connect/mobile/src-tauri)) -endif # ----------------------------------------------------------------------------- # Convenience targets for crates that are already part of the main workspace @@ -104,7 +101,7 @@ NAME_SERVICE_CONTRACT=$(CONTRACTS_OUT_DIR)/nym_name_service.wasm wasm: wasm-build wasm-opt wasm-build: - RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown + RUSTFLAGS='-C link-arg=-s' cargo build --lib --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown wasm-opt: wasm-opt --disable-sign-ext -Os $(VESTING_CONTRACT) -o $(VESTING_CONTRACT) @@ -112,6 +109,9 @@ wasm-opt: wasm-opt --disable-sign-ext -Os $(SERVICE_PROVIDER_DIRECTORY_CONTRACT) -o $(SERVICE_PROVIDER_DIRECTORY_CONTRACT) wasm-opt --disable-sign-ext -Os $(NAME_SERVICE_CONTRACT) -o $(NAME_SERVICE_CONTRACT) +contract-schema: + $(MAKE) -C contracts schema + # ----------------------------------------------------------------------------- # Misc # ----------------------------------------------------------------------------- diff --git a/clients/credential/README.md b/clients/credential/README.md deleted file mode 100644 index 9d82b4b839..0000000000 --- a/clients/credential/README.md +++ /dev/null @@ -1,32 +0,0 @@ - - -## Credential binary - -The credential binary is used to acquire coconut bandwidth credentials in exchange for nym tokens. Those credentials are stored in the client's `data` directory, so that they can be used as the client sees fit. - -### Warning - -The credential binary is still experimental software. The infrastructure for using it is not yet deployed to mainnet and it's still in the process of being deployed to sandbox. - -### Building - -From the project's root directory, run: -``` -cargo build -p nym-credential-client -``` -which generates the `nym-credential-client` binary in `target/debug/nym-credential-client`. - - -### Running - -For example, you can get a credential worth 3 nym (3000000 unym) in a socks5 client that was already initialized like so: - -``` -./target/debug/nym-credential-client --config-env-file envs/sandbox.env --client-home-directory ~/.nym/socks5-clients/cred_client --nyxd-url https://sandbox-validator1.nymtech.net --mnemonic $MNEMONIC --recovery-dir /tmp/recovery --amount 3000000 -``` - -More information regarding how to run the binary can be found by running it with the `--help` argument. - diff --git a/clients/credential/src/client.rs b/clients/credential/src/client.rs deleted file mode 100644 index 2db71d8e5e..0000000000 --- a/clients/credential/src/client.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::error::Result; -use bip39::Mnemonic; -use nym_network_defaults::{NymNetworkDetails, VOUCHER_INFO}; -use nym_validator_client::nyxd; -use nym_validator_client::nyxd::traits::CoconutBandwidthSigningClient; -use nym_validator_client::nyxd::{Coin, DirectSigningNyxdClient, Fee, NyxdClient}; -use std::str::FromStr; -use url::Url; - -pub(crate) struct Client { - nyxd_client: NyxdClient, - mix_denom_base: String, -} - -impl Client { - pub fn new(nyxd_url: &str, mnemonic: &str) -> Self { - let nyxd_url = Url::from_str(nyxd_url).unwrap(); - let mnemonic = Mnemonic::from_str(mnemonic).unwrap(); - let network_details = NymNetworkDetails::new_from_env(); - let config = nyxd::Config::try_from_nym_network_details(&network_details) - .expect("failed to construct valid validator client config with the provided network"); - let nyxd_client = - NyxdClient::connect_with_mnemonic(config, nyxd_url.as_ref(), mnemonic, None).unwrap(); - - Client { - nyxd_client, - mix_denom_base: network_details.chain_details.mix_denom.base, - } - } - - pub async fn deposit( - &self, - amount: u64, - verification_key: String, - encryption_key: String, - fee: Option, - ) -> Result { - let amount = Coin::new(amount as u128, self.mix_denom_base.clone()); - Ok(self - .nyxd_client - .deposit( - amount, - String::from(VOUCHER_INFO), - verification_key, - encryption_key, - fee, - ) - .await? - .transaction_hash - .to_string()) - } -} diff --git a/clients/credential/src/commands.rs b/clients/credential/src/commands.rs deleted file mode 100644 index 64993d2d7d..0000000000 --- a/clients/credential/src/commands.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use clap::{ArgGroup, Args, Subcommand}; -use log::*; -use nym_bandwidth_controller::acquire::state::State; -use nym_bin_common::completions::ArgShell; -use nym_credential_storage::persistent_storage::PersistentStorage; -use nym_validator_client::nyxd::traits::DkgQueryClient; - -use crate::error::Result; -use crate::recovery_storage::RecoveryStorage; - -#[derive(Subcommand)] -pub(crate) enum Command { - /// Run the binary to obtain a credential - Run(Run), - - /// Generate shell completions - Completions(ArgShell), - - /// Generate Fig specification - GenerateFigSpec, -} - -#[derive(Args)] -#[clap(group( -ArgGroup::new("recov") -.required(true) -.args(&["amount", "recovery_mode"]), -))] -pub(crate) struct Run { - /// Home directory of the client that is supposed to use the credential. - #[clap(long)] - pub(crate) client_home_directory: std::path::PathBuf, - - /// A mnemonic for the account that buys the credential - #[clap(long)] - pub(crate) mnemonic: String, - - /// The amount of utokens the credential will hold. If recovery mode is enabled, this value - /// is not needed - #[clap(long, default_value = "0")] - pub(crate) amount: u64, - - /// Path to a directory used to store recovery files for unconsumed deposits - #[clap(long)] - pub(crate) recovery_dir: std::path::PathBuf, - - /// Recovery mode, when enabled, tries to recover any deposit data dumped in recovery_dir - #[clap(long)] - pub(crate) recovery_mode: bool, -} - -pub(crate) async fn recover_credentials( - client: &C, - recovery_storage: &RecoveryStorage, - shared_storage: &PersistentStorage, -) -> Result<()> { - for voucher in recovery_storage.unconsumed_vouchers()? { - let state = State::new(voucher); - if let Err(e) = - nym_bandwidth_controller::acquire::get_credential(&state, client, shared_storage).await - { - error!( - "Could not recover deposit {} due to {:?}, try again later", - state.voucher.tx_hash(), - e - ) - } else { - info!( - "Converted deposit {} to a credential, removing recovery data for it", - state.voucher.tx_hash() - ); - if let Err(e) = recovery_storage.remove_voucher(state.voucher.tx_hash().to_string()) { - warn!("Could not remove recovery data - {:?}", e); - } - } - } - - Ok(()) -} diff --git a/clients/credential/src/error.rs b/clients/credential/src/error.rs deleted file mode 100644 index 1c6ac1521b..0000000000 --- a/clients/credential/src/error.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use std::time::SystemTimeError; -use thiserror::Error; - -use nym_credential_storage::error::StorageError; -use nym_credentials::error::Error as CredentialError; -use nym_validator_client::nyxd::error::NyxdError; -use nym_validator_client::ValidatorClientError; - -pub type Result = std::result::Result; - -#[derive(Error, Debug)] -pub enum CredentialClientError { - #[error("IO error: {0}")] - IOError(#[from] std::io::Error), - - #[error("Bandwidth controller error: {0}")] - BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError), - - #[error("Nyxd error: {0}")] - Nyxd(#[from] NyxdError), - - #[error("Validator client error: {0}")] - ValidatorClientError(#[from] ValidatorClientError), - - #[error("Credential error: {0}")] - Credential(#[from] CredentialError), - - #[error("Could not use shared storage")] - SharedStorageError(#[from] StorageError), - - #[error("Could not get system time")] - SysTimeError(#[from] SystemTimeError), -} diff --git a/clients/credential/src/main.rs b/clients/credential/src/main.rs deleted file mode 100644 index e37a825403..0000000000 --- a/clients/credential/src/main.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2022-2023 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -mod commands; -mod error; -mod recovery_storage; - -use commands::*; -use error::Result; -use log::*; -use nym_bin_common::completions::fig_generate; -use nym_config::DEFAULT_DATA_DIR; -use nym_network_defaults::{setup_env, NymNetworkDetails}; -use std::process::exit; -use std::time::{Duration, SystemTime}; - -use clap::{CommandFactory, Parser}; -use nym_bin_common::logging::setup_logging; -use nym_client_core::config::disk_persistence::CommonClientPaths; -use nym_validator_client::nyxd::traits::DkgQueryClient; -use nym_validator_client::nyxd::{Coin, CosmWasmClient}; -use nym_validator_client::Config; - -const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute - -#[derive(Parser)] -#[clap(author = "Nymtech", version, about)] -struct Cli { - /// Path pointing to an env file that configures the client. - #[clap(short, long)] - pub(crate) config_env_file: Option, - - #[clap(subcommand)] - pub(crate) command: Command, -} - -async fn block_until_coconut_is_available( - client: &nym_validator_client::Client, -) -> Result<()> { - loop { - let epoch = client.nyxd.get_current_epoch().await?; - let current_timestamp_secs = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH)? - .as_secs(); - if epoch.state.is_final() { - if current_timestamp_secs + SAFETY_BUFFER_SECS >= epoch.finish_timestamp.seconds() { - info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60); - exit(0); - } - - break; - } else { - // Use 1 additional second to not start the next iteration immediately and spam get_current_epoch queries - let secs_until_final = epoch - .final_timestamp_secs() - .saturating_sub(current_timestamp_secs) - + 1; - info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final); - std::thread::sleep(Duration::from_secs(secs_until_final)); - } - } - - Ok(()) -} - -#[tokio::main] -async fn main() -> Result<()> { - let args = Cli::parse(); - setup_logging(); - setup_env(args.config_env_file.as_ref()); - let bin_name = "nym-credential-client"; - - match args.command { - Command::Run(r) => { - // we assume the structure of /data - let data_dir = r.client_home_directory.join(DEFAULT_DATA_DIR); - let paths = CommonClientPaths::new_default(data_dir); - let db_path = paths.credentials_database; - - let shared_storage = - nym_credential_storage::initialise_persistent_storage(db_path).await; - let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?; - - let network_details = NymNetworkDetails::new_from_env(); - let config = Config::try_from_nym_network_details(&network_details).expect( - "failed to construct valid validator client config with the provided network", - ); - let amount = Coin::new( - r.amount as u128, - network_details.chain_details.mix_denom.base, - ); - let client = - nym_validator_client::Client::new_signing(config, r.mnemonic.parse().unwrap())?; - - block_until_coconut_is_available(&client).await?; - info!("Starting depositing funds, don't kill the process"); - - if !r.recovery_mode { - let state = - nym_bandwidth_controller::acquire::deposit(&client.nyxd, amount).await?; - if nym_bandwidth_controller::acquire::get_credential( - &state, - &client, - &shared_storage, - ) - .await - .is_err() - { - warn!("Failed to obtain credential. Dumping recovery data.",); - match recovery_storage.insert_voucher(&state.voucher) { - Ok(file_path) => { - warn!("Dumped recovery data to {:?}. Try using recovery mode to convert it to a credential", file_path); - } - Err(e) => { - error!("Could not dump recovery data to file system due to {:?}, the deposit will be lost!", e) - } - } - } - } else { - recover_credentials(&client.nyxd, &recovery_storage, &shared_storage).await?; - } - } - Command::Completions(c) => c.generate(&mut Cli::command(), bin_name), - Command::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), - } - - Ok(()) -} diff --git a/clients/credential/src/recovery_storage.rs b/clients/credential/src/recovery_storage.rs deleted file mode 100644 index db81d2ff6a..0000000000 --- a/clients/credential/src/recovery_storage.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use nym_credentials::coconut::bandwidth::BandwidthVoucher; -use std::fs::{create_dir_all, read_dir, File}; -use std::io::{Read, Write}; -use std::path::PathBuf; - -pub struct RecoveryStorage { - recovery_dir: PathBuf, -} - -impl RecoveryStorage { - pub fn new(recovery_dir: PathBuf) -> std::io::Result { - create_dir_all(&recovery_dir)?; - Ok(Self { recovery_dir }) - } - - pub fn unconsumed_vouchers(&self) -> std::io::Result> { - Ok(read_dir(&self.recovery_dir)? - .filter_map(|entry| entry.ok()) - .filter_map(|entry| { - let path = entry.path(); - if path.is_file() { - Some(path) - } else { - None - } - }) - .filter_map(|path| File::open(path).ok()) - .filter_map(|mut f| { - let mut buff = Vec::new(); - if f.read_to_end(&mut buff).is_ok() { - Some(buff) - } else { - None - } - }) - .filter_map(|buff| BandwidthVoucher::try_from_bytes(&buff).ok())) - } - - pub fn insert_voucher(&self, voucher: &BandwidthVoucher) -> std::io::Result { - let file_name = voucher.tx_hash().to_string(); - let file_path = self.recovery_dir.join(file_name); - let mut file = File::create(&file_path)?; - let buff = voucher.to_bytes(); - file.write_all(&buff)?; - - Ok(file_path) - } - - pub fn remove_voucher(&self, file_name: String) -> std::io::Result<()> { - let file_path = self.recovery_dir.join(file_name); - std::fs::remove_file(file_path) - } -} diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index 6064304a90..351f8fb295 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-client" -version = "1.1.23" +version = "1.1.27" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] description = "Implementation of the Nym Client" edition = "2021" @@ -14,11 +14,11 @@ path = "src/lib.rs" [dependencies] # dependencies to review: -futures = "0.3" # bunch of futures stuff, however, now that I think about it, it could perhaps be completely removed +futures = { workspace = true } # bunch of futures stuff, however, now that I think about it, it could perhaps be completely removed # the AsyncRead, AsyncWrite, Stream, Sink, etc. traits could be used from tokio # channels should really be replaced with crossbeam due to that implementation being more efficient # and the single instance of abortable we have should really be refactored anyway -url = "2.2" +url = { workspace = true } clap = { version = "4.0", features = ["cargo", "derive"] } dirs = "4.0" @@ -28,7 +28,7 @@ pretty_env_logger = "0.4" # for formatting log messages rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits + some rng implementation to use serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization serde_json = { workspace = true } -thiserror = "1.0.34" +thiserror = { workspace = true } tap = "1.0.1" tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } # async runtime tokio-tungstenite = "0.14" # websocket diff --git a/clients/native/src/client/mod.rs b/clients/native/src/client/mod.rs index 8457268d79..6f3982038f 100644 --- a/clients/native/src/client/mod.rs +++ b/clients/native/src/client/mod.rs @@ -19,8 +19,7 @@ use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx::params::PacketType; use nym_task::connections::TransmissionLane; use nym_task::TaskManager; -use nym_validator_client::nyxd::QueryNyxdClient; -use nym_validator_client::Client; +use nym_validator_client::QueryHttpRpcNyxdClient; use std::error::Error; use tokio::sync::watch::error::SendError; @@ -29,7 +28,7 @@ pub use nym_sphinx::receiver::ReconstructedMessage; pub mod config; -type NativeClientBuilder<'a> = BaseClientBuilder<'a, Client, OnDiskPersistent>; +type NativeClientBuilder<'a> = BaseClientBuilder<'a, QueryHttpRpcNyxdClient, OnDiskPersistent>; pub struct SocketClient { /// Client configuration options, including, among other things, packet sending rates, diff --git a/clients/native/src/commands/build_info.rs b/clients/native/src/commands/build_info.rs new file mode 100644 index 0000000000..e3385bb933 --- /dev/null +++ b/clients/native/src/commands/build_info.rs @@ -0,0 +1,16 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::Args; +use nym_bin_common::bin_info_owned; +use nym_bin_common::output_format::OutputFormat; + +#[derive(Args)] +pub(crate) struct BuildInfo { + #[clap(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +pub(crate) fn execute(args: BuildInfo) { + println!("{}", args.output.format(&bin_info_owned!())) +} diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index eca33f433c..d61b31b52d 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -174,14 +174,15 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> { let details_store = OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details); let init_details = nym_client_core::init::setup_gateway( - &gateway_setup, + gateway_setup, &key_store, &details_store, register_gateway, Some(&config.base.client.nym_api_urls), ) .await - .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; + .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))? + .details; let config_save_location = config.default_location(); config.save_to_default_location().tap_err(|_| { diff --git a/clients/native/src/commands/mod.rs b/clients/native/src/commands/mod.rs index 08c4f9592d..6badc58748 100644 --- a/clients/native/src/commands/mod.rs +++ b/clients/native/src/commands/mod.rs @@ -10,7 +10,7 @@ use clap::CommandFactory; use clap::{Parser, Subcommand}; use lazy_static::lazy_static; use log::{error, info}; -use nym_bin_common::build_information::BinaryBuildInformation; +use nym_bin_common::bin_info; use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_client_core::client::base_client::storage::gateway_details::{ OnDiskGatewayDetails, PersistedGatewayDetails, @@ -22,12 +22,12 @@ use nym_config::OptionalSet; use std::error::Error; use std::net::IpAddr; +pub(crate) mod build_info; pub(crate) mod init; pub(crate) mod run; lazy_static! { - pub static ref PRETTY_BUILD_INFORMATION: String = - BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print(); + pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print(); } // Helper for passing LONG_VERSION to clap @@ -42,6 +42,10 @@ pub(crate) struct Cli { #[clap(short, long)] pub(crate) config_env_file: Option, + /// Flag used for disabling the printed banner in tty. + #[clap(long)] + pub(crate) no_banner: bool, + #[clap(subcommand)] command: Commands, } @@ -54,6 +58,9 @@ pub(crate) enum Commands { /// Run the Nym client with provided configuration client optionally overriding set parameters Run(run::Run), + /// Show build information of this binary + BuildInfo(build_info::BuildInfo), + /// Generate shell completions Completions(ArgShell), @@ -73,12 +80,13 @@ pub(crate) struct OverrideConfig { enabled_credentials_mode: Option, } -pub(crate) async fn execute(args: &Cli) -> Result<(), Box> { +pub(crate) async fn execute(args: Cli) -> Result<(), Box> { let bin_name = "nym-native-client"; - match &args.command { - Commands::Init(m) => init::execute(m).await?, - Commands::Run(m) => run::execute(m).await?, + match args.command { + Commands::Init(m) => init::execute(&m).await?, + Commands::Run(m) => run::execute(&m).await?, + Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), } diff --git a/clients/native/src/main.rs b/clients/native/src/main.rs index cc862b2a88..cf3203b1e3 100644 --- a/clients/native/src/main.rs +++ b/clients/native/src/main.rs @@ -14,10 +14,13 @@ pub mod websocket; #[tokio::main] async fn main() -> Result<(), Box> { - setup_logging(); - maybe_print_banner(crate_name!(), crate_version!()); - let args = commands::Cli::parse(); setup_env(args.config_env_file.as_ref()); - commands::execute(&args).await + + if !args.no_banner { + maybe_print_banner(crate_name!(), crate_version!()); + } + setup_logging(); + + commands::execute(args).await } diff --git a/clients/native/websocket-requests/src/requests.rs b/clients/native/websocket-requests/src/requests.rs index 3c4c1a618b..645f2edf9c 100644 --- a/clients/native/websocket-requests/src/requests.rs +++ b/clients/native/websocket-requests/src/requests.rs @@ -105,10 +105,10 @@ impl ClientRequest { let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes(); std::iter::once(ClientRequestTag::Send as u8) - .chain(recipient.to_bytes().into_iter()) // will not be length prefixed because the length is constant - .chain(conn_id_bytes.into_iter()) - .chain(data_len_bytes.into_iter()) - .chain(data.into_iter()) + .chain(recipient.to_bytes()) // will not be length prefixed because the length is constant + .chain(conn_id_bytes) + .chain(data_len_bytes) + .chain(data) .collect() } @@ -180,11 +180,11 @@ impl ClientRequest { let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes(); std::iter::once(ClientRequestTag::SendAnonymous as u8) - .chain(reply_surbs.to_be_bytes().into_iter()) - .chain(recipient.to_bytes().into_iter()) // will not be length prefixed because the length is constant - .chain(conn_id_bytes.into_iter()) - .chain(data_len_bytes.into_iter()) - .chain(data.into_iter()) + .chain(reply_surbs.to_be_bytes()) + .chain(recipient.to_bytes()) // will not be length prefixed because the length is constant + .chain(conn_id_bytes) + .chain(data_len_bytes) + .chain(data) .collect() } @@ -258,10 +258,10 @@ impl ClientRequest { let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes(); std::iter::once(ClientRequestTag::Reply as u8) - .chain(sender_tag.to_bytes().into_iter()) - .chain(conn_id_bytes.into_iter()) - .chain(message_len_bytes.into_iter()) - .chain(message.into_iter()) + .chain(sender_tag.to_bytes()) + .chain(conn_id_bytes) + .chain(message_len_bytes) + .chain(message) .collect() } @@ -332,7 +332,7 @@ impl ClientRequest { fn serialize_closed_connection(connection_id: u64) -> Vec { let conn_id_bytes = connection_id.to_be_bytes(); std::iter::once(ClientRequestTag::ClosedConnection as u8) - .chain(conn_id_bytes.into_iter()) + .chain(conn_id_bytes) .collect() } @@ -359,7 +359,7 @@ impl ClientRequest { fn serialize_get_lane_queue_lengths(connection_id: u64) -> Vec { let conn_id_bytes = connection_id.to_be_bytes(); std::iter::once(ClientRequestTag::GetLaneQueueLength as u8) - .chain(conn_id_bytes.into_iter()) + .chain(conn_id_bytes) .collect() } diff --git a/clients/native/websocket-requests/src/responses.rs b/clients/native/websocket-requests/src/responses.rs index d326388570..713054ecf3 100644 --- a/clients/native/websocket-requests/src/responses.rs +++ b/clients/native/websocket-requests/src/responses.rs @@ -67,15 +67,15 @@ impl ServerResponse { if let Some(sender_tag) = reconstructed_message.sender_tag { std::iter::once(ServerResponseTag::Received as u8) .chain(std::iter::once(true as u8)) - .chain(sender_tag.to_bytes().into_iter()) + .chain(sender_tag.to_bytes()) .chain(message_len_bytes.iter().cloned()) - .chain(reconstructed_message.message.into_iter()) + .chain(reconstructed_message.message) .collect() } else { std::iter::once(ServerResponseTag::Received as u8) .chain(std::iter::once(false as u8)) .chain(message_len_bytes.iter().cloned()) - .chain(reconstructed_message.message.into_iter()) + .chain(reconstructed_message.message) .collect() } } @@ -149,7 +149,7 @@ impl ServerResponse { // SELF_ADDRESS_RESPONSE_TAG || self_address fn serialize_self_address(address: Recipient) -> Vec { std::iter::once(ServerResponseTag::SelfAddress as u8) - .chain(address.to_bytes().into_iter()) + .chain(address.to_bytes()) .collect() } @@ -211,8 +211,8 @@ impl ServerResponse { let message_len_bytes = (error.message.len() as u64).to_be_bytes(); std::iter::once(ServerResponseTag::Error as u8) .chain(std::iter::once(error.kind as u8)) - .chain(message_len_bytes.into_iter()) - .chain(error.message.into_bytes().into_iter()) + .chain(message_len_bytes) + .chain(error.message.into_bytes()) .collect() } diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index 658d52a047..a52422b5d0 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-socks5-client" -version = "1.1.23" +version = "1.1.27" authors = ["Dave Hrycyszyn "] description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address" edition = "2021" @@ -14,9 +14,9 @@ pretty_env_logger = "0.4" serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization serde_json = { workspace = true } tap = "1.0.1" -thiserror = "1.0.34" +thiserror = { workspace = true } tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } -url = "2.2" +url = { workspace = true } # internal nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } diff --git a/clients/socks5/src/commands/build_info.rs b/clients/socks5/src/commands/build_info.rs new file mode 100644 index 0000000000..e3385bb933 --- /dev/null +++ b/clients/socks5/src/commands/build_info.rs @@ -0,0 +1,16 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::Args; +use nym_bin_common::bin_info_owned; +use nym_bin_common::output_format::OutputFormat; + +#[derive(Args)] +pub(crate) struct BuildInfo { + #[clap(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +pub(crate) fn execute(args: BuildInfo) { + println!("{}", args.output.format(&bin_info_owned!())) +} diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index 76337b84ac..77782ed262 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -94,6 +94,7 @@ impl From for OverrideConfig { use_anonymous_replies: init_config.use_reply_surbs, fastmode: init_config.fastmode, no_cover: init_config.no_cover, + geo_routing: None, medium_toggle: false, nyxd_urls: init_config.nyxd_urls, enabled_credentials_mode: init_config.enabled_credentials_mode, @@ -185,14 +186,15 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> { let details_store = OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details); let init_details = nym_client_core::init::setup_gateway( - &gateway_setup, + gateway_setup, &key_store, &details_store, register_gateway, Some(&config.core.base.client.nym_api_urls), ) .await - .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; + .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))? + .details; // TODO: ask the service provider we specified for its interface version and set it in the config diff --git a/clients/socks5/src/commands/mod.rs b/clients/socks5/src/commands/mod.rs index 2b5fe425b6..d2b37b5a84 100644 --- a/clients/socks5/src/commands/mod.rs +++ b/clients/socks5/src/commands/mod.rs @@ -10,24 +10,25 @@ use clap::CommandFactory; use clap::{Parser, Subcommand}; use lazy_static::lazy_static; use log::{error, info}; -use nym_bin_common::build_information::BinaryBuildInformation; +use nym_bin_common::bin_info; use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_client_core::client::base_client::storage::gateway_details::{ OnDiskGatewayDetails, PersistedGatewayDetails, }; use nym_client_core::client::key_manager::persistence::OnDiskKeys; -use nym_client_core::config::GatewayEndpointConfig; +use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup; +use nym_client_core::config::{GatewayEndpointConfig, GroupBy, TopologyStructure}; use nym_client_core::error::ClientCoreError; use nym_config::OptionalSet; use nym_sphinx::params::{PacketSize, PacketType}; use std::error::Error; +pub(crate) mod build_info; pub mod init; pub(crate) mod run; lazy_static! { - pub static ref PRETTY_BUILD_INFORMATION: String = - BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print(); + pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print(); } // Helper for passing LONG_VERSION to clap @@ -42,6 +43,10 @@ pub(crate) struct Cli { #[clap(short, long)] pub(crate) config_env_file: Option, + /// Flag used for disabling the printed banner in tty. + #[clap(long)] + pub(crate) no_banner: bool, + #[clap(subcommand)] command: Commands, } @@ -54,6 +59,9 @@ pub(crate) enum Commands { /// Run the Nym client with provided configuration client optionally overriding set parameters Run(run::Run), + /// Show build information of this binary + BuildInfo(build_info::BuildInfo), + /// Generate shell completions Completions(ArgShell), @@ -68,18 +76,20 @@ pub(crate) struct OverrideConfig { use_anonymous_replies: Option, fastmode: bool, no_cover: bool, + geo_routing: Option, medium_toggle: bool, nyxd_urls: Option>, enabled_credentials_mode: Option, outfox: bool, } -pub(crate) async fn execute(args: &Cli) -> Result<(), Box> { +pub(crate) async fn execute(args: Cli) -> Result<(), Box> { let bin_name = "nym-socks5-client"; - match &args.command { - Commands::Init(m) => init::execute(m).await?, - Commands::Run(m) => run::execute(m).await?, + match args.command { + Commands::Init(m) => init::execute(&m).await?, + Commands::Run(m) => run::execute(&m).await?, + Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), } @@ -91,6 +101,21 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config { let secondary_packet_size = args.medium_toggle.then_some(PacketSize::ExtendedPacket16); let no_per_hop_delays = args.medium_toggle; + let topology_structure = if args.medium_toggle { + // Use the location of the network-requester + let address = config + .core + .socks5 + .provider_mix_address + .parse() + .expect("failed to parse provider mix address"); + TopologyStructure::GeoAware(GroupBy::NymAddress(address)) + } else if let Some(code) = args.geo_routing { + TopologyStructure::GeoAware(GroupBy::CountryGroup(code)) + } else { + TopologyStructure::default() + }; + let packet_type = if args.outfox { PacketType::Outfox } else { @@ -114,6 +139,10 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config { // NOTE: see comment above about the order of the other disble cover traffic config .with_base(BaseClientConfig::with_disabled_cover_traffic, args.no_cover) .with_base(BaseClientConfig::with_packet_type, packet_type) + .with_base( + BaseClientConfig::with_topology_structure, + topology_structure, + ) .with_optional(Config::with_anonymous_replies, args.use_anonymous_replies) .with_optional(Config::with_port, args.port) .with_optional_base_custom_env( diff --git a/clients/socks5/src/commands/run.rs b/clients/socks5/src/commands/run.rs index 5d54038c06..8dd289e2bf 100644 --- a/clients/socks5/src/commands/run.rs +++ b/clients/socks5/src/commands/run.rs @@ -11,6 +11,7 @@ use clap::Args; use log::*; use nym_bin_common::version_checker::is_minor_version_compatible; use nym_client_core::client::base_client::storage::OnDiskPersistent; +use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup; use nym_crypto::asymmetric::identity; use nym_socks5_client_core::NymClient; use nym_sphinx::addressing::clients::Recipient; @@ -60,6 +61,10 @@ pub(crate) struct Run { #[clap(long, hide = true)] no_cover: bool, + /// Set geo-aware mixnode selection when sending mixnet traffic, for experiments only. + #[clap(long, hide = true, value_parser = validate_country_group)] + geo_routing: Option, + /// Enable medium mixnet traffic, for experiments only. /// This includes things like disabling cover traffic, no per hop delays, etc. #[clap(long, hide = true)] @@ -82,6 +87,7 @@ impl From for OverrideConfig { use_anonymous_replies: run_config.use_anonymous_replies, fastmode: run_config.fastmode, no_cover: run_config.no_cover, + geo_routing: run_config.geo_routing, medium_toggle: run_config.medium_toggle, nyxd_urls: run_config.nyxd_urls, enabled_credentials_mode: run_config.enabled_credentials_mode, @@ -90,6 +96,13 @@ impl From for OverrideConfig { } } +fn validate_country_group(s: &str) -> Result { + match s.parse() { + Ok(cg) => Ok(cg), + Err(_) => Err(format!("failed to parse country group: {}", s)), + } +} + // this only checks compatibility between config the binary. It does not take into consideration // network version. It might do so in the future. fn version_check(cfg: &Config) -> bool { diff --git a/clients/socks5/src/main.rs b/clients/socks5/src/main.rs index b592c62817..8e6d899652 100644 --- a/clients/socks5/src/main.rs +++ b/clients/socks5/src/main.rs @@ -13,10 +13,13 @@ pub mod error; #[tokio::main] async fn main() -> Result<(), Box> { - setup_logging(); - maybe_print_banner(crate_name!(), crate_version!()); - let args = commands::Cli::parse(); setup_env(args.config_env_file.as_ref()); - commands::execute(&args).await + + if !args.no_banner { + maybe_print_banner(crate_name!(), crate_version!()); + } + setup_logging(); + + commands::execute(args).await } diff --git a/clients/webassembly/Cargo.lock b/clients/webassembly/Cargo.lock index f0733eac1b..271ea93ea8 100644 --- a/clients/webassembly/Cargo.lock +++ b/clients/webassembly/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[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" @@ -33,9 +42,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if 1.0.0", "cipher 0.4.4", @@ -44,12 +53,12 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" dependencies = [ "aead", - "aes 0.8.2", + "aes 0.8.3", "cipher 0.4.4", "ctr 0.9.2", "ghash", @@ -80,13 +89,19 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "anstream" version = "0.3.2" @@ -104,15 +119,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] @@ -123,33 +138,34 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "argon2" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95c2fcf79ad1932ac6269a738109997a83c227c09b75842ae564dc8ede6a861c" +checksum = "b2e554a8638bdc1e4eae9984845306cc95f8a9208ba8d49c3859fd958b46774d" dependencies = [ "base64ct", "blake2 0.10.6", + "cpufeatures", "password-hash", ] @@ -161,19 +177,19 @@ 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 = "async-trait" -version = "0.1.68" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -211,6 +227,21 @@ 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 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base16ct" version = "0.1.1" @@ -254,7 +285,7 @@ dependencies = [ "pbkdf2", "rand_core 0.6.4", "ripemd", - "sha2 0.10.6", + "sha2 0.10.7", "subtle 2.4.1", "zeroize", ] @@ -285,6 +316,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "bitvec" version = "1.0.1" @@ -320,9 +357,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", @@ -378,6 +415,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bs58" version = "0.4.0" @@ -390,14 +433,14 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" dependencies = [ - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] name = "bumpalo" -version = "3.12.2" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byte-tools" @@ -422,11 +465,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -497,9 +541,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.7" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" dependencies = [ "clap_builder", "clap_derive", @@ -508,31 +552,30 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.7" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" dependencies = [ "anstream", "anstyle", - "bitflags", "clap_lex", "strsim", ] [[package]] name = "clap_complete" -version = "4.2.3" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1594fe2312ec4abf402076e407628f5c313e54c32ade058521df4ee34ecac8a8" +checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce" dependencies = [ "clap", ] [[package]] name = "clap_complete_fig" -version = "4.2.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3af28956330989baa428ed4d3471b853715d445c62de21b67292e22cf8a41fa" +checksum = "99fee1d30a51305a6c2ed3fc5709be3c8af626c9c958e04dd9ae94e27bcbce9f" dependencies = [ "clap", "clap_complete", @@ -540,21 +583,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "colorchoice" @@ -564,13 +607,13 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ - "atty", + "is-terminal", "lazy_static", - "winapi", + "windows-sys", ] [[package]] @@ -585,15 +628,15 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[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 = "core-foundation" @@ -645,9 +688,9 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d076a08ec01ed23c4396aca98ec73a38fa1fee5f310465add52b4108181c7a8" +checksum = "7e272708a9745dad8b591ef8a718571512130f2b39b33e3d7a27c558e3069394" dependencies = [ "digest 0.10.7", "ed25519-zebra", @@ -658,18 +701,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.5" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" +checksum = "296db6a3caca5283425ae0cf347f4e46999ba3f6620dbea8939a0e00347831ce" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc" +checksum = "bb6b2fb76758ef59cddc77f2e2ae91c22f77da49037e9f182e9c2833f0e959b1" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -680,9 +723,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c" +checksum = "2bfa39422f0d9f1c9a6fd3711573258495314dfa3aae738ea825ecd9964bc659" dependencies = [ "proc-macro2", "quote", @@ -691,11 +734,12 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb" +checksum = "1f6dc2ee23313add5ecacc3ccac217b9967ad9d2d11bd56e5da6aa65a9da6138" dependencies = [ "base64 0.13.1", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -703,17 +747,16 @@ dependencies = [ "hex", "schemars", "serde", - "serde-json-wasm 0.5.1", - "sha2 0.10.6", + "serde-json-wasm", + "sha2 0.10.7", "thiserror", - "uint", ] [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -780,9 +823,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 1.0.0", @@ -803,19 +846,13 @@ 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 1.0.0", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.9" @@ -927,9 +964,9 @@ dependencies = [ [[package]] name = "cw-controllers" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071" +checksum = "d5d8edce4b78785f36413f67387e4be7d0cb7d032b5d4164bcc024f9c3f3f2ea" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -942,9 +979,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" +checksum = "3f0e92a069d62067f3472c62e30adedb4cab1754725c0f2a682b3128d2bf3c79" dependencies = [ "cosmwasm-std", "schemars", @@ -961,22 +998,23 @@ dependencies = [ "cosmwasm-std", "cw2", "schemars", - "semver 1.0.17", + "semver 1.0.18", "serde", "thiserror", ] [[package]] name = "cw2" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" +checksum = "29ac2dc7a55ad64173ca1e0a46697c31b7a5c51342f55a1e84a724da4eb99908" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", "serde", + "thiserror", ] [[package]] @@ -994,9 +1032,9 @@ dependencies = [ [[package]] name = "cw3" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa" +checksum = "171af3d9127de6805a7dd819fb070c7d2f6c3ea85f4193f42cef259f0a7f33d5" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1009,9 +1047,9 @@ dependencies = [ [[package]] name = "cw4" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b" +checksum = "a398696307efadaaa2d0850076f865fa706c959d493cb4203314f72be6b77a64" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1022,15 +1060,15 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.4.0" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" dependencies = [ "cfg-if 1.0.0", - "hashbrown 0.12.3", + "hashbrown 0.14.0", "lock_api", "once_cell", - "parking_lot_core 0.9.7", + "parking_lot_core 0.9.8", ] [[package]] @@ -1045,14 +1083,20 @@ dependencies = [ [[package]] name = "der" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" + [[package]] name = "derivative" version = "2.2.0" @@ -1132,7 +1176,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1149,9 +1193,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "ecdsa" @@ -1171,7 +1215,7 @@ version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ - "der 0.7.7", + "der 0.7.8", "digest 0.10.7", "elliptic-curve 0.13.5", "rfc6979 0.4.0", @@ -1191,9 +1235,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" dependencies = [ "pkcs8 0.10.2", "signature 2.1.0", @@ -1244,9 +1288,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -1313,7 +1357,7 @@ checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -1330,14 +1374,20 @@ dependencies = [ ] [[package]] -name = "errno" -version = "0.3.1" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1368,12 +1418,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "ff" @@ -1470,9 +1517,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -1539,7 +1586,7 @@ checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" dependencies = [ "futures-core", "lock_api", - "parking_lot", + "parking_lot 0.11.2", ] [[package]] @@ -1556,7 +1603,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -1658,13 +1705,19 @@ dependencies = [ "polyval", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "git2" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "libgit2-sys", "log", @@ -1731,9 +1784,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -1741,7 +1794,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -1782,11 +1835,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.13.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" dependencies = [ "ahash 0.8.3", + "allocator-api2", ] [[package]] @@ -1800,11 +1854,11 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0761a1b9491c4f2e3d66aa0f62d0fba0af9a0e2852e4d48ea506632a4b56e6aa" +checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" dependencies = [ - "hashbrown 0.13.2", + "hashbrown 0.14.0", ] [[package]] @@ -1814,7 +1868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", - "bitflags", + "bitflags 1.3.2", "bytes", "headers-core", "http", @@ -1852,18 +1906,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -1945,9 +1990,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -1976,9 +2021,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -1991,7 +2036,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -2050,9 +2095,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -2072,7 +2117,7 @@ checksum = "bfbcff6ae46750b15cc594bfd277b188cbddcfdc1817848f97f03f26f8625b9e" dependencies = [ "cfg-if 1.0.0", "js-sys", - "uuid 1.3.3", + "uuid 1.4.1", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -2089,6 +2134,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 = "inout" version = "0.1.3" @@ -2114,38 +2169,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", ] [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", + "hermit-abi 0.3.2", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2159,9 +2199,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" @@ -2190,7 +2230,7 @@ dependencies = [ "cfg-if 1.0.0", "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -2203,7 +2243,7 @@ dependencies = [ "ecdsa 0.16.8", "elliptic-curve 0.13.5", "once_cell", - "sha2 0.10.6", + "sha2 0.10.7", "signature 2.1.0", ] @@ -2221,9 +2261,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libgit2-sys" @@ -2256,9 +2296,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.9" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "libc", @@ -2268,9 +2308,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "lioness" @@ -2286,9 +2326,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -2296,12 +2336,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" @@ -2311,9 +2348,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", ] @@ -2347,14 +2384,13 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -2398,9 +2434,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", "libm", @@ -2408,11 +2444,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 0.3.2", "libc", ] @@ -2467,6 +2503,7 @@ version = "1.1.15" dependencies = [ "async-trait", "base64 0.21.2", + "cfg-if 1.0.0", "dashmap", "dirs 4.0.0", "futures", @@ -2477,6 +2514,7 @@ dependencies = [ "nym-config", "nym-credential-storage", "nym-crypto", + "nym-explorer-api-requests", "nym-gateway-client", "nym-gateway-requests", "nym-network-defaults", @@ -2487,9 +2525,10 @@ dependencies = [ "nym-topology", "nym-validator-client", "rand 0.7.3", + "reqwest", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "sqlx 0.6.3", "tap", "thiserror", @@ -2501,8 +2540,8 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-timer", "wasm-utils", + "wasmtimer", "zeroize", ] @@ -2539,8 +2578,8 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test", - "wasm-timer", "wasm-utils", + "wasmtimer", "wee_alloc", "zeroize", ] @@ -2569,22 +2608,20 @@ dependencies = [ name = "nym-coconut-bandwidth-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "nym-multisig-contract-common", - "schemars", - "serde", ] [[package]] name = "nym-coconut-dkg-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-utils", "nym-contracts-common", "nym-multisig-contract-common", - "schemars", - "serde", ] [[package]] @@ -2607,7 +2644,7 @@ dependencies = [ "log", "nym-network-defaults", "serde", - "toml 0.7.4", + "toml 0.7.6", "url", ] @@ -2616,6 +2653,7 @@ name = "nym-contracts-common" version = "0.5.0" dependencies = [ "bs58 0.4.0", + "cosmwasm-schema", "cosmwasm-std", "schemars", "serde", @@ -2639,6 +2677,7 @@ version = "0.1.0" dependencies = [ "bls12_381 0.5.0", "cosmrs", + "log", "nym-api-requests", "nym-coconut-interface", "nym-crypto", @@ -2650,7 +2689,7 @@ dependencies = [ name = "nym-crypto" version = "0.4.0" dependencies = [ - "aes 0.8.2", + "aes 0.8.3", "blake3", "bs58 0.4.0", "cipher 0.4.4", @@ -2692,6 +2731,27 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nym-ephemera-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "nym-contracts-common", +] + +[[package]] +name = "nym-explorer-api-requests" +version = "0.1.0" +dependencies = [ + "nym-api-requests", + "nym-contracts-common", + "nym-mixnet-contract-common", + "schemars", + "serde", +] + [[package]] name = "nym-gateway-client" version = "0.1.0" @@ -2719,8 +2779,8 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-timer", "wasm-utils", + "wasmtimer", ] [[package]] @@ -2760,13 +2820,14 @@ name = "nym-mixnet-contract-common" version = "0.6.0" dependencies = [ "bs58 0.4.0", + "cosmwasm-schema", "cosmwasm-std", "humantime-serde", "log", "nym-contracts-common", "schemars", "serde", - "serde-json-wasm 0.4.1", + "serde-json-wasm", "serde_repr", "thiserror", "time", @@ -2791,11 +2852,11 @@ dependencies = [ name = "nym-name-service-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-utils", "nym-contracts-common", - "schemars", "serde", "thiserror", ] @@ -2808,6 +2869,7 @@ dependencies = [ "dotenvy", "hex-literal", "once_cell", + "schemars", "serde", "thiserror", "url", @@ -2836,12 +2898,10 @@ dependencies = [ name = "nym-nonexhaustive-delayqueue" version = "0.1.0" dependencies = [ - "futures-core", - "slab", "tokio", "tokio-stream", "tokio-util", - "wasm-timer", + "wasmtimer", ] [[package]] @@ -2872,12 +2932,11 @@ dependencies = [ name = "nym-service-provider-directory-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-utils", "nym-contracts-common", - "schemars", - "serde", "thiserror", ] @@ -3082,6 +3141,9 @@ dependencies = [ "colored", "cosmrs", "cosmwasm-std", + "cw-controllers", + "cw-utils", + "cw2", "cw3", "cw4", "eyre", @@ -3095,13 +3157,13 @@ dependencies = [ "nym-coconut-interface", "nym-config", "nym-contracts-common", + "nym-ephemera-common", "nym-group-contract-common", "nym-mixnet-contract-common", "nym-multisig-contract-common", "nym-name-service-common", "nym-network-defaults", "nym-service-provider-directory-common", - "nym-vesting-contract", "nym-vesting-contract-common", "openssl", "prost", @@ -3113,43 +3175,36 @@ dependencies = [ "thiserror", "tokio", "url", + "wasmtimer", "zeroize", ] -[[package]] -name = "nym-vesting-contract" -version = "1.4.0" -dependencies = [ - "cosmwasm-derive", - "cosmwasm-std", - "cw-storage-plus", - "cw2", - "nym-contracts-common", - "nym-mixnet-contract-common", - "nym-vesting-contract-common", - "schemars", - "semver 1.0.17", - "serde", - "thiserror", - "vergen", -] - [[package]] name = "nym-vesting-contract-common" version = "0.7.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "nym-contracts-common", "nym-mixnet-contract-common", - "schemars", "serde", + "thiserror", +] + +[[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" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -3165,11 +3220,11 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "foreign-types", "libc", @@ -3186,7 +3241,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -3197,18 +3252,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.26.0+1.1.1u" +version = "111.27.0+1.1.1v" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37" +checksum = "06e8f197c82d7511c5b014030c9b1efeda40d7d5f99d23b4ceed3524a5e63f02" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" dependencies = [ "cc", "libc", @@ -3252,6 +3307,16 @@ dependencies = [ "parking_lot_core 0.8.6", ] +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.8", +] + [[package]] name = "parking_lot_core" version = "0.8.6" @@ -3268,15 +3333,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.45.0", + "windows-targets", ] [[package]] @@ -3292,9 +3357,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -3346,15 +3411,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.6.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" +checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" dependencies = [ "thiserror", "ucd-trie", @@ -3362,9 +3427,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.6.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" +checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" dependencies = [ "pest", "pest_generator", @@ -3372,53 +3437,53 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.6.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" +checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] name = "pest_meta" -version = "2.6.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" +checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" dependencies = [ "once_cell", "pest", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] name = "pin-project" -version = "1.1.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -3442,7 +3507,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.7", + "der 0.7.8", "spki 0.7.2", ] @@ -3465,9 +3530,9 @@ dependencies = [ [[package]] name = "polyval" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -3517,9 +3582,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -3570,9 +3635,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.27" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -3692,7 +3757,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -3701,7 +3766,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -3717,9 +3782,21 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" dependencies = [ "aho-corasick", "memchr", @@ -3728,9 +3805,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" @@ -3814,27 +3891,32 @@ dependencies = [ "digest 0.10.7", ] +[[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.17", + "semver 1.0.18", ] [[package]] name = "rustix" -version = "0.37.19" +version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags", + "bitflags 2.4.0", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -3876,24 +3958,24 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64 0.21.2", ] [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "same-file" @@ -3906,11 +3988,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -3920,7 +4002,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" dependencies = [ "dyn-clone", - "indexmap", + "indexmap 1.9.3", "schemars_derive", "serde", "serde_json", @@ -3946,9 +4028,9 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -3991,7 +4073,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", - "der 0.7.7", + "der 0.7.8", "generic-array 0.14.7", "pkcs8 0.10.2", "subtle 2.4.1", @@ -4000,11 +4082,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.0" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2855b3715770894e67cbfa3df957790aa0c9edc3bf06efa1a84d77fa0839d1" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -4013,9 +4095,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -4032,9 +4114,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "semver-parser" @@ -4047,27 +4129,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.163" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] name = "serde-json-wasm" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" -dependencies = [ - "serde", -] - -[[package]] -name = "serde-json-wasm" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" dependencies = [ "serde", ] @@ -4085,22 +4158,22 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.9" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -4116,9 +4189,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -4127,20 +4200,20 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[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", ] @@ -4196,9 +4269,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -4245,9 +4318,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "socket2" @@ -4259,6 +4332,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "sphinx-packet" version = "0.1.0" @@ -4315,7 +4398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der 0.7.7", + "der 0.7.8", ] [[package]] @@ -4368,7 +4451,7 @@ checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" dependencies = [ "ahash 0.7.6", "atoi 0.4.0", - "bitflags", + "bitflags 1.3.2", "byteorder", "bytes", "crc 2.1.0", @@ -4383,7 +4466,7 @@ dependencies = [ "futures-util", "hashlink 0.7.0", "hex", - "indexmap", + "indexmap 1.9.3", "itoa", "libc", "libsqlite3-sys", @@ -4393,7 +4476,7 @@ dependencies = [ "paste", "percent-encoding", "rustls 0.19.1", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "sqlformat 0.1.8", "sqlx-rt 0.5.13", @@ -4413,7 +4496,7 @@ checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" dependencies = [ "ahash 0.7.6", "atoi 1.0.0", - "bitflags", + "bitflags 1.3.2", "byteorder", "bytes", "crc 3.0.1", @@ -4427,9 +4510,9 @@ dependencies = [ "futures-executor", "futures-intrusive", "futures-util", - "hashlink 0.8.2", + "hashlink 0.8.3", "hex", - "indexmap", + "indexmap 1.9.3", "itoa", "libc", "libsqlite3-sys", @@ -4440,7 +4523,7 @@ dependencies = [ "percent-encoding", "rustls 0.20.8", "rustls-pemfile", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "sqlformat 0.2.1", "sqlx-rt 0.6.3", @@ -4463,7 +4546,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "sha2 0.10.6", + "sha2 0.10.7", "sqlx-core 0.5.13", "sqlx-rt 0.5.13", "syn 1.0.109", @@ -4482,7 +4565,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "sha2 0.10.6", + "sha2 0.10.7", "sqlx-core 0.6.3", "sqlx-rt 0.6.3", "syn 1.0.109", @@ -4511,17 +4594,11 @@ dependencies = [ "tokio-rustls 0.23.4", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "stringprep" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +checksum = "db3737bde7edce97102e0e2b15365bf7a20bfdb5f60f4f9e8d7004258a51a8da" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -4573,9 +4650,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.16" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -4590,15 +4667,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.5.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -4609,7 +4686,7 @@ checksum = "3f0a7d05cf78524782337f8edd55cbc578d159a16ad4affe2135c92f7dbac7f0" dependencies = [ "bytes", "digest 0.10.7", - "ed25519 2.2.1", + "ed25519 2.2.2", "ed25519-consensus", "flex-error", "futures", @@ -4623,7 +4700,7 @@ dependencies = [ "serde_bytes", "serde_json", "serde_repr", - "sha2 0.10.6", + "sha2 0.10.7", "signature 2.1.0", "subtle 2.4.1", "subtle-encoding", @@ -4681,7 +4758,7 @@ dependencies = [ "hyper-rustls", "peg", "pin-project", - "semver 1.0.17", + "semver 1.0.18", "serde", "serde_bytes", "serde_json", @@ -4710,30 +4787,31 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] name = "time" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" dependencies = [ + "deranged", "itoa", "js-sys", "serde", @@ -4749,9 +4827,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" dependencies = [ "time-core", ] @@ -4773,20 +4851,20 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.1" +version = "1.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "40de3a2ba249dcb097e01be5e67a5ff53cf250397715a071a81543e8a832a920" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.3", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -4797,7 +4875,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] [[package]] @@ -4883,9 +4961,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ "serde", "serde_spanned", @@ -4895,20 +4973,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.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "indexmap", + "indexmap 2.0.0", "serde", "serde_spanned", "toml_datetime", @@ -4975,21 +5053,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-bidi" @@ -4999,9 +5065,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -5026,9 +5092,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "universal-hash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", "subtle 2.4.1", @@ -5042,9 +5108,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -5072,9 +5138,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.3.3" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "getrandom 0.2.10", "wasm-bindgen", @@ -5121,11 +5187,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -5233,20 +5298,6 @@ dependencies = [ "quote", ] -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "git+https://github.com/mmsinclair/wasm-timer?rev=b9d1a54ad514c2f230a026afe0dde341e98cd7b6#b9d1a54ad514c2f230a026afe0dde341e98cd7b6" -dependencies = [ - "futures", - "js-sys", - "parking_lot", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "wasm-utils" version = "0.1.0" @@ -5265,6 +5316,20 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasmtimer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.12.1", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "web-sys" version = "0.3.60" @@ -5356,147 +5421,66 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" @@ -5505,9 +5489,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d" dependencies = [ "memchr", ] @@ -5559,5 +5543,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.28", ] diff --git a/clients/webassembly/Cargo.toml b/clients/webassembly/Cargo.toml index 11d1cb131a..d68b5d37f6 100644 --- a/clients/webassembly/Cargo.toml +++ b/clients/webassembly/Cargo.toml @@ -3,7 +3,7 @@ name = "nym-client-wasm" authors = ["Dave Hrycyszyn ", "Jedrzej Stuczynski "] version = "1.1.1" edition = "2021" -keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"] +keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy"] license = "Apache-2.0" repository = "https://github.com/nymtech/nym" description = "A webassembly client which can be used to interact with the the Nym privacy platform. Wasm is used for Sphinx packet generation." @@ -33,7 +33,7 @@ wasm-bindgen-futures = "0.4" thiserror = "1.0.40" zeroize = "1.6.0" -wasm-timer = { git = "https://github.com/mmsinclair/wasm-timer", rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6"} +wasmtimer = { version = "0.2.0", features = ["tokio"] } # internal nym-node-tester-utils = { path = "../../common/node-tester-utils" } diff --git a/clients/webassembly/src/client/config.rs b/clients/webassembly/src/client/config.rs index 07d9ab5f5f..f63d1456ac 100644 --- a/clients/webassembly/src/client/config.rs +++ b/clients/webassembly/src/client/config.rs @@ -246,6 +246,7 @@ impl From for ConfigTopology { topology.topology_resolution_timeout_ms, ), disable_refreshing: topology.disable_refreshing, + topology_structure: Default::default(), } } } diff --git a/clients/webassembly/src/client/helpers.rs b/clients/webassembly/src/client/helpers.rs index c3f456a891..7d5373853b 100644 --- a/clients/webassembly/src/client/helpers.rs +++ b/clients/webassembly/src/client/helpers.rs @@ -114,7 +114,7 @@ impl WasmTopologyExt for Arc { let this = Arc::clone(self); future_to_promise(async move { let Some(current_topology) = this.topology_accessor.current_topology().await else { - return Err(WasmClientError::UnavailableNetworkTopology.into()) + return Err(WasmClientError::UnavailableNetworkTopology.into()); }; match current_topology.find_mix_by_identity(&mixnode_identity) { @@ -135,7 +135,7 @@ impl WasmTopologyExt for Arc { let this = Arc::clone(self); future_to_promise(async move { let Some(current_topology) = this.topology_accessor.current_topology().await else { - return Err(WasmClientError::UnavailableNetworkTopology.into()) + return Err(WasmClientError::UnavailableNetworkTopology.into()); }; let Some(mix) = current_topology.find_mix_by_identity(&mixnode_identity) else { diff --git a/clients/webassembly/src/client/mod.rs b/clients/webassembly/src/client/mod.rs index 74af0c2c8e..733521ea97 100644 --- a/clients/webassembly/src/client/mod.rs +++ b/clients/webassembly/src/client/mod.rs @@ -14,7 +14,6 @@ use crate::storage::traits::FullWasmClientStorage; use crate::storage::ClientStorage; use crate::topology::WasmNymTopology; use js_sys::Promise; -use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient}; use nym_client_core::client::base_client::{ BaseClientBuilder, ClientInput, ClientOutput, ClientState, }; @@ -26,6 +25,7 @@ use nym_task::TaskManager; use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider}; use nym_topology::NymTopology; use nym_validator_client::client::IdentityKey; +use nym_validator_client::QueryReqwestRpcNyxdClient; use rand::rngs::OsRng; use rand::RngCore; use std::sync::Arc; @@ -152,7 +152,7 @@ impl NymClientBuilder { let maybe_topology_provider = self.topology_provider(); let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> = - BaseClientBuilder::, _>::new( + BaseClientBuilder::::new( &self.config.base, storage, None, diff --git a/clients/webassembly/src/encoded_payload_helper.rs b/clients/webassembly/src/encoded_payload_helper.rs index 84f463ec58..17ae92fbf8 100644 --- a/clients/webassembly/src/encoded_payload_helper.rs +++ b/clients/webassembly/src/encoded_payload_helper.rs @@ -52,7 +52,7 @@ pub fn encode_payload_with_headers( Ok(metadata) => { let metadata = metadata.as_bytes().to_vec(); let size = (metadata.len() as u64).to_be_bytes().to_vec(); - Ok(vec![size, metadata, payload].concat()) + Ok([size, metadata, payload].concat()) } Err(e) => Err(JsValue::from(JsError::new( format!("Could not encode message: {}", e).as_str(), diff --git a/clients/webassembly/src/helpers.rs b/clients/webassembly/src/helpers.rs index 19557c8fb3..68831dd19b 100644 --- a/clients/webassembly/src/helpers.rs +++ b/clients/webassembly/src/helpers.rs @@ -8,7 +8,7 @@ use js_sys::Promise; use nym_client_core::client::replies::reply_storage::browser_backend; use nym_client_core::config; use nym_client_core::init::helpers::current_gateways; -use nym_client_core::init::{setup_gateway_from, GatewaySetup, InitialisationDetails}; +use nym_client_core::init::{setup_gateway_from, GatewaySetup, InitialisationResult}; use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_topology::{gateway, NymTopology}; @@ -82,14 +82,14 @@ async fn setup_gateway( client_store: &ClientStorage, chosen_gateway: Option, gateways: &[gateway::Node], -) -> Result { +) -> Result { let setup = if client_store.has_full_gateway_info().await? { GatewaySetup::MustLoad } else { GatewaySetup::new_fresh(chosen_gateway.clone(), None) }; - setup_gateway_from(&setup, client_store, client_store, false, Some(gateways)) + setup_gateway_from(setup, client_store, client_store, false, Some(gateways)) .await .map_err(Into::into) } @@ -98,7 +98,7 @@ pub(crate) async fn setup_gateway_from_api( client_store: &ClientStorage, chosen_gateway: Option, nym_apis: &[Url], -) -> Result { +) -> Result { let mut rng = thread_rng(); let gateways = current_gateways(&mut rng, nym_apis).await?; setup_gateway(client_store, chosen_gateway, &gateways).await @@ -108,7 +108,7 @@ pub(crate) async fn setup_from_topology( explicit_gateway: Option, topology: &NymTopology, client_store: &ClientStorage, -) -> Result { +) -> Result { let gateways = topology.gateways(); setup_gateway(client_store, explicit_gateway, gateways).await } diff --git a/clients/webassembly/src/lib.rs b/clients/webassembly/src/lib.rs index af6d657940..a4d3aeaad4 100644 --- a/clients/webassembly/src/lib.rs +++ b/clients/webassembly/src/lib.rs @@ -1,6 +1,10 @@ // Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +// After reading https://github.com/rust-lang/rust-clippy/issues/11382 +// I suspect we *maybe* have hit a false positive, but I'm not sure. +#![allow(clippy::arc_with_non_send_sync)] + use wasm_bindgen::prelude::*; #[cfg(target_arch = "wasm32")] diff --git a/clients/webassembly/src/tester/ephemeral_receiver.rs b/clients/webassembly/src/tester/ephemeral_receiver.rs index 4f2ce3cd8a..2d3117addd 100644 --- a/clients/webassembly/src/tester/ephemeral_receiver.rs +++ b/clients/webassembly/src/tester/ephemeral_receiver.rs @@ -10,6 +10,7 @@ use std::collections::HashSet; use std::time::Duration; use tokio::sync::MutexGuard as AsyncMutexGuard; use wasm_utils::{console_error, console_log, console_warn}; +use wasmtimer::tokio::sleep; pub(crate) struct EphemeralTestReceiver<'a> { sent_packets: u32, @@ -57,7 +58,7 @@ impl<'a> EphemeralTestReceiver<'a> { let Some(received_packet) = packet else { // can't do anything more... console_error!("packet receiver has stopped processing results!"); - return true + return true; }; match received_packet { Received::Message(msg) => { @@ -90,7 +91,7 @@ impl<'a> EphemeralTestReceiver<'a> { } pub(crate) async fn perform_test(mut self) -> NodeTestResult { - let mut timeout_fut = wasm_timer::Delay::new(self.timeout_duration); + let mut timeout_fut = sleep(self.timeout_duration); loop { tokio::select! { diff --git a/clients/webassembly/src/tester/mod.rs b/clients/webassembly/src/tester/mod.rs index 1ce59a4024..ba70062f3b 100644 --- a/clients/webassembly/src/tester/mod.rs +++ b/clients/webassembly/src/tester/mod.rs @@ -12,10 +12,9 @@ use crate::tester::helpers::{ use crate::topology::WasmNymTopology; use futures::channel::mpsc; use js_sys::Promise; -use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient}; use nym_bandwidth_controller::BandwidthController; use nym_client_core::client::key_manager::ManagedKeys; -use nym_client_core::init::InitialisationDetails; +use nym_client_core::init::{InitialisationDetails, InitialisationResult}; use nym_credential_storage::ephemeral_storage::EphemeralStorage; use nym_gateway_client::GatewayClient; use nym_node_tester_utils::receiver::SimpleMessageReceiver; @@ -27,6 +26,7 @@ use nym_sphinx::preparer::PreparedFragment; use nym_task::TaskManager; use nym_topology::NymTopology; use nym_validator_client::client::IdentityKey; +use nym_validator_client::QueryReqwestRpcNyxdClient; use rand::rngs::OsRng; use std::collections::HashSet; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; @@ -42,7 +42,7 @@ pub(crate) mod helpers; pub type NodeTestMessage = TestMessage; type LockedGatewayClient = - Arc, EphemeralStorage>>>; + Arc>>; pub(crate) const DEFAULT_TEST_TIMEOUT: Duration = Duration::from_secs(10); pub(crate) const DEFAULT_TEST_PACKETS: u32 = 20; @@ -78,8 +78,7 @@ pub struct NymNodeTesterBuilder { base_topology: NymTopology, // unimplemented - bandwidth_controller: - Option, EphemeralStorage>>, + bandwidth_controller: Option>, } fn address(keys: &ManagedKeys, gateway_identity: NodeIdentity) -> Recipient { @@ -130,9 +129,9 @@ impl NymNodeTesterBuilder { async fn gateway_info( &self, client_store: &ClientStorage, - ) -> Result { + ) -> Result { if let Ok(loaded) = InitialisationDetails::try_load(client_store, client_store).await { - Ok(loaded) + Ok(loaded.into()) } else { setup_from_topology(self.gateway.clone(), &self.base_topology, client_store).await } @@ -148,26 +147,37 @@ impl NymNodeTesterBuilder { }; let client_store = ClientStorage::new_async(&storage_id, None).await?; - - let init_details = self.gateway_info(&client_store).await?; + let initialisation_result = self.gateway_info(&client_store).await?; + let init_details = initialisation_result.details; + let managed_keys = init_details.managed_keys; let gateway_endpoint = init_details.gateway_details; let gateway_identity = gateway_endpoint.try_get_gateway_identity_key()?; - let managed_keys = init_details.managed_keys; let (mixnet_message_sender, mixnet_message_receiver) = mpsc::unbounded(); let (ack_sender, ack_receiver) = mpsc::unbounded(); - let mut gateway_client = GatewayClient::new( - gateway_endpoint.gateway_listener, - managed_keys.identity_keypair(), - gateway_identity, - Some(managed_keys.must_get_gateway_shared_key()), - mixnet_message_sender, - ack_sender, - Duration::from_secs(10), - self.bandwidth_controller.take(), - task_manager.subscribe(), - ); + let mut gateway_client = + if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client { + existing_client.upgrade( + mixnet_message_sender, + ack_sender, + Duration::from_secs(10), + self.bandwidth_controller.take(), + task_manager.subscribe(), + ) + } else { + GatewayClient::new( + gateway_endpoint.gateway_listener, + managed_keys.identity_keypair(), + gateway_identity, + Some(managed_keys.must_get_gateway_shared_key()), + mixnet_message_sender, + ack_sender, + Duration::from_secs(10), + self.bandwidth_controller.take(), + task_manager.subscribe(), + ) + }; gateway_client.set_disabled_credentials_mode(true); gateway_client.authenticate_and_start().await?; diff --git a/common/async-file-watcher/Cargo.toml b/common/async-file-watcher/Cargo.toml index 05e7035cf2..0a44f239cf 100644 --- a/common/async-file-watcher/Cargo.toml +++ b/common/async-file-watcher/Cargo.toml @@ -8,5 +8,5 @@ edition = "2021" [dependencies] log = "0.4" tokio = { workspace = true, features = ["time"] } -futures = "0.3" +futures = { workspace = true } notify = "5.1.0" diff --git a/common/bandwidth-controller/Cargo.toml b/common/bandwidth-controller/Cargo.toml index 5b168d8b9f..3ce1f01454 100644 --- a/common/bandwidth-controller/Cargo.toml +++ b/common/bandwidth-controller/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" bip39 = { workspace = true } rand = "0.7.3" thiserror = "1.0" -url = "2.2" +url = { workspace = true } nym-coconut-interface = { path = "../coconut-interface" } nym-credential-storage = { path = "../credential-storage" } @@ -20,4 +20,4 @@ nym-validator-client = { path = "../client-libs/validator-client", default-featu [target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client] path = "../client-libs/validator-client" -features = ["signing"] +features = ["http-client"] diff --git a/common/bandwidth-controller/src/acquire/mod.rs b/common/bandwidth-controller/src/acquire/mod.rs index cdf72c3b00..c03ae8e90a 100644 --- a/common/bandwidth-controller/src/acquire/mod.rs +++ b/common/bandwidth-controller/src/acquire/mod.rs @@ -8,11 +8,11 @@ use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES}; use nym_credentials::coconut::utils::obtain_aggregate_signature; 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::coconut::all_coconut_api_clients; +use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; 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}; use std::str::FromStr; @@ -21,7 +21,7 @@ pub mod state; pub async fn deposit(client: &C, amount: Coin) -> Result where - C: CoconutBandwidthSigningClient, + C: CoconutBandwidthSigningClient + Sync, { let mut rng = OsRng; let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng)); @@ -70,7 +70,8 @@ where .get_current_epoch_threshold() .await? .ok_or(BandwidthControllerError::NoThreshold)?; - let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(client, epoch_id).await?; + + let coconut_api_clients = all_coconut_api_clients(client, epoch_id).await?; let signature = obtain_aggregate_signature( &state.params, diff --git a/common/bandwidth-controller/src/error.rs b/common/bandwidth-controller/src/error.rs index e9a862ebff..5f464c82e5 100644 --- a/common/bandwidth-controller/src/error.rs +++ b/common/bandwidth-controller/src/error.rs @@ -6,15 +6,18 @@ use nym_credential_storage::error::StorageError; use nym_credentials::error::Error as CredentialsError; use nym_crypto::asymmetric::encryption::KeyRecoveryError; use nym_crypto::asymmetric::identity::Ed25519RecoveryError; +use nym_validator_client::coconut::CoconutApiError; use nym_validator_client::error::ValidatorClientError; use thiserror::Error; #[derive(Debug, Error)] pub enum BandwidthControllerError { - #[cfg(not(target_arch = "wasm32"))] #[error("Nyxd error: {0}")] Nyxd(#[from] nym_validator_client::nyxd::error::NyxdError), + #[error("coconut api query failure: {0}")] + CoconutApiError(#[from] CoconutApiError), + #[error("There was a credential storage error - {0}")] CredentialStorageError(Box), diff --git a/common/bandwidth-controller/src/lib.rs b/common/bandwidth-controller/src/lib.rs index 66a6f0a6d2..d9a714b263 100644 --- a/common/bandwidth-controller/src/lib.rs +++ b/common/bandwidth-controller/src/lib.rs @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::BandwidthControllerError; - use nym_credential_storage::error::StorageError; use nym_credential_storage::storage::Storage; - +use nym_validator_client::coconut::all_coconut_api_clients; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use std::str::FromStr; use { nym_coconut_interface::Base58, @@ -14,17 +14,8 @@ use { }, }; -#[cfg(not(target_arch = "wasm32"))] -use nym_validator_client::nyxd::traits::DkgQueryClient; - -#[cfg(target_arch = "wasm32")] -use crate::wasm_mockups::DkgQueryClient; - -#[cfg(not(target_arch = "wasm32"))] pub mod acquire; pub mod error; -#[cfg(target_arch = "wasm32")] -pub mod wasm_mockups; pub struct BandwidthController { storage: St, @@ -64,12 +55,8 @@ impl BandwidthController { let epoch_id = u64::from_str(&bandwidth_credential.epoch_id) .map_err(|_| StorageError::InconsistentData)?; - #[cfg(not(target_arch = "wasm32"))] - let coconut_api_clients = - nym_validator_client::CoconutApiClient::all_coconut_api_clients(&self.client, epoch_id) - .await?; - #[cfg(target_arch = "wasm32")] - let coconut_api_clients = vec![]; + let coconut_api_clients = all_coconut_api_clients(&self.client, epoch_id).await?; + let verification_key = obtain_aggregate_verification_key(&coconut_api_clients).await?; // the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff) diff --git a/common/bandwidth-controller/src/wasm_mockups.rs b/common/bandwidth-controller/src/wasm_mockups.rs deleted file mode 100644 index be7ee06a79..0000000000 --- a/common/bandwidth-controller/src/wasm_mockups.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2022-2023 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use std::marker::PhantomData; - -pub struct DirectSigningNyxdClient {} - -pub trait DkgQueryClient {} - -// impl CosmWasmClient for DirectSigningNyxdClient {} - -#[derive(Clone)] -pub struct Client { - _phantom: PhantomData, -} - -impl DkgQueryClient for Client {} diff --git a/common/bin-common/src/build_information/mod.rs b/common/bin-common/src/build_information/mod.rs index d4fa7dd5c6..064f8a60fa 100644 --- a/common/bin-common/src/build_information/mod.rs +++ b/common/bin-common/src/build_information/mod.rs @@ -5,9 +5,13 @@ // and be used by our smart contracts use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; #[derive(Debug)] pub struct BinaryBuildInformation { + /// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable. + pub binary_name: &'static str, + // VERGEN_BUILD_TIMESTAMP /// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`. pub build_timestamp: &'static str, @@ -43,8 +47,9 @@ pub struct BinaryBuildInformation { impl BinaryBuildInformation { // explicitly require the build_version to be passed as it's binary specific - pub const fn new(build_version: &'static str) -> Self { + pub const fn new(binary_name: &'static str, build_version: &'static str) -> Self { BinaryBuildInformation { + binary_name, build_timestamp: env!("VERGEN_BUILD_TIMESTAMP"), build_version, commit_sha: env!("VERGEN_GIT_SHA"), @@ -58,6 +63,7 @@ impl BinaryBuildInformation { pub fn to_owned(&self) -> BinaryBuildInformationOwned { BinaryBuildInformationOwned { + binary_name: self.binary_name.to_owned(), build_timestamp: self.build_timestamp.to_owned(), build_version: self.build_version.to_owned(), commit_sha: self.commit_sha.to_owned(), @@ -70,39 +76,15 @@ impl BinaryBuildInformation { } pub fn pretty_print(&self) -> String { - format!( - r#" -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -{:<20}{} -"#, - "Build Timestamp:", - self.build_timestamp, - "Build Version:", - self.build_version, - "Commit SHA:", - self.commit_sha, - "Commit Date:", - self.commit_timestamp, - "Commit Branch:", - self.commit_branch, - "rustc Version:", - self.rustc_version, - "rustc Channel:", - self.rustc_channel, - "cargo Profile:", - self.cargo_profile, - ) + self.to_owned().to_string() } } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct BinaryBuildInformationOwned { + /// Provides the name of the binary, i.e. the content of `CARGO_PKG_NAME` environmental variable. + pub binary_name: String, + // VERGEN_BUILD_TIMESTAMP /// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`. pub build_timestamp: String, @@ -135,3 +117,62 @@ pub struct BinaryBuildInformationOwned { /// Provides the cargo profile that was used for the build, for example `debug`. pub cargo_profile: String, } + +impl Display for BinaryBuildInformationOwned { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + r#" +{:<20}{} +{:<20}{} +{:<20}{} +{:<20}{} +{:<20}{} +{:<20}{} +{:<20}{} +{:<20}{} +{:<20}{} +"#, + "Binary Name:", + self.binary_name, + "Build Timestamp:", + self.build_timestamp, + "Build Version:", + self.build_version, + "Commit SHA:", + self.commit_sha, + "Commit Date:", + self.commit_timestamp, + "Commit Branch:", + self.commit_branch, + "rustc Version:", + self.rustc_version, + "rustc Channel:", + self.rustc_channel, + "cargo Profile:", + self.cargo_profile, + ) + } +} + +// since this macro will get expanded at the callsite, it will pull in correct binary version +#[macro_export] +macro_rules! bin_info { + () => { + $crate::build_information::BinaryBuildInformation::new( + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + ) + }; +} + +#[macro_export] +macro_rules! bin_info_owned { + () => { + $crate::build_information::BinaryBuildInformation::new( + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + ) + .to_owned() + }; +} diff --git a/common/client-core/Cargo.toml b/common/client-core/Cargo.toml index d5ae32dbd2..56733b5ab8 100644 --- a/common/client-core/Cargo.toml +++ b/common/client-core/Cargo.toml @@ -10,27 +10,30 @@ rust-version = "1.66" [dependencies] async-trait = { workspace = true } base64 = "0.21.2" -dirs = "4.0" +cfg-if = "1.0.0" dashmap = "5.4.0" -futures = "0.3" +dirs = "4.0" +futures = { workspace = true } humantime-serde = "1.0" log = { workspace = true } rand = { version = "0.7.3", features = ["wasm-bindgen"] } +reqwest = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } sha2 = "0.10.6" tap = "1.0.1" thiserror = "1.0.34" -url = { version ="2.2", features = ["serde"] } -tungstenite = { version = "0.13.0", default-features = false } -tokio = { version = "1.24.1", features = ["macros"]} time = "0.3.17" +tokio = { version = "1.24.1", features = ["macros"]} +tungstenite = { version = "0.13.0", default-features = false } +url = { workspace = true, features = ["serde"] } zeroize = { workspace = true } # internal nym-bandwidth-controller = { path = "../bandwidth-controller" } nym-config = { path = "../config" } nym-crypto = { path = "../crypto" } +nym-explorer-api-requests = { path = "../../explorer-api/explorer-api-requests" } nym-gateway-client = { path = "../client-libs/gateway-client" } #gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm", "coconut"] } nym-gateway-requests = { path = "../../gateway/gateway-requests" } @@ -43,10 +46,6 @@ nym-task = { path = "../task" } nym-credential-storage = { path = "../credential-storage" } nym-network-defaults = { path = "../network-defaults" } -[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client] -path = "../client-libs/validator-client" -features = ["signing", "http-client"] - [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream] version = "0.1.11" features = ["time"] @@ -69,9 +68,9 @@ version = "0.4" [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen] version = "0.2.83" -[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-timer] -git = "https://github.com/mmsinclair/wasm-timer" -rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6" +[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer] +workspace = true +features = ["tokio"] [target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers] version = "0.2.4" diff --git a/common/client-core/src/client/base_client/mod.rs b/common/client-core/src/client/base_client/mod.rs index aecb7eb5c1..02615f2b95 100644 --- a/common/client-core/src/client/base_client/mod.rs +++ b/common/client-core/src/client/base_client/mod.rs @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use super::received_buffer::ReceivedBufferMessage; +use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider; +use crate::client::base_client::storage::gateway_details::GatewayDetailsStore; use crate::client::base_client::storage::MixnetClientStorage; use crate::client::cover_traffic_stream::LoopCoverTrafficStream; use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender}; use crate::client::key_manager::persistence::KeyStore; -use crate::client::key_manager::ManagedKeys; use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController}; use crate::client::real_messages_control; use crate::client::real_messages_control::RealMessagesController; @@ -22,8 +23,9 @@ use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider; use crate::client::topology_control::{ TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, }; -use crate::config::{Config, DebugConfig, GatewayEndpointConfig}; +use crate::config::{Config, DebugConfig}; use crate::error::ClientCoreError; +use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails, InitialisationResult}; use crate::{config, spawn_future}; use futures::channel::mpsc; use log::{debug, info}; @@ -42,18 +44,11 @@ use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver}; use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths}; use nym_task::{TaskClient, TaskManager}; use nym_topology::provider_trait::TopologyProvider; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use std::sync::Arc; use tap::TapFallible; use url::Url; -#[cfg(target_arch = "wasm32")] -use nym_bandwidth_controller::wasm_mockups::DkgQueryClient; - -use crate::client::base_client::storage::gateway_details::GatewayDetailsStore; -use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails}; -#[cfg(not(target_arch = "wasm32"))] -use nym_validator_client::nyxd::traits::DkgQueryClient; - #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))] pub mod non_wasm_helpers; @@ -199,16 +194,13 @@ where // note: do **NOT** make this method public as its only valid usage is from within `start_base` // because it relies on the crypto keys being already loaded - fn mix_address( - managed_keys: &ManagedKeys, - gateway_config: &GatewayEndpointConfig, - ) -> Recipient { + fn mix_address(details: &InitialisationDetails) -> Recipient { Recipient::new( - *managed_keys.identity_public_key(), - *managed_keys.encryption_public_key(), + *details.managed_keys.identity_public_key(), + *details.managed_keys.encryption_public_key(), // TODO: below only works under assumption that gateway address == gateway id // (which currently is true) - NodeIdentity::from_base58_string(&gateway_config.gateway_id).unwrap(), + NodeIdentity::from_base58_string(&details.gateway_details.gateway_id).unwrap(), ) } @@ -293,8 +285,7 @@ where async fn start_gateway_client( config: &Config, - gateway_config: GatewayEndpointConfig, - managed_keys: &ManagedKeys, + initialisation_result: InitialisationResult, bandwidth_controller: Option>, mixnet_message_sender: MixnetMessageSender, ack_sender: AcknowledgementSender, @@ -304,24 +295,39 @@ where ::StorageError: Send + Sync + 'static, ::StorageError: Send + Sync + 'static, { - let gateway_address = gateway_config.gateway_listener.clone(); - let gateway_id = gateway_config.gateway_id; + let managed_keys = initialisation_result.details.managed_keys; - // TODO: in theory, at this point, this should be infallible - let gateway_identity = identity::PublicKey::from_base58_string(gateway_id) - .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; + let mut gateway_client = + if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client { + existing_client.upgrade( + mixnet_message_sender, + ack_sender, + config.debug.gateway_connection.gateway_response_timeout, + bandwidth_controller, + shutdown, + ) + } else { + let gateway_config = initialisation_result.details.gateway_details; - let mut gateway_client = GatewayClient::new( - gateway_address, - managed_keys.identity_keypair(), - gateway_identity, - Some(managed_keys.must_get_gateway_shared_key()), - mixnet_message_sender, - ack_sender, - config.debug.gateway_connection.gateway_response_timeout, - bandwidth_controller, - shutdown, - ); + let gateway_address = gateway_config.gateway_listener.clone(); + let gateway_id = gateway_config.gateway_id; + + // TODO: in theory, at this point, this should be infallible + let gateway_identity = identity::PublicKey::from_base58_string(gateway_id) + .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; + + GatewayClient::new( + gateway_address, + managed_keys.identity_keypair(), + gateway_identity, + Some(managed_keys.must_get_gateway_shared_key()), + mixnet_message_sender, + ack_sender, + config.debug.gateway_connection.gateway_response_timeout, + bandwidth_controller, + shutdown, + ) + }; gateway_client.set_disabled_credentials_mode(config.client.disabled_credentials_mode); @@ -339,14 +345,22 @@ where fn setup_topology_provider( custom_provider: Option>, + provider_from_config: config::TopologyStructure, nym_api_urls: Vec, ) -> Box { // if no custom provider was ... provided ..., create one using nym-api - custom_provider.unwrap_or_else(|| { - Box::new(NymApiTopologyProvider::new( + custom_provider.unwrap_or_else(|| match provider_from_config { + config::TopologyStructure::NymApi => Box::new(NymApiTopologyProvider::new( nym_api_urls, env!("CARGO_PKG_VERSION").to_string(), - )) + )), + config::TopologyStructure::GeoAware(group_by) => { + Box::new(GeoAwareTopologyProvider::new( + nym_api_urls, + env!("CARGO_PKG_VERSION").to_string(), + group_by, + )) + } }) } @@ -438,17 +452,23 @@ where Ok(mem_store) } - async fn initialise_keys_and_gateway(&self) -> Result + async fn initialise_keys_and_gateway( + setup_method: GatewaySetup, + key_store: &S::KeyStore, + details_store: &S::GatewayDetailsStore, + overwrite_data: bool, + validator_servers: Option<&[Url]>, + ) -> Result where ::StorageError: Sync + Send, ::StorageError: Sync + Send, { setup_gateway( - &self.setup_method, - self.client_store.key_store(), - self.client_store.gateway_details_store(), - false, - Some(&self.config.client.nym_api_urls), + setup_method, + key_store, + details_store, + overwrite_data, + validator_servers, ) .await } @@ -464,9 +484,14 @@ where info!("Starting nym client"); // derive (or load) client keys and gateway configuration - let details = self.initialise_keys_and_gateway().await?; - let gateway_config = details.gateway_details; - let managed_keys = details.managed_keys; + let init_res = Self::initialise_keys_and_gateway( + self.setup_method, + self.client_store.key_store(), + self.client_store.gateway_details_store(), + false, + Some(&self.config.client.nym_api_urls), + ) + .await?; let (reply_storage_backend, credential_store) = self.client_store.into_runtime_stores(); @@ -500,14 +525,15 @@ where let (reply_controller_sender, reply_controller_receiver) = reply_controller::requests::new_control_channels(); - let self_address = Self::mix_address(&managed_keys, &gateway_config); + let self_address = Self::mix_address(&init_res.details); + let ack_key = init_res.details.managed_keys.ack_key(); + let encryption_keys = init_res.details.managed_keys.encryption_keypair(); // the components are started in very specific order. Unless you know what you are doing, // do not change that. let gateway_client = Self::start_gateway_client( self.config, - gateway_config, - &managed_keys, + init_res, bandwidth_controller, mixnet_messages_sender, ack_sender, @@ -521,8 +547,10 @@ where let topology_provider = Self::setup_topology_provider( self.custom_topology_provider.take(), + self.config.debug.topology.topology_structure, self.config.get_nym_api_endpoints(), ); + Self::start_topology_refresher( topology_provider, self.config.debug.topology, @@ -532,7 +560,7 @@ where .await?; Self::start_received_messages_buffer_controller( - managed_keys.encryption_keypair(), + encryption_keys, received_buffer_request_receiver, mixnet_messages_receiver, reply_storage.key_storage(), @@ -557,7 +585,7 @@ where let controller_config = real_messages_control::Config::new( &self.config.debug, - managed_keys.ack_key(), + Arc::clone(&ack_key), self_address, ); @@ -584,7 +612,7 @@ where { Self::start_cover_traffic_stream( &self.config.debug, - managed_keys.ack_key(), + ack_key, self_address, shared_topology_accessor.clone(), message_sender, diff --git a/common/client-core/src/client/base_client/non_wasm_helpers.rs b/common/client-core/src/client/base_client/non_wasm_helpers.rs index 6b901f0f82..d0d2d5a058 100644 --- a/common/client-core/src/client/base_client/non_wasm_helpers.rs +++ b/common/client-core/src/client/base_client/non_wasm_helpers.rs @@ -10,8 +10,8 @@ use crate::error::ClientCoreError; use log::{error, info}; use nym_bandwidth_controller::BandwidthController; use nym_credential_storage::storage::Storage as CredentialStorage; -use nym_validator_client::nyxd::QueryNyxdClient; -use nym_validator_client::Client; +use nym_validator_client::nyxd; +use nym_validator_client::QueryHttpRpcNyxdClient; use std::path::Path; use std::{fs, io}; use time::OffsetDateTime; @@ -104,48 +104,38 @@ pub async fn setup_fs_reply_surb_backend>( pub fn create_bandwidth_controller( config: &Config, storage: St, -) -> BandwidthController, St> { +) -> BandwidthController { let nyxd_url = config .get_validator_endpoints() .pop() .expect("No nyxd validator endpoint provided"); - let api_url = config - .get_nym_api_endpoints() - .pop() - .expect("No validator api endpoint provided"); - create_bandwidth_controller_with_urls(nyxd_url, api_url, storage) + create_bandwidth_controller_with_urls(nyxd_url, storage) } pub fn create_bandwidth_controller_with_urls( nyxd_url: Url, - nym_api_url: Url, storage: St, -) -> BandwidthController, St> { - let client = default_query_dkg_client(nyxd_url, nym_api_url); +) -> BandwidthController { + let client = default_query_dkg_client(nyxd_url); BandwidthController::new(storage, client) } -pub fn default_query_dkg_client_from_config(config: &Config) -> Client { +pub fn default_query_dkg_client_from_config(config: &Config) -> QueryHttpRpcNyxdClient { let nyxd_url = config .get_validator_endpoints() .pop() .expect("No nyxd validator endpoint provided"); - let api_url = config - .get_nym_api_endpoints() - .pop() - .expect("No validator api endpoint provided"); - default_query_dkg_client(nyxd_url, api_url) + default_query_dkg_client(nyxd_url) } -pub fn default_query_dkg_client(nyxd_url: Url, nym_api_url: Url) -> Client { +pub fn default_query_dkg_client(nyxd_url: Url) -> QueryHttpRpcNyxdClient { let details = nym_network_defaults::NymNetworkDetails::new_from_env(); - let mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details) + let client_config = nyxd::Config::try_from_nym_network_details(&details) .expect("failed to construct validator client config"); // overwrite env configuration with config URLs - client_config = client_config.with_urls(nyxd_url, nym_api_url); - nym_validator_client::Client::new_query(client_config) + QueryHttpRpcNyxdClient::connect(client_config, nyxd_url.as_str()) .expect("Could not construct query client") } diff --git a/common/client-core/src/client/cover_traffic_stream.rs b/common/client-core/src/client/cover_traffic_stream.rs index 8784bfb7ba..c603926933 100644 --- a/common/client-core/src/client/cover_traffic_stream.rs +++ b/common/client-core/src/client/cover_traffic_stream.rs @@ -19,10 +19,10 @@ use std::time::Duration; use tokio::sync::mpsc::error::TrySendError; #[cfg(not(target_arch = "wasm32"))] -use tokio::time; +use tokio::time::{sleep, Sleep}; #[cfg(target_arch = "wasm32")] -use wasm_timer; +use wasmtimer::tokio::{sleep, Sleep}; pub struct LoopCoverTrafficStream where @@ -39,11 +39,7 @@ where /// Internal state, determined by `average_message_sending_delay`, /// used to keep track of when a next packet should be sent out. - #[cfg(not(target_arch = "wasm32"))] - next_delay: Pin>, - - #[cfg(target_arch = "wasm32")] - next_delay: Pin>, + next_delay: Pin>, /// Channel used for sending prepared nym packets to `MixTrafficController` that sends them /// out to the network without any further delays. @@ -90,17 +86,9 @@ where // The next interval value is `next_poisson_delay` after the one that just // yielded. - #[cfg(not(target_arch = "wasm32"))] - { - let now = self.next_delay.deadline(); - let next = now + next_poisson_delay; - self.next_delay.as_mut().reset(next); - } - - #[cfg(target_arch = "wasm32")] - { - self.next_delay.as_mut().reset(next_poisson_delay); - } + let now = self.next_delay.deadline(); + let next = now + next_poisson_delay; + self.next_delay.as_mut().reset(next); Poll::Ready(Some(())) } @@ -120,11 +108,7 @@ impl LoopCoverTrafficStream { ) -> Self { let rng = OsRng; - #[cfg(not(target_arch = "wasm32"))] - let next_delay = Box::pin(time::sleep(Default::default())); - - #[cfg(target_arch = "wasm32")] - let next_delay = Box::pin(wasm_timer::Delay::new(Default::default())); + let next_delay = Box::pin(sleep(Default::default())); LoopCoverTrafficStream { ack_key, @@ -142,12 +126,7 @@ impl LoopCoverTrafficStream { } fn set_next_delay(&mut self, amount: Duration) { - #[cfg(not(target_arch = "wasm32"))] - let next_delay = Box::pin(time::sleep(amount)); - - #[cfg(target_arch = "wasm32")] - let next_delay = Box::pin(wasm_timer::Delay::new(amount)); - + let next_delay = Box::pin(sleep(amount)); self.next_delay = next_delay; } diff --git a/common/client-core/src/client/helpers/wasm.rs b/common/client-core/src/client/helpers/wasm.rs index f04dd64028..5046463ee5 100644 --- a/common/client-core/src/client/helpers/wasm.rs +++ b/common/client-core/src/client/helpers/wasm.rs @@ -2,13 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use std::time::Duration; -use wasm_timer; -pub use wasm_timer::*; +pub use wasmtimer::{std::Instant, tokio::*}; pub type IntervalStream = gloo_timers::future::IntervalStream; pub(crate) fn get_time_now() -> Instant { - wasm_timer::Instant::now() + Instant::now() } pub(crate) fn new_interval_stream(polling_rate: Duration) -> IntervalStream { diff --git a/common/client-core/src/client/mix_traffic.rs b/common/client-core/src/client/mix_traffic.rs index 8e7f7ce2fb..cb12a55d85 100644 --- a/common/client-core/src/client/mix_traffic.rs +++ b/common/client-core/src/client/mix_traffic.rs @@ -3,15 +3,10 @@ use crate::spawn_future; use log::*; +use nym_credential_storage::storage::Storage; use nym_gateway_client::GatewayClient; use nym_sphinx::forwarding::packet::MixPacket; - -use nym_credential_storage::storage::Storage; -#[cfg(not(target_arch = "wasm32"))] -use nym_validator_client::nyxd::traits::DkgQueryClient; - -#[cfg(target_arch = "wasm32")] -use nym_bandwidth_controller::wasm_mockups::DkgQueryClient; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; pub type BatchMixMessageSender = tokio::sync::mpsc::Sender>; pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver>; diff --git a/common/client-core/src/client/real_messages_control/real_traffic_stream.rs b/common/client-core/src/client/real_messages_control/real_traffic_stream.rs index 44c9810e70..a160a51894 100644 --- a/common/client-core/src/client/real_messages_control/real_traffic_stream.rs +++ b/common/client-core/src/client/real_messages_control/real_traffic_stream.rs @@ -27,9 +27,10 @@ use std::sync::Arc; use std::time::Duration; #[cfg(not(target_arch = "wasm32"))] -use tokio::time; +use tokio::time::{sleep, Sleep}; + #[cfg(target_arch = "wasm32")] -use wasm_timer; +use wasmtimer::tokio::{sleep, Sleep}; mod sending_delay_controller; @@ -82,11 +83,7 @@ where /// Internal state, determined by `average_message_sending_delay`, /// used to keep track of when a next packet should be sent out. - #[cfg(not(target_arch = "wasm32"))] - next_delay: Option>>, - - #[cfg(target_arch = "wasm32")] - next_delay: Option>>, + next_delay: Option>>, // To make sure we don't overload the mix_tx channel, we limit the rate we are pushing // messages. @@ -373,17 +370,9 @@ where // The next interval value is `next_poisson_delay` after the one that just // yielded. - #[cfg(not(target_arch = "wasm32"))] - { - let now = next_delay.deadline(); - let next = now + next_poisson_delay; - next_delay.as_mut().reset(next); - } - - #[cfg(target_arch = "wasm32")] - { - next_delay.as_mut().reset(next_poisson_delay); - } + let now = next_delay.deadline(); + let next = now + next_poisson_delay; + next_delay.as_mut().reset(next); // On every iteration we get new messages from upstream. Given that these come bunched // in `Vec`, this ensures that on average we will fetch messages faster than we can @@ -421,12 +410,7 @@ where self.config.traffic.message_sending_average_delay, ); - #[cfg(not(target_arch = "wasm32"))] - let next_delay = Box::pin(time::sleep(sampled)); - - #[cfg(target_arch = "wasm32")] - let next_delay = Box::pin(wasm_timer::Delay::new(sampled)); - + let next_delay = Box::pin(sleep(sampled)); self.next_delay = Some(next_delay); Poll::Pending diff --git a/common/client-core/src/client/topology_control/geo_aware_provider.rs b/common/client-core/src/client/topology_control/geo_aware_provider.rs new file mode 100644 index 0000000000..5239f87411 --- /dev/null +++ b/common/client-core/src/client/topology_control/geo_aware_provider.rs @@ -0,0 +1,434 @@ +use std::{collections::HashMap, fmt}; + +use log::{debug, error, info}; +use nym_explorer_api_requests::{PrettyDetailedGatewayBond, PrettyDetailedMixNodeBond}; +use nym_network_defaults::var_names::EXPLORER_API; +use nym_topology::{ + nym_topology_from_detailed, + provider_trait::{async_trait, TopologyProvider}, + NymTopology, +}; +use nym_validator_client::client::{IdentityKey, MixId}; +use rand::{prelude::SliceRandom, thread_rng}; +use serde::{Deserialize, Serialize}; +use tap::TapOptional; +use url::Url; + +use crate::config::GroupBy; + +const MIN_NODES_PER_LAYER: usize = 1; + +#[cfg(target_arch = "wasm32")] +fn reqwest_client() -> Option { + reqwest::Client::builder().build().ok() +} + +#[cfg(not(target_arch = "wasm32"))] +fn reqwest_client() -> Option { + reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(5)) + .build() + .ok() +} + +// TODO: create a explorer-api-client +async fn fetch_mixnodes_from_explorer_api() -> Option> { + let explorer_api_url = std::env::var(EXPLORER_API).ok()?; + let explorer_api_url = Url::parse(&explorer_api_url) + .ok()? + .join("v1/mix-nodes") + .ok()?; + + debug!("Fetching: {}", explorer_api_url); + reqwest_client()? + .get(explorer_api_url) + .send() + .await + .ok()? + .json::>() + .await + .ok() +} + +// TODO: create a explorer-api-client +async fn fetch_gateways_from_explorer_api() -> Option> { + let explorer_api_url = std::env::var(EXPLORER_API).ok()?; + let explorer_api_url = Url::parse(&explorer_api_url) + .ok()? + .join("v1/gateways") + .ok()?; + + debug!("Fetching: {}", explorer_api_url); + reqwest_client()? + .get(explorer_api_url) + .send() + .await + .ok()? + .json::>() + .await + .ok() +} + +#[derive(Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Debug)] +pub enum CountryGroup { + Europe, + NorthAmerica, + SouthAmerica, + Oceania, + Asia, + Africa, + Unknown, +} + +impl CountryGroup { + // We map contry codes into group, which initially are continent codes to a first approximation, + // but we do it manually to reserve the right to tweak this distribution for our purposes. + fn new(country_code: &str) -> Self { + let country_code = country_code.to_uppercase(); + use CountryGroup::*; + match country_code.as_ref() { + // Europe + "AT" => Europe, + "BG" => Europe, + "CH" => Europe, + "CY" => Europe, + "CZ" => Europe, + "DE" => Europe, + "DK" => Europe, + "ES" => Europe, + "FI" => Europe, + "FR" => Europe, + "GB" => Europe, + "GR" => Europe, + "IE" => Europe, + "IT" => Europe, + "LT" => Europe, + "LU" => Europe, + "LV" => Europe, + "MD" => Europe, + "MT" => Europe, + "NL" => Europe, + "NO" => Europe, + "PL" => Europe, + "RO" => Europe, + "SE" => Europe, + "SK" => Europe, + "TR" => Europe, + "UA" => Europe, + + // North America + "CA" => NorthAmerica, + "MX" => NorthAmerica, + "US" => NorthAmerica, + + // South America + "AR" => SouthAmerica, + "BR" => SouthAmerica, + "CL" => SouthAmerica, + "CO" => SouthAmerica, + "CR" => SouthAmerica, + "GT" => SouthAmerica, + + // Oceania + "AU" => Oceania, + + // Asia + "AM" => Asia, + "BH" => Asia, + "CN" => Asia, + "GE" => Asia, + "HK" => Asia, + "ID" => Asia, + "IL" => Asia, + "IN" => Asia, + "JP" => Asia, + "KH" => Asia, + "KR" => Asia, + "KZ" => Asia, + "MY" => Asia, + "RU" => Asia, + "SG" => Asia, + "TH" => Asia, + "VN" => Asia, + + // Africa + "SC" => Africa, + "UG" => Africa, + "ZA" => Africa, + + // And group level codes work too + "EU" => Europe, + "NA" => NorthAmerica, + "SA" => SouthAmerica, + "OC" => Oceania, + "AS" => Asia, + "AF" => Africa, + + // And some aliases + "EUROPE" => Europe, + "NORTHAMERICA" => NorthAmerica, + "SOUTHAMERICA" => SouthAmerica, + "OCEANIA" => Oceania, + "ASIA" => Asia, + "AFRICA" => Africa, + + _ => { + info!("Unknown country code: {}", country_code); + Unknown + } + } + } +} + +impl fmt::Display for CountryGroup { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use CountryGroup::*; + match self { + Europe => write!(f, "EU"), + NorthAmerica => write!(f, "NA"), + SouthAmerica => write!(f, "SA"), + Oceania => write!(f, "OC"), + Asia => write!(f, "AS"), + Africa => write!(f, "AF"), + Unknown => write!(f, "Unknown"), + } + } +} + +impl std::str::FromStr for CountryGroup { + type Err = (); + + fn from_str(s: &str) -> Result { + let group = CountryGroup::new(s); + if group == CountryGroup::Unknown { + Err(()) + } else { + Ok(group) + } + } +} + +impl CountryGroup { + #[allow(unused)] + fn known(self) -> Option { + use CountryGroup::*; + match self { + Europe | NorthAmerica | SouthAmerica | Oceania | Asia | Africa => Some(self), + Unknown => None, + } + } +} + +fn group_mixnodes_by_country_code( + mixnodes: Vec, +) -> HashMap> { + mixnodes + .into_iter() + .fold(HashMap::>::new(), |mut acc, m| { + if let Some(ref location) = m.location { + let country_code = location.two_letter_iso_country_code.clone(); + let group_code = CountryGroup::new(country_code.as_str()); + let mixnodes = acc.entry(group_code).or_insert_with(Vec::new); + mixnodes.push(m.mix_id); + } + acc + }) +} + +fn group_gateways_by_country_code( + gateways: Vec, +) -> HashMap> { + gateways.into_iter().fold( + HashMap::>::new(), + |mut acc, g| { + if let Some(ref location) = g.location { + let country_code = location.two_letter_iso_country_code.clone(); + let group_code = CountryGroup::new(country_code.as_str()); + let gateways = acc.entry(group_code).or_insert_with(Vec::new); + gateways.push(g.gateway.identity_key) + } + acc + }, + ) +} + +fn log_mixnode_distribution(mixnodes: &HashMap>) { + let mixnode_distribution = mixnodes + .iter() + .map(|(k, v)| format!("{}: {}", k, v.len())) + .collect::>() + .join(", "); + debug!("Mixnode distribution - {}", mixnode_distribution); +} + +fn log_gateway_distribution(gateways: &HashMap>) { + let gateway_distribution = gateways + .iter() + .map(|(k, v)| format!("{}: {}", k, v.len())) + .collect::>() + .join(", "); + debug!("Gateway distribution - {}", gateway_distribution); +} + +fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> { + let mixes = topology.mixes(); + if mixes.keys().len() < 3 { + error!("Layer is missing in topology!"); + return Err(()); + } + for (layer, mixnodes) in mixes { + debug!("Layer {:?} has {} mixnodes", layer, mixnodes.len()); + if mixnodes.len() < MIN_NODES_PER_LAYER { + error!( + "There are only {} mixnodes in layer {:?}", + mixnodes.len(), + layer + ); + return Err(()); + } + } + Ok(()) +} + +pub struct GeoAwareTopologyProvider { + validator_client: nym_validator_client::client::NymApiClient, + filter_on: GroupBy, + client_version: String, +} + +impl GeoAwareTopologyProvider { + pub fn new( + mut nym_api_urls: Vec, + client_version: String, + filter_on: GroupBy, + ) -> GeoAwareTopologyProvider { + log::info!( + "Creating geo-aware topology provider with filter on {}", + filter_on + ); + nym_api_urls.shuffle(&mut thread_rng()); + + GeoAwareTopologyProvider { + validator_client: nym_validator_client::client::NymApiClient::new( + nym_api_urls[0].clone(), + ), + filter_on, + client_version, + } + } + + async fn get_topology(&self) -> Option { + let mixnodes = match self.validator_client.get_cached_active_mixnodes().await { + Err(err) => { + error!("failed to get network mixnodes - {err}"); + return None; + } + Ok(mixes) => mixes, + }; + + let gateways = match self.validator_client.get_cached_gateways().await { + Err(err) => { + error!("failed to get network gateways - {err}"); + return None; + } + Ok(gateways) => gateways, + }; + + // Also fetch mixnodes cached by explorer-api, with the purpose of getting their + // geolocation. + debug!("Fetching mixnodes from explorer-api..."); + let Some(mixnodes_from_explorer_api) = fetch_mixnodes_from_explorer_api().await else { + error!("failed to get mixnodes from explorer-api"); + return None; + }; + + debug!("Fetching gateways from explorer-api..."); + let Some(gateways_from_explorer_api) = fetch_gateways_from_explorer_api().await else { + error!("failed to get mixnodes from explorer-api"); + return None; + }; + + // Determine what we should filter around + let filter_on = match self.filter_on { + GroupBy::CountryGroup(group) => group, + GroupBy::NymAddress(recipient) => { + // Convert recipient into a country group by extracting out the gateway part and + // using that as the country code. + let gateway = recipient.gateway().to_base58_string(); + + // Lookup the location of this gateway by using the location data from the + // explorer-api + let gateway_location = gateways_from_explorer_api + .iter() + .find(|g| g.gateway.identity_key == gateway) + .and_then(|g| g.location.clone()) + .map(|location| location.two_letter_iso_country_code) + .tap_none(|| error!("No location found for the gateway: {}", gateway))?; + debug!( + "Filtering on nym-address: {}, with location: {}", + recipient, gateway_location + ); + + CountryGroup::new(&gateway_location) + } + }; + debug!("Filter group: {}", filter_on); + + // Partition mixnodes_from_explorer_api according to the value of + // two_letter_iso_country_code. + // NOTE: we construct the full distribution here, but only use the one we're interested in. + // The reason we this instead of a straight filter is that this opens up the possibility to + // complement a small grouping with mixnodes from adjecent countries. + let mixnode_distribution = group_mixnodes_by_country_code(mixnodes_from_explorer_api); + log_mixnode_distribution(&mixnode_distribution); + + let gateway_distribution = group_gateways_by_country_code(gateways_from_explorer_api); + log_gateway_distribution(&gateway_distribution); + + let Some(filtered_mixnode_ids) = mixnode_distribution.get(&filter_on) else { + error!("no mixnodes found for: {}", filter_on); + return None; + }; + + let Some(filtered_gateway_ids) = gateway_distribution.get(&filter_on) else { + error!("no gateways found for: {}", filter_on); + return None; + }; + + let mixnodes = mixnodes + .into_iter() + .filter(|m| filtered_mixnode_ids.contains(&m.mix_id())) + .collect::>(); + + let gateways = gateways + .into_iter() + .filter(|g| filtered_gateway_ids.contains(g.identity())) + .collect::>(); + + let topology = nym_topology_from_detailed(mixnodes, gateways) + .filter_system_version(&self.client_version); + + // TODO: return real error type + check_layer_integrity(topology.clone()).ok()?; + + Some(topology) + } +} + +#[cfg(not(target_arch = "wasm32"))] +#[async_trait] +impl TopologyProvider for GeoAwareTopologyProvider { + // this will be manually refreshed on a timer specified inside mixnet client config + async fn get_new_topology(&mut self) -> Option { + self.get_topology().await + } +} + +#[cfg(target_arch = "wasm32")] +#[async_trait(?Send)] +impl TopologyProvider for GeoAwareTopologyProvider { + // this will be manually refreshed on a timer specified inside mixnet client config + async fn get_new_topology(&mut self) -> Option { + self.get_topology().await + } +} diff --git a/common/client-core/src/client/topology_control/mod.rs b/common/client-core/src/client/topology_control/mod.rs index 0fef523b1e..ed426dde50 100644 --- a/common/client-core/src/client/topology_control/mod.rs +++ b/common/client-core/src/client/topology_control/mod.rs @@ -10,6 +10,7 @@ use nym_topology::NymTopologyError; use std::time::Duration; mod accessor; +pub mod geo_aware_provider; pub(crate) mod nym_api_provider; // TODO: move it to config later diff --git a/common/client-core/src/config/mod.rs b/common/client-core/src/config/mod.rs index e8f08adfaa..c1d5b9d3cb 100644 --- a/common/client-core/src/config/mod.rs +++ b/common/client-core/src/config/mod.rs @@ -3,12 +3,15 @@ use nym_config::defaults::NymNetworkDetails; use nym_crypto::asymmetric::identity; -use nym_sphinx::params::{PacketSize, PacketType}; +use nym_sphinx::{ + addressing::clients::Recipient, + params::{PacketSize, PacketType}, +}; use serde::{Deserialize, Serialize}; use std::time::Duration; use url::Url; -use crate::error::ClientCoreError; +use crate::{client::topology_control::geo_aware_provider::CountryGroup, error::ClientCoreError}; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; @@ -158,6 +161,15 @@ impl Config { self } + pub fn with_topology_structure(mut self, topology_structure: TopologyStructure) -> Self { + self.set_topology_structure(topology_structure); + self + } + + pub fn set_topology_structure(&mut self, topology_structure: TopologyStructure) { + self.debug.topology.topology_structure = topology_structure; + } + pub fn with_no_per_hop_delays(mut self, no_per_hop_delays: bool) -> Self { if no_per_hop_delays { self.set_no_per_hop_delays() @@ -466,6 +478,33 @@ pub struct Topology { /// the first valid instance. /// Supersedes `topology_refresh_rate_ms`. pub disable_refreshing: bool, + + /// Specifies the mixnode topology to be used for sending packets. + pub topology_structure: TopologyStructure, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum TopologyStructure { + #[default] + NymApi, + GeoAware(GroupBy), +} + +#[allow(clippy::large_enum_variant)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum GroupBy { + CountryGroup(CountryGroup), + NymAddress(Recipient), +} + +impl std::fmt::Display for GroupBy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GroupBy::CountryGroup(group) => write!(f, "group: {}", group), + GroupBy::NymAddress(address) => write!(f, "address: {}", address), + } + } } impl Default for Topology { @@ -474,6 +513,7 @@ impl Default for Topology { topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE, topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT, disable_refreshing: false, + topology_structure: TopologyStructure::default(), } } } diff --git a/common/client-core/src/config/old_config_v1_1_20_2.rs b/common/client-core/src/config/old_config_v1_1_20_2.rs index ed0cd0356a..a097b55f2a 100644 --- a/common/client-core/src/config/old_config_v1_1_20_2.rs +++ b/common/client-core/src/config/old_config_v1_1_20_2.rs @@ -267,6 +267,7 @@ impl From for Topology { topology_refresh_rate: value.topology_refresh_rate, topology_resolution_timeout: value.topology_resolution_timeout, disable_refreshing: value.disable_refreshing, + topology_structure: Default::default(), } } } diff --git a/common/client-core/src/init/helpers.rs b/common/client-core/src/init/helpers.rs index 1c23d562f9..6237321a1b 100644 --- a/common/client-core/src/init/helpers.rs +++ b/common/client-core/src/init/helpers.rs @@ -3,11 +3,11 @@ use crate::config::GatewayEndpointConfig; use crate::error::ClientCoreError; +use crate::init::RegistrationResult; use futures::{SinkExt, StreamExt}; use log::{debug, info, trace, warn}; use nym_crypto::asymmetric::identity; use nym_gateway_client::GatewayClient; -use nym_gateway_requests::registration::handshake::SharedKeys; use nym_topology::{filter::VersionFilterable, gateway}; use rand::{seq::SliceRandom, Rng}; use std::{sync::Arc, time::Duration}; @@ -15,8 +15,6 @@ use tap::TapFallible; use tungstenite::Message; use url::Url; -#[cfg(not(target_arch = "wasm32"))] -use nym_validator_client::nyxd::DirectSigningNyxdClient; #[cfg(not(target_arch = "wasm32"))] use tokio::net::TcpStream; #[cfg(not(target_arch = "wasm32"))] @@ -27,13 +25,15 @@ use tokio_tungstenite::connect_async; use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; #[cfg(not(target_arch = "wasm32"))] type WsConn = WebSocketStream>; +#[cfg(not(target_arch = "wasm32"))] +use tokio::time::sleep; -#[cfg(target_arch = "wasm32")] -use nym_bandwidth_controller::wasm_mockups::DirectSigningNyxdClient; -#[cfg(target_arch = "wasm32")] -use wasm_timer::Instant; #[cfg(target_arch = "wasm32")] use wasm_utils::websocket::JSWebsocket; +#[cfg(target_arch = "wasm32")] +use wasmtimer::std::Instant; +#[cfg(target_arch = "wasm32")] +use wasmtimer::tokio::sleep; #[cfg(target_arch = "wasm32")] type WsConn = JSWebsocket; @@ -125,15 +125,9 @@ async fn measure_latency(gateway: &gateway::Node) -> Result(()) }; - // thanks to wasm we can't use tokio::time::timeout : ( - #[cfg(not(target_arch = "wasm32"))] - let timeout = tokio::time::sleep(PING_TIMEOUT); - #[cfg(not(target_arch = "wasm32"))] + let timeout = sleep(PING_TIMEOUT); tokio::pin!(timeout); - #[cfg(target_arch = "wasm32")] - let mut timeout = wasm_timer::Delay::new(PING_TIMEOUT); - tokio::select! { _ = &mut timeout => { warn!("timed out while trying to perform measurement...") @@ -155,7 +149,7 @@ async fn measure_latency(gateway: &gateway::Node) -> Result( +pub async fn choose_gateway_by_latency( rng: &mut R, gateways: &[gateway::Node], ) -> Result { @@ -207,9 +201,9 @@ pub(super) fn uniformly_random_gateway( pub(super) async fn register_with_gateway( gateway: &GatewayEndpointConfig, our_identity: Arc, -) -> Result, ClientCoreError> { +) -> Result { let timeout = Duration::from_millis(1500); - let mut gateway_client: GatewayClient = GatewayClient::new_init( + let mut gateway_client = GatewayClient::new_init( gateway.gateway_listener.clone(), gateway.try_get_gateway_identity_key()?, our_identity.clone(), @@ -223,5 +217,8 @@ pub(super) async fn register_with_gateway( .perform_initial_authentication() .await .tap_err(|_| log::warn!("Failed to register with the gateway!"))?; - Ok(shared_keys) + Ok(RegistrationResult { + shared_keys, + authenticated_ephemeral_client: Some(gateway_client), + }) } diff --git a/common/client-core/src/init/mod.rs b/common/client-core/src/init/mod.rs index 6ebc123b4b..8e848837ef 100644 --- a/common/client-core/src/init/mod.rs +++ b/common/client-core/src/init/mod.rs @@ -14,16 +14,39 @@ use crate::{ error::ClientCoreError, }; use nym_crypto::asymmetric::identity; +use nym_gateway_client::client::InitOnly; +use nym_gateway_client::GatewayClient; +use nym_gateway_requests::registration::handshake::SharedKeys; use nym_sphinx::addressing::{clients::Recipient, nodes::NodeIdentity}; use nym_topology::gateway; use nym_validator_client::client::IdentityKey; use rand::rngs::OsRng; use serde::Serialize; use std::fmt::{Debug, Display}; +use std::sync::Arc; use url::Url; pub mod helpers; +pub struct RegistrationResult { + pub shared_keys: Arc, + pub authenticated_ephemeral_client: Option>, +} + +pub struct InitialisationResult { + pub details: InitialisationDetails, + pub authenticated_ephemeral_client: Option>, +} + +impl From for InitialisationResult { + fn from(details: InitialisationDetails) -> Self { + InitialisationResult { + details, + authenticated_ephemeral_client: None, + } + } +} + // TODO: rename to something better... #[derive(Debug)] pub struct InitialisationDetails { @@ -74,7 +97,6 @@ impl InitialisationDetails { } } -#[derive(Debug, Clone)] pub enum GatewaySetup { /// The gateway specification MUST BE loaded from the underlying storage. MustLoad, @@ -92,6 +114,13 @@ pub enum GatewaySetup { /// Full gateway configuration details: PersistedGatewayDetails, }, + ReuseConnection { + /// The authenticated ephemeral client that was created during `init` + authenticated_ephemeral_client: GatewayClient, + + /// Details of this pre-initialised client + details: InitialisationDetails, + }, } impl From for GatewaySetup { @@ -266,18 +295,32 @@ fn ensure_valid_details( } pub async fn setup_gateway_from( - setup: &GatewaySetup, + setup: GatewaySetup, key_store: &K, details_store: &D, overwrite_data: bool, gateways: Option<&[gateway::Node]>, -) -> Result +) -> Result where K: KeyStore, D: GatewayDetailsStore, K::StorageError: Send + Sync + 'static, D::StorageError: Send + Sync + 'static, { + // I don't like how we can't deal with this variant in the match below, but we need to take ownership of internal values. + if let GatewaySetup::ReuseConnection { + authenticated_ephemeral_client, + details, + } = setup + { + // if we have already performed the full setup, forward the details. + // it's up to the caller to ensure persistence + return Ok(InitialisationResult { + details, + authenticated_ephemeral_client: Some(authenticated_ephemeral_client), + }); + } + let mut rng = OsRng; // try load gateway details @@ -286,14 +329,14 @@ where // try load keys and decide what to do based on the GatewaySetup let mut managed_keys = match ManagedKeys::try_load(key_store).await { Ok(loaded_keys) => { - match setup { + match &setup { GatewaySetup::MustLoad => { // get EVERYTHING from the storage let details = loaded_details?; ensure_valid_details(&details, &loaded_keys)?; // no need to persist anything as we got everything from the storage - return Ok(InitialisationDetails::new(details.into(), loaded_keys)); + return Ok(InitialisationDetails::new(details.into(), loaded_keys).into()); } GatewaySetup::Predefined { details } => { // we already have defined gateway details AND a shared key @@ -304,10 +347,9 @@ where _store_gateway_details(details_store, details).await?; } - return Ok(InitialisationDetails::new( - details.clone().into(), - loaded_keys, - )); + return Ok( + InitialisationDetails::new(details.clone().into(), loaded_keys).into(), + ); } GatewaySetup::Specified { gateway_identity } => { // if that data was already stored... @@ -323,7 +365,8 @@ where return Ok(InitialisationDetails::new( existing_gateway.into(), loaded_keys, - )); + ) + .into()); } } @@ -341,7 +384,8 @@ where return Ok(InitialisationDetails::new( existing_gateway.into(), loaded_keys, - )); + ) + .into()); } // we didn't get full details from the store and we have loaded some keys @@ -352,6 +396,9 @@ where return Err(ClientCoreError::ForbiddenKeyOverwrite); } } + GatewaySetup::ReuseConnection { .. } => { + unreachable!("the reuse connection variant was already manually covered") + } } } Err(_) => { @@ -371,7 +418,9 @@ where let our_identity = managed_keys.identity_keypair(); // Establish connection, authenticate and generate keys for talking with the gateway - let shared_keys = helpers::register_with_gateway(&gateway_details, our_identity).await?; + let registration_result = + helpers::register_with_gateway(&gateway_details, our_identity).await?; + let shared_keys = registration_result.shared_keys; let persisted_details = PersistedGatewayDetails::new(gateway_details, &shared_keys); @@ -386,19 +435,19 @@ where // persist gateway config _store_gateway_details(details_store, &persisted_details).await?; - Ok(InitialisationDetails::new( - persisted_details.into(), - managed_keys, - )) + Ok(InitialisationResult { + details: InitialisationDetails::new(persisted_details.into(), managed_keys), + authenticated_ephemeral_client: registration_result.authenticated_ephemeral_client, + }) } pub async fn setup_gateway( - setup: &GatewaySetup, + setup: GatewaySetup, key_store: &K, details_store: &D, overwrite_data: bool, validator_servers: Option<&[Url]>, -) -> Result +) -> Result where K: KeyStore, D: GatewayDetailsStore, diff --git a/common/client-libs/gateway-client/Cargo.toml b/common/client-libs/gateway-client/Cargo.toml index 20d5c958b0..3f8f4ac0e4 100644 --- a/common/client-libs/gateway-client/Cargo.toml +++ b/common/client-libs/gateway-client/Cargo.toml @@ -9,10 +9,10 @@ edition = "2021" [dependencies] # TODO: (for this and other crates), similarly to 'tokio', import only required "futures" modules rather than # the entire crate -futures = "0.3" +futures = { workspace = true } log = { workspace = true } -thiserror = "1.0" -url = "2.2" +thiserror = { workspace = true } +url = { workspace = true } rand = { version = "0.7.3", features = ["wasm-bindgen"] } tokio = { version = "1.24.1", features = ["macros"] } @@ -25,7 +25,7 @@ nym-gateway-requests = { path = "../../../gateway/gateway-requests" } nym-network-defaults = { path = "../../network-defaults" } nym-sphinx = { path = "../../nymsphinx" } nym-pemstore = { path = "../../pemstore" } -nym-validator-client = { path = "../validator-client" } +nym-validator-client = { path = "../validator-client", default-features = false } nym-task = { path = "../../task" } serde = { workspace = true, features = ["derive"] } @@ -58,9 +58,9 @@ path = "../../wasm-utils" features = ["websocket"] # only import it in wasm. Prefer proper tokio timer in non-wasm -[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-timer] -git = "https://github.com/mmsinclair/wasm-timer" -rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6" +[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer] +workspace = true +features = ["tokio"] # this is due to tungstenite using `rand` 0.8 and associated changes in `getrandom` crate # which now does not support wasm32-unknown-unknown target by default. diff --git a/common/client-libs/gateway-client/src/client.rs b/common/client-libs/gateway-client/src/client.rs index a6ef774d8f..188d84e2c9 100644 --- a/common/client-libs/gateway-client/src/client.rs +++ b/common/client-libs/gateway-client/src/client.rs @@ -1,4 +1,4 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::error::GatewayClientError; @@ -22,6 +22,7 @@ use nym_gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse, use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN}; use nym_sphinx::forwarding::packet::MixPacket; use nym_task::TaskClient; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use rand::rngs::OsRng; use std::convert::TryFrom; use std::sync::Arc; @@ -29,21 +30,19 @@ use std::time::Duration; use tungstenite::protocol::Message; #[cfg(not(target_arch = "wasm32"))] -use nym_validator_client::nyxd::traits::DkgQueryClient; +use tokio::time::sleep; #[cfg(not(target_arch = "wasm32"))] use tokio_tungstenite::connect_async; -#[cfg(target_arch = "wasm32")] -use nym_bandwidth_controller::wasm_mockups::DkgQueryClient; -#[cfg(target_arch = "wasm32")] -use wasm_timer; #[cfg(target_arch = "wasm32")] use wasm_utils::websocket::JSWebsocket; +#[cfg(target_arch = "wasm32")] +use wasmtimer::tokio::sleep; const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10; const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5); -pub struct GatewayClient { +pub struct GatewayClient { authenticated: bool, disabled_credentials_mode: bool, bandwidth_remaining: i64, @@ -197,16 +196,7 @@ impl GatewayClient { return Ok(()); } - #[cfg(not(target_arch = "wasm32"))] - tokio::time::sleep(self.reconnection_backoff).await; - - #[cfg(target_arch = "wasm32")] - if let Err(err) = wasm_timer::Delay::new(self.reconnection_backoff).await { - error!( - "the timer has gone away while in reconnection backoff! - {}", - err - ); - } + sleep(self.reconnection_backoff).await; } // final attempt (done separately to be able to return a proper error) @@ -235,16 +225,9 @@ impl GatewayClient { _ => return Err(GatewayClientError::ConnectionInInvalidState), }; - #[cfg(not(target_arch = "wasm32"))] - let timeout = tokio::time::sleep(self.response_timeout_duration); - #[cfg(not(target_arch = "wasm32"))] + let timeout = sleep(self.response_timeout_duration); tokio::pin!(timeout); - // technically the `wasm_timer` also works outside wasm, but unless required, - // I really prefer to just stick to tokio - #[cfg(target_arch = "wasm32")] - let mut timeout = wasm_timer::Delay::new(self.response_timeout_duration); - loop { tokio::select! { _ = self.shutdown.recv() => { @@ -484,6 +467,14 @@ impl GatewayClient { pub async fn perform_initial_authentication( &mut self, ) -> Result, GatewayClientError> { + if self.authenticated { + return if let Some(shared_key) = &self.shared_key { + Ok(Arc::clone(shared_key)) + } else { + Err(GatewayClientError::AuthenticationFailure) + }; + } + if self.shared_key.is_some() { self.authenticate(None).await?; } else { @@ -768,7 +759,9 @@ impl GatewayClient { } } -impl GatewayClient { +pub struct InitOnly; + +impl GatewayClient { // for initialisation we do not need credential storage. Though it's still a bit weird we have to set the generic... pub fn new_init( gateway_address: String, @@ -785,7 +778,7 @@ impl GatewayClient { let shutdown = TaskClient::dummy(); let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown.clone()); - GatewayClient:: { + GatewayClient { authenticated: false, disabled_credentials_mode: true, bandwidth_remaining: 0, @@ -803,4 +796,37 @@ impl GatewayClient { shutdown, } } + + pub fn upgrade( + self, + mixnet_message_sender: MixnetMessageSender, + ack_sender: AcknowledgementSender, + response_timeout_duration: Duration, + bandwidth_controller: Option>, + shutdown: TaskClient, + ) -> GatewayClient { + // invariants that can't be broken + // (unless somebody decided to expose some field that wasn't meant to be exposed) + assert!(self.authenticated); + assert!(self.connection.is_available()); + assert!(self.shared_key.is_some()); + + GatewayClient { + authenticated: self.authenticated, + disabled_credentials_mode: self.disabled_credentials_mode, + bandwidth_remaining: self.bandwidth_remaining, + gateway_address: self.gateway_address, + gateway_identity: self.gateway_identity, + local_identity: self.local_identity, + shared_key: self.shared_key, + connection: self.connection, + packet_router: PacketRouter::new(ack_sender, mixnet_message_sender, shutdown.clone()), + response_timeout_duration, + bandwidth_controller, + should_reconnect_on_failure: self.should_reconnect_on_failure, + reconnection_attempts: self.reconnection_attempts, + reconnection_backoff: self.reconnection_backoff, + shutdown, + } + } } diff --git a/common/client-libs/mixnet-client/Cargo.toml b/common/client-libs/mixnet-client/Cargo.toml index fabcca892d..9eede069da 100644 --- a/common/client-libs/mixnet-client/Cargo.toml +++ b/common/client-libs/mixnet-client/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -futures = "0.3" +futures = { workspace = true } log = { workspace = true } tokio = { version = "1.24.1", features = ["time", "net", "rt"] } tokio-util = { version = "0.7.4", features = ["codec"] } diff --git a/common/client-libs/validator-client/Cargo.toml b/common/client-libs/validator-client/Cargo.toml index 31475887bc..0ee5c8b3af 100644 --- a/common/client-libs/validator-client/Cargo.toml +++ b/common/client-libs/validator-client/Cargo.toml @@ -13,6 +13,7 @@ colored = "2.0" nym-coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" } nym-contracts-common = { path = "../../cosmwasm-smart-contracts/contracts-common" } +nym-ephemera-common = { path = "../../cosmwasm-smart-contracts/ephemera" } nym-mixnet-contract-common = { path = "../../cosmwasm-smart-contracts/mixnet-contract" } nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-contract" } nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" } @@ -20,71 +21,69 @@ nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig nym-name-service-common = { path = "../../cosmwasm-smart-contracts/name-service" } nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" } nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts/service-provider-directory" } -nym-vesting-contract = { path = "../../../contracts/vesting" } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } -reqwest = { version = "0.11", features = ["json"] } -thiserror = "1" +reqwest = { workspace = true, features = ["json"] } +thiserror = { workspace = true } log = { workspace = true } -url = { version = "2.2", features = ["serde"] } -tokio = { version = "1.24.1", features = ["sync", "time"] } -futures = "0.3" -openssl = { version = "0.10", features = ["vendored"], optional = true } +url = { workspace = true, features = ["serde"] } +tokio = { workspace = true, features = ["sync", "time"] } +futures = { workspace = true } +openssl = { version = "^0.10.55", features = ["vendored"], optional = true } nym-coconut-interface = { path = "../../coconut-interface" } nym-network-defaults = { path = "../../network-defaults" } nym-api-requests = { path = "../../../nym-api/nym-api-requests" } async-trait = { workspace = true } -bip39 = { workspace = true, features = ["rand"], optional = true } +bip39 = { workspace = true, features = ["rand"] } 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 } +eyre = { version = "0.6" } +cw-utils = { workspace = true } +cw2 = { workspace = true } cw3 = { workspace = true } cw4 = { workspace = true } +cw-controllers = { 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"] } +zeroize = { workspace = true, features = ["zeroize_derive"] } cosmwasm-std = { workspace = true } +# required for polling for broadcast result +[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer] +workspace = true +features = ["tokio"] + [dev-dependencies] bip39 = { workspace = true } -#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" +ts-rs = { workspace = true } [[example]] 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 = ["http-client", "signing"] +required-features = ["http-client"] [[example]] name = "query_service_provider_directory" -# TODO: validate the requirements -required-features = ["http-client", "signing"] +required-features = ["http-client"] [[example]] name = "query_name_service" -# TODO: validate the requirements -required-features = ["http-client", "signing"] +required-features = ["http-client"] [features] +default = ["http-client"] http-client = ["cosmrs/rpc", "openssl"] -signing = [ - "bip39", - "eyre", - "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 a21bfa4475..0e5bce5681 100644 --- a/common/client-libs/validator-client/examples/offline_signing.rs +++ b/common/client-libs/validator-client/examples/offline_signing.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use cosmrs::bank::MsgSend; -use cosmrs::rpc::HttpClient; use cosmrs::tx::Msg; use cosmrs::{tx, AccountId, Coin, Denom}; +use nym_validator_client::http_client; use nym_validator_client::nyxd::CosmWasmClient; use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet; use nym_validator_client::signing::tx_signer::TxSigner; @@ -23,11 +23,11 @@ async fn main() { let signer_address = signer.try_derive_accounts().unwrap()[0].address().clone(); // local 'client' ONLY signing messages - let tx_signer = TxSigner::new(signer); + let tx_signer = signer; // possibly remote client that doesn't do ANY signing // (only broadcasts + queries for sequence numbers) - let broadcaster = HttpClient::new(validator).unwrap(); + let broadcaster = http_client(validator).unwrap(); // get signer information let sequence_response = broadcaster.get_sequence(&signer_address).await.unwrap(); diff --git a/common/client-libs/validator-client/examples/query_name_service.rs b/common/client-libs/validator-client/examples/query_name_service.rs index 2f6e36b3bc..f705cb3564 100644 --- a/common/client-libs/validator-client/examples/query_name_service.rs +++ b/common/client-libs/validator-client/examples/query_name_service.rs @@ -3,11 +3,13 @@ use std::str::FromStr; use cosmrs::AccountId; use nym_name_service_common::Address; use nym_network_defaults::{setup_env, NymNetworkDetails}; -use nym_validator_client::nyxd::traits::NameServiceQueryClient; +use nym_validator_client::nyxd::contract_traits::{ + NameServiceQueryClient, PagedNameServiceQueryClient, +}; #[tokio::main] async fn main() { - setup_env(Some(&"../../../envs/qa-qwerty.env".parse().unwrap())); + setup_env(Some("../../../envs/qa-qwerty.env")); let network_details = NymNetworkDetails::new_from_env(); let config = nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap(); @@ -26,7 +28,7 @@ async fn main() { let names_by_owner = client.nyxd.get_names_by_owner(owner).await.unwrap(); println!("names (by owner): {names_by_owner:#?}"); - let nym_address = Address::new("client_id.client_key@gateway_id"); + let nym_address = Address::new("client_id.client_key@gateway_id").unwrap(); let names_by_address = client.nyxd.get_names_by_address(nym_address).await.unwrap(); println!("names (by address): {names_by_address:#?}"); diff --git a/common/client-libs/validator-client/examples/query_service_provider_directory.rs b/common/client-libs/validator-client/examples/query_service_provider_directory.rs index 12369b2696..e489564e94 100644 --- a/common/client-libs/validator-client/examples/query_service_provider_directory.rs +++ b/common/client-libs/validator-client/examples/query_service_provider_directory.rs @@ -3,11 +3,13 @@ use std::str::FromStr; use cosmrs::AccountId; use nym_network_defaults::{setup_env, NymNetworkDetails}; use nym_service_provider_directory_common::NymAddress; -use nym_validator_client::nyxd::traits::SpDirectoryQueryClient; +use nym_validator_client::nyxd::contract_traits::{ + PagedSpDirectoryQueryClient, SpDirectoryQueryClient, +}; #[tokio::main] async fn main() { - setup_env(Some(&"../../../envs/qa-qwerty.env".parse().unwrap())); + setup_env(Some("../../../envs/qa-qwerty.env")); let network_details = NymNetworkDetails::new_from_env(); let config = nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap(); diff --git a/common/client-libs/validator-client/src/client.rs b/common/client-libs/validator-client/src/client.rs index 54539403fb..c5ae83a6bf 100644 --- a/common/client-libs/validator-client/src/client.rs +++ b/common/client-libs/validator-client/src/client.rs @@ -1,41 +1,35 @@ // Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::{nym_api, ValidatorClientError}; +use crate::nyxd::{self, NyxdClient}; +use crate::signing::direct_wallet::DirectSecp256k1HdWallet; +use crate::signing::signer::{NoSigner, OfflineSigner}; +use crate::{ + nym_api, DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient, + ReqwestRpcClient, ValidatorClientError, +}; use nym_api_requests::coconut::{ BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse, }; +use nym_api_requests::models::MixNodeBondAnnotated; use nym_api_requests::models::{ GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse, RewardEstimationResponse, StakeSaturationResponse, }; -use nym_coconut_dkg_common::types::NodeIndex; -use nym_coconut_interface::VerificationKey; +use nym_network_defaults::NymNetworkDetails; +use url::Url; + pub use nym_mixnet_contract_common::{ mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, MixId, }; -use url::Url; -use crate::nyxd::traits::{DkgQueryClient, MixnetQueryClient}; +// re-export the type to not break existing imports +pub use crate::coconut::CoconutApiClient; + #[cfg(feature = "http-client")] -use crate::nyxd::QueryNyxdClient; -use crate::nyxd::{self, CosmWasmClient, NyxdClient}; -use nym_api_requests::models::MixNodeBondAnnotated; -use nym_coconut_dkg_common::{types::EpochId, verification_key::ContractVKShare}; -use nym_coconut_interface::Base58; -use nym_mixnet_contract_common::{ - families::{Family, FamilyHead}, - mixnode::MixNodeBond, - pending_events::{PendingEpochEvent, PendingIntervalEvent}, - Delegation, RewardedSetNodeStatus, UnbondedMixnode, -}; -use nym_network_defaults::NymNetworkDetails; -use std::str::FromStr; - -#[cfg(all(feature = "signing", feature = "http-client"))] -use crate::nyxd::SigningNyxdClient; -#[cfg(all(feature = "signing", feature = "http-client"))] -use crate::signing::direct_wallet::DirectSecp256k1HdWallet; +use crate::rpc::http_client; +#[cfg(feature = "http-client")] +use crate::{DirectSigningHttpRpcValidatorClient, HttpRpcClient, QueryHttpRpcValidatorClient}; #[must_use] #[derive(Debug, Clone)] @@ -44,13 +38,7 @@ pub struct Config { 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, - gateway_page_limit: Option, - mixnode_delegations_page_limit: Option, - rewarded_set_page_limit: Option, } impl Config { @@ -75,10 +63,6 @@ impl Config { .parse() .map_err(ValidatorClientError::MalformedUrlProvided)?, nyxd_config: nyxd::Config::try_from_nym_network_details(details)?, - mixnode_page_limit: None, - gateway_page_limit: None, - mixnode_delegations_page_limit: None, - rewarded_set_page_limit: None, }) } @@ -95,87 +79,57 @@ impl Config { self } - pub fn with_mixnode_page_limit(mut self, limit: Option) -> Config { - self.mixnode_page_limit = limit; - self - } - - pub fn with_gateway_page_limit(mut self, limit: Option) -> Config { - self.gateway_page_limit = limit; - self - } - - pub fn with_mixnode_delegations_page_limit(mut self, limit: Option) -> Config { - self.mixnode_delegations_page_limit = limit; - self - } - - pub fn with_rewarded_set_page_limit(mut self, limit: Option) -> Config { - self.rewarded_set_page_limit = limit; + pub fn with_simulated_gas_multiplier(mut self, gas_multiplier: f32) -> Self { + self.nyxd_config.simulated_gas_multiplier = gas_multiplier; self } } -pub struct Client { - mixnode_page_limit: Option, - gateway_page_limit: Option, - mixnode_delegations_page_limit: Option, - rewarded_set_page_limit: Option, - +pub struct Client { // ideally they would have been read-only, but unfortunately rust doesn't have such features pub nym_api: nym_api::Client, - pub nyxd: NyxdClient, + pub nyxd: NyxdClient, } -#[cfg(all(feature = "signing", feature = "http-client"))] -impl Client> { +#[cfg(feature = "http-client")] +impl Client { pub fn new_signing( config: Config, mnemonic: bip39::Mnemonic, - ) -> Result>, ValidatorClientError> { - let nym_api_client = nym_api::Client::new(config.api_url.clone()); - let nyxd_client = NyxdClient::connect_with_mnemonic( - config.nyxd_config.clone(), - config.nyxd_url.as_str(), - mnemonic, - None, - )?; + ) -> Result { + let rpc_client = http_client(config.nyxd_url.as_str())?; + let prefix = &config.nyxd_config.chain_details.bech32_account_prefix; + let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic); - Ok(Client { - mixnode_page_limit: config.mixnode_page_limit, - gateway_page_limit: config.gateway_page_limit, - mixnode_delegations_page_limit: config.mixnode_delegations_page_limit, - rewarded_set_page_limit: config.rewarded_set_page_limit, - nym_api: nym_api_client, - nyxd: nyxd_client, - }) + Ok(Self::new_signing_with_rpc_client( + config, rpc_client, wallet, + )) } pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { self.nyxd.change_endpoint(new_endpoint.as_ref())?; Ok(()) } +} - pub fn set_nyxd_simulated_gas_multiplier(&mut self, multiplier: f32) { - self.nyxd.set_simulated_gas_multiplier(multiplier) +impl Client { + pub fn new_reqwest_signing( + config: Config, + mnemonic: bip39::Mnemonic, + ) -> DirectSigningReqwestRpcValidatorClient { + let rpc_client = ReqwestRpcClient::new(config.nyxd_url.clone()); + let prefix = &config.nyxd_config.chain_details.bech32_account_prefix; + let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic); + + Self::new_signing_with_rpc_client(config, rpc_client, wallet) } } #[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()); - let nyxd_client = - NyxdClient::connect(config.nyxd_config.clone(), config.nyxd_url.as_str())?; - - Ok(Client { - mixnode_page_limit: config.mixnode_page_limit, - gateway_page_limit: config.gateway_page_limit, - mixnode_delegations_page_limit: config.mixnode_delegations_page_limit, - rewarded_set_page_limit: config.rewarded_set_page_limit, - nym_api: nym_api_client, - nyxd: nyxd_client, - }) +impl Client { + pub fn new_query(config: Config) -> Result { + let rpc_client = http_client(config.nyxd_url.as_str())?; + Ok(Self::new_with_rpc_client(config, rpc_client)) } pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { @@ -184,390 +138,44 @@ impl Client { } } -// nyxd wrappers +impl Client { + pub fn new_reqwest_query(config: Config) -> QueryReqwestRpcValidatorClient { + let rpc_client = ReqwestRpcClient::new(config.nyxd_url.clone()); + Self::new_with_rpc_client(config, rpc_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 + pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self { + let nym_api_client = nym_api::Client::new(config.api_url.clone()); - pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) { - self.nyxd - .set_mixnet_contract_address(mixnet_contract_address) - } - - pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId { - self.nyxd.mixnet_contract_address().clone() - } - - pub async fn get_all_node_families(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut families = Vec::new(); - let mut start_after = None; - - loop { - let paged_response = self - .nyxd - .get_all_node_families_paged(start_after.take(), None) - .await?; - families.extend(paged_response.families); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } + Client { + nym_api: nym_api_client, + nyxd: NyxdClient::new(config.nyxd_config, rpc_client), } - - Ok(families) } +} - pub async fn get_all_family_members( - &self, - ) -> Result, ValidatorClientError> +impl Client { + pub fn new_signing_with_rpc_client(config: Config, rpc_client: C, signer: S) -> Self where - C: CosmWasmClient + Sync + Send, + S: OfflineSigner, { - let mut members = Vec::new(); - let mut start_after = None; + let nym_api_client = nym_api::Client::new(config.api_url.clone()); - loop { - let paged_response = self - .nyxd - .get_all_family_members_paged(start_after.take(), None) - .await?; - members.extend(paged_response.members); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } + Client { + nym_api: nym_api_client, + nyxd: NyxdClient::new_signing(config.nyxd_config, rpc_client, signer), } - - Ok(members) - } - - // basically handles paging for us - pub async fn get_all_nyxd_rewarded_set_mixnodes( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut identities = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self - .nyxd - .get_rewarded_set_paged(start_after.take(), self.rewarded_set_page_limit) - .await?; - identities.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(identities) - } - - pub async fn get_all_nyxd_mixnode_bonds(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_mixnode_bonds_paged(self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_mixnodes_detailed( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_mixnodes_detailed_paged(self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_unbonded_mixnodes( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_unbonded_paged(self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_unbonded_mixnodes_by_owner( - &self, - owner: &cosmrs::AccountId, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_unbonded_by_owner_paged(owner, self.mixnode_page_limit, start_after.take()) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_unbonded_mixnodes_by_identity( - &self, - identity_key: String, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut mixnodes = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_unbonded_by_identity_paged( - identity_key.clone(), - self.mixnode_page_limit, - start_after.take(), - ) - .await?; - mixnodes.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(mixnodes) - } - - pub async fn get_all_nyxd_gateways(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut gateways = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_gateways_paged(start_after.take(), self.gateway_page_limit) - .await?; - gateways.append(&mut paged_response.nodes); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(gateways) - } - - pub async fn get_all_nyxd_single_mixnode_delegations( - &self, - mix_id: MixId, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut delegations = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_mixnode_delegations_paged( - mix_id, - start_after.take(), - self.mixnode_delegations_page_limit, - ) - .await?; - delegations.append(&mut paged_response.delegations); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(delegations) - } - - pub async fn get_all_delegator_delegations( - &self, - delegation_owner: &cosmrs::AccountId, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut delegations = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_delegator_delegations_paged( - delegation_owner.to_string(), - start_after.take(), - self.mixnode_delegations_page_limit, - ) - .await?; - delegations.append(&mut paged_response.delegations); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(delegations) - } - - pub async fn get_all_network_delegations(&self) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut delegations = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .nyxd - .get_all_network_delegations_paged( - start_after.take(), - self.mixnode_delegations_page_limit, - ) - .await?; - delegations.append(&mut paged_response.delegations); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(delegations) - } - - pub async fn get_all_nyxd_pending_epoch_events( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut events = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self - .nyxd - .get_pending_epoch_events_paged(start_after.take(), self.rewarded_set_page_limit) - .await?; - events.append(&mut paged_response.events); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(events) - } - - pub async fn get_all_nyxd_pending_interval_events( - &self, - ) -> Result, ValidatorClientError> - where - C: CosmWasmClient + Sync + Send, - { - let mut events = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self - .nyxd - .get_pending_interval_events_paged(start_after.take(), self.rewarded_set_page_limit) - .await?; - events.append(&mut paged_response.events); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(events) } } // validator-api wrappers -impl Client { +impl Client { + pub fn api_url(&self) -> &Url { + self.nym_api.current_url() + } + pub fn change_nym_api(&mut self, new_endpoint: Url) { self.nym_api.change_url(new_endpoint) } @@ -624,85 +232,46 @@ impl Client { } } -#[derive(Clone)] -pub struct CoconutApiClient { - pub api_client: NymApiClient, - pub verification_key: VerificationKey, - pub node_id: NodeIndex, - pub cosmos_address: cosmrs::AccountId, -} - -impl CoconutApiClient { - pub async fn all_coconut_api_clients( - client: &C, - epoch_id: EpochId, - ) -> Result, ValidatorClientError> - where - C: DkgQueryClient + Sync + Send, - { - Ok(client - .get_all_verification_key_shares(epoch_id) - .await? - .into_iter() - .filter_map(Self::try_from) - .collect()) - } - - fn try_from(share: ContractVKShare) -> Option { - if share.verified { - if let Ok(url_address) = Url::parse(&share.announce_address) { - if let Ok(verification_key) = VerificationKey::try_from_bs58(&share.share) { - if let Ok(cosmos_address) = cosmrs::AccountId::from_str(share.owner.as_str()) { - return Some(CoconutApiClient { - api_client: NymApiClient::new(url_address), - verification_key, - node_id: share.node_index, - cosmos_address, - }); - } - } - } - } - None - } -} - #[derive(Clone)] pub struct NymApiClient { - pub nym_api_client: nym_api::Client, + pub nym_api: nym_api::Client, // TODO: perhaps if we really need it at some (currently I don't see any reasons for it) // we could re-implement the communication with the REST API on port 1317 } impl NymApiClient { pub fn new(api_url: Url) -> Self { - let nym_api_client = nym_api::Client::new(api_url); + let nym_api = nym_api::Client::new(api_url); - NymApiClient { nym_api_client } + NymApiClient { nym_api } + } + + pub fn api_url(&self) -> &Url { + self.nym_api.current_url() } pub fn change_nym_api(&mut self, new_endpoint: Url) { - self.nym_api_client.change_url(new_endpoint); + self.nym_api.change_url(new_endpoint); } pub async fn get_cached_active_mixnodes( &self, ) -> Result, ValidatorClientError> { - Ok(self.nym_api_client.get_active_mixnodes().await?) + Ok(self.nym_api.get_active_mixnodes().await?) } pub async fn get_cached_rewarded_mixnodes( &self, ) -> Result, ValidatorClientError> { - Ok(self.nym_api_client.get_rewarded_mixnodes().await?) + Ok(self.nym_api.get_rewarded_mixnodes().await?) } pub async fn get_cached_mixnodes(&self) -> Result, ValidatorClientError> { - Ok(self.nym_api_client.get_mixnodes().await?) + Ok(self.nym_api.get_mixnodes().await?) } pub async fn get_cached_gateways(&self) -> Result, ValidatorClientError> { - Ok(self.nym_api_client.get_gateways().await?) + Ok(self.nym_api.get_gateways().await?) } pub async fn get_gateway_core_status_count( @@ -711,7 +280,7 @@ impl NymApiClient { since: Option, ) -> Result { Ok(self - .nym_api_client + .nym_api .get_gateway_core_status_count(identity, since) .await?) } @@ -722,7 +291,7 @@ impl NymApiClient { since: Option, ) -> Result { Ok(self - .nym_api_client + .nym_api .get_mixnode_core_status_count(mix_id, since) .await?) } @@ -731,34 +300,28 @@ impl NymApiClient { &self, mix_id: MixId, ) -> Result { - Ok(self.nym_api_client.get_mixnode_status(mix_id).await?) + Ok(self.nym_api.get_mixnode_status(mix_id).await?) } pub async fn get_mixnode_reward_estimation( &self, mix_id: MixId, ) -> Result { - Ok(self - .nym_api_client - .get_mixnode_reward_estimation(mix_id) - .await?) + Ok(self.nym_api.get_mixnode_reward_estimation(mix_id).await?) } pub async fn get_mixnode_stake_saturation( &self, mix_id: MixId, ) -> Result { - Ok(self - .nym_api_client - .get_mixnode_stake_saturation(mix_id) - .await?) + Ok(self.nym_api.get_mixnode_stake_saturation(mix_id).await?) } pub async fn blind_sign( &self, request_body: &BlindSignRequestBody, ) -> Result { - Ok(self.nym_api_client.blind_sign(request_body).await?) + Ok(self.nym_api.blind_sign(request_body).await?) } pub async fn verify_bandwidth_credential( @@ -766,7 +329,7 @@ impl NymApiClient { request_body: &VerifyCredentialBody, ) -> Result { Ok(self - .nym_api_client + .nym_api .verify_bandwidth_credential(request_body) .await?) } diff --git a/common/client-libs/validator-client/src/coconut/mod.rs b/common/client-libs/validator-client/src/coconut/mod.rs new file mode 100644 index 0000000000..97ac95cad7 --- /dev/null +++ b/common/client-libs/validator-client/src/coconut/mod.rs @@ -0,0 +1,97 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::{DkgQueryClient, PagedDkgQueryClient}; +use crate::nyxd::error::NyxdError; +use crate::NymApiClient; +use nym_coconut_dkg_common::types::{EpochId, NodeIndex}; +use nym_coconut_dkg_common::verification_key::ContractVKShare; +use nym_coconut_interface::{Base58, CoconutError, VerificationKey}; +use thiserror::Error; +use url::Url; + +// TODO: it really doesn't feel like this should live in this crate. +#[derive(Clone)] +pub struct CoconutApiClient { + pub api_client: NymApiClient, + pub verification_key: VerificationKey, + pub node_id: NodeIndex, + pub cosmos_address: cosmrs::AccountId, +} + +// TODO: this should be using the coconut error +// (which is in different crate; perhaps this client should be moved there?) + +#[derive(Debug, Error)] +pub enum CoconutApiError { + // TODO: ask @BN whether this is a correct error message + #[error("the provided key share hasn't been verified")] + UnverifiedShare, + + #[error("failed to query the contract: {source}")] + ContractQueryFailure { + #[from] + source: NyxdError, + }, + + #[error("the provided announce address is malformed: {source}")] + MalformedAnnounceAddress { + #[from] + source: url::ParseError, + }, + + #[error("the provided verification key is malformed: {source}")] + MalformedVerificationKey { + #[from] + source: CoconutError, + }, + + #[error("the provided account address is malformed: {source}")] + MalformedAccountAddress { + #[from] + source: cosmrs::ErrorReport, + }, +} + +impl TryFrom for CoconutApiClient { + type Error = CoconutApiError; + + fn try_from(share: ContractVKShare) -> Result { + if !share.verified { + return Err(CoconutApiError::UnverifiedShare); + } + + let url_address = Url::parse(&share.announce_address)?; + + Ok(CoconutApiClient { + api_client: NymApiClient::new(url_address), + verification_key: VerificationKey::try_from_bs58(&share.share)?, + node_id: share.node_index, + cosmos_address: share.owner.as_str().parse()?, + }) + } +} + +pub async fn all_coconut_api_clients( + client: &C, + epoch_id: EpochId, +) -> Result, CoconutApiError> +where + C: DkgQueryClient + Sync + Send, +{ + // TODO: this will error out if there's an invalid share out there. is that what we want? + client + .get_all_verification_key_shares(epoch_id) + .await? + .into_iter() + .map(TryInto::try_into) + .collect::, _>>() + + // ... if not, let's switch to the below: + // client + // .get_all_verification_key_shares(epoch_id) + // .await? + // .into_iter() + // .filter_map(TryInto::try_into) + // .collect::, _>>() +} diff --git a/common/client-libs/validator-client/src/connection_tester.rs b/common/client-libs/validator-client/src/connection_tester.rs index 800399d415..15d2efe250 100644 --- a/common/client-libs/validator-client/src/connection_tester.rs +++ b/common/client-libs/validator-client/src/connection_tester.rs @@ -1,8 +1,7 @@ +use crate::nyxd::contract_traits::MixnetQueryClient; use crate::nyxd::error::NyxdError; -use crate::nyxd::{Config as ClientConfig, NyxdClient, QueryNyxdClient}; -use crate::{NymApiClient, ValidatorClientError}; - -use crate::nyxd::traits::MixnetQueryClient; +use crate::nyxd::Config as ClientConfig; +use crate::{NymApiClient, QueryHttpRpcNyxdClient, ValidatorClientError}; use colored::Colorize; use core::fmt; use itertools::Itertools; @@ -53,7 +52,7 @@ pub async fn test_nyxd_url_connection( let config = ClientConfig::try_from_nym_network_details(&network) .expect("failed to create valid nyxd client config"); - let mut nyxd_client = NyxdClient::::connect(config, nyxd_url.as_str())?; + let mut nyxd_client = QueryHttpRpcNyxdClient::connect(config, nyxd_url.as_str())?; // possibly redundant, but lets just leave it here nyxd_client.set_mixnet_contract_address(address); match test_nyxd_connection(network, &nyxd_url, &nyxd_client).await { @@ -75,7 +74,7 @@ fn setup_connection_tests( let config = ClientConfig::try_from_nym_network_details(&network) .expect("failed to create valid nyxd client config"); - if let Ok(mut client) = NyxdClient::::connect(config, url.as_str()) { + if let Ok(mut client) = QueryHttpRpcNyxdClient::connect(config, url.as_str()) { // possibly redundant, but lets just leave it here client.set_mixnet_contract_address(address); Some(ClientForConnectionTest::Nyxd( @@ -112,7 +111,7 @@ fn extract_and_collect_results_into_map( async fn test_nyxd_connection( network: NymNetworkDetails, url: &Url, - client: &NyxdClient, + client: &QueryHttpRpcNyxdClient, ) -> ConnectionResult { let result = match timeout( Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC), @@ -186,7 +185,7 @@ async fn test_nym_api_connection( } enum ClientForConnectionTest { - Nyxd(NymNetworkDetails, Url, Box>), + Nyxd(NymNetworkDetails, Url, Box), Api(NymNetworkDetails, Url, NymApiClient), } diff --git a/common/client-libs/validator-client/src/error.rs b/common/client-libs/validator-client/src/error.rs index a675a27717..d439a86848 100644 --- a/common/client-libs/validator-client/src/error.rs +++ b/common/client-libs/validator-client/src/error.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::nym_api; +pub use tendermint_rpc::error::Error as TendermintRpcError; use thiserror::Error; #[derive(Error, Debug)] @@ -12,6 +13,9 @@ pub enum ValidatorClientError { source: nym_api::error::NymAPIError, }, + #[error("Tendermint RPC request failure: {0}")] + TendermintErrorRpc(#[from] TendermintRpcError), + #[error("One of the provided URLs was malformed - {0}")] MalformedUrlProvided(#[from] url::ParseError), diff --git a/common/client-libs/validator-client/src/lib.rs b/common/client-libs/validator-client/src/lib.rs index ab0cff646e..b3dca0a473 100644 --- a/common/client-libs/validator-client/src/lib.rs +++ b/common/client-libs/validator-client/src/lib.rs @@ -1,18 +1,46 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 pub mod client; +pub mod coconut; #[cfg(feature = "http-client")] pub mod connection_tester; pub mod error; pub mod nym_api; pub mod nyxd; - -#[cfg(feature = "signing")] +pub mod rpc; pub mod signing; pub use crate::error::ValidatorClientError; +pub use crate::rpc::reqwest::ReqwestRpcClient; +pub use crate::signing::direct_wallet::DirectSecp256k1HdWallet; pub use client::NymApiClient; +pub use client::{Client, CoconutApiClient, Config}; pub use nym_api_requests::*; -pub use client::{Client, CoconutApiClient, Config}; +#[cfg(feature = "http-client")] +pub use cosmrs::rpc::HttpClient as HttpRpcClient; +#[cfg(feature = "http-client")] +pub use rpc::http_client; + +// some type aliasing + +pub type ValidatorClient = Client; +pub type SigningValidatorClient = Client; + +#[cfg(feature = "http-client")] +pub type QueryHttpRpcValidatorClient = Client; +#[cfg(feature = "http-client")] +pub type QueryHttpRpcNyxdClient = nyxd::NyxdClient; + +#[cfg(feature = "http-client")] +pub type DirectSigningHttpRpcValidatorClient = Client; +#[cfg(feature = "http-client")] +pub type DirectSigningHttpRpcNyxdClient = nyxd::NyxdClient; + +pub type QueryReqwestRpcValidatorClient = Client; +pub type QueryReqwestRpcNyxdClient = nyxd::NyxdClient; + +pub type DirectSigningReqwestRpcValidatorClient = Client; +pub type DirectSigningReqwestRpcNyxdClient = + nyxd::NyxdClient; diff --git a/common/client-libs/validator-client/src/nym_api/mod.rs b/common/client-libs/validator-client/src/nym_api/mod.rs index 67b62decd9..a7d2f1c1c1 100644 --- a/common/client-libs/validator-client/src/nym_api/mod.rs +++ b/common/client-libs/validator-client/src/nym_api/mod.rs @@ -7,11 +7,11 @@ use nym_api_requests::coconut::{ BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse, }; use nym_api_requests::models::{ - ComputeRewardEstParam, GatewayCoreStatusResponse, GatewayStatusReportResponse, - GatewayUptimeHistoryResponse, InclusionProbabilityResponse, MixNodeBondAnnotated, - MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse, - MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse, StakeSaturationResponse, - UptimeResponse, + ComputeRewardEstParam, GatewayBondAnnotated, GatewayCoreStatusResponse, + GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse, + MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse, + MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse, + StakeSaturationResponse, UptimeResponse, }; use nym_mixnet_contract_common::mixnode::MixNodeDetails; use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId}; @@ -148,6 +148,19 @@ impl Client { .await } + pub async fn get_gateways_detailed(&self) -> Result, NymAPIError> { + self.query_nym_api( + &[ + routes::API_VERSION, + routes::STATUS, + routes::GATEWAYS, + routes::DETAILED, + ], + NO_PARAMS, + ) + .await + } + pub async fn get_mixnodes_detailed_unfiltered( &self, ) -> Result, NymAPIError> { diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_query_client.rs new file mode 100644 index 0000000000..1737edf613 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_query_client.rs @@ -0,0 +1,100 @@ +// Copyright 2022-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::collect_paged; +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::error::NyxdError; +use crate::nyxd::CosmWasmClient; +use async_trait::async_trait; +use nym_coconut_bandwidth_contract_common::msg::QueryMsg as CoconutBandwidthQueryMsg; +use nym_coconut_bandwidth_contract_common::spend_credential::{ + PagedSpendCredentialResponse, SpendCredential, SpendCredentialResponse, +}; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait CoconutBandwidthQueryClient { + async fn query_coconut_bandwidth_contract( + &self, + query: CoconutBandwidthQueryMsg, + ) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn get_spent_credential( + &self, + blinded_serial_number: String, + ) -> Result { + self.query_coconut_bandwidth_contract(CoconutBandwidthQueryMsg::GetSpentCredential { + blinded_serial_number, + }) + .await + } + + async fn get_all_spent_credential_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_coconut_bandwidth_contract(CoconutBandwidthQueryMsg::GetAllSpentCredentials { + limit, + start_after, + }) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedCoconutBandwidthQueryClient: CoconutBandwidthQueryClient { + async fn get_all_spent_credentials(&self) -> Result, NyxdError> { + collect_paged!(self, get_all_spent_credential_paged, spend_credentials) + } +} + +#[async_trait] +impl PagedCoconutBandwidthQueryClient for T where T: CoconutBandwidthQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl CoconutBandwidthQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_coconut_bandwidth_contract( + &self, + query: CoconutBandwidthQueryMsg, + ) -> Result + where + for<'a> T: Deserialize<'a>, + { + let coconut_bandwidth_contract_address = self + .coconut_bandwidth_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("coconut bandwidth contract"))?; + self.query_contract_smart(coconut_bandwidth_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: CoconutBandwidthQueryMsg, + ) { + match msg { + CoconutBandwidthQueryMsg::GetSpentCredential { + blinded_serial_number, + } => client.get_spent_credential(blinded_serial_number).ignore(), + CoconutBandwidthQueryMsg::GetAllSpentCredentials { limit, start_after } => client + .get_all_spent_credential_paged(start_after, limit) + .ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_signing_client.rs new file mode 100644 index 0000000000..030d78b29a --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/coconut_bandwidth_signing_client.rs @@ -0,0 +1,153 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; +use async_trait::async_trait; +use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialData; +use nym_coconut_bandwidth_contract_common::{ + deposit::DepositData, msg::ExecuteMsg as CoconutBandwidthExecuteMsg, +}; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait CoconutBandwidthSigningClient { + async fn execute_coconut_bandwidth_contract( + &self, + fee: Option, + msg: CoconutBandwidthExecuteMsg, + memo: String, + funds: Vec, + ) -> Result; + + async fn deposit( + &self, + amount: Coin, + info: String, + verification_key: String, + encryption_key: String, + fee: Option, + ) -> Result { + let req = CoconutBandwidthExecuteMsg::DepositFunds { + data: DepositData::new(info.to_string(), verification_key, encryption_key), + }; + self.execute_coconut_bandwidth_contract( + fee, + req, + "CoconutBandwidth::Deposit".to_string(), + vec![amount], + ) + .await + } + + async fn spend_credential( + &self, + funds: Coin, + blinded_serial_number: String, + gateway_cosmos_address: String, + fee: Option, + ) -> Result { + let req = CoconutBandwidthExecuteMsg::SpendCredential { + data: SpendCredentialData::new( + funds.into(), + blinded_serial_number, + gateway_cosmos_address, + ), + }; + self.execute_coconut_bandwidth_contract( + fee, + req, + "CoconutBandwidth::SpendCredential".to_string(), + vec![], + ) + .await + } + + async fn release_funds( + &self, + amount: Coin, + fee: Option, + ) -> Result { + self.execute_coconut_bandwidth_contract( + fee, + CoconutBandwidthExecuteMsg::ReleaseFunds { + funds: amount.into(), + }, + "CoconutBandwidth::ReleaseFunds".to_string(), + vec![], + ) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl CoconutBandwidthSigningClient for C +where + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, +{ + async fn execute_coconut_bandwidth_contract( + &self, + fee: Option, + msg: CoconutBandwidthExecuteMsg, + memo: String, + funds: Vec, + ) -> Result { + let coconut_bandwidth_contract_address = self + .coconut_bandwidth_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("coconut bandwidth contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); + let signer_address = &self.signer_addresses()?[0]; + + self.execute( + signer_address, + coconut_bandwidth_contract_address, + &msg, + fee, + memo, + funds, + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: CoconutBandwidthExecuteMsg, + ) { + match msg { + CoconutBandwidthExecuteMsg::DepositFunds { data } => client + .deposit( + mock_coin(), + data.deposit_info().to_string(), + data.identity_key().to_string(), + data.encryption_key().to_string(), + None, + ) + .ignore(), + CoconutBandwidthExecuteMsg::SpendCredential { data } => client + .spend_credential( + mock_coin(), + data.blinded_serial_number().to_string(), + data.gateway_cosmos_address().to_string(), + None, + ) + .ignore(), + CoconutBandwidthExecuteMsg::ReleaseFunds { funds } => { + client.release_funds(funds.into(), None).ignore() + } + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_query_client.rs new file mode 100644 index 0000000000..d3ac4807e8 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_query_client.rs @@ -0,0 +1,182 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::collect_paged; +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::error::NyxdError; +use crate::nyxd::CosmWasmClient; +use async_trait::async_trait; +use cosmrs::AccountId; +use nym_coconut_dkg_common::dealer::{ + ContractDealing, DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse, +}; +use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg; +use nym_coconut_dkg_common::types::{DealerDetails, Epoch, EpochId, InitialReplacementData}; +use nym_coconut_dkg_common::verification_key::{ContractVKShare, PagedVKSharesResponse}; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait DkgQueryClient { + async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn get_current_epoch(&self) -> Result { + let request = DkgQueryMsg::GetCurrentEpochState {}; + self.query_dkg_contract(request).await + } + async fn get_current_epoch_threshold(&self) -> Result, NyxdError> { + let request = DkgQueryMsg::GetCurrentEpochThreshold {}; + self.query_dkg_contract(request).await + } + + async fn get_initial_dealers(&self) -> Result, NyxdError> { + let request = DkgQueryMsg::GetInitialDealers {}; + self.query_dkg_contract(request).await + } + + async fn get_dealer_details( + &self, + address: &AccountId, + ) -> Result { + let request = DkgQueryMsg::GetDealerDetails { + dealer_address: address.to_string(), + }; + self.query_dkg_contract(request).await + } + + async fn get_current_dealers_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + let request = DkgQueryMsg::GetCurrentDealers { start_after, limit }; + self.query_dkg_contract(request).await + } + + async fn get_past_dealers_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + let request = DkgQueryMsg::GetPastDealers { start_after, limit }; + self.query_dkg_contract(request).await + } + + async fn get_dealings_paged( + &self, + idx: u64, + start_after: Option, + limit: Option, + ) -> Result { + let request = DkgQueryMsg::GetDealing { + idx, + limit, + start_after, + }; + self.query_dkg_contract(request).await + } + + async fn get_vk_shares_paged( + &self, + epoch_id: EpochId, + start_after: Option, + limit: Option, + ) -> Result { + let request = DkgQueryMsg::GetVerificationKeys { + epoch_id, + limit, + start_after, + }; + self.query_dkg_contract(request).await + } +} + +// extension trait to the query client to deal with the paged queries +// (it didn't feel appropriate to combine it with the existing trait +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedDkgQueryClient: DkgQueryClient { + async fn get_all_current_dealers(&self) -> Result, NyxdError> { + collect_paged!(self, get_current_dealers_paged, dealers) + } + + async fn get_all_past_dealers(&self) -> Result, NyxdError> { + collect_paged!(self, get_past_dealers_paged, dealers) + } + + async fn get_all_epoch_dealings(&self, idx: u64) -> Result, NyxdError> { + collect_paged!(self, get_dealings_paged, dealings, idx) + } + + async fn get_all_verification_key_shares( + &self, + epoch_id: EpochId, + ) -> Result, NyxdError> { + collect_paged!(self, get_vk_shares_paged, shares, epoch_id) + } +} + +#[async_trait] +impl PagedDkgQueryClient for T where T: DkgQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl DkgQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let dkg_contract_address = &self + .dkg_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("dkg contract"))?; + self.query_contract_smart(dkg_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: DkgQueryMsg, + ) { + match msg { + DkgQueryMsg::GetCurrentEpochState {} => client.get_current_epoch().ignore(), + DkgQueryMsg::GetCurrentEpochThreshold {} => { + client.get_current_epoch_threshold().ignore() + } + DkgQueryMsg::GetInitialDealers {} => client.get_initial_dealers().ignore(), + DkgQueryMsg::GetDealerDetails { dealer_address } => client + .get_dealer_details(&dealer_address.parse().unwrap()) + .ignore(), + DkgQueryMsg::GetCurrentDealers { limit, start_after } => client + .get_current_dealers_paged(start_after, limit) + .ignore(), + DkgQueryMsg::GetPastDealers { limit, start_after } => { + client.get_past_dealers_paged(start_after, limit).ignore() + } + DkgQueryMsg::GetDealing { + idx, + limit, + start_after, + } => client.get_dealings_paged(idx, start_after, limit).ignore(), + DkgQueryMsg::GetVerificationKeys { + epoch_id, + limit, + start_after, + } => client + .get_vk_shares_paged(epoch_id, start_after, limit) + .ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_signing_client.rs new file mode 100644 index 0000000000..73bce467b7 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/dkg_signing_client.rs @@ -0,0 +1,176 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; +use async_trait::async_trait; +use cosmrs::AccountId; +use cosmwasm_std::Addr; +use nym_coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg; +use nym_coconut_dkg_common::types::EncodedBTEPublicKeyWithProof; +use nym_coconut_dkg_common::verification_key::VerificationKeyShare; +use nym_contracts_common::dealings::ContractSafeBytes; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait DkgSigningClient { + async fn execute_dkg_contract( + &self, + fee: Option, + msg: DkgExecuteMsg, + memo: String, + funds: Vec, + ) -> Result; + + async fn advance_dkg_epoch_state(&self, fee: Option) -> Result { + let req = DkgExecuteMsg::AdvanceEpochState {}; + + self.execute_dkg_contract(fee, req, "advancing DKG state".to_string(), vec![]) + .await + } + + async fn surpass_threshold(&self, fee: Option) -> Result { + let req = DkgExecuteMsg::SurpassedThreshold {}; + + self.execute_dkg_contract(fee, req, "surpass DKG threshold".to_string(), vec![]) + .await + } + + async fn register_dealer( + &self, + bte_key: EncodedBTEPublicKeyWithProof, + announce_address: String, + resharing: bool, + fee: Option, + ) -> Result { + let req = DkgExecuteMsg::RegisterDealer { + bte_key_with_proof: bte_key, + announce_address, + resharing, + }; + + self.execute_dkg_contract(fee, req, "registering as a dealer".to_string(), vec![]) + .await + } + + async fn submit_dealing_bytes( + &self, + dealing_bytes: ContractSafeBytes, + resharing: bool, + fee: Option, + ) -> Result { + let req = DkgExecuteMsg::CommitDealing { + dealing_bytes, + resharing, + }; + + self.execute_dkg_contract(fee, req, "dealing commitment".to_string(), vec![]) + .await + } + + async fn submit_verification_key_share( + &self, + share: VerificationKeyShare, + resharing: bool, + fee: Option, + ) -> Result { + let req = DkgExecuteMsg::CommitVerificationKeyShare { share, resharing }; + + self.execute_dkg_contract( + fee, + req, + "verification key share commitment".to_string(), + vec![], + ) + .await + } + + async fn verify_verification_key_share( + &self, + owner: &AccountId, + resharing: bool, + fee: Option, + ) -> Result { + // the call to unchecked is fine as we're converting from pre-validated `AccountId` + let owner = Addr::unchecked(owner.to_string()); + let req = DkgExecuteMsg::VerifyVerificationKeyShare { owner, resharing }; + + self.execute_dkg_contract( + fee, + req, + "verification key VerifyVerificationKeyShare".to_string(), + vec![], + ) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl DkgSigningClient for C +where + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, +{ + async fn execute_dkg_contract( + &self, + fee: Option, + msg: DkgExecuteMsg, + memo: String, + funds: Vec, + ) -> Result { + let dkg_contract_address = self + .dkg_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("dkg contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); + let signer_address = &self.signer_addresses()?[0]; + + self.execute(signer_address, dkg_contract_address, &msg, fee, memo, funds) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: DkgExecuteMsg, + ) { + match msg { + DkgExecuteMsg::RegisterDealer { + bte_key_with_proof, + announce_address, + resharing, + } => client + .register_dealer(bte_key_with_proof, announce_address, resharing, None) + .ignore(), + DkgExecuteMsg::CommitDealing { + dealing_bytes, + resharing, + } => client + .submit_dealing_bytes(dealing_bytes, resharing, None) + .ignore(), + DkgExecuteMsg::CommitVerificationKeyShare { share, resharing } => client + .submit_verification_key_share(share, resharing, None) + .ignore(), + DkgExecuteMsg::VerifyVerificationKeyShare { owner, resharing } => client + .verify_verification_key_share( + &owner.into_string().parse().unwrap(), + resharing, + None, + ) + .ignore(), + DkgExecuteMsg::SurpassedThreshold {} => client.surpass_threshold(None).ignore(), + DkgExecuteMsg::AdvanceEpochState {} => client.advance_dkg_epoch_state(None).ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/ephemera_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/ephemera_query_client.rs new file mode 100644 index 0000000000..66595be1a1 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/ephemera_query_client.rs @@ -0,0 +1,79 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::collect_paged; +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::error::NyxdError; +use crate::nyxd::CosmWasmClient; +use async_trait::async_trait; +use nym_ephemera_common::msg::QueryMsg as EphemeraQueryMsg; +use nym_ephemera_common::peers::PagedPeerResponse; +use nym_ephemera_common::types::JsonPeerInfo; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait EphemeraQueryClient { + async fn query_ephemera_contract(&self, query: EphemeraQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn get_peers_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + let request = EphemeraQueryMsg::GetPeers { start_after, limit }; + self.query_ephemera_contract(request).await + } +} + +// extension trait to the query client to deal with the paged queries +// (it didn't feel appropriate to combine it with the existing trait +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedEphemeraQueryClient: EphemeraQueryClient { + async fn get_all_ephemera_peers(&self) -> Result, NyxdError> { + collect_paged!(self, get_peers_paged, peers) + } +} + +#[async_trait] +impl PagedEphemeraQueryClient for T where T: EphemeraQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl EphemeraQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_ephemera_contract(&self, query: EphemeraQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let ephemera_contract_address = &self + .ephemera_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("ephemera contract"))?; + self.query_contract_smart(ephemera_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: EphemeraQueryMsg, + ) { + match msg { + EphemeraQueryMsg::GetPeers { limit, start_after } => { + client.get_peers_paged(start_after, limit).ignore() + } + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/ephemera_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/ephemera_signing_client.rs new file mode 100644 index 0000000000..a79ca3fe5a --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/ephemera_signing_client.rs @@ -0,0 +1,86 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; +use async_trait::async_trait; +use nym_ephemera_common::msg::ExecuteMsg as EphemeraExecuteMsg; +use nym_ephemera_common::types::JsonPeerInfo; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait EphemeraSigningClient { + async fn execute_ephemera_contract( + &self, + fee: Option, + msg: EphemeraExecuteMsg, + memo: String, + funds: Vec, + ) -> Result; + + async fn register_as_peer( + &self, + peer_info: JsonPeerInfo, + fee: Option, + ) -> Result { + let req = EphemeraExecuteMsg::RegisterPeer { peer_info }; + + self.execute_ephemera_contract(fee, req, "registering as peer".to_string(), vec![]) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl EphemeraSigningClient for C +where + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, +{ + async fn execute_ephemera_contract( + &self, + fee: Option, + msg: EphemeraExecuteMsg, + memo: String, + funds: Vec, + ) -> Result { + let ephemera_contract_address = self + .ephemera_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("ephemera contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); + let signer_address = &self.signer_addresses()?[0]; + + self.execute( + signer_address, + ephemera_contract_address, + &msg, + fee, + memo, + funds, + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: EphemeraExecuteMsg, + ) { + match msg { + EphemeraExecuteMsg::RegisterPeer { peer_info } => { + client.register_as_peer(peer_info, None).ignore() + } + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/group_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/group_query_client.rs new file mode 100644 index 0000000000..8513dfa5bb --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/group_query_client.rs @@ -0,0 +1,118 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::error::NyxdError; +use crate::nyxd::CosmWasmClient; +use async_trait::async_trait; +use cw4::{Member, MemberListResponse, MemberResponse, TotalWeightResponse}; +use nym_group_contract_common::msg::QueryMsg as GroupQueryMsg; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait GroupQueryClient { + async fn query_group_contract(&self, query: GroupQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn admin(&self) -> Result { + self.query_group_contract(GroupQueryMsg::Admin {}).await + } + + async fn total_weight(&self, at_height: Option) -> Result { + self.query_group_contract(GroupQueryMsg::TotalWeight { at_height }) + .await + } + + async fn list_members_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_group_contract(GroupQueryMsg::ListMembers { start_after, limit }) + .await + } + + async fn member( + &self, + addr: String, + at_height: Option, + ) -> Result { + self.query_group_contract(GroupQueryMsg::Member { addr, at_height }) + .await + } + + async fn hooks(&self) -> Result { + self.query_group_contract(GroupQueryMsg::Hooks {}).await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedGroupQueryClient: GroupQueryClient { + // can't use the macro due to different paging behaviour + async fn get_all_members(&self) -> Result, NyxdError> { + let mut members = Vec::new(); + let mut start_after = None; + + loop { + let mut paged_response = self.list_members_paged(start_after.take(), None).await?; + + let last_id = paged_response.members.last().map(|mem| mem.addr.clone()); + members.append(&mut paged_response.members); + + if let Some(start_after_res) = last_id { + start_after = Some(start_after_res) + } else { + break; + } + } + + Ok(members) + } +} + +#[async_trait] +impl PagedGroupQueryClient for T where T: GroupQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl GroupQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_group_contract(&self, query: GroupQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let group_contract_address = &self + .group_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("group contract"))?; + self.query_contract_smart(group_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: GroupQueryMsg, + ) { + match msg { + GroupQueryMsg::Admin {} => client.admin().ignore(), + GroupQueryMsg::TotalWeight { at_height } => client.total_weight(at_height).ignore(), + GroupQueryMsg::ListMembers { start_after, limit } => { + client.list_members_paged(start_after, limit).ignore() + } + GroupQueryMsg::Member { addr, at_height } => client.member(addr, at_height).ignore(), + GroupQueryMsg::Hooks {} => client.hooks().ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/group_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/group_signing_client.rs new file mode 100644 index 0000000000..5ac1954849 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/group_signing_client.rs @@ -0,0 +1,131 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; +use async_trait::async_trait; +use cw4::Member; +use nym_group_contract_common::msg::ExecuteMsg as GroupExecuteMsg; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait GroupSigningClient { + async fn execute_group_contract( + &self, + fee: Option, + msg: GroupExecuteMsg, + memo: String, + funds: Vec, + ) -> Result; + + async fn update_admin( + &self, + admin: Option, + fee: Option, + ) -> Result { + self.execute_group_contract( + fee, + GroupExecuteMsg::UpdateAdmin { admin }, + "GroupExecuteMsg::UpdateAdmin".to_string(), + vec![], + ) + .await + } + + async fn update_members( + &self, + add: Vec, + remove: Vec, + fee: Option, + ) -> Result { + self.execute_group_contract( + fee, + GroupExecuteMsg::UpdateMembers { add, remove }, + "GroupExecuteMsg::UpdateMembers".to_string(), + vec![], + ) + .await + } + + async fn add_hook(&self, addr: String, fee: Option) -> Result { + self.execute_group_contract( + fee, + GroupExecuteMsg::AddHook { addr }, + "GroupExecuteMsg::AddHook".to_string(), + vec![], + ) + .await + } + + async fn remove_hook( + &self, + addr: String, + fee: Option, + ) -> Result { + self.execute_group_contract( + fee, + GroupExecuteMsg::RemoveHook { addr }, + "GroupExecuteMsg::RemoveHook".to_string(), + vec![], + ) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl GroupSigningClient for C +where + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, +{ + async fn execute_group_contract( + &self, + fee: Option, + msg: GroupExecuteMsg, + memo: String, + funds: Vec, + ) -> Result { + let group_contract_address = self + .group_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("group contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); + + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + group_contract_address, + &msg, + fee, + memo, + funds, + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: GroupExecuteMsg, + ) { + match msg { + GroupExecuteMsg::UpdateAdmin { admin } => client.update_admin(admin, None).ignore(), + GroupExecuteMsg::UpdateMembers { remove, add } => { + client.update_members(add, remove, None).ignore() + } + GroupExecuteMsg::AddHook { addr } => client.add_hook(addr, None).ignore(), + GroupExecuteMsg::RemoveHook { addr } => client.remove_hook(addr, None).ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_query_client.rs new file mode 100644 index 0000000000..95d692d592 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_query_client.rs @@ -0,0 +1,745 @@ +// Copyright 2022-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::collect_paged; +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::error::NyxdError; +use crate::nyxd::CosmWasmClient; +use async_trait::async_trait; +use cosmrs::AccountId; +use nym_contracts_common::signing::Nonce; +use nym_mixnet_contract_common::{ + delegation, + delegation::{MixNodeDelegationResponse, OwnerProxySubKey}, + families::{Family, FamilyHead}, + mixnode::{ + MixnodeRewardingDetailsResponse, PagedMixnodesDetailsResponse, + PagedUnbondedMixnodesResponse, StakeSaturationResponse, UnbondedMixnodeResponse, + }, + reward_params::{Performance, RewardingParams}, + rewarding::{EstimatedCurrentEpochRewardResponse, PendingRewardResponse}, + ContractBuildInformation, ContractState, ContractStateParams, CurrentIntervalResponse, + Delegation, EpochEventId, EpochStatus, FamilyByHeadResponse, FamilyByLabelResponse, + FamilyMembersByHeadResponse, FamilyMembersByLabelResponse, GatewayBond, GatewayBondResponse, + GatewayOwnershipResponse, IdentityKey, IdentityKeyRef, IntervalEventId, LayerDistribution, + MixId, MixNodeBond, MixNodeDetails, MixOwnershipResponse, MixnodeDetailsByIdentityResponse, + MixnodeDetailsResponse, NumberOfPendingEventsResponse, PagedAllDelegationsResponse, + PagedDelegatorDelegationsResponse, PagedFamiliesResponse, PagedGatewayResponse, + PagedMembersResponse, PagedMixNodeDelegationsResponse, PagedMixnodeBondsResponse, + PagedRewardedSetResponse, PendingEpochEvent, PendingEpochEventResponse, + PendingEpochEventsResponse, PendingIntervalEvent, PendingIntervalEventResponse, + PendingIntervalEventsResponse, QueryMsg as MixnetQueryMsg, RewardedSetNodeStatus, + UnbondedMixnode, +}; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait MixnetQueryClient { + async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + // state/sys-params-related + + async fn get_mixnet_contract_version(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetContractVersion {}) + .await + } + + async fn get_mixnet_contract_cw2_version(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetCW2ContractVersion {}) + .await + } + + async fn get_rewarding_validator_address(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetRewardingValidatorAddress {}) + .await + } + + async fn get_mixnet_contract_settings(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetStateParams {}) + .await + } + + async fn get_mixnet_contract_state_params(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetStateParams {}) + .await + } + + async fn get_mixnet_contract_state(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetState {}) + .await + } + + async fn get_rewarding_parameters(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetRewardingParams {}) + .await + } + + async fn get_current_epoch_status(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetEpochStatus {}) + .await + } + + async fn get_current_interval_details(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetCurrentIntervalDetails {}) + .await + } + + async fn get_rewarded_set_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetRewardedSet { limit, start_after }) + .await + } + + async fn get_all_node_families_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after }) + .await + } + + async fn get_all_family_members_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetAllMembersPaged { limit, start_after }) + .await + } + + async fn get_family_members_by_head + Send>( + &self, + head: S, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyMembersByHead { head: head.into() }) + .await + } + + async fn get_family_members_by_label + Send>( + &self, + label: S, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyMembersByLabel { + label: label.into(), + }) + .await + } + + // mixnode-related: + + async fn get_mixnode_bonds_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetMixNodeBonds { limit, start_after }) + .await + } + + async fn get_mixnodes_detailed_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetMixNodesDetailed { limit, start_after }) + .await + } + + async fn get_unbonded_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after }) + .await + } + + async fn get_unbonded_by_owner_paged( + &self, + owner: &AccountId, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByOwner { + owner: owner.to_string(), + limit, + start_after, + }) + .await + } + + async fn get_unbonded_by_identity_paged( + &self, + identity_key: IdentityKeyRef<'_>, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey { + identity_key: identity_key.to_string(), + limit, + start_after, + }) + .await + } + + async fn get_owned_mixnode( + &self, + address: &AccountId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetOwnedMixnode { + address: address.to_string(), + }) + .await + } + + async fn get_mixnode_details( + &self, + mix_id: MixId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDetails { mix_id }) + .await + } + + async fn get_mixnode_details_by_identity( + &self, + mix_identity: IdentityKey, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity { + mix_identity, + }) + .await + } + + async fn get_mixnode_rewarding_details( + &self, + mix_id: MixId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id }) + .await + } + + async fn get_mixnode_stake_saturation( + &self, + mix_id: MixId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetStakeSaturation { mix_id }) + .await + } + + async fn get_unbonded_mixnode_information( + &self, + mix_id: MixId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id }) + .await + } + + async fn get_layer_distribution(&self) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetLayerDistribution {}) + .await + } + + // gateway-related: + + async fn get_gateways_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetGateways { start_after, limit }) + .await + } + + /// Checks whether there is a bonded gateway associated with the provided identity key + async fn get_gateway_bond( + &self, + identity: IdentityKey, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetGatewayBond { identity }) + .await + } + + /// Checks whether there is a bonded gateway associated with the provided client's address + async fn get_owned_gateway( + &self, + address: &AccountId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetOwnedGateway { + address: address.to_string(), + }) + .await + } + + // delegation-related: + + /// Gets list of all delegations towards particular mixnode on particular page. + async fn get_mixnode_delegations_paged( + &self, + mix_id: MixId, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDelegations { + mix_id, + start_after, + limit, + }) + .await + } + + /// Gets list of all the mixnodes to which a particular address delegated. + async fn get_delegator_delegations_paged( + &self, + delegator: &AccountId, + start_after: Option<(MixId, OwnerProxySubKey)>, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetDelegatorDelegations { + delegator: delegator.to_string(), + start_after, + limit, + }) + .await + } + + /// Checks value of delegation of given client towards particular mixnode. + async fn get_delegation_details( + &self, + mix_id: MixId, + delegator: &AccountId, + proxy: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetDelegationDetails { + mix_id, + delegator: delegator.to_string(), + proxy, + }) + .await + } + + /// Gets all the delegations on the entire network + async fn get_all_network_delegations_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetAllDelegations { start_after, limit }) + .await + } + + // rewards related + async fn get_pending_operator_reward( + &self, + operator: &AccountId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingOperatorReward { + address: operator.to_string(), + }) + .await + } + + async fn get_pending_mixnode_operator_reward( + &self, + mix_id: MixId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id }) + .await + } + + async fn get_pending_delegator_reward( + &self, + delegator: &AccountId, + mix_id: MixId, + proxy: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingDelegatorReward { + address: delegator.to_string(), + mix_id, + proxy, + }) + .await + } + + // given the provided performance, estimate the reward at the end of the current epoch + async fn get_estimated_current_epoch_operator_reward( + &self, + mix_id: MixId, + estimated_performance: Performance, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward { + mix_id, + estimated_performance, + }) + .await + } + + // given the provided performance, estimate the reward at the end of the current epoch + async fn get_estimated_current_epoch_delegator_reward( + &self, + delegator: &AccountId, + mix_id: MixId, + proxy: Option, + estimated_performance: Performance, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward { + address: delegator.to_string(), + mix_id, + proxy, + estimated_performance, + }) + .await + } + + // interval-related + + async fn get_pending_epoch_events_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvents { start_after, limit }) + .await + } + + async fn get_pending_interval_events_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvents { start_after, limit }) + .await + } + + async fn get_pending_epoch_event( + &self, + event_id: EpochEventId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvent { event_id }) + .await + } + + async fn get_pending_interval_event( + &self, + event_id: IntervalEventId, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvent { event_id }) + .await + } + + async fn get_number_of_pending_events( + &self, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetNumberOfPendingEvents {}) + .await + } + + async fn get_signing_nonce(&self, address: &AccountId) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetSigningNonce { + address: address.to_string(), + }) + .await + } + + async fn get_node_family_by_label( + &self, + label: String, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel { label }) + .await + } + + async fn get_node_family_by_head( + &self, + head: String, + ) -> Result { + self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByHead { head }) + .await + } +} + +// extension trait to the query client to deal with the paged queries +// (it didn't feel appropriate to combine it with the existing trait +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedMixnetQueryClient: MixnetQueryClient { + async fn get_all_node_families(&self) -> Result, NyxdError> { + collect_paged!(self, get_all_node_families_paged, families) + } + + async fn get_all_family_members(&self) -> Result, NyxdError> { + collect_paged!(self, get_all_family_members_paged, members) + } + + async fn get_all_rewarded_set_mixnodes( + &self, + ) -> Result, NyxdError> { + collect_paged!(self, get_rewarded_set_paged, nodes) + } + + async fn get_all_mixnode_bonds(&self) -> Result, NyxdError> { + collect_paged!(self, get_mixnode_bonds_paged, nodes) + } + + async fn get_all_mixnodes_detailed(&self) -> Result, NyxdError> { + collect_paged!(self, get_mixnodes_detailed_paged, nodes) + } + + async fn get_all_unbonded_mixnodes(&self) -> Result, NyxdError> { + collect_paged!(self, get_unbonded_paged, nodes) + } + + async fn get_all_unbonded_mixnodes_by_owner( + &self, + owner: &AccountId, + ) -> Result, NyxdError> { + collect_paged!(self, get_unbonded_by_owner_paged, nodes, owner) + } + + async fn get_all_unbonded_mixnodes_by_identity( + &self, + identity_key: IdentityKeyRef<'_>, + ) -> Result, NyxdError> { + collect_paged!(self, get_unbonded_by_identity_paged, nodes, identity_key) + } + + async fn get_all_gateways(&self) -> Result, NyxdError> { + collect_paged!(self, get_gateways_paged, nodes) + } + + async fn get_all_single_mixnode_delegations( + &self, + mix_id: MixId, + ) -> Result, NyxdError> { + collect_paged!(self, get_mixnode_delegations_paged, delegations, mix_id) + } + + async fn get_all_delegator_delegations( + &self, + delegation_owner: &AccountId, + ) -> Result, NyxdError> { + collect_paged!( + self, + get_delegator_delegations_paged, + delegations, + delegation_owner + ) + } + + async fn get_all_network_delegations(&self) -> Result, NyxdError> { + collect_paged!(self, get_all_network_delegations_paged, delegations) + } + + async fn get_all_pending_epoch_events(&self) -> Result, NyxdError> { + collect_paged!(self, get_pending_epoch_events_paged, events) + } + + async fn get_all_pending_interval_events( + &self, + ) -> Result, NyxdError> { + collect_paged!(self, get_pending_interval_events_paged, events) + } +} + +#[async_trait] +impl PagedMixnetQueryClient for T where T: MixnetQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl MixnetQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let mixnet_contract_address = &self + .mixnet_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("mixnet contract"))?; + self.query_contract_smart(mixnet_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: MixnetQueryMsg, + ) -> u32 { + match msg { + MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after } => client + .get_all_family_members_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetAllMembersPaged { limit, start_after } => client + .get_all_family_members_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetFamilyByHead { head } => { + client.get_node_family_by_head(head).ignore() + } + MixnetQueryMsg::GetFamilyByLabel { label } => { + client.get_node_family_by_label(label).ignore() + } + MixnetQueryMsg::GetFamilyMembersByHead { head } => { + client.get_family_members_by_head(head).ignore() + } + MixnetQueryMsg::GetFamilyMembersByLabel { label } => { + client.get_family_members_by_label(label).ignore() + } + MixnetQueryMsg::GetContractVersion {} => client.get_mixnet_contract_version().ignore(), + MixnetQueryMsg::GetCW2ContractVersion {} => { + client.get_mixnet_contract_cw2_version().ignore() + } + MixnetQueryMsg::GetRewardingValidatorAddress {} => { + client.get_rewarding_validator_address().ignore() + } + MixnetQueryMsg::GetStateParams {} => client.get_mixnet_contract_state_params().ignore(), + MixnetQueryMsg::GetState {} => client.get_mixnet_contract_state().ignore(), + MixnetQueryMsg::GetRewardingParams {} => client.get_rewarding_parameters().ignore(), + MixnetQueryMsg::GetEpochStatus {} => client.get_current_epoch_status().ignore(), + MixnetQueryMsg::GetCurrentIntervalDetails {} => { + client.get_current_interval_details().ignore() + } + MixnetQueryMsg::GetRewardedSet { limit, start_after } => { + client.get_rewarded_set_paged(start_after, limit).ignore() + } + MixnetQueryMsg::GetMixNodeBonds { limit, start_after } => { + client.get_mixnode_bonds_paged(start_after, limit).ignore() + } + MixnetQueryMsg::GetMixNodesDetailed { limit, start_after } => client + .get_mixnodes_detailed_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after } => { + client.get_unbonded_paged(start_after, limit).ignore() + } + MixnetQueryMsg::GetUnbondedMixNodesByOwner { + owner, + limit, + start_after, + } => client + .get_unbonded_by_owner_paged(&owner.parse().unwrap(), start_after, limit) + .ignore(), + MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey { + identity_key, + limit, + start_after, + } => client + .get_unbonded_by_identity_paged(&identity_key, start_after, limit) + .ignore(), + MixnetQueryMsg::GetOwnedMixnode { address } => { + client.get_owned_mixnode(&address.parse().unwrap()).ignore() + } + MixnetQueryMsg::GetMixnodeDetails { mix_id } => { + client.get_mixnode_details(mix_id).ignore() + } + MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id } => { + client.get_mixnode_rewarding_details(mix_id).ignore() + } + MixnetQueryMsg::GetStakeSaturation { mix_id } => { + client.get_mixnode_stake_saturation(mix_id).ignore() + } + MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id } => { + client.get_unbonded_mixnode_information(mix_id).ignore() + } + MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity { mix_identity } => client + .get_mixnode_details_by_identity(mix_identity) + .ignore(), + MixnetQueryMsg::GetLayerDistribution {} => client.get_layer_distribution().ignore(), + MixnetQueryMsg::GetGateways { start_after, limit } => { + client.get_gateways_paged(start_after, limit).ignore() + } + MixnetQueryMsg::GetGatewayBond { identity } => { + client.get_gateway_bond(identity).ignore() + } + MixnetQueryMsg::GetOwnedGateway { address } => { + client.get_owned_gateway(&address.parse().unwrap()).ignore() + } + MixnetQueryMsg::GetMixnodeDelegations { + mix_id, + start_after, + limit, + } => client + .get_mixnode_delegations_paged(mix_id, start_after, limit) + .ignore(), + MixnetQueryMsg::GetDelegatorDelegations { + delegator, + start_after, + limit, + } => client + .get_delegator_delegations_paged(&delegator.parse().unwrap(), start_after, limit) + .ignore(), + MixnetQueryMsg::GetDelegationDetails { + mix_id, + delegator, + proxy, + } => client + .get_delegation_details(mix_id, &delegator.parse().unwrap(), proxy) + .ignore(), + MixnetQueryMsg::GetAllDelegations { start_after, limit } => client + .get_all_network_delegations_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetPendingOperatorReward { address } => client + .get_pending_operator_reward(&address.parse().unwrap()) + .ignore(), + MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id } => { + client.get_pending_mixnode_operator_reward(mix_id).ignore() + } + MixnetQueryMsg::GetPendingDelegatorReward { + address, + mix_id, + proxy, + } => client + .get_pending_delegator_reward(&address.parse().unwrap(), mix_id, proxy) + .ignore(), + MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward { + mix_id, + estimated_performance, + } => client + .get_estimated_current_epoch_operator_reward(mix_id, estimated_performance) + .ignore(), + MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward { + address, + mix_id, + proxy, + estimated_performance, + } => client + .get_estimated_current_epoch_delegator_reward( + &address.parse().unwrap(), + mix_id, + proxy, + estimated_performance, + ) + .ignore(), + MixnetQueryMsg::GetPendingEpochEvents { limit, start_after } => client + .get_pending_epoch_events_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetPendingIntervalEvents { limit, start_after } => client + .get_pending_interval_events_paged(start_after, limit) + .ignore(), + MixnetQueryMsg::GetPendingEpochEvent { event_id } => { + client.get_pending_epoch_event(event_id).ignore() + } + MixnetQueryMsg::GetPendingIntervalEvent { event_id } => { + client.get_pending_interval_event(event_id).ignore() + } + MixnetQueryMsg::GetNumberOfPendingEvents {} => { + client.get_number_of_pending_events().ignore() + } + MixnetQueryMsg::GetSigningNonce { address } => { + client.get_signing_nonce(&address.parse().unwrap()).ignore() + } + } + } +} diff --git a/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs similarity index 62% rename from common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs index 49fc651d5d..f8b5521e0d 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/mixnet_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/mixnet_signing_client.rs @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use crate::nyxd::coin::Coin; -pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::cosmwasm_client::types::ExecuteResult; use crate::nyxd::error::NyxdError; -use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient}; +use crate::nyxd::{Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; use async_trait::async_trait; use cosmrs::AccountId; use nym_contracts_common::signing::MessageSignature; @@ -14,10 +15,12 @@ use nym_mixnet_contract_common::gateway::GatewayConfigUpdate; use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance}; use nym_mixnet_contract_common::{ - ContractStateParams, ExecuteMsg as MixnetExecuteMsg, Gateway, LayerAssignment, MixId, MixNode, + ContractStateParams, ExecuteMsg as MixnetExecuteMsg, Gateway, Layer, LayerAssignment, MixId, + MixNode, }; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait MixnetSigningClient { async fn execute_mixnet_contract( &self, @@ -131,6 +134,20 @@ pub trait MixnetSigningClient { .await } + async fn assign_node_layer( + &self, + mix_id: MixId, + layer: Layer, + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::AssignNodeLayer { mix_id, layer }, + vec![], + ) + .await + } + async fn reconcile_epoch_events( &self, limit: Option, @@ -665,12 +682,26 @@ pub trait MixnetSigningClient { ) .await } + + #[cfg(feature = "nym_mixnet_contract_common/contract-testing")] + async fn testing_resolve_all_pending_events( + fee: Option, + ) -> Result { + self.execute_mixnet_contract( + fee, + MixnetExecuteMsg::TestingResolveAllPendingEvents {}, + vec![], + ) + .await + } } -#[async_trait] -impl MixnetSigningClient for NyxdClient +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl MixnetSigningClient for C where - C: SigningCosmWasmClient + Sync + Send, + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, { async fn execute_mixnet_contract( &self, @@ -678,32 +709,229 @@ where msg: MixnetExecuteMsg, funds: Vec, ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let mixnet_contract_address = &self + .mixnet_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("mixnet contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); let memo = msg.default_memo(); - self.client - .execute( - self.address(), - self.mixnet_contract_address(), - &msg, - fee, - memo, - funds, - ) - .await + + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + mixnet_contract_address, + &msg, + fee, + memo, + funds, + ) + .await } } -#[async_trait] -impl MixnetSigningClient for crate::Client -where - C: SigningCosmWasmClient + Sync + Send + Clone, -{ - async fn execute_mixnet_contract( - &self, - fee: Option, +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, msg: MixnetExecuteMsg, - funds: Vec, - ) -> Result { - self.nyxd.execute_mixnet_contract(fee, msg, funds).await + ) { + match msg { + MixnetExecuteMsg::AssignNodeLayer { mix_id, layer } => { + client.assign_node_layer(mix_id, layer, None).ignore() + } + MixnetExecuteMsg::CreateFamily { label } => client.create_family(label, None).ignore(), + MixnetExecuteMsg::JoinFamily { + join_permit, + family_head, + } => client.join_family(join_permit, family_head, None).ignore(), + MixnetExecuteMsg::LeaveFamily { family_head } => { + client.leave_family(family_head, None).ignore() + } + MixnetExecuteMsg::KickFamilyMember { member } => { + client.kick_family_member(member, None).ignore() + } + MixnetExecuteMsg::CreateFamilyOnBehalf { + owner_address, + label, + } => client + .create_family_on_behalf(owner_address, label, None) + .ignore(), + MixnetExecuteMsg::JoinFamilyOnBehalf { + member_address, + join_permit, + family_head, + } => client + .join_family_on_behalf(member_address, join_permit, family_head, None) + .ignore(), + MixnetExecuteMsg::LeaveFamilyOnBehalf { + member_address, + family_head, + } => client + .leave_family_on_behalf(member_address, family_head, None) + .ignore(), + MixnetExecuteMsg::KickFamilyMemberOnBehalf { + head_address, + member, + } => client + .kick_family_member_on_behalf(head_address, member, None) + .ignore(), + MixnetExecuteMsg::UpdateRewardingValidatorAddress { address } => client + .update_rewarding_validator_address(address.parse().unwrap(), None) + .ignore(), + MixnetExecuteMsg::UpdateContractStateParams { updated_parameters } => client + .update_contract_state_params(updated_parameters, None) + .ignore(), + MixnetExecuteMsg::UpdateActiveSetSize { + active_set_size, + force_immediately, + } => client + .update_active_set_size(active_set_size, force_immediately, None) + .ignore(), + MixnetExecuteMsg::UpdateRewardingParams { + updated_params, + force_immediately, + } => client + .update_rewarding_parameters(updated_params, force_immediately, None) + .ignore(), + MixnetExecuteMsg::UpdateIntervalConfig { + epochs_in_interval, + epoch_duration_secs, + force_immediately, + } => client + .update_interval_config( + epochs_in_interval, + epoch_duration_secs, + force_immediately, + None, + ) + .ignore(), + MixnetExecuteMsg::BeginEpochTransition {} => { + client.begin_epoch_transition(None).ignore() + } + MixnetExecuteMsg::AdvanceCurrentEpoch { + new_rewarded_set, + expected_active_set_size, + } => client + .advance_current_epoch(new_rewarded_set, expected_active_set_size, None) + .ignore(), + MixnetExecuteMsg::ReconcileEpochEvents { limit } => { + client.reconcile_epoch_events(limit, None).ignore() + } + MixnetExecuteMsg::BondMixnode { + mix_node, + cost_params, + owner_signature, + } => client + .bond_mixnode(mix_node, cost_params, owner_signature, mock_coin(), None) + .ignore(), + MixnetExecuteMsg::BondMixnodeOnBehalf { + mix_node, + cost_params, + owner_signature, + owner, + } => client + .bond_mixnode_on_behalf( + owner.parse().unwrap(), + mix_node, + cost_params, + owner_signature, + mock_coin(), + None, + ) + .ignore(), + MixnetExecuteMsg::PledgeMore {} => client.pledge_more(mock_coin(), None).ignore(), + MixnetExecuteMsg::PledgeMoreOnBehalf { owner } => client + .pledge_more_on_behalf(owner.parse().unwrap(), mock_coin(), None) + .ignore(), + MixnetExecuteMsg::DecreasePledge { decrease_by } => { + client.decrease_pledge(decrease_by.into(), None).ignore() + } + MixnetExecuteMsg::DecreasePledgeOnBehalf { owner, decrease_by } => client + .decrease_pledge_on_behalf(owner.parse().unwrap(), decrease_by.into(), None) + .ignore(), + MixnetExecuteMsg::UnbondMixnode {} => client.unbond_mixnode(None).ignore(), + MixnetExecuteMsg::UnbondMixnodeOnBehalf { owner } => client + .unbond_mixnode_on_behalf(owner.parse().unwrap(), None) + .ignore(), + MixnetExecuteMsg::UpdateMixnodeCostParams { new_costs } => { + client.update_mixnode_cost_params(new_costs, None).ignore() + } + MixnetExecuteMsg::UpdateMixnodeCostParamsOnBehalf { new_costs, owner } => client + .update_mixnode_cost_params_on_behalf(owner.parse().unwrap(), new_costs, None) + .ignore(), + MixnetExecuteMsg::UpdateMixnodeConfig { new_config } => { + client.update_mixnode_config(new_config, None).ignore() + } + MixnetExecuteMsg::UpdateMixnodeConfigOnBehalf { new_config, owner } => client + .update_mixnode_config_on_behalf(owner.parse().unwrap(), new_config, None) + .ignore(), + MixnetExecuteMsg::BondGateway { + gateway, + owner_signature, + } => client + .bond_gateway(gateway, owner_signature, mock_coin(), None) + .ignore(), + MixnetExecuteMsg::BondGatewayOnBehalf { + gateway, + owner, + owner_signature, + } => client + .bond_gateway_on_behalf( + owner.parse().unwrap(), + gateway, + owner_signature, + mock_coin(), + None, + ) + .ignore(), + MixnetExecuteMsg::UnbondGateway {} => client.unbond_gateway(None).ignore(), + MixnetExecuteMsg::UnbondGatewayOnBehalf { owner } => client + .unbond_gateway_on_behalf(owner.parse().unwrap(), None) + .ignore(), + MixnetExecuteMsg::UpdateGatewayConfig { new_config } => { + client.update_gateway_config(new_config, None).ignore() + } + MixnetExecuteMsg::UpdateGatewayConfigOnBehalf { new_config, owner } => client + .update_gateway_config_on_behalf(owner.parse().unwrap(), new_config, None) + .ignore(), + MixnetExecuteMsg::DelegateToMixnode { mix_id } => client + .delegate_to_mixnode(mix_id, mock_coin(), None) + .ignore(), + MixnetExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, delegate } => client + .delegate_to_mixnode_on_behalf(delegate.parse().unwrap(), mix_id, mock_coin(), None) + .ignore(), + MixnetExecuteMsg::UndelegateFromMixnode { mix_id } => { + client.undelegate_from_mixnode(mix_id, None).ignore() + } + MixnetExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_id, delegate } => client + .undelegate_to_mixnode_on_behalf(delegate.parse().unwrap(), mix_id, None) + .ignore(), + MixnetExecuteMsg::RewardMixnode { + mix_id, + performance, + } => client.reward_mixnode(mix_id, performance, None).ignore(), + MixnetExecuteMsg::WithdrawOperatorReward {} => { + client.withdraw_operator_reward(None).ignore() + } + MixnetExecuteMsg::WithdrawOperatorRewardOnBehalf { owner } => client + .withdraw_operator_reward_on_behalf(owner.parse().unwrap(), None) + .ignore(), + MixnetExecuteMsg::WithdrawDelegatorReward { mix_id } => { + client.withdraw_delegator_reward(mix_id, None).ignore() + } + MixnetExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, owner } => client + .withdraw_delegator_reward_on_behalf(owner.parse().unwrap(), mix_id, None) + .ignore(), + + #[cfg(feature = "nym_mixnet_contract_common/contract-testing")] + MixnetExecuteMsg::TestingResolveAllPendingEvents {} => { + client.testing_resolve_all_pending_events(None).ignore() + } + }; } } diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/mod.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/mod.rs new file mode 100644 index 0000000000..dac63a2530 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/mod.rs @@ -0,0 +1,192 @@ +// Copyright 2021-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmrs::AccountId; +use nym_network_defaults::NymContracts; +use std::str::FromStr; + +// TODO: all of those could/should be derived via a macro + +// query clients +mod coconut_bandwidth_query_client; +mod dkg_query_client; +mod ephemera_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; + +// signing clients +mod coconut_bandwidth_signing_client; +mod dkg_signing_client; +mod ephemera_signing_client; +mod group_signing_client; +mod mixnet_signing_client; +mod multisig_signing_client; +mod name_service_signing_client; +mod sp_directory_signing_client; +mod vesting_signing_client; + +// re-export query traits +pub use coconut_bandwidth_query_client::{ + CoconutBandwidthQueryClient, PagedCoconutBandwidthQueryClient, +}; +pub use dkg_query_client::{DkgQueryClient, PagedDkgQueryClient}; +pub use ephemera_query_client::{EphemeraQueryClient, PagedEphemeraQueryClient}; +pub use group_query_client::{GroupQueryClient, PagedGroupQueryClient}; +pub use mixnet_query_client::{MixnetQueryClient, PagedMixnetQueryClient}; +pub use multisig_query_client::{MultisigQueryClient, PagedMultisigQueryClient}; +pub use name_service_query_client::{NameServiceQueryClient, PagedNameServiceQueryClient}; +pub use sp_directory_query_client::{PagedSpDirectoryQueryClient, SpDirectoryQueryClient}; +pub use vesting_query_client::{PagedVestingQueryClient, VestingQueryClient}; + +// re-export signing traits +pub use coconut_bandwidth_signing_client::CoconutBandwidthSigningClient; +pub use dkg_signing_client::DkgSigningClient; +pub use ephemera_signing_client::EphemeraSigningClient; +pub use group_signing_client::GroupSigningClient; +pub use mixnet_signing_client::MixnetSigningClient; +pub use multisig_signing_client::MultisigSigningClient; +pub use name_service_signing_client::NameServiceSigningClient; +pub use sp_directory_signing_client::SpDirectorySigningClient; +pub use vesting_signing_client::VestingSigningClient; + +// helper for providing blanket implementation for query clients +pub trait NymContractsProvider { + // main + fn mixnet_contract_address(&self) -> Option<&AccountId>; + fn vesting_contract_address(&self) -> Option<&AccountId>; + + // coconut-related + fn coconut_bandwidth_contract_address(&self) -> Option<&AccountId>; + fn dkg_contract_address(&self) -> Option<&AccountId>; + fn group_contract_address(&self) -> Option<&AccountId>; + fn multisig_contract_address(&self) -> Option<&AccountId>; + + // ephemera-related + fn ephemera_contract_address(&self) -> Option<&AccountId>; + + // SPs + fn name_service_contract_address(&self) -> Option<&AccountId>; + fn service_provider_contract_address(&self) -> Option<&AccountId>; +} + +#[derive(Debug, Clone)] +pub struct TypedNymContracts { + pub mixnet_contract_address: Option, + pub vesting_contract_address: Option, + + pub coconut_bandwidth_contract_address: Option, + pub group_contract_address: Option, + pub multisig_contract_address: Option, + pub coconut_dkg_contract_address: Option, + + pub ephemera_contract_address: Option, + + pub service_provider_directory_contract_address: Option, + pub name_service_contract_address: Option, +} + +impl TryFrom for TypedNymContracts { + type Error = ::Err; + + fn try_from(value: NymContracts) -> Result { + Ok(TypedNymContracts { + mixnet_contract_address: value + .mixnet_contract_address + .map(|addr| addr.parse()) + .transpose()?, + vesting_contract_address: value + .vesting_contract_address + .map(|addr| addr.parse()) + .transpose()?, + coconut_bandwidth_contract_address: value + .coconut_bandwidth_contract_address + .map(|addr| addr.parse()) + .transpose()?, + group_contract_address: value + .group_contract_address + .map(|addr| addr.parse()) + .transpose()?, + multisig_contract_address: value + .multisig_contract_address + .map(|addr| addr.parse()) + .transpose()?, + coconut_dkg_contract_address: value + .coconut_dkg_contract_address + .map(|addr| addr.parse()) + .transpose()?, + ephemera_contract_address: value + .ephemera_contract_address + .map(|addr| addr.parse()) + .transpose()?, + service_provider_directory_contract_address: value + .service_provider_directory_contract_address + .map(|addr| addr.parse()) + .transpose()?, + name_service_contract_address: value + .name_service_contract_address + .map(|addr| addr.parse()) + .transpose()?, + }) + } +} + +// a simple helper macro to define to repeatedly call a paged query until a full response is constructed +#[macro_export] +macro_rules! collect_paged { + // TODO: deal with the args in a nicer way + ( $self:ident, $f: ident, $field: ident ) => {{ + let mut res = Vec::new(); + let mut start_after = None; + loop { + let paged_response = $self.$f(start_after.take(), None).await?; + res.extend(paged_response.$field); + + if let Some(start_next_after) = paged_response.start_next_after { + start_after = Some(start_next_after.into()) + } else { + break Ok(res); + } + } + }}; + + ( $self:ident, $f: ident, $field: ident, $($args:tt),*) => {{ + let mut res = Vec::new(); + let mut start_after = None; + loop { + let paged_response = $self.$f($($args),*, start_after.take(), None).await?; + res.extend(paged_response.$field); + + if let Some(start_next_after) = paged_response.start_next_after { + start_after = Some(start_next_after.into()) + } else { + break Ok(res); + } + } + }}; +} + +#[cfg(test)] +mod tests { + use crate::nyxd::Coin; + + pub(crate) trait IgnoreValue { + fn ignore(self) -> u32 + where + Self: Sized, + { + 42 + // reason we're returning a value as opposed to just `()` is that whenever we match on all enums + // we don't want to accidentally miss a variant because compiler will treat it the same way + } + } + + impl IgnoreValue for T {} + + pub(crate) fn mock_coin() -> Coin { + Coin::new(42, "ufoomp") + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_query_client.rs new file mode 100644 index 0000000000..48fcadc050 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_query_client.rs @@ -0,0 +1,178 @@ +// Copyright 2022-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::error::NyxdError; +use crate::nyxd::CosmWasmClient; +use async_trait::async_trait; +use cw3::{ + ProposalListResponse, ProposalResponse, VoteListResponse, VoteResponse, VoterListResponse, + VoterResponse, +}; +use cw_utils::ThresholdResponse; +use nym_multisig_contract_common::msg::QueryMsg as MultisigQueryMsg; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait MultisigQueryClient { + async fn query_multisig_contract(&self, query: MultisigQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn query_threshold(&self) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Threshold {}) + .await + } + + async fn query_proposal(&self, proposal_id: u64) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Proposal { proposal_id }) + .await + } + + async fn list_proposals( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ListProposals { start_after, limit }) + .await + } + + async fn reverse_proposals( + &self, + start_before: Option, + limit: Option, + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ReverseProposals { + start_before, + limit, + }) + .await + } + + async fn query_vote(&self, proposal_id: u64, voter: String) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Vote { proposal_id, voter }) + .await + } + + async fn list_votes( + &self, + proposal_id: u64, + start_after: Option, + limit: Option, + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ListVotes { + proposal_id, + start_after, + limit, + }) + .await + } + + async fn query_voter(&self, address: String) -> Result { + self.query_multisig_contract(MultisigQueryMsg::Voter { address }) + .await + } + + async fn list_voters( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_multisig_contract(MultisigQueryMsg::ListVoters { start_after, limit }) + .await + } + + async fn query_config(&self) -> Result<(), NyxdError> { + unimplemented!("requires exporting state::Config type") + } +} + +// extension trait to the query client to deal with the paged queries +// (it didn't feel appropriate to combine it with the existing trait +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedMultisigQueryClient: MultisigQueryClient { + // can't use the macro due to different paging behaviour + async fn get_all_proposals(&self) -> Result, NyxdError> { + let mut proposals = Vec::new(); + let mut start_after = None; + + loop { + let mut paged_response = self.list_proposals(start_after.take(), None).await?; + + let last_id = paged_response.proposals.last().map(|prop| prop.id); + proposals.append(&mut paged_response.proposals); + + if let Some(start_after_res) = last_id { + start_after = Some(start_after_res) + } else { + break; + } + } + + Ok(proposals) + } +} + +#[async_trait] +impl PagedMultisigQueryClient for T where T: MultisigQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl MultisigQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_multisig_contract(&self, query: MultisigQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let multisig_contract_address = &self + .multisig_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("multisig contract"))?; + self.query_contract_smart(multisig_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: MultisigQueryMsg, + ) { + match msg { + MultisigQueryMsg::Threshold {} => client.query_threshold().ignore(), + MultisigQueryMsg::Proposal { proposal_id } => { + client.query_proposal(proposal_id).ignore() + } + MultisigQueryMsg::ListProposals { start_after, limit } => { + client.list_proposals(start_after, limit).ignore() + } + MultisigQueryMsg::ReverseProposals { + start_before, + limit, + } => client.reverse_proposals(start_before, limit).ignore(), + MultisigQueryMsg::Vote { proposal_id, voter } => { + client.query_vote(proposal_id, voter).ignore() + } + MultisigQueryMsg::ListVotes { + proposal_id, + start_after, + limit, + } => client.list_votes(proposal_id, start_after, limit).ignore(), + MultisigQueryMsg::Voter { address } => client.query_voter(address).ignore(), + MultisigQueryMsg::ListVoters { start_after, limit } => { + client.list_voters(start_after, limit).ignore() + } + MultisigQueryMsg::Config {} => client.query_config().ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_signing_client.rs new file mode 100644 index 0000000000..f7249f02ea --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/multisig_signing_client.rs @@ -0,0 +1,192 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::cosmwasm_client::types::ExecuteResult; +use crate::nyxd::error::NyxdError; +use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; +use async_trait::async_trait; +use cosmwasm_std::{to_binary, CosmosMsg, WasmMsg}; +use cw3::Vote; +use cw4::{MemberChangedHookMsg, MemberDiff}; +use nym_coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg; +use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait MultisigSigningClient: NymContractsProvider { + async fn execute_multisig_contract( + &self, + fee: Option, + msg: MultisigExecuteMsg, + memo: String, + funds: Vec, + ) -> Result; + + async fn propose_release_funds( + &self, + title: String, + blinded_serial_number: String, + voucher_value: Coin, + fee: Option, + ) -> Result { + let coconut_bandwidth_contract_address = self + .coconut_bandwidth_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("coconut bandwidth contract"))?; + + let release_funds_req = CoconutBandwidthExecuteMsg::ReleaseFunds { + funds: voucher_value.into(), + }; + let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: coconut_bandwidth_contract_address.to_string(), + msg: to_binary(&release_funds_req)?, + funds: vec![], + }); + let req = MultisigExecuteMsg::Propose { + title, + description: blinded_serial_number, + msgs: vec![release_funds_msg], + latest: None, + }; + self.execute_multisig_contract( + fee, + req, + "Multisig::Propose::Execute::ReleaseFunds".to_string(), + vec![], + ) + .await + } + + async fn vote_proposal( + &self, + proposal_id: u64, + vote_yes: bool, + fee: Option, + ) -> Result { + let vote = if vote_yes { Vote::Yes } else { Vote::No }; + let req = MultisigExecuteMsg::Vote { proposal_id, vote }; + self.execute_multisig_contract(fee, req, "Multisig::Vote".to_string(), vec![]) + .await + } + + // alternative variant to vote_proposal that lets you to abstain and veto a proposal + async fn vote( + &self, + proposal_id: u64, + vote: Vote, + fee: Option, + ) -> Result { + self.execute_multisig_contract( + fee, + MultisigExecuteMsg::Vote { proposal_id, vote }, + "Multisig::Vote".to_string(), + vec![], + ) + .await + } + + async fn execute_proposal( + &self, + proposal_id: u64, + fee: Option, + ) -> Result { + let req = MultisigExecuteMsg::Execute { proposal_id }; + self.execute_multisig_contract(fee, req, "Multisig::Execute".to_string(), vec![]) + .await + } + + async fn close_proposal( + &self, + proposal_id: u64, + fee: Option, + ) -> Result { + self.execute_multisig_contract( + fee, + MultisigExecuteMsg::Close { proposal_id }, + "Multisig::Close".to_string(), + vec![], + ) + .await + } + + async fn changed_member_hook( + &self, + member_diff: Vec, + fee: Option, + ) -> Result { + self.execute_multisig_contract( + fee, + MultisigExecuteMsg::MemberChangedHook(MemberChangedHookMsg::new(member_diff)), + "Multisig::MemberChangedHook".to_string(), + vec![], + ) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl MultisigSigningClient for C +where + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, +{ + async fn execute_multisig_contract( + &self, + fee: Option, + msg: MultisigExecuteMsg, + memo: String, + funds: Vec, + ) -> Result { + let multisig_contract_address = self + .multisig_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("multisig contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); + + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + multisig_contract_address, + &msg, + fee, + memo, + funds, + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: MultisigExecuteMsg, + ) { + match msg { + MultisigExecuteMsg::Propose { + title, description, .. + } => client + .propose_release_funds(title, description, mock_coin(), None) + .ignore(), + MultisigExecuteMsg::Vote { proposal_id, vote } => { + client.vote(proposal_id, vote, None).ignore() + } + MultisigExecuteMsg::Execute { proposal_id } => { + client.execute_proposal(proposal_id, None).ignore() + } + MultisigExecuteMsg::Close { proposal_id } => { + client.close_proposal(proposal_id, None).ignore() + } + MultisigExecuteMsg::MemberChangedHook(hook_msg) => { + client.changed_member_hook(hook_msg.diffs, None).ignore() + } + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_query_client.rs new file mode 100644 index 0000000000..60071f674d --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_query_client.rs @@ -0,0 +1,144 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::collect_paged; +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::{error::NyxdError, CosmWasmClient}; +use async_trait::async_trait; +use cosmrs::AccountId; +use nym_contracts_common::{signing::Nonce, ContractBuildInformation}; +use nym_name_service_common::{ + msg::QueryMsg as NameQueryMsg, + response::{ConfigResponse, NamesListResponse, PagedNamesListResponse}, + Address, NameId, NymName, RegisteredName, +}; +use serde::Deserialize; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait NameServiceQueryClient { + async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn get_name_service_config(&self) -> Result { + self.query_name_service_contract(NameQueryMsg::Config {}) + .await + } + + async fn get_name_entry(&self, name_id: NameId) -> Result { + self.query_name_service_contract(NameQueryMsg::NameId { name_id }) + .await + } + + async fn get_names_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_name_service_contract(NameQueryMsg::All { limit, start_after }) + .await + } + + async fn get_name_signing_nonce(&self, address: &AccountId) -> Result { + self.query_name_service_contract(NameQueryMsg::SigningNonce { + address: address.to_string(), + }) + .await + } + + async fn get_names_by_owner(&self, owner: AccountId) -> Result { + self.query_name_service_contract(NameQueryMsg::ByOwner { + owner: owner.to_string(), + }) + .await + } + + async fn get_names_by_nym_name(&self, name: NymName) -> Result { + self.query_name_service_contract(NameQueryMsg::ByName { name }) + .await + } + + async fn get_names_by_address(&self, address: Address) -> Result { + self.query_name_service_contract(NameQueryMsg::ByAddress { address }) + .await + } + + async fn get_name_service_contract_version( + &self, + ) -> Result { + self.query_name_service_contract(NameQueryMsg::GetContractVersion {}) + .await + } + + async fn get_name_service_contract_cw2_version( + &self, + ) -> Result { + self.query_name_service_contract(NameQueryMsg::GetCW2ContractVersion {}) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedNameServiceQueryClient: NameServiceQueryClient { + async fn get_all_names(&self) -> Result, NyxdError> { + collect_paged!(self, get_names_paged, names) + } +} + +#[async_trait] +impl PagedNameServiceQueryClient for T where T: NameServiceQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl NameServiceQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let name_service_contract_address = &self + .name_service_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("name service contract"))?; + self.query_contract_smart(name_service_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: NameQueryMsg, + ) { + match msg { + NameQueryMsg::NameId { name_id } => client.get_name_entry(name_id).ignore(), + NameQueryMsg::ByOwner { owner } => { + client.get_names_by_owner(owner.parse().unwrap()).ignore() + } + NameQueryMsg::ByName { name } => client.get_names_by_nym_name(name).ignore(), + NameQueryMsg::ByAddress { address } => client.get_names_by_address(address).ignore(), + NameQueryMsg::All { limit, start_after } => { + client.get_names_paged(limit, start_after).ignore() + } + NameQueryMsg::SigningNonce { address } => client + .get_name_signing_nonce(&address.parse().unwrap()) + .ignore(), + NameQueryMsg::Config {} => client.get_name_service_config().ignore(), + NameQueryMsg::GetContractVersion {} => { + client.get_name_service_contract_version().ignore() + } + NameQueryMsg::GetCW2ContractVersion {} => { + client.get_name_service_contract_cw2_version().ignore() + } + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/traits/name_service_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_signing_client.rs similarity index 52% rename from common/client-libs/validator-client/src/nyxd/traits/name_service_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/contract_traits/name_service_signing_client.rs index 1235043094..0a363f991b 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/name_service_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/name_service_signing_client.rs @@ -5,12 +5,14 @@ use async_trait::async_trait; use nym_contracts_common::signing::MessageSignature; use nym_name_service_common::{msg::ExecuteMsg as NameExecuteMsg, NameDetails, NameId, NymName}; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::{ - coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, NyxdClient, - SigningCosmWasmClient, + coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, SigningCosmWasmClient, }; +use crate::signing::signer::OfflineSigner; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait NameServiceSigningClient { async fn execute_name_service_contract( &self, @@ -71,10 +73,12 @@ pub trait NameServiceSigningClient { } } -#[async_trait] -impl NameServiceSigningClient for NyxdClient +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl NameServiceSigningClient for C where - C: SigningCosmWasmClient + Sync + Send, + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, { async fn execute_name_service_contract( &self, @@ -82,19 +86,53 @@ where msg: NameExecuteMsg, funds: Vec, ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let name_service_contract_address = &self + .name_service_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("name service contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); let memo = msg.default_memo(); - self.client - .execute( - self.address(), - self.name_service_contract_address().ok_or( - NyxdError::NoContractAddressAvailable("name service contract".to_string()), - )?, - &msg, - fee, - memo, - funds, - ) - .await + + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + name_service_contract_address, + &msg, + fee, + memo, + funds, + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: NameExecuteMsg, + ) { + match msg { + NameExecuteMsg::Register { + name, + owner_signature, + } => client + .register_name(name, owner_signature, mock_coin(), None) + .ignore(), + NameExecuteMsg::DeleteId { name_id } => { + client.delete_name_by_id(name_id, None).ignore() + } + NameExecuteMsg::DeleteName { name } => { + client.delete_service_provider_by_name(name, None).ignore() + } + NameExecuteMsg::UpdateDepositRequired { deposit_required } => client + .update_deposit_required(deposit_required.into(), None) + .ignore(), + }; } } diff --git a/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_query_client.rs new file mode 100644 index 0000000000..09279c543a --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_query_client.rs @@ -0,0 +1,142 @@ +use crate::collect_paged; +use async_trait::async_trait; +use cosmrs::AccountId; +use nym_contracts_common::{signing::Nonce, ContractBuildInformation}; +use nym_service_provider_directory_common::{ + msg::QueryMsg as SpQueryMsg, + response::{ + ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse, + }, + NymAddress, Service, ServiceId, +}; +use serde::Deserialize; + +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::{error::NyxdError, CosmWasmClient}; + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait SpDirectoryQueryClient { + async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>; + + async fn get_service_config(&self) -> Result { + self.query_service_provider_contract(SpQueryMsg::Config {}) + .await + } + + async fn get_service_info( + &self, + service_id: ServiceId, + ) -> Result { + self.query_service_provider_contract(SpQueryMsg::ServiceId { service_id }) + .await + } + + async fn get_services_paged( + &self, + start_after: Option, + limit: Option, + ) -> Result { + self.query_service_provider_contract(SpQueryMsg::All { limit, start_after }) + .await + } + + async fn get_services_by_announcer( + &self, + announcer: AccountId, + ) -> Result { + self.query_service_provider_contract(SpQueryMsg::ByAnnouncer { + announcer: announcer.to_string(), + }) + .await + } + + async fn get_services_by_nym_address( + &self, + nym_address: NymAddress, + ) -> Result { + self.query_service_provider_contract(SpQueryMsg::ByNymAddress { nym_address }) + .await + } + + async fn get_sp_contract_version(&self) -> Result { + self.query_service_provider_contract(SpQueryMsg::GetContractVersion {}) + .await + } + + async fn get_sp_contract_cw2_version(&self) -> Result { + self.query_service_provider_contract(SpQueryMsg::GetCW2ContractVersion {}) + .await + } + + async fn get_service_signing_nonce(&self, address: &AccountId) -> Result { + self.query_service_provider_contract(SpQueryMsg::SigningNonce { + address: address.to_string(), + }) + .await + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedSpDirectoryQueryClient: SpDirectoryQueryClient { + async fn get_all_services(&self) -> Result, NyxdError> { + collect_paged!(self, get_services_paged, services) + } +} + +#[async_trait] +impl PagedSpDirectoryQueryClient for T where T: SpDirectoryQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl SpDirectoryQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ + async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result + where + for<'a> T: Deserialize<'a>, + { + let sp_directory_contract_address = + &self.service_provider_contract_address().ok_or_else(|| { + NyxdError::unavailable_contract_address("service provider directory contract") + })?; + self.query_contract_smart(sp_directory_contract_address, &query) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: SpQueryMsg, + ) { + match msg { + SpQueryMsg::ServiceId { service_id } => client.get_service_info(service_id).ignore(), + SpQueryMsg::ByAnnouncer { announcer } => client + .get_services_by_announcer(announcer.parse().unwrap()) + .ignore(), + SpQueryMsg::ByNymAddress { nym_address } => { + client.get_services_by_nym_address(nym_address).ignore() + } + SpQueryMsg::All { limit, start_after } => { + client.get_services_paged(start_after, limit).ignore() + } + SpQueryMsg::SigningNonce { address } => client + .get_service_signing_nonce(&address.parse().unwrap()) + .ignore(), + SpQueryMsg::Config {} => client.get_service_config().ignore(), + SpQueryMsg::GetContractVersion {} => client.get_sp_contract_version().ignore(), + SpQueryMsg::GetCW2ContractVersion {} => client.get_sp_contract_cw2_version().ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/traits/sp_directory_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_signing_client.rs similarity index 54% rename from common/client-libs/validator-client/src/nyxd/traits/sp_directory_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_signing_client.rs index 31cf30839a..c1efde20f5 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/sp_directory_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/sp_directory_signing_client.rs @@ -1,18 +1,19 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::nyxd::contract_traits::NymContractsProvider; +use crate::nyxd::{ + coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, SigningCosmWasmClient, +}; +use crate::signing::signer::OfflineSigner; use async_trait::async_trait; use nym_contracts_common::signing::MessageSignature; use nym_service_provider_directory_common::{ msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceDetails, ServiceId, }; -use crate::nyxd::{ - coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, NyxdClient, - SigningCosmWasmClient, -}; - -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait SpDirectorySigningClient { async fn execute_service_provider_directory_contract( &self, @@ -81,10 +82,12 @@ pub trait SpDirectorySigningClient { } } -#[async_trait] -impl SpDirectorySigningClient for NyxdClient +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl SpDirectorySigningClient for C where - C: SigningCosmWasmClient + Sync + Send, + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, { async fn execute_service_provider_directory_contract( &self, @@ -92,21 +95,54 @@ where msg: SpExecuteMsg, funds: Vec, ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let sp_directory_contract_address = + &self.service_provider_contract_address().ok_or_else(|| { + NyxdError::unavailable_contract_address("service provider directory contract") + })?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); let memo = msg.default_memo(); - self.client - .execute( - self.address(), - self.service_provider_contract_address().ok_or( - NyxdError::NoContractAddressAvailable( - "service provider directory contract".to_string(), - ), - )?, - &msg, - fee, - memo, - funds, - ) - .await + + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + sp_directory_contract_address, + &msg, + fee, + memo, + funds, + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: SpExecuteMsg, + ) { + match msg { + SpExecuteMsg::Announce { + service, + owner_signature, + } => client + .announce_service_provider(service, owner_signature, mock_coin(), None) + .ignore(), + SpExecuteMsg::DeleteId { service_id } => client + .delete_service_provider_by_id(service_id, None) + .ignore(), + SpExecuteMsg::DeleteNymAddress { nym_address } => client + .delete_service_provider_by_nym_address(nym_address, None) + .ignore(), + SpExecuteMsg::UpdateDepositRequired { deposit_required } => client + .update_deposit_required(deposit_required.into(), None) + .ignore(), + }; } } diff --git a/common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_query_client.rs similarity index 58% rename from common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs rename to common/client-libs/validator-client/src/nyxd/contract_traits/vesting_query_client.rs index 2f8931f98a..fa6bd643fa 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/vesting_query_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_query_client.rs @@ -1,23 +1,24 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::collect_paged; use crate::nyxd::coin::Coin; -pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::error::NyxdError; -use crate::nyxd::NyxdClient; +use crate::nyxd::CosmWasmClient; use async_trait::async_trait; use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp}; use nym_contracts_common::ContractBuildInformation; use nym_mixnet_contract_common::MixId; use nym_vesting_contract_common::{ - messages::QueryMsg as VestingQueryMsg, AccountVestingCoins, AccountsResponse, + messages::QueryMsg as VestingQueryMsg, Account, AccountVestingCoins, AccountsResponse, AllDelegationsResponse, BaseVestingAccountInfo, DelegationTimesResponse, OriginalVestingResponse, Period, PledgeData, VestingCoinsResponse, VestingDelegation, }; use serde::Deserialize; -use vesting_contract::vesting::Account; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait VestingQueryClient { async fn query_vesting_contract(&self, query: VestingQueryMsg) -> Result where @@ -28,6 +29,11 @@ pub trait VestingQueryClient { .await } + async fn get_vesting_contract_cw2_version(&self) -> Result { + self.query_vesting_contract(VestingQueryMsg::GetCW2ContractVersion {}) + .await + } + async fn get_all_accounts_paged( &self, start_next_after: Option, @@ -275,73 +281,161 @@ pub trait VestingQueryClient { self.query_vesting_contract(VestingQueryMsg::GetAllDelegations { start_after, limit }) .await } +} +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait PagedVestingQueryClient: VestingQueryClient { async fn get_all_vesting_delegations(&self) -> Result, NyxdError> { - let mut delegations = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_all_vesting_delegations_paged(start_after.take(), None) - .await?; - delegations.append(&mut paged_response.delegations); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(delegations) + collect_paged!(self, get_all_vesting_delegations_paged, delegations) } async fn get_all_accounts_info(&self) -> Result, NyxdError> { - let mut accounts = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_all_accounts_paged(start_after.take(), None) - .await?; - accounts.append(&mut paged_response.accounts); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(accounts) + collect_paged!(self, get_all_accounts_paged, accounts) } async fn get_all_accounts_vesting_coins(&self) -> Result, NyxdError> { - let mut accounts = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_all_accounts_vesting_coins_paged(start_after.take(), None) - .await?; - accounts.append(&mut paged_response.accounts); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(accounts) + collect_paged!(self, get_all_accounts_vesting_coins_paged, accounts) } } #[async_trait] -impl VestingQueryClient for NyxdClient { +impl PagedVestingQueryClient for T where T: VestingQueryClient {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl VestingQueryClient for C +where + C: CosmWasmClient + NymContractsProvider + Send + Sync, +{ async fn query_vesting_contract(&self, query: VestingQueryMsg) -> Result where for<'a> T: Deserialize<'a>, { - self.client - .query_contract_smart(self.vesting_contract_address(), &query) + let vesting_contract_address = &self + .vesting_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("vesting contract"))?; + self.query_contract_smart(vesting_contract_address, &query) .await } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::IgnoreValue; + + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_query_variants_are_covered( + client: C, + msg: VestingQueryMsg, + ) { + match msg { + VestingQueryMsg::GetContractVersion {} => { + client.get_vesting_contract_version().ignore() + } + VestingQueryMsg::GetCW2ContractVersion {} => { + client.get_vesting_contract_cw2_version().ignore() + } + VestingQueryMsg::GetAccountsPaged { + start_next_after, + limit, + } => client + .get_all_accounts_paged(start_next_after, limit) + .ignore(), + VestingQueryMsg::GetAccountsVestingCoinsPaged { + start_next_after, + limit, + } => client + .get_all_accounts_vesting_coins_paged(start_next_after, limit) + .ignore(), + VestingQueryMsg::LockedCoins { + vesting_account_address, + block_time, + } => client + .locked_coins(&vesting_account_address, block_time) + .ignore(), + VestingQueryMsg::SpendableCoins { + vesting_account_address, + block_time, + } => client + .spendable_coins(&vesting_account_address, block_time) + .ignore(), + VestingQueryMsg::GetVestedCoins { + vesting_account_address, + block_time, + } => client + .vested_coins(&vesting_account_address, block_time) + .ignore(), + VestingQueryMsg::GetVestingCoins { + vesting_account_address, + block_time, + } => client + .vesting_coins(&vesting_account_address, block_time) + .ignore(), + VestingQueryMsg::GetStartTime { + vesting_account_address, + } => client.vesting_start_time(&vesting_account_address).ignore(), + VestingQueryMsg::GetEndTime { + vesting_account_address, + } => client.vesting_end_time(&vesting_account_address).ignore(), + VestingQueryMsg::GetOriginalVesting { + vesting_account_address, + } => client.original_vesting(&vesting_account_address).ignore(), + VestingQueryMsg::GetHistoricalVestingStakingReward { + vesting_account_address, + } => client + .get_historical_vesting_staking_reward(&vesting_account_address) + .ignore(), + VestingQueryMsg::GetSpendableVestedCoins { + vesting_account_address, + } => client + .get_spendable_vested_coins(&vesting_account_address) + .ignore(), + VestingQueryMsg::GetSpendableRewardCoins { + vesting_account_address, + } => client + .get_spendable_reward_coins(&vesting_account_address) + .ignore(), + VestingQueryMsg::GetDelegatedCoins { + vesting_account_address, + } => client + .get_delegated_coins(&vesting_account_address) + .ignore(), + VestingQueryMsg::GetPledgedCoins { + vesting_account_address, + } => client.get_pledged_coins(&vesting_account_address).ignore(), + VestingQueryMsg::GetStakedCoins { + vesting_account_address, + } => client.get_staked_coins(&vesting_account_address).ignore(), + VestingQueryMsg::GetWithdrawnCoins { + vesting_account_address, + } => client + .get_withdrawn_coins(&vesting_account_address) + .ignore(), + VestingQueryMsg::GetAccount { address } => client.get_account(&address).ignore(), + VestingQueryMsg::GetMixnode { address } => client.get_mixnode_pledge(&address).ignore(), + VestingQueryMsg::GetGateway { address } => client.get_gateway_pledge(&address).ignore(), + VestingQueryMsg::GetCurrentVestingPeriod { address } => { + client.get_current_vesting_period(&address).ignore() + } + VestingQueryMsg::GetDelegation { + address, + mix_id, + block_timestamp_secs, + } => client + .get_vesting_delegation(&address, mix_id, block_timestamp_secs) + .ignore(), + VestingQueryMsg::GetTotalDelegationAmount { address, mix_id } => client + .get_total_delegation_amount(&address, mix_id) + .ignore(), + VestingQueryMsg::GetDelegationTimes { address, mix_id } => { + client.get_delegation_timestamps(&address, mix_id).ignore() + } + VestingQueryMsg::GetAllDelegations { start_after, limit } => client + .get_all_vesting_delegations_paged(start_after, limit) + .ignore(), + }; + } +} diff --git a/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_signing_client.rs similarity index 52% rename from common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs rename to common/client-libs/validator-client/src/nyxd/contract_traits/vesting_signing_client.rs index aba0d219cf..11c449fccd 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/vesting_signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/contract_traits/vesting_signing_client.rs @@ -1,10 +1,11 @@ -// Copyright 2021 - Nym Technologies SA +// Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; +use crate::nyxd::contract_traits::NymContractsProvider; use crate::nyxd::cosmwasm_client::types::ExecuteResult; use crate::nyxd::error::NyxdError; -use crate::nyxd::{Coin, Fee, NyxdClient}; +use crate::nyxd::{Coin, Fee, SigningCosmWasmClient}; +use crate::signing::signer::OfflineSigner; use async_trait::async_trait; use cosmrs::AccountId; use nym_contracts_common::signing::MessageSignature; @@ -12,12 +13,11 @@ use nym_mixnet_contract_common::families::FamilyHead; use nym_mixnet_contract_common::gateway::GatewayConfigUpdate; use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; use nym_mixnet_contract_common::{Gateway, MixId, MixNode}; -use nym_vesting_contract_common::messages::{ - ExecuteMsg as VestingExecuteMsg, VestingSpecification, -}; -use nym_vesting_contract_common::PledgeCap; +use nym_vesting_contract_common::messages::ExecuteMsg as VestingExecuteMsg; +use nym_vesting_contract_common::{PledgeCap, VestingSpecification}; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait VestingSigningClient { async fn execute_vesting_contract( &self, @@ -30,25 +30,64 @@ pub trait VestingSigningClient { &self, new_costs: MixNodeCostParams, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::UpdateMixnodeCostParams { new_costs }, + vec![], + ) + .await + } async fn vesting_update_mixnode_config( &self, new_config: MixNodeConfigUpdate, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::UpdateMixnodeConfig { new_config }; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn vesting_update_gateway_config( &self, new_config: GatewayConfigUpdate, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::UpdateGatewayConfig { new_config }, + vec![], + ) + .await + } async fn update_mixnet_address( &self, address: &str, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::UpdateMixnetAddress { + address: address.to_string(), + }; + self.execute_vesting_contract(fee, req, vec![]).await + } + + async fn vesting_track_decrease_pledge( + &self, + owner: String, + amount: Coin, + fee: Option, + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::TrackDecreasePledge { + owner, + amount: amount.into(), + }, + Vec::new(), + ) + .await + } async fn vesting_bond_gateway( &self, @@ -56,16 +95,31 @@ pub trait VestingSigningClient { owner_signature: MessageSignature, pledge: Coin, fee: Option, - ) -> Result; - - async fn vesting_unbond_gateway(&self, fee: Option) -> Result; + ) -> Result { + let req = VestingExecuteMsg::BondGateway { + gateway, + owner_signature, + amount: pledge.into(), + }; + self.execute_vesting_contract(fee, req, vec![]).await + } + async fn vesting_unbond_gateway(&self, fee: Option) -> Result { + let req = VestingExecuteMsg::UnbondGateway {}; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn vesting_track_unbond_gateway( &self, owner: &str, amount: Coin, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::TrackUnbondGateway { + owner: owner.to_string(), + amount: amount.into(), + }; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn vesting_bond_mixnode( &self, @@ -74,7 +128,19 @@ pub trait VestingSigningClient { owner_signature: MessageSignature, pledge: Coin, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::BondMixnode { + mix_node, + cost_params, + owner_signature, + amount: pledge.into(), + }, + vec![], + ) + .await + } async fn vesting_pledge_more( &self, @@ -106,20 +172,34 @@ pub trait VestingSigningClient { .await } - async fn vesting_unbond_mixnode(&self, fee: Option) -> Result; + async fn vesting_unbond_mixnode(&self, fee: Option) -> Result { + let req = VestingExecuteMsg::UnbondMixnode {}; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn vesting_track_unbond_mixnode( &self, owner: &str, amount: Coin, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::TrackUnbondMixnode { + owner: owner.to_string(), + amount: amount.into(), + }; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn withdraw_vested_coins( &self, amount: Coin, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::WithdrawVestedCoins { + amount: amount.into(), + }; + self.execute_vesting_contract(fee, req, vec![]).await + } async fn vesting_track_undelegation( &self, @@ -127,7 +207,18 @@ pub trait VestingSigningClient { mix_id: MixId, amount: Coin, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::TrackUndelegation { + owner: address.to_string(), + mix_id, + amount: amount.into(), + }, + vec![], + ) + .await + } async fn vesting_delegate_to_mixnode( &self, @@ -135,14 +226,35 @@ pub trait VestingSigningClient { amount: Coin, on_behalf_of: Option, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::DelegateToMixnode { + mix_id, + amount: amount.into(), + on_behalf_of, + }, + vec![], + ) + .await + } async fn vesting_undelegate_from_mixnode( &self, mix_id: MixId, on_behalf_of: Option, fee: Option, - ) -> Result; + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::UndelegateFromMixnode { + mix_id, + on_behalf_of, + }, + vec![], + ) + .await + } async fn create_periodic_vesting_account( &self, @@ -152,7 +264,32 @@ pub trait VestingSigningClient { amount: Coin, cap: Option, fee: Option, - ) -> Result; + ) -> Result { + let req = VestingExecuteMsg::CreateAccount { + owner_address: owner_address.to_string(), + staking_address, + vesting_spec, + cap, + }; + self.execute_vesting_contract(fee, req, vec![amount]).await + } + + async fn vesting_track_reward( + &self, + amount: Coin, + address: String, + fee: Option, + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::TrackReward { + amount: amount.into(), + address, + }, + Vec::new(), + ) + .await + } async fn vesting_withdraw_operator_reward( &self, @@ -175,6 +312,32 @@ pub trait VestingSigningClient { .await } + async fn vesting_transfer_ownership( + &self, + to_address: String, + fee: Option, + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::TransferOwnership { to_address }, + Vec::new(), + ) + .await + } + + async fn update_staking_address( + &self, + to_address: Option, + fee: Option, + ) -> Result { + self.execute_vesting_contract( + fee, + VestingExecuteMsg::UpdateStakingAddress { to_address }, + Vec::new(), + ) + .await + } + async fn update_locked_pledge_cap( &self, address: AccountId, @@ -237,317 +400,166 @@ pub trait VestingSigningClient { } } -#[async_trait] -impl VestingSigningClient for NyxdClient { +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl VestingSigningClient for C +where + C: SigningCosmWasmClient + NymContractsProvider + Sync, + NyxdError: From<::Error>, +{ async fn execute_vesting_contract( &self, fee: Option, msg: VestingExecuteMsg, funds: Vec, ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let vesting_contract_address = &self + .vesting_contract_address() + .ok_or_else(|| NyxdError::unavailable_contract_address("vesting contract"))?; + + let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier()))); let memo = msg.name().to_string(); - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &msg, - fee, - memo, - funds, - ) - .await - } - async fn vesting_update_mixnode_cost_params( - &self, - new_costs: MixNodeCostParams, - fee: Option, - ) -> Result { - self.execute_vesting_contract( + let signer_address = &self.signer_addresses()?[0]; + self.execute( + signer_address, + vesting_contract_address, + &msg, fee, - VestingExecuteMsg::UpdateMixnodeCostParams { new_costs }, - vec![], + memo, + funds, ) .await } +} - async fn vesting_update_mixnode_config( - &self, - new_config: MixNodeConfigUpdate, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::UpdateMixnodeConfig { new_config }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::UpdateMixnetConfig", - vec![], - ) - .await - } +#[cfg(test)] +mod tests { + use super::*; + use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue}; - async fn vesting_update_gateway_config( - &self, - new_config: GatewayConfigUpdate, - fee: Option, - ) -> Result { - self.execute_vesting_contract( - fee, - VestingExecuteMsg::UpdateGatewayConfig { new_config }, - vec![], - ) - .await - } - - async fn update_mixnet_address( - &self, - address: &str, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::UpdateMixnetAddress { - address: address.to_string(), - }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::UpdateMixnetAddress", - vec![], - ) - .await - } - - async fn vesting_bond_gateway( - &self, - gateway: Gateway, - owner_signature: MessageSignature, - pledge: Coin, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::BondGateway { - gateway, - owner_signature, - amount: pledge.into(), - }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::BondGateway", - vec![], - ) - .await - } - - async fn vesting_unbond_gateway(&self, fee: Option) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::UnbondGateway {}; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::UnbondGateway", - vec![], - ) - .await - } - - async fn vesting_track_unbond_gateway( - &self, - owner: &str, - amount: Coin, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::TrackUnbondGateway { - owner: owner.to_string(), - amount: amount.into(), - }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::TrackUnbondGateway", - vec![], - ) - .await - } - - async fn vesting_bond_mixnode( - &self, - mix_node: MixNode, - cost_params: MixNodeCostParams, - owner_signature: MessageSignature, - pledge: Coin, - fee: Option, - ) -> Result { - self.execute_vesting_contract( - fee, + // it's enough that this compiles and clippy is happy about it + #[allow(dead_code)] + fn all_execute_variants_are_covered( + client: C, + msg: VestingExecuteMsg, + ) { + match msg { + VestingExecuteMsg::CreateFamily { label } => { + client.vesting_create_family(label, None).ignore() + } + VestingExecuteMsg::JoinFamily { + join_permit, + family_head, + } => client + .vesting_join_family(join_permit, family_head, None) + .ignore(), + VestingExecuteMsg::LeaveFamily { family_head } => { + client.vesting_leave_family(family_head, None).ignore() + } + VestingExecuteMsg::KickFamilyMember { member } => { + client.vesting_kick_family_member(member, None).ignore() + } + VestingExecuteMsg::TrackReward { amount, address } => client + .vesting_track_reward(amount.into(), address, None) + .ignore(), + VestingExecuteMsg::ClaimOperatorReward {} => { + client.vesting_withdraw_operator_reward(None).ignore() + } + VestingExecuteMsg::ClaimDelegatorReward { mix_id } => client + .vesting_withdraw_delegator_reward(mix_id, None) + .ignore(), + VestingExecuteMsg::UpdateMixnodeCostParams { new_costs } => client + .vesting_update_mixnode_cost_params(new_costs, None) + .ignore(), + VestingExecuteMsg::UpdateMixnodeConfig { new_config } => client + .vesting_update_mixnode_config(new_config, None) + .ignore(), + VestingExecuteMsg::UpdateMixnetAddress { address } => { + client.update_mixnet_address(&address, None).ignore() + } + VestingExecuteMsg::DelegateToMixnode { + mix_id, + amount, + on_behalf_of, + } => client + .vesting_delegate_to_mixnode(mix_id, amount.into(), on_behalf_of, None) + .ignore(), + VestingExecuteMsg::UndelegateFromMixnode { + mix_id, + on_behalf_of, + } => client + .vesting_undelegate_from_mixnode(mix_id, on_behalf_of, None) + .ignore(), + VestingExecuteMsg::CreateAccount { + owner_address, + staking_address, + vesting_spec, + cap, + } => client + .create_periodic_vesting_account( + &owner_address, + staking_address, + vesting_spec, + mock_coin(), + cap, + None, + ) + .ignore(), + VestingExecuteMsg::WithdrawVestedCoins { amount } => { + client.withdraw_vested_coins(amount.into(), None).ignore() + } + VestingExecuteMsg::TrackUndelegation { + owner, + mix_id, + amount, + } => client + .vesting_track_undelegation(&owner, mix_id, amount.into(), None) + .ignore(), VestingExecuteMsg::BondMixnode { mix_node, cost_params, owner_signature, - amount: pledge.into(), - }, - vec![], - ) - .await - } - - async fn vesting_unbond_mixnode(&self, fee: Option) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::UnbondMixnode {}; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::UnbondMixnode", - vec![], - ) - .await - } - - async fn vesting_track_unbond_mixnode( - &self, - owner: &str, - amount: Coin, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::TrackUnbondMixnode { - owner: owner.to_string(), - amount: amount.into(), + amount, + } => client + .vesting_bond_mixnode(mix_node, cost_params, owner_signature, amount.into(), None) + .ignore(), + VestingExecuteMsg::PledgeMore { amount } => { + client.vesting_pledge_more(amount.into(), None).ignore() + } + VestingExecuteMsg::DecreasePledge { amount } => { + client.vesting_decrease_pledge(amount.into(), None).ignore() + } + VestingExecuteMsg::UnbondMixnode {} => client.vesting_unbond_mixnode(None).ignore(), + VestingExecuteMsg::TrackUnbondMixnode { owner, amount } => client + .vesting_track_unbond_mixnode(&owner, amount.into(), None) + .ignore(), + VestingExecuteMsg::TrackDecreasePledge { owner, amount } => client + .vesting_track_decrease_pledge(owner, amount.into(), None) + .ignore(), + VestingExecuteMsg::BondGateway { + gateway, + owner_signature, + amount, + } => client + .vesting_bond_gateway(gateway, owner_signature, amount.into(), None) + .ignore(), + VestingExecuteMsg::UnbondGateway {} => client.vesting_unbond_gateway(None).ignore(), + VestingExecuteMsg::TrackUnbondGateway { owner, amount } => client + .vesting_track_unbond_gateway(&owner, amount.into(), None) + .ignore(), + VestingExecuteMsg::UpdateGatewayConfig { new_config } => client + .vesting_update_gateway_config(new_config, None) + .ignore(), + VestingExecuteMsg::TransferOwnership { to_address } => { + client.vesting_transfer_ownership(to_address, None).ignore() + } + VestingExecuteMsg::UpdateStakingAddress { to_address } => { + client.update_staking_address(to_address, None).ignore() + } + VestingExecuteMsg::UpdateLockedPledgeCap { address, cap } => client + .update_locked_pledge_cap(address.parse().unwrap(), cap, None) + .ignore(), }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::TrackUnbondMixnode", - vec![], - ) - .await - } - - async fn withdraw_vested_coins( - &self, - amount: Coin, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::WithdrawVestedCoins { - amount: amount.into(), - }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::WithdrawVested", - vec![], - ) - .await - } - - async fn vesting_track_undelegation( - &self, - address: &str, - mix_id: MixId, - amount: Coin, - fee: Option, - ) -> Result { - self.execute_vesting_contract( - fee, - VestingExecuteMsg::TrackUndelegation { - owner: address.to_string(), - mix_id, - amount: amount.into(), - }, - vec![], - ) - .await - } - - async fn vesting_delegate_to_mixnode( - &self, - mix_id: MixId, - amount: Coin, - on_behalf_of: Option, - fee: Option, - ) -> Result { - self.execute_vesting_contract( - fee, - VestingExecuteMsg::DelegateToMixnode { - mix_id, - amount: amount.into(), - on_behalf_of, - }, - vec![], - ) - .await - } - - async fn vesting_undelegate_from_mixnode( - &self, - mix_id: MixId, - on_behalf_of: Option, - fee: Option, - ) -> Result { - self.execute_vesting_contract( - fee, - VestingExecuteMsg::UndelegateFromMixnode { - mix_id, - on_behalf_of, - }, - vec![], - ) - .await - } - - async fn create_periodic_vesting_account( - &self, - owner_address: &str, - staking_address: Option, - vesting_spec: Option, - amount: Coin, - cap: Option, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = VestingExecuteMsg::CreateAccount { - owner_address: owner_address.to_string(), - staking_address, - vesting_spec, - cap, - }; - self.client - .execute( - self.address(), - self.vesting_contract_address(), - &req, - fee, - "VestingContract::CreatePeriodicVestingAccount", - vec![amount], - ) - .await } } diff --git a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/mod.rs b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/mod.rs new file mode 100644 index 0000000000..bfa126ba37 --- /dev/null +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/mod.rs @@ -0,0 +1,8 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod query_client; +pub mod signing_client; + +pub use query_client::CosmWasmClient; +pub use signing_client::SigningCosmWasmClient; 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_traits/query_client.rs similarity index 90% rename from common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs index 4a512a02db..69e94b3387 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/query_client.rs @@ -1,4 +1,4 @@ -// Copyright 2021-2023 - Nym Technologies SA +// Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use crate::nyxd; @@ -8,7 +8,7 @@ use crate::nyxd::cosmwasm_client::types::{ Account, CodeDetails, Contract, ContractCodeId, SequenceResponse, SimulateResponse, }; use crate::nyxd::error::NyxdError; -use crate::nyxd::TendermintClient; +use crate::rpc::TendermintRpcClient; use async_trait::async_trait; use cosmrs::cosmwasm::{CodeInfoResponse, ContractCodeHistoryEntry}; use cosmrs::proto::cosmos::auth::v1beta1::{QueryAccountRequest, QueryAccountResponse}; @@ -28,6 +28,7 @@ use cosmrs::proto::cosmwasm::wasm::v1::{ }; use cosmrs::tendermint::{block, chain, Hash}; use cosmrs::{AccountId, Coin as CosmosCoin, Tx}; +use log::trace; use prost::Message; use serde::{Deserialize, Serialize}; use std::convert::TryFrom; @@ -38,25 +39,26 @@ use tendermint_rpc::{ Order, }; +#[cfg(not(target_arch = "wasm32"))] +use tokio::time::sleep; +#[cfg(not(target_arch = "wasm32"))] +use tokio::time::Instant; + +#[cfg(target_arch = "wasm32")] +use wasmtimer::std::Instant; +#[cfg(target_arch = "wasm32")] +use wasmtimer::tokio::sleep; + +pub const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4); +pub const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60); + #[cfg(feature = "http-client")] #[async_trait] -impl CosmWasmClient for cosmrs::rpc::HttpClient { - fn broadcast_polling_rate(&self) -> Duration { - Duration::from_secs(4) - } - - fn broadcast_timeout(&self) -> Duration { - Duration::from_secs(60) - } -} - -#[async_trait] -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; - fn broadcast_timeout(&self) -> Duration; +impl CosmWasmClient for cosmrs::rpc::HttpClient {} +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait CosmWasmClient: TendermintRpcClient { // helper method to remove duplicate code involved in making abci requests with protobuf messages // TODO: perhaps it should have an additional argument to determine whether the response should // require proof? @@ -69,6 +71,9 @@ pub trait CosmWasmClient: TendermintClient { Req: Message, Res: Message + Default, { + if let Some(ref abci_path) = path { + trace!("performing query on abci path {abci_path}") + } let mut buf = Vec::with_capacity(req.encoded_len()); req.encode(&mut buf)?; @@ -242,7 +247,7 @@ pub trait CosmWasmClient: TendermintClient { where T: Into> + Send, { - Ok(tendermint_rpc::client::Client::broadcast_tx_async(self, tx).await?) + Ok(TendermintRpcClient::broadcast_tx_async(self, tx).await?) } /// Broadcast a transaction, returning the response from `CheckTx`. @@ -250,7 +255,7 @@ pub trait CosmWasmClient: TendermintClient { where T: Into> + Send, { - Ok(tendermint_rpc::client::Client::broadcast_tx_sync(self, tx).await?) + Ok(TendermintRpcClient::broadcast_tx_sync(self, tx).await?) } /// Broadcast a transaction, returning the response from `DeliverTx`. @@ -261,13 +266,23 @@ pub trait CosmWasmClient: TendermintClient { where T: Into> + Send, { - Ok(tendermint_rpc::client::Client::broadcast_tx_commit(self, tx).await?) + Ok(TendermintRpcClient::broadcast_tx_commit(self, tx).await?) } - async fn broadcast_tx(&self, tx: T) -> Result + async fn broadcast_tx( + &self, + tx: T, + timeout: impl Into> + Send, + poll_interval: impl Into> + Send, + ) -> Result where T: Into> + Send, { + let timeout = timeout.into().unwrap_or(DEFAULT_BROADCAST_TIMEOUT); + let poll_interval = poll_interval + .into() + .unwrap_or(DEFAULT_BROADCAST_POLLING_RATE); + let broadcasted = CosmWasmClient::broadcast_tx_sync(self, tx).await?; if broadcasted.code.is_err() { @@ -282,16 +297,16 @@ pub trait CosmWasmClient: TendermintClient { let tx_hash = broadcasted.hash; - let start = tokio::time::Instant::now(); + let start = Instant::now(); loop { log::debug!( "Polling for result of including {} in a block...", broadcasted.hash ); - if tokio::time::Instant::now().duration_since(start) >= self.broadcast_timeout() { + if Instant::now().duration_since(start) >= timeout { return Err(NyxdError::BroadcastTimeout { hash: tx_hash, - timeout: self.broadcast_timeout(), + timeout, }); } @@ -299,7 +314,7 @@ pub trait CosmWasmClient: TendermintClient { return Ok(poll_res); } - tokio::time::sleep(self.broadcast_polling_rate()).await; + sleep(poll_interval).await; } } @@ -477,6 +492,7 @@ pub trait CosmWasmClient: TendermintClient { .make_abci_query::<_, QuerySmartContractStateResponse>(path, req) .await?; + trace!("raw query response: {}", String::from_utf8_lossy(&res.data)); Ok(serde_json::from_slice(&res.data)?) } 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/client_traits/signing_client.rs similarity index 85% rename from common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs rename to common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs index f4d6a41829..5b3deadb68 100644 --- a/common/client-libs/validator-client/src/nyxd/cosmwasm_client/signing_client.rs +++ b/common/client-libs/validator-client/src/nyxd/cosmwasm_client/client_traits/signing_client.rs @@ -1,7 +1,7 @@ -// Copyright 2021-2023 - Nym Technologies SA +// Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +use crate::nyxd::cosmwasm_client::client_traits::CosmWasmClient; use crate::nyxd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse}; use crate::nyxd::cosmwasm_client::logs::{self, parse_raw_logs}; use crate::nyxd::cosmwasm_client::types::*; @@ -9,6 +9,7 @@ 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; @@ -26,21 +27,9 @@ use serde::Serialize; use sha2::Digest; use sha2::Sha256; use std::convert::TryInto; -use std::time::{Duration, SystemTime}; +use std::time::SystemTime; use tendermint_rpc::endpoint::broadcast; -#[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 { amount: vec![], @@ -64,18 +53,17 @@ fn single_unspecified_signer_auth( .auth_info(empty_fee()) } -#[async_trait] -pub trait SigningCosmWasmClient: CosmWasmClient { - type Signer: OfflineSigner + Send + Sync; - - fn signer(&self) -> &Self::Signer; - +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait SigningCosmWasmClient: CosmWasmClient + TxSigner +where + NyxdError: From<::Error>, +{ + // TODO: would it somehow be possible to get rid of this method and allow for + // blanket implementation for anything that provides CosmWasmClient + TxSigner? fn gas_price(&self) -> &GasPrice; - fn signer_public_key(&self, signer_address: &AccountId) -> Option { - let account = self.signer().find_account(signer_address).ok()?; - Some(account.public_key().into()) - } + fn simulated_gas_multiplier(&self) -> f32; async fn simulate( &self, @@ -587,9 +575,9 @@ pub trait SigningCosmWasmClient: CosmWasmClient { let multiplier = multiplier.unwrap_or(DEFAULT_SIMULATED_GAS_MULTIPLIER); let gas = gas_estimation.adjust_gas(multiplier); - debug!("Gas estimation: {}", gas_estimation); - debug!("Multiplying the estimation by {}", multiplier); - debug!("Final gas limit used: {}", gas); + debug!("Gas estimation: {gas_estimation}"); + debug!("Multiplying the estimation by {multiplier}"); + debug!("Final gas limit used: {gas}"); let fee = self.gas_price() * gas; Ok::(tx::Fee::from_amount_and_gas(fee, gas)) @@ -687,7 +675,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient { .to_bytes() .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; - self.broadcast_tx(tx_bytes).await + self.broadcast_tx(tx_bytes, None, None).await } async fn sign( @@ -710,161 +698,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient { } }; - self.sign_direct(signer_address, messages, fee, memo, signer_data) - } - - fn sign_amino( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> Result; - - fn sign_direct( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> 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, - // maybe just convert it into an option? - // or maybe we need another level of indirection. tbd. - rpc_client: HttpClient, - tx_signer: TxSigner, - gas_price: GasPrice, - - broadcast_polling_rate: Duration, - broadcast_timeout: Duration, -} - -#[cfg(feature = "http-client")] -impl Client { - pub fn connect_with_signer( - endpoint: U, - signer: S, - gas_price: GasPrice, - ) -> Result - where - U: TryInto, - { - let rpc_client = HttpClient::new(endpoint)?; - Ok(Client { - rpc_client, - tx_signer: TxSigner::new(signer), - gas_price, - broadcast_polling_rate: DEFAULT_BROADCAST_POLLING_RATE, - broadcast_timeout: DEFAULT_BROADCAST_TIMEOUT, - }) - } - - pub fn offline(signer: S) -> TxSigner - where - S: OfflineSigner, - { - TxSigner::new(signer) - } - - pub fn change_endpoint(&mut self, new_endpoint: U) -> Result<(), NyxdError> - where - U: TryInto, - { - let new_rpc_client = HttpClient::new(new_endpoint)?; - self.rpc_client = new_rpc_client; - Ok(()) - } - - pub fn into_signer(self) -> S { - self.tx_signer.into_inner_signer() - } - - pub fn set_broadcast_polling_rate(&mut self, broadcast_polling_rate: Duration) { - self.broadcast_polling_rate = broadcast_polling_rate - } - - pub fn set_broadcast_timeout(&mut self, broadcast_timeout: Duration) { - self.broadcast_timeout = broadcast_timeout - } -} - -#[cfg(feature = "http-client")] -#[async_trait] -impl tendermint_rpc::client::Client for Client -where - S: Send + Sync, -{ - async fn perform(&self, request: R) -> Result - where - R: SimpleRequest, - { - self.rpc_client.perform(request).await - } -} - -#[cfg(feature = "http-client")] -#[async_trait] -impl CosmWasmClient for Client -where - S: Send + Sync, -{ - fn broadcast_polling_rate(&self) -> Duration { - self.broadcast_polling_rate - } - - fn broadcast_timeout(&self) -> Duration { - self.broadcast_timeout - } -} - -#[cfg(feature = "http-client")] -#[async_trait] -impl SigningCosmWasmClient for Client -where - S: OfflineSigner + Send + Sync, - NyxdError: From, -{ - type Signer = S; - - fn signer(&self) -> &Self::Signer { - self.tx_signer.signer() - } - - fn gas_price(&self) -> &GasPrice { - &self.gas_price - } - - fn sign_amino( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> Result { - Ok(self - .tx_signer - .sign_amino(signer_address, messages, fee, memo, signer_data)?) - } - - fn sign_direct( - &self, - signer_address: &AccountId, - messages: Vec, - fee: tx::Fee, - memo: impl Into + Send + 'static, - signer_data: SignerData, - ) -> Result { - Ok(self - .tx_signer - .sign_direct(signer_address, messages, fee, memo, signer_data)?) + Ok(::sign_direct( + self, + signer_address, + messages, + fee, + memo, + signer_data, + )?) } } 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 64c2fd25fa..b639b223e3 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 @@ -48,7 +48,6 @@ 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; 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 0a2d2c7c32..ab78a8c418 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,37 +1,375 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -#[cfg(feature = "http-client")] +use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient}; use crate::nyxd::error::NyxdError; -#[cfg(feature = "http-client")] -use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl}; -#[cfg(feature = "http-client")] -use std::convert::TryInto; +use crate::nyxd::{Config, GasPrice, Hash, Height}; +use crate::rpc::TendermintRpcClient; +use crate::signing::{ + signer::{NoSigner, OfflineSigner}, + AccountData, +}; +use async_trait::async_trait; +use cosmrs::tendermint::{abci, evidence::Evidence, Genesis}; +use cosmrs::tx::{Raw, SignDoc}; +use serde::{de::DeserializeOwned, Serialize}; +use std::fmt::Debug; +use tendermint_rpc::endpoint::*; +use tendermint_rpc::query::Query; +use tendermint_rpc::{Error as TendermintRpcError, Order, Paging, SimpleRequest}; -pub mod client; +#[cfg(feature = "http-client")] +use crate::http_client; +#[cfg(feature = "http-client")] +use cosmrs::rpc::{HttpClient, HttpClientUrl}; + +pub mod client_traits; mod helpers; pub mod logs; pub mod types; -#[cfg(feature = "signing")] -pub mod signing_client; +#[derive(Debug)] +pub(crate) struct SigningClientOptions { + gas_price: GasPrice, + simulated_gas_multiplier: f32, +} + +impl<'a> From<&'a Config> for SigningClientOptions { + fn from(value: &'a Config) -> Self { + SigningClientOptions { + gas_price: value.gas_price.clone(), + simulated_gas_multiplier: value.simulated_gas_multiplier, + } + } +} + +// convenience wrapper around query client to allow for optional signing +#[derive(Debug)] +pub(crate) struct MaybeSigningClient { + client: C, + signer: S, + opts: SigningClientOptions, +} + +impl MaybeSigningClient { + pub(crate) fn new(client: C, opts: SigningClientOptions) -> Self { + MaybeSigningClient { + client, + signer: Default::default(), + opts, + } + } +} + +impl MaybeSigningClient { + pub(crate) fn new_signing(client: C, signer: S, opts: SigningClientOptions) -> Self + where + S: OfflineSigner, + { + MaybeSigningClient { + client, + signer, + opts, + } + } +} #[cfg(feature = "http-client")] -pub fn connect(endpoint: U) -> Result -where - U: TryInto, -{ - Ok(HttpClient::new(endpoint)?) +impl MaybeSigningClient { + pub(crate) fn change_endpoint(&mut self, new_endpoint: U) -> Result<(), NyxdError> + where + U: TryInto, + { + self.client = http_client(new_endpoint)?; + Ok(()) + } } -#[cfg(all(feature = "signing", feature = "http-client"))] -pub fn connect_with_signer( - endpoint: U, - signer: S, - gas_price: crate::nyxd::GasPrice, -) -> Result, NyxdError> +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TendermintRpcClient for MaybeSigningClient where - U: TryInto, + C: TendermintRpcClient + Send + Sync, + S: Send + Sync, { - signing_client::Client::connect_with_signer(endpoint, signer, gas_price) + async fn abci_info(&self) -> Result { + self.client.abci_info().await + } + + async fn abci_query( + &self, + path: Option, + data: V, + height: Option, + prove: bool, + ) -> Result + where + V: Into> + Send, + { + self.client.abci_query(path, data, height, prove).await + } + + async fn block(&self, height: H) -> Result + where + H: Into + Send, + { + self.client.block(height).await + } + + async fn block_by_hash( + &self, + hash: Hash, + ) -> Result { + self.client.block_by_hash(hash).await + } + + async fn latest_block(&self) -> Result { + self.client.latest_block().await + } + + async fn header(&self, height: H) -> Result + where + H: Into + Send, + { + self.client.header(height).await + } + + async fn header_by_hash( + &self, + hash: Hash, + ) -> Result { + self.client.header_by_hash(hash).await + } + + async fn block_results( + &self, + height: H, + ) -> Result + where + H: Into + Send, + { + self.client.block_results(height).await + } + + async fn latest_block_results(&self) -> Result { + self.client.latest_block_results().await + } + + async fn block_search( + &self, + query: Query, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.client.block_search(query, page, per_page, order).await + } + + async fn blockchain( + &self, + min: H, + max: H, + ) -> Result + where + H: Into + Send, + { + self.client.blockchain(min, max).await + } + + async fn broadcast_tx_async( + &self, + tx: T, + ) -> Result + where + T: Into> + Send, + { + self.client.broadcast_tx_async(tx).await + } + + async fn broadcast_tx_sync( + &self, + tx: T, + ) -> Result + where + T: Into> + Send, + { + self.client.broadcast_tx_sync(tx).await + } + + async fn broadcast_tx_commit( + &self, + tx: T, + ) -> Result + where + T: Into> + Send, + { + self.client.broadcast_tx_commit(tx).await + } + + async fn commit(&self, height: H) -> Result + where + H: Into + Send, + { + self.client.commit(height).await + } + + async fn consensus_params( + &self, + height: H, + ) -> Result + where + H: Into + Send, + { + self.client.consensus_params(height).await + } + + async fn consensus_state(&self) -> Result { + self.client.consensus_state().await + } + + async fn validators( + &self, + height: H, + paging: Paging, + ) -> Result + where + H: Into + Send, + { + self.client.validators(height, paging).await + } + + async fn latest_consensus_params( + &self, + ) -> Result { + self.client.latest_consensus_params().await + } + + async fn latest_commit(&self) -> Result { + self.client.latest_commit().await + } + + async fn health(&self) -> Result<(), TendermintRpcError> { + self.client.health().await + } + + async fn genesis(&self) -> Result, TendermintRpcError> + where + AppState: Debug + Serialize + DeserializeOwned + Send, + { + self.client.genesis().await + } + + async fn net_info(&self) -> Result { + self.client.net_info().await + } + + async fn status(&self) -> Result { + self.client.status().await + } + + async fn broadcast_evidence( + &self, + e: Evidence, + ) -> Result { + self.client.broadcast_evidence(e).await + } + + async fn tx(&self, hash: Hash, prove: bool) -> Result { + self.client.tx(hash, prove).await + } + + async fn tx_search( + &self, + query: Query, + prove: bool, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.client + .tx_search(query, prove, page, per_page, order) + .await + } + + #[cfg(any( + feature = "tendermint-rpc/http-client", + feature = "tendermint-rpc/websocket-client" + ))] + async fn wait_until_healthy(&self, timeout: T) -> Result<(), Error> + where + T: Into + Send, + { + self.client.wait_until_healthy(timeout).await + } + + async fn perform(&self, request: R) -> Result + where + R: SimpleRequest, + { + self.client.perform(request).await + } } + +impl OfflineSigner for MaybeSigningClient +where + S: OfflineSigner, +{ + type Error = S::Error; + + fn get_accounts(&self) -> Result, Self::Error> { + self.signer.get_accounts() + } + + fn sign_direct_with_account( + &self, + signer: &AccountData, + sign_doc: SignDoc, + ) -> Result { + self.signer.sign_direct_with_account(signer, sign_doc) + } +} + +#[async_trait] +impl CosmWasmClient for MaybeSigningClient +where + C: TendermintRpcClient + Send + Sync, + S: Send + Sync, +{ +} + +#[async_trait] +impl SigningCosmWasmClient for MaybeSigningClient +where + C: TendermintRpcClient + Send + Sync, + S: OfflineSigner + Send + Sync, + NyxdError: From, +{ + fn gas_price(&self) -> &GasPrice { + &self.opts.gas_price + } + + fn simulated_gas_multiplier(&self) -> f32 { + self.opts.simulated_gas_multiplier + } +} + +// +// #[cfg(feature = "http-client")] +// pub fn connect(endpoint: U) -> Result, NyxdError> +// where +// U: TryInto, +// { +// Ok(HttpClient::new(endpoint)?) +// } +// +// #[cfg(all(feature = "signing", feature = "http-client"))] +// pub fn connect_with_signer( +// endpoint: U, +// signer: S, +// gas_price: crate::nyxd::GasPrice, +// ) -> Result, NyxdError> +// where +// U: TryInto, +// { +// signing_client::Client::connect_with_signer(endpoint, signer, gas_price) +// } diff --git a/common/client-libs/validator-client/src/nyxd/error.rs b/common/client-libs/validator-client/src/nyxd/error.rs index b63be6e75c..e247dff6a1 100644 --- a/common/client-libs/validator-client/src/nyxd/error.rs +++ b/common/client-libs/validator-client/src/nyxd/error.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::nyxd::cosmwasm_client::types::ContractCodeId; +use crate::signing::direct_wallet::DirectSecp256k1HdWalletError; use cosmrs::tendermint::Hash; use cosmrs::{ tendermint::{abci::Code as AbciCode, block}, @@ -11,9 +12,6 @@ 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::tendermint::error::Error as TendermintError; pub use tendermint_rpc::{ error::{Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail}, @@ -25,7 +23,6 @@ pub enum NyxdError { #[error("No contract address is available to perform the call: {0}")] NoContractAddressAvailable(String), - #[cfg(feature = "signing")] #[error(transparent)] WalletError(#[from] DirectSecp256k1HdWalletError), @@ -224,4 +221,8 @@ impl NyxdError { _ => false, } } + + pub fn unavailable_contract_address>(contract_type: S) -> Self { + NyxdError::NoContractAddressAvailable(contract_type.into()) + } } 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 caccceb436..6f68dbbcf2 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 @@ -6,6 +6,7 @@ use cosmrs::Coin; use cosmrs::Gas; use cosmwasm_std::{Decimal, Fraction, Uint128}; use nym_config::defaults; +use nym_network_defaults::NymNetworkDetails; use std::ops::Mul; use std::str::FromStr; @@ -73,6 +74,19 @@ impl FromStr for GasPrice { } } +impl<'a> TryFrom<&'a NymNetworkDetails> for GasPrice { + type Error = NyxdError; + + fn try_from(value: &'a NymNetworkDetails) -> Result { + format!( + "{}{}", + value.default_gas_price_amount(), + value.chain_details.mix_denom.base + ) + .parse() + } +} + impl GasPrice { pub fn new_with_default_price(denom: &str) -> Result { format!("{}{}", defaults::GAS_PRICE_AMOUNT, denom).parse() 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 0d67544203..ee30d1ffe5 100644 --- a/common/client-libs/validator-client/src/nyxd/fee/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/fee/mod.rs @@ -32,7 +32,7 @@ impl Display for AutoFeeGrant { #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Fee { - Manual(#[serde(with = "sealed::TxFee")] tx::Fee), + Manual(tx::Fee), Auto(Option), PayerGranterAuto(AutoFeeGrant), } @@ -121,58 +121,3 @@ 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}; - use cosmrs::{AccountId, Denom as CosmosDenom}; - use cosmrs::{Coin as CosmosCoin, Gas}; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - #[derive(Serialize, Deserialize, Clone)] - struct Coin { - denom: CosmosDenom, - amount: u128, - } - - impl From for CosmosCoin { - fn from(val: Coin) -> Self { - CosmosCoin { - denom: val.denom, - amount: val.amount, - } - } - } - - impl From for Coin { - fn from(val: CosmosCoin) -> Self { - Coin { - denom: val.denom, - amount: val.amount, - } - } - } - - fn coin_vec_ser(val: &[CosmosCoin], serializer: S) -> Result { - let vec: Vec = val.iter().cloned().map(Into::into).collect(); - vec.serialize(serializer) - } - fn coin_vec_deser<'de, D: Deserializer<'de>>( - deserializer: D, - ) -> Result, D::Error> { - let vec: Vec = Deserialize::deserialize(deserializer)?; - Ok(vec.iter().cloned().map(Into::into).collect()) - } - - #[derive(Serialize, Deserialize)] - #[serde(remote = "tx::Fee")] - pub(super) struct TxFee { - #[serde(serialize_with = "coin_vec_ser")] - #[serde(deserialize_with = "coin_vec_deser")] - pub amount: Vec, - pub gas_limit: Gas, - pub payer: Option, - pub granter: Option, - } -} diff --git a/common/client-libs/validator-client/src/nyxd/mod.rs b/common/client-libs/validator-client/src/nyxd/mod.rs index 404124df8d..10cca6de7e 100644 --- a/common/client-libs/validator-client/src/nyxd/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/mod.rs @@ -1,18 +1,37 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::nyxd::cosmwasm_client::types::Account; +use crate::nyxd::contract_traits::{NymContractsProvider, TypedNymContracts}; +use crate::nyxd::cosmwasm_client::types::{ + ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult, + MigrateResult, SequenceResponse, SimulateResponse, UploadResult, +}; +use crate::nyxd::cosmwasm_client::MaybeSigningClient; use crate::nyxd::error::NyxdError; -use log::{debug, trace}; +use crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER; +use crate::signing::direct_wallet::DirectSecp256k1HdWallet; +use crate::signing::signer::NoSigner; +use crate::signing::signer::OfflineSigner; +use crate::signing::tx_signer::TxSigner; +use crate::signing::AccountData; +use crate::{DirectSigningReqwestRpcNyxdClient, QueryReqwestRpcNyxdClient, ReqwestRpcClient}; +use async_trait::async_trait; +use cosmrs::cosmwasm; +use cosmrs::tendermint::{abci, evidence::Evidence, Genesis}; +use cosmrs::tx::{Msg, Raw, SignDoc}; +use cosmwasm_std::Addr; use nym_network_defaults::{ChainDetails, NymNetworkDetails}; -use serde::{Deserialize, Serialize}; -use tendermint_rpc::{endpoint::block::Response as BlockResponse, query::Query}; +use serde::{de::DeserializeOwned, Serialize}; +use std::fmt::Debug; +use std::time::SystemTime; +use tendermint_rpc::endpoint::block::Response as BlockResponse; +use tendermint_rpc::endpoint::*; +use tendermint_rpc::{Error as TendermintRpcError, Order}; +use url::Url; -#[cfg(feature = "http-client")] -use tendermint_rpc::Error as TendermintRpcError; - -pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; +pub use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient}; pub use crate::nyxd::fee::Fee; +pub use crate::rpc::TendermintRpcClient; pub use coin::Coin; pub use cosmrs::bank::MsgSend; pub use cosmrs::tendermint::abci::{response::DeliverTx, Event, EventAttribute}; @@ -26,274 +45,315 @@ 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 tendermint_rpc::{client::Client as TendermintClient, Request, Response, SimpleRequest}; pub use tendermint_rpc::{ endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse}, Paging, }; +pub use tendermint_rpc::{Request, Response, SimpleRequest}; #[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; +use crate::http_client; +#[cfg(feature = "http-client")] +use crate::{DirectSigningHttpRpcNyxdClient, QueryHttpRpcNyxdClient}; +#[cfg(feature = "http-client")] +use cosmrs::rpc::{HttpClient, HttpClientUrl}; +use tendermint_rpc::query::Query; pub mod coin; +pub mod contract_traits; pub mod cosmwasm_client; pub mod error; pub mod fee; -pub mod traits; #[derive(Debug, Clone)] pub struct Config { pub(crate) chain_details: ChainDetails, - - // I'd love to have used `NymContracts` struct directly here instead, - // however, I'd really prefer to use something more strongly typed (i.e. AccountId vs String) - pub(crate) mixnet_contract_address: Option, - pub(crate) vesting_contract_address: Option, - pub(crate) bandwidth_claim_contract_address: Option, - pub(crate) coconut_bandwidth_contract_address: Option, - pub(crate) group_contract_address: Option, - pub(crate) multisig_contract_address: Option, - pub(crate) coconut_dkg_contract_address: Option, - pub(crate) service_provider_contract_address: Option, - pub(crate) name_service_contract_address: Option, - // TODO: add this in later commits - // pub(crate) gas_price: GasPrice, + pub(crate) contracts: TypedNymContracts, + pub(crate) gas_price: GasPrice, + pub(crate) simulated_gas_multiplier: f32, } impl Config { - fn parse_optional_account( - raw: Option<&String>, - expected_prefix: &str, - ) -> Result, NyxdError> { - if let Some(address) = raw { - trace!("Raw address:{:?}", raw); - trace!("Expected prefix:{:?}", expected_prefix); - let parsed: AccountId = address - .parse() - .map_err(|_| NyxdError::MalformedAccountAddress(address.clone()))?; - debug!("Parsed prefix:{:?}", parsed); - if parsed.prefix() != expected_prefix { - Err(NyxdError::UnexpectedBech32Prefix { - got: parsed.prefix().into(), - expected: expected_prefix.into(), - }) - } else { - Ok(Some(parsed)) - } - } else { - Ok(None) - } - } - pub fn try_from_nym_network_details(details: &NymNetworkDetails) -> Result { - let prefix = &details.chain_details.bech32_account_prefix; Ok(Config { chain_details: details.chain_details.clone(), - mixnet_contract_address: Self::parse_optional_account( - details.contracts.mixnet_contract_address.as_ref(), - prefix, - )?, - vesting_contract_address: Self::parse_optional_account( - details.contracts.vesting_contract_address.as_ref(), - prefix, - )?, - bandwidth_claim_contract_address: Self::parse_optional_account( - details.contracts.bandwidth_claim_contract_address.as_ref(), - prefix, - )?, - coconut_bandwidth_contract_address: Self::parse_optional_account( - details - .contracts - .coconut_bandwidth_contract_address - .as_ref(), - prefix, - )?, - group_contract_address: Self::parse_optional_account( - details.contracts.group_contract_address.as_ref(), - prefix, - )?, - multisig_contract_address: Self::parse_optional_account( - details.contracts.multisig_contract_address.as_ref(), - prefix, - )?, - coconut_dkg_contract_address: Self::parse_optional_account( - details.contracts.coconut_dkg_contract_address.as_ref(), - prefix, - )?, - service_provider_contract_address: Self::parse_optional_account( - details - .contracts - .service_provider_directory_contract_address - .as_ref(), - prefix, - )?, - name_service_contract_address: Self::parse_optional_account( - details.contracts.name_service_contract_address.as_ref(), - prefix, - )?, + contracts: TypedNymContracts::try_from(details.contracts.clone())?, + gas_price: details.try_into()?, + simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER, }) } + + pub fn with_simulated_gas_multplier(mut self, simulated_gas_multiplier: f32) -> Self { + self.simulated_gas_multiplier = simulated_gas_multiplier; + self + } } #[derive(Debug)] -pub struct NyxdClient { - client: C, +pub struct NyxdClient { + client: MaybeSigningClient, config: Config, - // TODO: refactor because that field is only really used for signing - #[allow(dead_code)] - client_address: Option>, - simulated_gas_multiplier: f32, } +// terrible name, but can't really change it because it will break so many uses #[cfg(feature = "http-client")] -impl NyxdClient { - pub fn connect(config: Config, endpoint: U) -> Result, NyxdError> +impl NyxdClient { + pub fn connect(config: Config, endpoint: U) -> Result where U: TryInto, { + let client = http_client(endpoint)?; + Ok(NyxdClient { - client: QueryNyxdClient::new(endpoint)?, + client: MaybeSigningClient::new(client, (&config).into()), config, - client_address: None, - 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( +impl NyxdClient { + pub fn connect_reqwest( + config: Config, + endpoint: Url, + ) -> Result { + let client = ReqwestRpcClient::new(endpoint); + + Ok(NyxdClient { + client: MaybeSigningClient::new(client, (&config).into()), + config, + }) + } +} + +impl NyxdClient { + pub fn new(config: Config, client: C) -> Self { + NyxdClient { + client: MaybeSigningClient::new(client, (&config).into()), + config, + } + } +} + +// terrible name, but can't really change it because it will break so many uses +#[cfg(feature = "http-client")] +impl NyxdClient { + pub fn connect_with_mnemonic( config: Config, endpoint: U, mnemonic: bip39::Mnemonic, - gas_price: Option, - ) -> Result>, NyxdError> + ) -> Result where U: TryInto, { + let client = http_client(endpoint)?; + let prefix = &config.chain_details.bech32_account_prefix; let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic); - Self::connect_with_signer(config, endpoint, wallet, gas_price) + Ok(Self::connect_with_signer(config, client, wallet)) } } -#[cfg(all(feature = "signing", feature = "http-client"))] -impl NyxdClient> +impl NyxdClient { + pub fn connect_reqwest_with_mnemonic( + config: Config, + endpoint: Url, + mnemonic: bip39::Mnemonic, + ) -> DirectSigningReqwestRpcNyxdClient { + let client = ReqwestRpcClient::new(endpoint); + + let prefix = &config.chain_details.bech32_account_prefix; + let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic); + Self::connect_with_signer(config, client, wallet) + } +} + +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, - signer: S, - gas_price: Option, - ) -> Result>, NyxdError> - where - U: TryInto, - { - let denom = &config.chain_details.mix_denom.base; - let client_address = signer - .get_accounts()? - .into_iter() - .map(|account| account.address) - .collect(); - let gas_price = gas_price.unwrap_or(GasPrice::new_with_default_price(denom)?); - - Ok(NyxdClient { - client: SigningNyxdClient::connect_with_signer(endpoint, signer, gas_price)?, + pub fn connect_with_signer(config: Config, client: C, signer: S) -> NyxdClient { + NyxdClient { + client: MaybeSigningClient::new_signing(client, signer, (&config).into()), config, - client_address: Some(client_address), - simulated_gas_multiplier: crate::nyxd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER, - }) + } } +} - #[cfg(feature = "http-client")] +#[cfg(feature = "http-client")] +impl NyxdClient { pub fn change_endpoint(&mut self, new_endpoint: U) -> Result<(), NyxdError> where U: TryInto, { self.client.change_endpoint(new_endpoint) } +} - pub fn into_signer(self) -> S { - self.client.into_signer() +// no trait bounds +impl NyxdClient { + pub fn new_signing(config: Config, client: C, signer: S) -> Self + where + S: OfflineSigner, + { + NyxdClient { + client: MaybeSigningClient::new_signing(client, signer, (&config).into()), + config, + } + } + + 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.contracts.mixnet_contract_address = Some(address); + } + + pub fn set_vesting_contract_address(&mut self, address: AccountId) { + self.config.contracts.vesting_contract_address = Some(address); + } + + pub fn set_coconut_bandwidth_contract_address(&mut self, address: AccountId) { + self.config.contracts.coconut_bandwidth_contract_address = Some(address); + } + + pub fn set_multisig_contract_address(&mut self, address: AccountId) { + self.config.contracts.multisig_contract_address = Some(address); + } + + pub fn set_service_provider_contract_address(&mut self, address: AccountId) { + self.config + .contracts + .service_provider_directory_contract_address = Some(address); + } + + pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) { + self.config.simulated_gas_multiplier = multiplier; } } -#[cfg(feature = "signing")] -impl NyxdClient -where - C: SigningCosmWasmClient + Sync, -{ - 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] +impl NymContractsProvider for NyxdClient { + fn mixnet_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.mixnet_contract_address.as_ref() } - pub fn cw_address(&self) -> Addr - where - C: SigningCosmWasmClient, - { + fn vesting_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.vesting_contract_address.as_ref() + } + + fn coconut_bandwidth_contract_address(&self) -> Option<&AccountId> { + self.config + .contracts + .coconut_bandwidth_contract_address + .as_ref() + } + + fn dkg_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.coconut_dkg_contract_address.as_ref() + } + + fn group_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.group_contract_address.as_ref() + } + + fn multisig_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.multisig_contract_address.as_ref() + } + + fn ephemera_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.ephemera_contract_address.as_ref() + } + + fn name_service_contract_address(&self) -> Option<&AccountId> { + self.config.contracts.name_service_contract_address.as_ref() + } + + fn service_provider_contract_address(&self) -> Option<&AccountId> { + self.config + .contracts + .service_provider_directory_contract_address + .as_ref() + } +} + +// queries +impl NyxdClient +where + C: TendermintRpcClient + Send + Sync, + S: Send + Sync, +{ + pub async fn get_account_public_key( + &self, + address: &AccountId, + ) -> Result, NyxdError> { + 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 { + self.get_block_timestamp(None).await + } + + pub async fn get_block_timestamp( + &self, + height: Option, + ) -> Result { + Ok(self.client.get_block(height).await?.block.header.time) + } + + pub async fn get_block(&self, height: Option) -> Result { + self.client.get_block(height).await + } + + pub async fn get_current_block_height(&self) -> Result { + 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 { + self.client + .get_block(Some(height)) + .await + .map(|block| block.block_id.hash) + } +} + +// signing +impl NyxdClient +where + C: TendermintRpcClient + Send + Sync, + S: OfflineSigner + Send + Sync, + NyxdError: From<::Error>, +{ + pub fn address(&self) -> AccountId { + match self.client.signer_addresses() { + Ok(addresses) => addresses[0].clone(), + Err(_) => { + panic!("key derivation failure") + } + } + } + + pub fn cw_address(&self) -> Addr { // 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 async fn account_sequence(&self) -> Result { + self.client.get_sequence(&self.address()).await } pub fn wrap_contract_execute_message( @@ -303,11 +363,10 @@ where funds: Vec, ) -> Result where - C: SigningCosmWasmClient, M: ?Sized + Serialize, { Ok(cosmwasm::MsgExecuteContract { - sender: self.address().clone(), + sender: self.address(), contract: contract_address.clone(), msg: serde_json::to_vec(msg)?, funds: funds.into_iter().map(Into::into).collect(), @@ -316,13 +375,12 @@ where pub async fn simulate(&self, messages: I) -> Result where - C: SigningCosmWasmClient + Sync, I: IntoIterator + Send, M: Msg, { self.client .simulate( - self.address(), + &self.address(), messages .into_iter() .map(|msg| msg.into_any()) @@ -342,13 +400,10 @@ where amount: Vec, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .send_tokens(self.address(), recipient, amount, fee, memo) + .send_tokens(&self.address(), recipient, amount, fee, memo) .await } @@ -358,13 +413,10 @@ where msgs: Vec<(AccountId, Vec)>, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .send_tokens_multiple(self.address(), msgs, fee, memo) + .send_tokens_multiple(&self.address(), msgs, fee, memo) .await } @@ -377,14 +429,11 @@ where allowed_messages: Vec, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client .grant_allowance( - self.address(), + &self.address(), grantee, spend_limit, expiration, @@ -401,13 +450,10 @@ where grantee: &AccountId, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .revoke_allowance(self.address(), grantee, fee, memo) + .revoke_allowance(&self.address(), grantee, fee, memo) .await } @@ -420,12 +466,11 @@ where funds: Vec, ) -> Result where - C: SigningCosmWasmClient + Sync, M: ?Sized + Serialize + Sync, { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .execute(self.address(), contract_address, msg, fee, memo, funds) + .execute(&self.address(), contract_address, msg, fee, memo, funds) .await } @@ -437,13 +482,12 @@ where memo: impl Into + Send + 'static, ) -> Result where - C: SigningCosmWasmClient + Sync, I: IntoIterator)> + Send, M: Serialize, { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .execute_multiple(self.address(), contract_address, msgs, fee, memo) + .execute_multiple(&self.address(), contract_address, msgs, fee, memo) .await } @@ -452,13 +496,10 @@ where wasm_code: Vec, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .upload(self.address(), wasm_code, fee, memo) + .upload(&self.address(), wasm_code, fee, memo) .await } @@ -472,12 +513,11 @@ where fee: Option, ) -> Result where - C: SigningCosmWasmClient + Sync, M: ?Sized + Serialize + Sync, { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .instantiate(self.address(), code_id, msg, label, fee, memo, options) + .instantiate(&self.address(), code_id, msg, label, fee, memo, options) .await } @@ -487,13 +527,10 @@ where new_admin: &AccountId, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .update_admin(self.address(), contract_address, new_admin, fee, memo) + .update_admin(&self.address(), contract_address, new_admin, fee, memo) .await } @@ -502,13 +539,10 @@ where contract_address: &AccountId, memo: impl Into + Send + 'static, fee: Option, - ) -> Result - where - C: SigningCosmWasmClient + Sync, - { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + ) -> Result { + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .clear_admin(self.address(), contract_address, fee, memo) + .clear_admin(&self.address(), contract_address, fee, memo) .await } @@ -521,273 +555,281 @@ where fee: Option, ) -> Result where - C: SigningCosmWasmClient + Sync, M: ?Sized + Serialize + Sync, { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); + let fee = fee.unwrap_or(Fee::Auto(Some(self.config.simulated_gas_multiplier))); self.client - .migrate(self.address(), contract_address, code_id, fee, msg, memo) + .migrate(&self.address(), contract_address, code_id, fee, msg, memo) .await } } -impl NyxdClient { - pub fn current_config(&self) -> &Config { - &self.config +// ugh. is there a way to avoid that nasty trait implementation? + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TendermintRpcClient for NyxdClient +where + C: TendermintRpcClient + Send + Sync, + S: Send + Sync, +{ + async fn abci_info(&self) -> Result { + self.client.abci_info().await } - 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( + async fn abci_query( &self, - contract: &AccountId, - query_msg: &M, - ) -> Result + path: Option, + data: V, + height: Option, + prove: bool, + ) -> Result where - C: CosmWasmClient + Sync, - M: ?Sized + Serialize + Sync, - for<'a> T: Deserialize<'a>, + V: Into> + Send, { - self.client.query_contract_smart(contract, query_msg).await + self.client.abci_query(path, data, height, prove).await } - pub async fn query_contract_raw( + async fn block(&self, height: H) -> Result + where + H: Into + Send, + { + self.client.block(height).await + } + + async fn block_by_hash( &self, - contract: &AccountId, - query_data: Vec, - ) -> Result, NyxdError> + hash: Hash, + ) -> Result { + self.client.block_by_hash(hash).await + } + + async fn latest_block(&self) -> Result { + self.client.latest_block().await + } + + async fn header(&self, height: H) -> Result where - C: CosmWasmClient + Sync, + H: Into + Send, { - self.client.query_contract_raw(contract, query_data).await + self.client.header(height).await } - pub fn gas_adjustment(&self) -> GasAdjustment { - self.simulated_gas_multiplier - } - - // ============= - // CHAIN RELATED - // ============= - - // CHAIN QUERIES - - pub async fn get_account_details( + async fn header_by_hash( &self, - address: &AccountId, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_account(address).await + hash: Hash, + ) -> Result { + self.client.header_by_hash(hash).await } - pub async fn get_account_public_key( + async fn block_results( &self, - address: &AccountId, - ) -> Result, NyxdError> + height: H, + ) -> Result where - C: CosmWasmClient + Sync, + H: Into + Send, { - 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) + self.client.block_results(height).await } - pub async fn get_current_block_timestamp(&self) -> Result - where - C: CosmWasmClient + Sync, - { - self.get_block_timestamp(None).await + async fn latest_block_results(&self) -> Result { + self.client.latest_block_results().await } - pub async fn get_block_timestamp( + async fn block_search( &self, - height: Option, - ) -> Result - where - C: CosmWasmClient + Sync, - { - Ok(self.client.get_block(height).await?.block.header.time) + query: Query, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.client.block_search(query, page, per_page, order).await } - 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( + async fn blockchain( &self, - height: u64, + min: H, + max: H, + ) -> Result + where + H: Into + Send, + { + self.client.blockchain(min, max).await + } + + async fn broadcast_tx_async( + &self, + tx: T, + ) -> Result + where + T: Into> + Send, + { + TendermintRpcClient::broadcast_tx_async(&self.client, tx).await + } + + async fn broadcast_tx_sync( + &self, + tx: T, + ) -> Result + where + T: Into> + Send, + { + TendermintRpcClient::broadcast_tx_sync(&self.client, tx).await + } + + async fn broadcast_tx_commit( + &self, + tx: T, + ) -> Result + where + T: Into> + Send, + { + TendermintRpcClient::broadcast_tx_commit(&self.client, tx).await + } + + async fn commit(&self, height: H) -> Result + where + H: Into + Send, + { + self.client.commit(height).await + } + + async fn consensus_params( + &self, + height: H, + ) -> Result + where + H: Into + Send, + { + self.client.consensus_params(height).await + } + + async fn consensus_state(&self) -> Result { + self.client.consensus_state().await + } + + async fn validators( + &self, + height: H, paging: Paging, - ) -> Result + ) -> Result where - C: CosmWasmClient + Sync, + H: Into + Send, { - Ok(self.client.validators(height as u32, paging).await?) + self.client.validators(height, paging).await } - pub async fn get_balance( + async fn latest_consensus_params( &self, - address: &AccountId, - denom: String, - ) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_balance(address, denom).await + ) -> Result { + self.client.latest_consensus_params().await } - pub async fn get_all_balances(&self, address: &AccountId) -> Result, NyxdError> - where - C: CosmWasmClient + Sync, - { - self.client.get_all_balances(address).await + async fn latest_commit(&self) -> Result { + self.client.latest_commit().await } - pub async fn get_tx(&self, id: Hash) -> Result - where - C: CosmWasmClient + Sync, - { - self.client.get_tx(id).await + async fn health(&self) -> Result<(), TendermintRpcError> { + self.client.health().await } - pub async fn search_tx(&self, query: Query) -> Result, NyxdError> + async fn genesis(&self) -> Result, TendermintRpcError> where - C: CosmWasmClient + Sync, + AppState: Debug + Serialize + DeserializeOwned + Send, { - self.client.search_tx(query).await + self.client.genesis().await } - pub async fn get_total_supply(&self) -> Result, NyxdError> + async fn net_info(&self) -> Result { + self.client.net_info().await + } + + async fn status(&self) -> Result { + self.client.status().await + } + + async fn broadcast_evidence( + &self, + e: Evidence, + ) -> Result { + self.client.broadcast_evidence(e).await + } + + async fn tx(&self, hash: Hash, prove: bool) -> Result { + self.client.tx(hash, prove).await + } + + async fn tx_search( + &self, + query: Query, + prove: bool, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.client + .tx_search(query, prove, page, per_page, order) + .await + } + + #[cfg(any( + feature = "tendermint-rpc/http-client", + feature = "tendermint-rpc/websocket-client" + ))] + async fn wait_until_healthy(&self, timeout: T) -> Result<(), Error> where - C: CosmWasmClient + Sync, + T: Into + Send, { - self.client.get_total_supply().await + self.client.wait_until_healthy(timeout).await + } + + async fn perform(&self, request: R) -> Result + where + R: SimpleRequest, + { + self.client.perform(request).await + } +} + +#[async_trait] +impl CosmWasmClient for NyxdClient +where + C: TendermintRpcClient + Send + Sync, + S: Send + Sync, +{ +} + +impl OfflineSigner for NyxdClient +where + S: OfflineSigner, +{ + type Error = S::Error; + + fn get_accounts(&self) -> Result, Self::Error> { + self.client.get_accounts() + } + + fn sign_direct_with_account( + &self, + signer: &AccountData, + sign_doc: SignDoc, + ) -> Result { + self.client.sign_direct_with_account(signer, sign_doc) + } +} + +#[async_trait] +impl SigningCosmWasmClient for NyxdClient +where + C: TendermintRpcClient + Send + Sync, + S: TxSigner + Send + Sync, + NyxdError: From, +{ + fn gas_price(&self) -> &GasPrice { + self.client.gas_price() + } + + fn simulated_gas_multiplier(&self) -> f32 { + self.client.simulated_gas_multiplier() } } diff --git a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs deleted file mode 100644 index 66ac21947b..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_query_client.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nyxd::error::NyxdError; -use crate::nyxd::{CosmWasmClient, NyxdClient}; - -use nym_coconut_bandwidth_contract_common::msg::QueryMsg; -use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse; - -use async_trait::async_trait; - -#[async_trait] -pub trait CoconutBandwidthQueryClient { - async fn get_spent_credential( - &self, - blinded_serial_number: String, - ) -> Result; -} - -#[async_trait] -impl CoconutBandwidthQueryClient for NyxdClient { - async fn get_spent_credential( - &self, - blinded_serial_number: String, - ) -> Result { - let request = QueryMsg::GetSpentCredential { - blinded_serial_number, - }; - self.client - .query_contract_smart(self.coconut_bandwidth_contract_address(), &request) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs deleted file mode 100644 index 1efb4bf494..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/coconut_bandwidth_signing_client.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; -use crate::nyxd::cosmwasm_client::types::ExecuteResult; -use crate::nyxd::error::NyxdError; -use crate::nyxd::{Coin, Fee, NyxdClient}; -use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialData; -use nym_coconut_bandwidth_contract_common::{deposit::DepositData, msg::ExecuteMsg}; - -use async_trait::async_trait; - -#[async_trait] -pub trait CoconutBandwidthSigningClient { - async fn deposit( - &self, - amount: Coin, - info: String, - verification_key: String, - encryption_key: String, - fee: Option, - ) -> Result; - async fn spend_credential( - &self, - funds: Coin, - blinded_serial_number: String, - gateway_cosmos_address: String, - fee: Option, - ) -> Result; -} - -#[async_trait] -impl CoconutBandwidthSigningClient for NyxdClient { - async fn deposit( - &self, - amount: Coin, - info: String, - verification_key: String, - encryption_key: String, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = ExecuteMsg::DepositFunds { - data: DepositData::new(info.to_string(), verification_key, encryption_key), - }; - self.client - .execute( - self.address(), - self.coconut_bandwidth_contract_address(), - &req, - fee, - "CoconutBandwidth::Deposit", - vec![amount], - ) - .await - } - async fn spend_credential( - &self, - funds: Coin, - blinded_serial_number: String, - gateway_cosmos_address: String, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = ExecuteMsg::SpendCredential { - data: SpendCredentialData::new( - funds.into(), - blinded_serial_number, - gateway_cosmos_address, - ), - }; - self.client - .execute( - self.address(), - self.coconut_bandwidth_contract_address(), - &req, - fee, - "CoconutBandwidth::SpendCredential", - vec![], - ) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs deleted file mode 100644 index 92334424c2..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/dkg_query_client.rs +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nyxd::error::NyxdError; -use crate::nyxd::{CosmWasmClient, NyxdClient}; -use async_trait::async_trait; -use cosmrs::AccountId; -use nym_coconut_dkg_common::dealer::{ - ContractDealing, DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse, -}; -use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg; -use nym_coconut_dkg_common::types::{DealerDetails, Epoch, EpochId, InitialReplacementData}; -use nym_coconut_dkg_common::verification_key::{ContractVKShare, PagedVKSharesResponse}; -use serde::Deserialize; - -#[async_trait] -pub trait DkgQueryClient { - async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>; - - async fn get_current_epoch(&self) -> Result { - let request = DkgQueryMsg::GetCurrentEpochState {}; - self.query_dkg_contract(request).await - } - async fn get_current_epoch_threshold(&self) -> Result, NyxdError> { - let request = DkgQueryMsg::GetCurrentEpochThreshold {}; - self.query_dkg_contract(request).await - } - - async fn get_initial_dealers(&self) -> Result, NyxdError> { - let request = DkgQueryMsg::GetInitialDealers {}; - self.query_dkg_contract(request).await - } - - async fn get_dealer_details( - &self, - address: &AccountId, - ) -> Result { - let request = DkgQueryMsg::GetDealerDetails { - dealer_address: address.to_string(), - }; - self.query_dkg_contract(request).await - } - - async fn get_current_dealers_paged( - &self, - start_after: Option, - page_limit: Option, - ) -> Result { - let request = DkgQueryMsg::GetCurrentDealers { - start_after, - limit: page_limit, - }; - self.query_dkg_contract(request).await - } - - async fn get_past_dealers_paged( - &self, - start_after: Option, - page_limit: Option, - ) -> Result { - let request = DkgQueryMsg::GetPastDealers { - start_after, - limit: page_limit, - }; - self.query_dkg_contract(request).await - } - - async fn get_dealings_paged( - &self, - idx: usize, - start_after: Option, - page_limit: Option, - ) -> Result { - let request = DkgQueryMsg::GetDealing { - idx: idx as u64, - limit: page_limit, - start_after, - }; - self.query_dkg_contract(request).await - } - - async fn get_vk_shares_paged( - &self, - epoch_id: EpochId, - start_after: Option, - page_limit: Option, - ) -> Result { - let request = DkgQueryMsg::GetVerificationKeys { - epoch_id, - limit: page_limit, - start_after, - }; - self.query_dkg_contract(request).await - } - - async fn get_all_current_dealers(&self) -> Result, NyxdError> { - let mut dealers = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_current_dealers_paged(start_after.take(), None) - .await?; - dealers.append(&mut paged_response.dealers); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(dealers) - } - - async fn get_all_past_dealers(&self) -> Result, NyxdError> { - let mut dealers = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_past_dealers_paged(start_after.take(), None) - .await?; - dealers.append(&mut paged_response.dealers); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(dealers) - } - - async fn get_all_epoch_dealings(&self, idx: usize) -> Result, NyxdError> { - let mut dealings = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_dealings_paged(idx, start_after.take(), None) - .await?; - dealings.append(&mut paged_response.dealings); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(dealings) - } - - async fn get_all_verification_key_shares( - &self, - epoch_id: EpochId, - ) -> Result, NyxdError> { - let mut shares = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self - .get_vk_shares_paged(epoch_id, start_after.take(), None) - .await?; - shares.append(&mut paged_response.shares); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res.into_string()) - } else { - break; - } - } - - Ok(shares) - } -} - -#[async_trait] -impl DkgQueryClient for NyxdClient -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.client - .query_contract_smart(self.coconut_dkg_contract_address(), &query) - .await - } -} - -#[async_trait] -impl DkgQueryClient for crate::Client -where - C: CosmWasmClient + Sync + Send, -{ - async fn query_dkg_contract(&self, query: DkgQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.nyxd.query_dkg_contract(query).await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs deleted file mode 100644 index c35747f40f..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/dkg_signing_client.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nyxd::cosmwasm_client::types::ExecuteResult; -use crate::nyxd::error::NyxdError; -use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient}; -use async_trait::async_trait; -use nym_coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg; -use nym_coconut_dkg_common::types::EncodedBTEPublicKeyWithProof; -use nym_coconut_dkg_common::verification_key::VerificationKeyShare; -use nym_contracts_common::dealings::ContractSafeBytes; - -#[async_trait] -pub trait DkgSigningClient { - async fn advance_dkg_epoch_state(&self, fee: Option) -> Result; - async fn register_dealer( - &self, - bte_key: EncodedBTEPublicKeyWithProof, - announce_address: String, - resharing: bool, - fee: Option, - ) -> Result; - - async fn submit_dealing_bytes( - &self, - commitment: ContractSafeBytes, - resharing: bool, - fee: Option, - ) -> Result; - - async fn submit_verification_key_share( - &self, - share: VerificationKeyShare, - resharing: bool, - fee: Option, - ) -> Result; -} - -#[async_trait] -impl DkgSigningClient for NyxdClient -where - C: SigningCosmWasmClient + Send + Sync, -{ - async fn advance_dkg_epoch_state(&self, fee: Option) -> Result { - let req = DkgExecuteMsg::AdvanceEpochState {}; - - self.client - .execute( - self.address(), - self.coconut_dkg_contract_address(), - &req, - fee.unwrap_or_default(), - "advancing DKG state", - vec![], - ) - .await - } - - async fn register_dealer( - &self, - bte_key: EncodedBTEPublicKeyWithProof, - announce_address: String, - resharing: bool, - fee: Option, - ) -> Result { - let req = DkgExecuteMsg::RegisterDealer { - bte_key_with_proof: bte_key, - announce_address, - resharing, - }; - - self.client - .execute( - self.address(), - self.coconut_dkg_contract_address(), - &req, - fee.unwrap_or_default(), - format!("registering {} as a dealer", self.address()), - vec![], - ) - .await - } - - async fn submit_dealing_bytes( - &self, - dealing_bytes: ContractSafeBytes, - resharing: bool, - fee: Option, - ) -> Result { - let req = DkgExecuteMsg::CommitDealing { - dealing_bytes, - resharing, - }; - - self.client - .execute( - self.address(), - self.coconut_dkg_contract_address(), - &req, - fee.unwrap_or_default(), - "dealing commitment", - vec![], - ) - .await - } - - async fn submit_verification_key_share( - &self, - share: VerificationKeyShare, - resharing: bool, - fee: Option, - ) -> Result { - let req = DkgExecuteMsg::CommitVerificationKeyShare { share, resharing }; - - self.client - .execute( - self.address(), - self.coconut_dkg_contract_address(), - &req, - fee.unwrap_or_default(), - "verification key share commitment", - vec![], - ) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/group_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/group_query_client.rs deleted file mode 100644 index b5b4c1d1ee..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/group_query_client.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2023 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nyxd::error::NyxdError; -use crate::nyxd::{CosmWasmClient, NyxdClient}; - -use nym_group_contract_common::msg::QueryMsg; - -use async_trait::async_trait; -use cw4::MemberResponse; - -#[async_trait] -pub trait GroupQueryClient { - async fn member(&self, addr: String) -> Result; -} - -#[async_trait] -impl GroupQueryClient for NyxdClient { - async fn member(&self, addr: String) -> Result { - let request = QueryMsg::Member { - addr, - at_height: None, - }; - self.client - .query_contract_smart(self.group_contract_address(), &request) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs deleted file mode 100644 index 84e4defaee..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/mixnet_query_client.rs +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient; -use crate::nyxd::error::NyxdError; -use crate::nyxd::NyxdClient; -use async_trait::async_trait; -use cosmrs::AccountId; -use nym_contracts_common::signing::Nonce; -use nym_mixnet_contract_common::delegation::{MixNodeDelegationResponse, OwnerProxySubKey}; -use nym_mixnet_contract_common::families::Family; -use nym_mixnet_contract_common::mixnode::{ - MixNodeDetails, MixnodeRewardingDetailsResponse, PagedMixnodesDetailsResponse, - PagedUnbondedMixnodesResponse, StakeSaturationResponse, UnbondedMixnodeResponse, -}; -use nym_mixnet_contract_common::reward_params::{Performance, RewardingParams}; -use nym_mixnet_contract_common::rewarding::{ - EstimatedCurrentEpochRewardResponse, PendingRewardResponse, -}; -use nym_mixnet_contract_common::{ - delegation, ContractBuildInformation, ContractState, ContractStateParams, - CurrentIntervalResponse, EpochEventId, EpochStatus, GatewayBondResponse, - GatewayOwnershipResponse, IdentityKey, IntervalEventId, LayerDistribution, MixId, - MixOwnershipResponse, MixnodeDetailsResponse, NumberOfPendingEventsResponse, - PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedFamiliesResponse, - PagedGatewayResponse, PagedMembersResponse, PagedMixNodeDelegationsResponse, - PagedMixnodeBondsResponse, PagedRewardedSetResponse, PendingEpochEventResponse, - PendingEpochEventsResponse, PendingIntervalEventResponse, PendingIntervalEventsResponse, - QueryMsg as MixnetQueryMsg, -}; -use serde::Deserialize; - -#[async_trait] -pub trait MixnetQueryClient { - async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>; - - // state/sys-params-related - - async fn get_mixnet_contract_version(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetContractVersion {}) - .await - } - - async fn get_rewarding_validator_address(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetRewardingValidatorAddress {}) - .await - } - - async fn get_mixnet_contract_settings(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetStateParams {}) - .await - } - - async fn get_mixnet_contract_state(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetState {}) - .await - } - - async fn get_rewarding_parameters(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetRewardingParams {}) - .await - } - - async fn get_current_epoch_status(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetEpochStatus {}) - .await - } - - async fn get_current_interval_details(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetCurrentIntervalDetails {}) - .await - } - - async fn get_rewarded_set_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetRewardedSet { limit, start_after }) - .await - } - - async fn get_all_node_families_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after }) - .await - } - - async fn get_all_family_members_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetAllMembersPaged { limit, start_after }) - .await - } - - // mixnode-related: - - async fn get_mixnode_bonds_paged( - &self, - limit: Option, - start_after: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetMixNodeBonds { limit, start_after }) - .await - } - - async fn get_mixnodes_detailed_paged( - &self, - limit: Option, - start_after: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetMixNodesDetailed { limit, start_after }) - .await - } - - async fn get_unbonded_paged( - &self, - limit: Option, - start_after: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after }) - .await - } - - async fn get_unbonded_by_owner_paged( - &self, - owner: &AccountId, - limit: Option, - start_after: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByOwner { - owner: owner.to_string(), - limit, - start_after, - }) - .await - } - - async fn get_unbonded_by_identity_paged( - &self, - identity_key: String, - limit: Option, - start_after: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodesByIdentityKey { - identity_key, - limit, - start_after, - }) - .await - } - - async fn get_owned_mixnode( - &self, - address: &AccountId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetOwnedMixnode { - address: address.to_string(), - }) - .await - } - - async fn get_mixnode_details( - &self, - mix_id: MixId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDetails { mix_id }) - .await - } - - async fn get_mixnode_details_by_identity( - &self, - mix_identity: IdentityKey, - ) -> Result, NyxdError> { - self.query_mixnet_contract(MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity { - mix_identity, - }) - .await - } - - async fn get_mixnode_rewarding_details( - &self, - mix_id: MixId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id }) - .await - } - - async fn get_mixnode_stake_saturation( - &self, - mix_id: MixId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetStakeSaturation { mix_id }) - .await - } - - async fn get_unbonded_mixnode_information( - &self, - mix_id: MixId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id }) - .await - } - - async fn get_layer_distribution(&self) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetLayerDistribution {}) - .await - } - - // gateway-related: - - async fn get_gateways_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetGateways { start_after, limit }) - .await - } - - /// Checks whether there is a bonded gateway associated with the provided identity key - async fn get_gateway_bond( - &self, - identity: IdentityKey, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetGatewayBond { identity }) - .await - } - - /// Checks whether there is a bonded gateway associated with the provided client's address - async fn get_owned_gateway( - &self, - address: &AccountId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetOwnedGateway { - address: address.to_string(), - }) - .await - } - - // delegation-related: - - /// Gets list of all delegations towards particular mixnode on particular page. - async fn get_mixnode_delegations_paged( - &self, - mix_id: MixId, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDelegations { - mix_id, - start_after, - limit, - }) - .await - } - - /// Gets list of all the mixnodes to which a particular address delegated. - async fn get_delegator_delegations_paged( - &self, - delegator: String, - start_after: Option<(MixId, OwnerProxySubKey)>, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetDelegatorDelegations { - delegator, - start_after, - limit, - }) - .await - } - - /// Checks value of delegation of given client towards particular mixnode. - async fn get_delegation_details( - &self, - mix_id: MixId, - delegator: &AccountId, - proxy: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetDelegationDetails { - mix_id, - delegator: delegator.to_string(), - proxy, - }) - .await - } - - /// Gets all the delegations on the entire network - async fn get_all_network_delegations_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetAllDelegations { start_after, limit }) - .await - } - - // rewards related - async fn get_pending_operator_reward( - &self, - operator: &AccountId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingOperatorReward { - address: operator.to_string(), - }) - .await - } - - async fn get_pending_mixnode_operator_reward( - &self, - mix_id: MixId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id }) - .await - } - - async fn get_pending_delegator_reward( - &self, - delegator: &AccountId, - mix_id: MixId, - proxy: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingDelegatorReward { - address: delegator.to_string(), - mix_id, - proxy, - }) - .await - } - - // given the provided performance, estimate the reward at the end of the current epoch - async fn get_estimated_current_epoch_operator_reward( - &self, - mix_id: MixId, - estimated_performance: Performance, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochOperatorReward { - mix_id, - estimated_performance, - }) - .await - } - - // given the provided performance, estimate the reward at the end of the current epoch - async fn get_estimated_current_epoch_delegator_reward( - &self, - delegator: &AccountId, - mix_id: MixId, - proxy: Option, - estimated_performance: Performance, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetEstimatedCurrentEpochDelegatorReward { - address: delegator.to_string(), - mix_id, - proxy, - estimated_performance, - }) - .await - } - - // interval-related - - async fn get_pending_epoch_events_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvents { start_after, limit }) - .await - } - - async fn get_pending_interval_events_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvents { start_after, limit }) - .await - } - - async fn get_pending_epoch_event( - &self, - event_id: EpochEventId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvent { event_id }) - .await - } - - async fn get_pending_interval_event( - &self, - event_id: IntervalEventId, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvent { event_id }) - .await - } - - async fn get_number_of_pending_events( - &self, - ) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetNumberOfPendingEvents {}) - .await - } - - async fn get_signing_nonce(&self, address: &AccountId) -> Result { - self.query_mixnet_contract(MixnetQueryMsg::GetSigningNonce { - address: address.to_string(), - }) - .await - } - - async fn get_node_family_by_label(&self, label: &str) -> Result, NyxdError> { - self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel { - label: label.to_string(), - }) - .await - } - - async fn get_node_family_by_head(&self, head: &str) -> Result, NyxdError> { - self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByHead { - head: head.to_string(), - }) - .await - } -} - -#[async_trait] -impl MixnetQueryClient for NyxdClient -where - C: CosmWasmClient + Sync + Send, -{ - async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.client - .query_contract_smart(self.mixnet_contract_address(), &query) - .await - } -} - -#[async_trait] -impl MixnetQueryClient for crate::Client -where - C: CosmWasmClient + Sync + Send, -{ - async fn query_mixnet_contract(&self, query: MixnetQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.nyxd.query_mixnet_contract(query).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 9f6dac5354..e69de29bb2 100644 --- a/common/client-libs/validator-client/src/nyxd/traits/mod.rs +++ b/common/client-libs/validator-client/src/nyxd/traits/mod.rs @@ -1,52 +0,0 @@ -// Copyright 2021-2023 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -// TODO: expose query-related capabilities to wasm client... - -mod coconut_bandwidth_query_client; -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; -#[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; -pub use group_query_client::GroupQueryClient; -pub use mixnet_query_client::MixnetQueryClient; -pub use multisig_query_client::MultisigQueryClient; -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/nyxd/traits/multisig_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs deleted file mode 100644 index ea461d1080..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/multisig_query_client.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::nyxd::error::NyxdError; -use crate::nyxd::{CosmWasmClient, NyxdClient}; - -use cw3::{ProposalListResponse, ProposalResponse}; -use nym_multisig_contract_common::msg::QueryMsg; - -use async_trait::async_trait; - -#[async_trait] -pub trait MultisigQueryClient { - async fn get_proposal(&self, proposal_id: u64) -> Result; - - async fn list_proposals( - &self, - start_after: Option, - limit: Option, - ) -> Result; - - async fn get_all_proposals(&self) -> Result, NyxdError> { - let mut proposals = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self.list_proposals(start_after.take(), None).await?; - - let last_id = paged_response.proposals.last().map(|prop| prop.id); - proposals.append(&mut paged_response.proposals); - - if let Some(start_after_res) = last_id { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(proposals) - } -} - -#[async_trait] -impl MultisigQueryClient for NyxdClient { - async fn get_proposal(&self, proposal_id: u64) -> Result { - let request = QueryMsg::Proposal { proposal_id }; - self.client - .query_contract_smart(self.multisig_contract_address(), &request) - .await - } - - async fn list_proposals( - &self, - start_after: Option, - limit: Option, - ) -> Result { - let request = QueryMsg::ListProposals { start_after, limit }; - self.client - .query_contract_smart(self.multisig_contract_address(), &request) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs b/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs deleted file mode 100644 index b5ad0a8e96..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/multisig_signing_client.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2022 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient; -use crate::nyxd::cosmwasm_client::types::ExecuteResult; -use crate::nyxd::error::NyxdError; -use crate::nyxd::{Fee, NyxdClient}; - -use cw3::Vote; -use nym_coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg; -use nym_multisig_contract_common::msg::ExecuteMsg; - -use async_trait::async_trait; -use cosmwasm_std::{to_binary, Coin, CosmosMsg, WasmMsg}; - -#[async_trait] -pub trait MultisigSigningClient { - async fn propose_release_funds( - &self, - title: String, - blinded_serial_number: String, - voucher_value: u128, - fee: Option, - ) -> Result; - - async fn vote_proposal( - &self, - proposal_id: u64, - yes: bool, - fee: Option, - ) -> Result; - - async fn execute_proposal( - &self, - proposal_id: u64, - fee: Option, - ) -> Result; -} - -#[async_trait] -impl MultisigSigningClient for NyxdClient { - async fn propose_release_funds( - &self, - title: String, - blinded_serial_number: String, - voucher_value: u128, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let release_funds_req = CoconutBandwidthExecuteMsg::ReleaseFunds { - funds: Coin::new( - voucher_value, - self.config.chain_details.mix_denom.base.clone(), - ), - }; - let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: self.coconut_bandwidth_contract_address().to_string(), - msg: to_binary(&release_funds_req)?, - funds: vec![], - }); - let req = ExecuteMsg::Propose { - title, - description: blinded_serial_number, - msgs: vec![release_funds_msg], - latest: None, - }; - self.client - .execute( - self.address(), - self.multisig_contract_address(), - &req, - fee, - "Multisig::Propose::Execute::ReleaseFunds", - vec![], - ) - .await - } - - async fn vote_proposal( - &self, - proposal_id: u64, - vote_yes: bool, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let vote = if vote_yes { Vote::Yes } else { Vote::No }; - let req = ExecuteMsg::Vote { proposal_id, vote }; - self.client - .execute( - self.address(), - self.multisig_contract_address(), - &req, - fee, - "Multisig::Vote", - vec![], - ) - .await - } - - async fn execute_proposal( - &self, - proposal_id: u64, - fee: Option, - ) -> Result { - let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier))); - let req = ExecuteMsg::Execute { proposal_id }; - self.client - .execute( - self.address(), - self.multisig_contract_address(), - &req, - fee, - "Multisig::Execute", - vec![], - ) - .await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/name_service_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/name_service_query_client.rs deleted file mode 100644 index 0823d97d6a..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/name_service_query_client.rs +++ /dev/null @@ -1,109 +0,0 @@ -use async_trait::async_trait; -use cosmrs::AccountId; -use nym_contracts_common::ContractBuildInformation; -use nym_name_service_common::{ - msg::QueryMsg as NameQueryMsg, - response::{ConfigResponse, NamesListResponse, PagedNamesListResponse}, - Address, NameId, RegisteredName, -}; -use serde::Deserialize; - -use crate::nyxd::{error::NyxdError, CosmWasmClient, NyxdClient}; - -#[async_trait] -pub trait NameServiceQueryClient { - async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>; - - async fn get_name_service_config(&self) -> Result { - self.query_name_service_contract(NameQueryMsg::Config {}) - .await - } - - async fn get_name_entry(&self, name_id: NameId) -> Result { - self.query_name_service_contract(NameQueryMsg::NameId { name_id }) - .await - } - - async fn get_names_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_name_service_contract(NameQueryMsg::All { limit, start_after }) - .await - } - - async fn get_names_by_owner(&self, owner: AccountId) -> Result { - self.query_name_service_contract(NameQueryMsg::ByOwner { - owner: owner.to_string(), - }) - .await - } - - async fn get_names_by_address(&self, address: Address) -> Result { - self.query_name_service_contract(NameQueryMsg::ByAddress { address }) - .await - } - - async fn get_name_service_contract_version( - &self, - ) -> Result { - self.query_name_service_contract(NameQueryMsg::GetContractVersion {}) - .await - } - - async fn get_all_names(&self) -> Result, NyxdError> { - let mut services = Vec::new(); - let mut start_after = None; - - loop { - let mut paged_response = self.get_names_paged(start_after.take(), None).await?; - - let last_id = paged_response.names.last().map(|serv| serv.id); - services.append(&mut paged_response.names); - - if let Some(start_after_res) = last_id { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(services) - } -} - -#[async_trait] -impl NameServiceQueryClient for NyxdClient -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.client - .query_contract_smart( - self.name_service_contract_address().ok_or( - NyxdError::NoContractAddressAvailable("name service contract".to_string()), - )?, - &query, - ) - .await - } -} - -#[async_trait] -impl NameServiceQueryClient for crate::Client -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_name_service_contract(&self, query: NameQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.nyxd.query_name_service_contract(query).await - } -} diff --git a/common/client-libs/validator-client/src/nyxd/traits/sp_directory_query_client.rs b/common/client-libs/validator-client/src/nyxd/traits/sp_directory_query_client.rs deleted file mode 100644 index 0ed99d5c1c..0000000000 --- a/common/client-libs/validator-client/src/nyxd/traits/sp_directory_query_client.rs +++ /dev/null @@ -1,124 +0,0 @@ -use async_trait::async_trait; -use cosmrs::AccountId; -use nym_contracts_common::{signing::Nonce, ContractBuildInformation}; -use nym_service_provider_directory_common::{ - msg::QueryMsg as SpQueryMsg, - response::{ - ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse, - }, - NymAddress, Service, ServiceId, -}; -use serde::Deserialize; - -use crate::nyxd::{error::NyxdError, CosmWasmClient, NyxdClient}; - -#[async_trait] -pub trait SpDirectoryQueryClient { - async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>; - - async fn get_service_config(&self) -> Result { - self.query_service_provider_contract(SpQueryMsg::Config {}) - .await - } - - async fn get_service_info( - &self, - service_id: ServiceId, - ) -> Result { - self.query_service_provider_contract(SpQueryMsg::ServiceId { service_id }) - .await - } - - async fn get_services_paged( - &self, - start_after: Option, - limit: Option, - ) -> Result { - self.query_service_provider_contract(SpQueryMsg::All { limit, start_after }) - .await - } - - async fn get_services_by_announcer( - &self, - announcer: AccountId, - ) -> Result { - self.query_service_provider_contract(SpQueryMsg::ByAnnouncer { - announcer: announcer.to_string(), - }) - .await - } - - async fn get_services_by_nym_address( - &self, - nym_address: NymAddress, - ) -> Result { - self.query_service_provider_contract(SpQueryMsg::ByNymAddress { nym_address }) - .await - } - - async fn get_sp_contract_version(&self) -> Result { - self.query_service_provider_contract(SpQueryMsg::GetContractVersion {}) - .await - } - - async fn get_all_services(&self) -> Result, NyxdError> { - let mut services = Vec::new(); - let mut start_after = None; - loop { - let mut paged_response = self.get_services_paged(start_after.take(), None).await?; - services.append(&mut paged_response.services); - - if let Some(start_after_res) = paged_response.start_next_after { - start_after = Some(start_after_res) - } else { - break; - } - } - - Ok(services) - } - - async fn get_service_signing_nonce(&self, address: &AccountId) -> Result { - self.query_service_provider_contract(SpQueryMsg::SigningNonce { - address: address.to_string(), - }) - .await - } -} - -#[async_trait] -impl SpDirectoryQueryClient for NyxdClient -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.client - .query_contract_smart( - self.service_provider_contract_address().ok_or( - NyxdError::NoContractAddressAvailable( - "service provider directory contract".to_string(), - ), - )?, - &query, - ) - .await - } -} - -#[async_trait] -impl SpDirectoryQueryClient for crate::Client -where - C: CosmWasmClient + Send + Sync, -{ - async fn query_service_provider_contract(&self, query: SpQueryMsg) -> Result - where - for<'a> T: Deserialize<'a>, - { - self.nyxd.query_service_provider_contract(query).await - } -} diff --git a/common/client-libs/validator-client/src/rpc/mod.rs b/common/client-libs/validator-client/src/rpc/mod.rs new file mode 100644 index 0000000000..fd3f7991ba --- /dev/null +++ b/common/client-libs/validator-client/src/rpc/mod.rs @@ -0,0 +1,533 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use async_trait::async_trait; +use cosmrs::tendermint::{self, abci, block::Height, evidence::Evidence, Genesis, Hash}; +use serde::{de::DeserializeOwned, Serialize}; +use std::fmt; +use tendermint_rpc::{ + endpoint::{validators::DEFAULT_VALIDATORS_PER_PAGE, *}, + query::Query, + Error, Order, Paging, SimpleRequest, +}; + +#[cfg(feature = "http-client")] +use crate::error::TendermintRpcError; +#[cfg(feature = "http-client")] +use crate::HttpRpcClient; +#[cfg(feature = "http-client")] +use tendermint_rpc::client::CompatMode; +#[cfg(feature = "http-client")] +use tendermint_rpc::HttpClientUrl; + +pub mod reqwest; + +#[cfg(feature = "http-client")] +pub fn http_client(url: U) -> Result +where + U: TryInto, +{ + HttpRpcClient::builder(url.try_into()?) + .compat_mode(CompatMode::V0_34) + .build() +} + +// we have to create a sealed trait since `TendermintClient` needs T: Send (due to how async trait is created) +// which we can't do in wasm +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait TendermintRpcClient { + /// `/abci_info`: get information about the ABCI application. + async fn abci_info(&self) -> Result { + Ok(self.perform(abci_info::Request).await?.response) + } + + /// `/abci_query`: query the ABCI application + async fn abci_query( + &self, + path: Option, + data: V, + height: Option, + prove: bool, + ) -> Result + where + V: Into> + Send, + { + Ok(self + .perform(abci_query::Request::new(path, data, height, prove)) + .await? + .response) + } + + /// `/block`: get block at a given height. + async fn block(&self, height: H) -> Result + where + H: Into + Send, + { + self.perform(block::Request::new(height.into())).await + } + + /// `/block_by_hash`: get block by hash. + async fn block_by_hash( + &self, + hash: tendermint::Hash, + ) -> Result { + self.perform(block_by_hash::Request::new(hash)).await + } + + /// `/block`: get the latest block. + async fn latest_block(&self) -> Result { + self.perform(block::Request::default()).await + } + + /// `/header`: get block header at a given height. + async fn header(&self, height: H) -> Result + where + H: Into + Send, + { + self.perform(header::Request::new(height.into())).await + } + + /// `/header_by_hash`: get block by hash. + async fn header_by_hash( + &self, + hash: tendermint::Hash, + ) -> Result { + self.perform(header_by_hash::Request::new(hash)).await + } + + /// `/block_results`: get ABCI results for a block at a particular height. + async fn block_results(&self, height: H) -> Result + where + H: Into + Send, + { + self.perform(block_results::Request::new(height.into())) + .await + } + + /// `/block_results`: get ABCI results for the latest block. + async fn latest_block_results(&self) -> Result { + self.perform(block_results::Request::default()).await + } + + /// `/block_search`: search for blocks by BeginBlock and EndBlock events. + async fn block_search( + &self, + query: Query, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.perform(block_search::Request::new(query, page, per_page, order)) + .await + } + + /// `/blockchain`: get block headers for `min` <= `height` <= `max`. + /// + /// Block headers are returned in descending order (highest first). + /// + /// Returns at most 20 items. + async fn blockchain(&self, min: H, max: H) -> Result + where + H: Into + Send, + { + // TODO(tarcieri): return errors for invalid params before making request? + self.perform(blockchain::Request::new(min.into(), max.into())) + .await + } + + /// `/broadcast_tx_async`: broadcast a transaction, returning immediately. + async fn broadcast_tx_async(&self, tx: T) -> Result + where + T: Into> + Send, + { + self.perform(broadcast::tx_async::Request::new(tx)).await + } + + /// `/broadcast_tx_sync`: broadcast a transaction, returning the response + /// from `CheckTx`. + async fn broadcast_tx_sync(&self, tx: T) -> Result + where + T: Into> + Send, + { + self.perform(broadcast::tx_sync::Request::new(tx)).await + } + + /// `/broadcast_tx_commit`: broadcast a transaction, returning the response + /// from `DeliverTx`. + async fn broadcast_tx_commit(&self, tx: T) -> Result + where + T: Into> + Send, + { + self.perform(broadcast::tx_commit::Request::new(tx)).await + } + + /// `/commit`: get block commit at a given height. + async fn commit(&self, height: H) -> Result + where + H: Into + Send, + { + self.perform(commit::Request::new(height.into())).await + } + + /// `/consensus_params`: get current consensus parameters at the specified + /// height. + async fn consensus_params(&self, height: H) -> Result + where + H: Into + Send, + { + self.perform(consensus_params::Request::new(Some(height.into()))) + .await + } + + /// `/consensus_state`: get current consensus state + async fn consensus_state(&self) -> Result { + self.perform(consensus_state::Request::new()).await + } + + // TODO(thane): Simplify once validators endpoint removes pagination. + /// `/validators`: get validators a given height. + async fn validators(&self, height: H, paging: Paging) -> Result + where + H: Into + Send, + { + let height = height.into(); + match paging { + Paging::Default => { + self.perform(validators::Request::new(Some(height), None, None)) + .await + } + Paging::Specific { + page_number, + per_page, + } => { + self.perform(validators::Request::new( + Some(height), + Some(page_number), + Some(per_page), + )) + .await + } + Paging::All => { + let mut page_num = 1_usize; + let mut validators = Vec::new(); + let per_page = DEFAULT_VALIDATORS_PER_PAGE.into(); + loop { + let response = self + .perform(validators::Request::new( + Some(height), + Some(page_num.into()), + Some(per_page), + )) + .await?; + validators.extend(response.validators); + if validators.len() as i32 == response.total { + return Ok(validators::Response::new( + response.block_height, + validators, + response.total, + )); + } + page_num += 1; + } + } + } + } + + /// `/consensus_params`: get the latest consensus parameters. + async fn latest_consensus_params(&self) -> Result { + self.perform(consensus_params::Request::new(None)).await + } + + /// `/commit`: get the latest block commit + async fn latest_commit(&self) -> Result { + self.perform(commit::Request::default()).await + } + + /// `/health`: get node health. + /// + /// Returns empty result (200 OK) on success, no response in case of an error. + async fn health(&self) -> Result<(), Error> { + self.perform(health::Request).await?; + Ok(()) + } + + /// `/genesis`: get genesis file. + async fn genesis(&self) -> Result, Error> + where + AppState: fmt::Debug + Serialize + DeserializeOwned + Send, + { + Ok(self.perform(genesis::Request::default()).await?.genesis) + } + + /// `/net_info`: obtain information about P2P and other network connections. + async fn net_info(&self) -> Result { + self.perform(net_info::Request).await + } + + /// `/status`: get Tendermint status including node info, pubkey, latest + /// block hash, app hash, block height and time. + async fn status(&self) -> Result { + self.perform(status::Request).await + } + + /// `/broadcast_evidence`: broadcast an evidence. + async fn broadcast_evidence(&self, e: Evidence) -> Result { + self.perform(evidence::Request::new(e)).await + } + + /// `/tx`: find transaction by hash. + async fn tx(&self, hash: Hash, prove: bool) -> Result { + self.perform(tx::Request::new(hash, prove)).await + } + + /// `/tx_search`: search for transactions with their results. + async fn tx_search( + &self, + query: Query, + prove: bool, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.perform(tx_search::Request::new(query, prove, page, per_page, order)) + .await + } + + #[cfg(any( + feature = "tendermint-rpc/http-client", + feature = "tendermint-rpc/websocket-client" + ))] + /// Poll the `/health` endpoint until it returns a successful result or + /// the given `timeout` has elapsed. + async fn wait_until_healthy(&self, timeout: T) -> Result<(), Error> + where + T: Into + Send, + { + let timeout = timeout.into(); + let poll_interval = core::time::Duration::from_millis(200); + let mut attempts_remaining = timeout.as_millis() / poll_interval.as_millis(); + + while self.health().await.is_err() { + if attempts_remaining == 0 { + return Err(Error::timeout(timeout)); + } + + attempts_remaining -= 1; + tokio::time::sleep(poll_interval).await; + } + + Ok(()) + } + + /// Perform a request against the RPC endpoint. + /// + /// This method is used by the default implementations of specific + /// endpoint methods. The latest protocol dialect is assumed to be invoked. + async fn perform(&self, request: R) -> Result + where + R: SimpleRequest; +} + +#[cfg(not(target_arch = "wasm32"))] +mod non_wasm { + use super::*; + use cosmrs::tendermint::abci::response::Info; + use std::fmt::Debug; + use tendermint_rpc::endpoint::abci_query::AbciQuery; + use tendermint_rpc::endpoint::block::Response; + + #[async_trait] + impl TendermintRpcClient for C + where + C: tendermint_rpc::client::Client + Sync, + { + async fn abci_info(&self) -> Result { + self.abci_info().await + } + + async fn abci_query( + &self, + path: Option, + data: V, + height: Option, + prove: bool, + ) -> Result + where + V: Into> + Send, + { + self.abci_query(path, data, height, prove).await + } + + async fn block(&self, height: H) -> Result + where + H: Into + Send, + { + self.block(height).await + } + + async fn block_by_hash(&self, hash: Hash) -> Result { + self.block_by_hash(hash).await + } + + async fn latest_block(&self) -> Result { + self.latest_block().await + } + + async fn header(&self, height: H) -> Result + where + H: Into + Send, + { + self.header(height).await + } + + async fn header_by_hash(&self, hash: Hash) -> Result { + self.header_by_hash(hash).await + } + + async fn block_results(&self, height: H) -> Result + where + H: Into + Send, + { + self.block_results(height).await + } + + async fn latest_block_results(&self) -> Result { + self.latest_block_results().await + } + + async fn block_search( + &self, + query: Query, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.block_search(query, page, per_page, order).await + } + + async fn blockchain(&self, min: H, max: H) -> Result + where + H: Into + Send, + { + self.blockchain(min, max).await + } + + async fn broadcast_tx_async(&self, tx: T) -> Result + where + T: Into> + Send, + { + self.broadcast_tx_async(tx).await + } + + async fn broadcast_tx_sync(&self, tx: T) -> Result + where + T: Into> + Send, + { + self.broadcast_tx_sync(tx).await + } + + async fn broadcast_tx_commit( + &self, + tx: T, + ) -> Result + where + T: Into> + Send, + { + self.broadcast_tx_commit(tx).await + } + + async fn commit(&self, height: H) -> Result + where + H: Into + Send, + { + self.commit(height).await + } + + async fn consensus_params(&self, height: H) -> Result + where + H: Into + Send, + { + self.consensus_params(height).await + } + + async fn consensus_state(&self) -> Result { + self.consensus_state().await + } + + async fn validators( + &self, + height: H, + paging: Paging, + ) -> Result + where + H: Into + Send, + { + self.validators(height, paging).await + } + + async fn latest_consensus_params(&self) -> Result { + self.latest_consensus_params().await + } + + async fn latest_commit(&self) -> Result { + self.latest_commit().await + } + + async fn health(&self) -> Result<(), Error> { + self.health().await + } + + async fn genesis(&self) -> Result, Error> + where + AppState: Debug + Serialize + DeserializeOwned + Send, + { + self.genesis().await + } + + async fn net_info(&self) -> Result { + self.net_info().await + } + + async fn status(&self) -> Result { + self.status().await + } + + async fn broadcast_evidence(&self, e: Evidence) -> Result { + self.broadcast_evidence(e).await + } + + async fn tx(&self, hash: Hash, prove: bool) -> Result { + self.tx(hash, prove).await + } + + async fn tx_search( + &self, + query: Query, + prove: bool, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + self.tx_search(query, prove, page, per_page, order).await + } + + #[cfg(any( + feature = "tendermint-rpc/http-client", + feature = "tendermint-rpc/websocket-client" + ))] + async fn wait_until_healthy(&self, timeout: T) -> Result<(), Error> + where + T: Into + Send, + { + self.wait_until_healthy(timeout).await + } + + async fn perform(&self, request: R) -> Result + where + R: SimpleRequest, + { + self.perform(request).await + } + } +} diff --git a/common/client-libs/validator-client/src/rpc/reqwest.rs b/common/client-libs/validator-client/src/rpc/reqwest.rs new file mode 100644 index 0000000000..ffad7dd815 --- /dev/null +++ b/common/client-libs/validator-client/src/rpc/reqwest.rs @@ -0,0 +1,243 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::rpc::TendermintRpcClient; +use async_trait::async_trait; +use cosmrs::tendermint::{block::Height, evidence::Evidence, Hash}; +use reqwest::header::HeaderMap; +use reqwest::{header, RequestBuilder}; +use tendermint_rpc::{ + client::CompatMode, + dialect::{self, Dialect}, + endpoint::{self, *}, + query::Query, + Error, Order, Response, SimpleRequest, +}; + +use url::Url; + +// copied macro from tendermint-rpc crate because that's exactly what we have to do here too +macro_rules! perform_with_compat { + ($self:expr, $request:expr) => {{ + let request = $request; + match $self.compat { + CompatMode::V0_37 => $self.perform_v0_37(request).await, + CompatMode::V0_34 => $self.perform_v0_34(request).await, + } + }}; +} + +pub struct ReqwestRpcClient { + compat: CompatMode, + inner: reqwest::Client, + url: Url, +} + +impl ReqwestRpcClient { + pub fn new(url: Url) -> Self { + ReqwestRpcClient { + compat: CompatMode::V0_34, + inner: reqwest::Client::new(), + url, + } + } + + pub fn set_compat_mode(&mut self, compat: CompatMode) { + self.compat = compat; + } + + fn build_request(&self, request: R) -> RequestBuilder + where + R: SimpleRequest, + S: Dialect, + { + let mut headers = HeaderMap::new(); + headers.insert(header::CONTENT_TYPE, "application/json".parse().unwrap()); + headers.insert( + header::USER_AGENT, + format!("nym-reqwest-rpc-client/{}", env!("CARGO_PKG_VERSION")) + .parse() + .unwrap(), + ); + if let Some(auth) = extract_authorization(&self.url) { + headers.insert(header::AUTHORIZATION, auth.parse().unwrap()); + } + + self.inner + .post(self.url.clone()) + .body(request.into_json().into_bytes()) + .headers(headers) + } + + async fn perform_request(&self, request: R) -> Result + where + R: SimpleRequest, + S: Dialect, + { + let request = self.build_request(request); + // that's extremely unfortunate. the trait requires returning tendermint rpc error so we have to make best effort error mapping + let response = request + .send() + .await + .map_err(TendermintRpcErrorMap::into_rpc_err)?; + let bytes = response + .bytes() + .await + .map_err(TendermintRpcErrorMap::into_rpc_err)?; + R::Response::from_string(bytes).map(Into::into) + } + + async fn perform_v0_34(&self, request: R) -> Result + where + R: SimpleRequest, + { + self.perform_request(request).await + } + + async fn perform_v0_37(&self, request: R) -> Result + where + R: SimpleRequest, + { + self.perform_request(request).await + } +} + +trait TendermintRpcErrorMap { + fn into_rpc_err(self) -> Error; +} + +impl TendermintRpcErrorMap for reqwest::Error { + fn into_rpc_err(self) -> Error { + todo!() + } +} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl TendermintRpcClient for ReqwestRpcClient { + async fn perform(&self, request: R) -> Result + where + R: SimpleRequest, + { + self.perform_request(request).await + } + + async fn block_results(&self, height: H) -> Result + where + H: Into + Send, + { + perform_with_compat!(self, block_results::Request::new(height.into())) + } + + async fn latest_block_results(&self) -> Result { + perform_with_compat!(self, block_results::Request::default()) + } + + async fn header(&self, height: H) -> Result + where + H: Into + Send, + { + let height = height.into(); + match self.compat { + CompatMode::V0_37 => self.perform(endpoint::header::Request::new(height)).await, + CompatMode::V0_34 => { + // Back-fill with a request to /block endpoint and + // taking just the header from the response. + let resp = self.perform_v0_34(block::Request::new(height)).await?; + Ok(resp.into()) + } + } + } + + async fn header_by_hash(&self, hash: Hash) -> Result { + match self.compat { + CompatMode::V0_37 => self.perform(header_by_hash::Request::new(hash)).await, + CompatMode::V0_34 => { + // Back-fill with a request to /block_by_hash endpoint and + // taking just the header from the response. + let resp = self + .perform_v0_34(block_by_hash::Request::new(hash)) + .await?; + Ok(resp.into()) + } + } + } + + /// `/broadcast_evidence`: broadcast an evidence. + async fn broadcast_evidence(&self, e: Evidence) -> Result { + match self.compat { + CompatMode::V0_37 => self.perform(evidence::Request::new(e)).await, + CompatMode::V0_34 => self.perform_v0_34(evidence::Request::new(e)).await, + } + } + + async fn tx(&self, hash: Hash, prove: bool) -> Result { + perform_with_compat!(self, tx::Request::new(hash, prove)) + } + + async fn tx_search( + &self, + query: Query, + prove: bool, + page: u32, + per_page: u8, + order: Order, + ) -> Result { + perform_with_compat!( + self, + tx_search::Request::new(query, prove, page, per_page, order) + ) + } + + async fn broadcast_tx_commit(&self, tx: T) -> Result + where + T: Into> + Send, + { + perform_with_compat!(self, broadcast::tx_commit::Request::new(tx)) + } +} + +// essentially https://github.com/informalsystems/tendermint-rs/blob/v0.32.0/rpc/src/client/transport/auth.rs#L31 +pub fn extract_authorization(url: &Url) -> Option { + if !url.has_authority() { + return None; + } + + let authority = url.authority(); + if let Some((userpass, _)) = authority.split_once('@') { + Some(base64::encode(userpass)) + } else { + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(test)] + mod extracting_url_authorization { + use super::*; + use std::str::FromStr; + + #[test] + fn extract_auth_absent() { + let uri = Url::from_str("http://example.com").unwrap(); + assert_eq!(extract_authorization(&uri), None); + } + + #[test] + fn extract_auth_username_only() { + let uri = Url::from_str("http://toto@example.com").unwrap(); + let base64 = "dG90bw==".to_string(); + assert_eq!(extract_authorization(&uri), Some(base64)); + } + + #[test] + fn extract_auth_username_password() { + let uri = Url::from_str("http://toto:tata@example.com").unwrap(); + let base64 = "dG90bzp0YXRh".to_string(); + assert_eq!(extract_authorization(&uri), Some(base64)); + } + } +} diff --git a/common/client-libs/validator-client/src/signing/direct_wallet.rs b/common/client-libs/validator-client/src/signing/direct_wallet.rs index 177e50a2f2..3de09502ae 100644 --- a/common/client-libs/validator-client/src/signing/direct_wallet.rs +++ b/common/client-libs/validator-client/src/signing/direct_wallet.rs @@ -35,6 +35,7 @@ pub enum DirectSecp256k1HdWalletError { AccountDerivationError { source: eyre::Report }, } +// TODO: maybe lock this one behind feature flag? #[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)] pub struct DirectSecp256k1HdWallet { /// Base secret @@ -198,16 +199,14 @@ mod tests { #[test] fn generating_account_addresses() { // test vectors produced from our js wallet - let mnemonics = vec![ - "crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove", + let mnemonics = ["crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove", "acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel", - "step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball" - ]; + "step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball"]; let prefix = NymNetworkDetails::new_mainnet() .chain_details .bech32_account_prefix; - let addrs = vec![ + let addrs = [ "n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf", "n1h5hgn94nsq4kh99rjj794hr5h5q6yfm2lr52es", "n17n9flp6jflljg6fp05dsy07wcprf2uuu8g40rf", diff --git a/common/client-libs/validator-client/src/signing/signer.rs b/common/client-libs/validator-client/src/signing/signer.rs index 074924a0cd..4b620291fe 100644 --- a/common/client-libs/validator-client/src/signing/signer.rs +++ b/common/client-libs/validator-client/src/signing/signer.rs @@ -33,6 +33,16 @@ pub enum SignerType { pub trait OfflineSigner { type Error: From; + // I really dislike existence of this function because it makes you re-derive your key **twice** for each contract transaction + fn signer_addresses(&self) -> Result, Self::Error> { + let derived_addresses = self + .get_accounts()? + .into_iter() + .map(|account| account.address) + .collect(); + Ok(derived_addresses) + } + fn get_accounts(&self) -> Result, Self::Error>; fn find_account(&self, signer_address: &AccountId) -> Result { @@ -94,3 +104,25 @@ pub trait OfflineSigner { // fn sign_amino_with_account(&self, signer: &AccountData, sign_doc: AminoSignDoc) -> Result; } + +#[derive(Debug, Default, Copy, Clone)] +pub struct NoSigner; + +// #[derive(Debug, Copy, Clone, Error)] +// #[error("no signer is available")] +// struct SignerUnavailable; +// +// // trait bound requirements +// impl From for SignerUnavailable { +// fn from(_: SigningError) -> Self { +// SignerUnavailable +// } +// } +// +// impl OfflineSigner for NoSigner { +// type Error = SignerUnavailable; +// +// fn get_accounts(&self) -> Result, Self::Error> { +// return Err(SignerUnavailable); +// } +// } diff --git a/common/client-libs/validator-client/src/signing/tx_signer.rs b/common/client-libs/validator-client/src/signing/tx_signer.rs index f14f640793..f04047c39e 100644 --- a/common/client-libs/validator-client/src/signing/tx_signer.rs +++ b/common/client-libs/validator-client/src/signing/tx_signer.rs @@ -6,53 +6,33 @@ use crate::signing::SignerData; use cosmrs::tx::{SignDoc, SignerInfo}; use cosmrs::{tx, AccountId, Any}; -#[derive(Debug)] -/// A client that has only one responsibility - sign transactions -/// and not touch chain. -pub struct TxSigner { - signer: S, -} - -impl TxSigner { - pub fn new(signer: S) -> Self { - TxSigner { signer } +// extension trait for the OfflineSigner to allow to sign transactions +pub trait TxSigner: OfflineSigner { + fn signer_public_key(&self, signer_address: &AccountId) -> Option { + let account = self.find_account(signer_address).ok()?; + Some(account.public_key().into()) } - pub fn signer(&self) -> &S { - &self.signer - } - - pub fn into_inner_signer(self) -> S { - self.signer - } - - pub fn sign_amino( + fn sign_amino( &self, _signer_address: &AccountId, _messages: Vec, _fee: tx::Fee, _memo: impl Into + Send + 'static, _signer_data: SignerData, - ) -> Result - where - S: OfflineSigner, - { + ) -> Result::Error> { unimplemented!() } - // TODO: change this sucker to use the trait better - pub fn sign_direct( + fn sign_direct( &self, signer_address: &AccountId, messages: Vec, fee: tx::Fee, memo: impl Into + Send + 'static, signer_data: SignerData, - ) -> Result - where - S: OfflineSigner, - { - let account_from_signer = self.signer.find_account(signer_address)?; + ) -> Result::Error> { + let account_from_signer = self.find_account(signer_address)?; // TODO: experiment with this field let timeout_height = 0u32; @@ -70,7 +50,8 @@ impl TxSigner { ) .map_err(|source| SigningError::SignDocFailure { source })?; - self.signer - .sign_direct_with_account(&account_from_signer, sign_doc) + self.sign_direct_with_account(&account_from_signer, sign_doc) } } + +impl TxSigner for T where T: OfflineSigner {} diff --git a/common/coconut-interface/src/lib.rs b/common/coconut-interface/src/lib.rs index 4f69c2c9fa..2322bc3978 100644 --- a/common/coconut-interface/src/lib.rs +++ b/common/coconut-interface/src/lib.rs @@ -8,7 +8,14 @@ use serde::{Deserialize, Serialize}; use error::CoconutInterfaceError; -pub use nym_coconut::*; +// We list these explicity instead of glob export due to shadowing warnings with the pub tests +// module. +pub use nym_coconut::{ + aggregate_signature_shares, aggregate_verification_keys, blind_sign, hash_to_scalar, + prepare_blind_sign, prove_bandwidth_credential, Attribute, Base58, BlindSignRequest, + BlindedSignature, Bytable, CoconutError, KeyPair, Parameters, PrivateAttribute, + PublicAttribute, Signature, SignatureShare, Theta, VerificationKey, +}; #[derive(Debug, Serialize, Deserialize, Getters, CopyGetters, Clone, PartialEq, Eq)] pub struct Credential { @@ -57,7 +64,7 @@ impl Credential { pub fn verify(&self, verification_key: &VerificationKey) -> bool { let params = Parameters::new(self.n_params).unwrap(); - let public_attributes = vec![ + let public_attributes = [ self.voucher_value.to_string().as_bytes(), self.voucher_info.as_bytes(), ] @@ -138,6 +145,8 @@ impl Base58 for Credential {} #[cfg(test)] mod tests { + use nym_coconut::{prove_bandwidth_credential, Signature}; + use super::*; #[test] diff --git a/common/commands/Cargo.toml b/common/commands/Cargo.toml index 28f319b875..df6860641e 100644 --- a/common/commands/Cargo.toml +++ b/common/commands/Cargo.toml @@ -5,6 +5,7 @@ authors.workspace = true edition = "2021" [dependencies] +anyhow = { workspace = true } base64 = "0.13.0" bip39 = { workspace = true } bs58 = "0.4" @@ -19,20 +20,21 @@ log = { workspace = true } rand = {version = "0.6", features = ["std"] } serde = { version = "1.0", features = ["derive"] } serde_json = { workspace = true } -thiserror = "1" +thiserror = { workspace = true } time = { version = "0.3.6", features = ["parsing", "formatting"] } toml = "0.5.6" -url = "2.2" +url = { workspace = true } tap = "1" cosmrs = { workspace = true } cosmwasm-std = { workspace = true } -nym-validator-client = { path = "../client-libs/validator-client", features = ["signing", "http-client"] } +nym-validator-client = { path = "../client-libs/validator-client" } nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] } nym-network-defaults = { path = "../network-defaults" } nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common" } +nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" } nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-contract" } nym-coconut-bandwidth-contract-common = { path = "../cosmwasm-smart-contracts/coconut-bandwidth-contract" } @@ -41,6 +43,11 @@ nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-co nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" } nym-name-service-common = { path = "../cosmwasm-smart-contracts/name-service" } nym-sphinx = { path = "../../common/nymsphinx" } +nym-client-core = { path = "../../common/client-core" } +nym-config = { path = "../../common/config" } +nym-credentials = { path = "../../common/credentials" } +nym-credential-storage = { path = "../../common/credential-storage" } +nym-credential-utils = { path = "../../common/credential-utils" } nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" } nym-types = { path = "../../common/types" } diff --git a/common/commands/src/coconut/issue_credentials.rs b/common/commands/src/coconut/issue_credentials.rs new file mode 100644 index 0000000000..cd8a9b5f36 --- /dev/null +++ b/common/commands/src/coconut/issue_credentials.rs @@ -0,0 +1,51 @@ +// Copyright 2022-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::context::SigningClient; +use crate::utils::CommonConfigsWrapper; +use anyhow::bail; +use clap::Parser; +use nym_credential_storage::initialise_persistent_storage; +use nym_credential_utils::utils; +use nym_validator_client::nyxd::Coin; +use std::path::PathBuf; + +#[derive(Debug, Parser)] +pub struct Args { + /// Config file of the client that is supposed to use the credential. + #[clap(long)] + pub(crate) client_config: PathBuf, + + /// The amount of utokens the credential will hold. + #[clap(long, default_value = "0")] + pub(crate) amount: u64, + + /// Path to a directory used to store recovery files for unconsumed deposits + #[clap(long)] + pub(crate) recovery_dir: PathBuf, +} + +pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> { + let loaded = CommonConfigsWrapper::try_load(args.client_config)?; + + if let Ok(id) = loaded.try_get_id() { + println!("loaded config file for client '{id}'"); + } + + let Ok(credentials_store) = loaded.try_get_credentials_store() else { + bail!("the loaded config does not have a credentials store information") + }; + + println!( + "using credentials store at '{}'", + credentials_store.display() + ); + + let denom = &client.current_chain_details().mix_denom.base; + let coin = Coin::new(args.amount as u128, denom); + + let persistent_storage = initialise_persistent_storage(credentials_store).await; + utils::issue_credential(&client, coin, &persistent_storage, args.recovery_dir).await?; + + Ok(()) +} diff --git a/common/commands/src/coconut/mod.rs b/common/commands/src/coconut/mod.rs index 6cfc921228..531a316125 100644 --- a/common/commands/src/coconut/mod.rs +++ b/common/commands/src/coconut/mod.rs @@ -1,4 +1,20 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -// TODO: add coconut commands here +use clap::{Args, Subcommand}; + +pub mod issue_credentials; +pub mod recover_credentials; + +#[derive(Debug, Args)] +#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] +pub struct Coconut { + #[clap(subcommand)] + pub command: CoconutCommands, +} + +#[derive(Debug, Subcommand)] +pub enum CoconutCommands { + IssueCredentials(issue_credentials::Args), + RecoverCredentials(recover_credentials::Args), +} diff --git a/common/commands/src/coconut/recover_credentials.rs b/common/commands/src/coconut/recover_credentials.rs new file mode 100644 index 0000000000..025ea68c2c --- /dev/null +++ b/common/commands/src/coconut/recover_credentials.rs @@ -0,0 +1,48 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::context::QueryClient; +use crate::utils::CommonConfigsWrapper; +use anyhow::bail; +use clap::Parser; +use nym_credential_storage::initialise_persistent_storage; +use nym_credential_utils::{recovery_storage, utils}; +use std::path::PathBuf; + +#[derive(Debug, Parser)] +pub struct Args { + /// Config file of the client that is supposed to use the credential. + #[clap(long)] + pub(crate) client_config: PathBuf, + + /// Path to a directory used to store recovery files for unconsumed deposits + #[clap(long)] + pub(crate) recovery_dir: PathBuf, +} + +pub async fn execute(args: Args, client: QueryClient) -> anyhow::Result<()> { + let loaded = CommonConfigsWrapper::try_load(args.client_config)?; + + if let Ok(id) = loaded.try_get_id() { + println!("loaded config file for client '{id}'"); + } + + let Ok(credentials_store) = loaded.try_get_credentials_store() else { + bail!("the loaded config does not have a credentials store information") + }; + + println!( + "using credentials store at '{}'", + credentials_store.display() + ); + + let persistent_storage = initialise_persistent_storage(credentials_store).await; + let recovery_storage = recovery_storage::RecoveryStorage::new(args.recovery_dir)?; + + let recovered = + utils::recover_credentials(&client, &recovery_storage, &persistent_storage).await?; + + // TODO: denom? + println!("recovered {recovered} worth of credentials"); + Ok(()) +} diff --git a/common/commands/src/context/mod.rs b/common/commands/src/context/mod.rs index e2d648fbf0..db20ed0ac3 100644 --- a/common/commands/src/context/mod.rs +++ b/common/commands/src/context/mod.rs @@ -8,17 +8,19 @@ use nym_network_defaults::{ NymNetworkDetails, }; pub use nym_validator_client::nym_api::Client as NymApiClient; -use nym_validator_client::nyxd::{ - self, AccountId, DirectSigningNyxdClient, NyxdClient, QueryNyxdClient, +use nym_validator_client::nyxd::{self, AccountId, NyxdClient}; +use nym_validator_client::{ + DirectSigningHttpRpcNyxdClient, DirectSigningHttpRpcValidatorClient, QueryHttpRpcNyxdClient, + QueryHttpRpcValidatorClient, }; use tap::prelude::*; pub mod errors; -pub type SigningClient = nym_validator_client::nyxd::NyxdClient; -pub type QueryClient = nym_validator_client::nyxd::NyxdClient; -pub type SigningClientWithNyxd = nym_validator_client::Client; -pub type QueryClientWithNyxd = nym_validator_client::Client; +pub type SigningClient = DirectSigningHttpRpcNyxdClient; +pub type QueryClient = QueryHttpRpcNyxdClient; +pub type SigningClientWithNyxd = DirectSigningHttpRpcValidatorClient; +pub type QueryClientWithNyxd = QueryHttpRpcValidatorClient; #[derive(Debug)] pub struct ClientArgs { @@ -79,7 +81,7 @@ pub fn create_signing_client( .nyxd_url .as_str(); - match NyxdClient::connect_with_mnemonic(client_config, nyxd_url, mnemonic, None) { + match NyxdClient::connect_with_mnemonic(client_config, nyxd_url, mnemonic) { Ok(client) => Ok(client), Err(e) => Err(ContextError::NyxdError(format!("{e}"))), } diff --git a/common/commands/src/utils.rs b/common/commands/src/utils.rs index 63f8b6a085..25797c71e9 100644 --- a/common/commands/src/utils.rs +++ b/common/commands/src/utils.rs @@ -1,13 +1,17 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use anyhow::{anyhow, bail}; use cosmrs::AccountId; use cosmwasm_std::{Addr, Coin as CosmWasmCoin, Decimal}; use log::error; +use nym_client_core::config::disk_persistence::CommonClientPaths; use nym_validator_client::nyxd::Coin; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::error::Error; use std::fmt::{Display, Formatter}; +use std::fs; +use std::path::{Path, PathBuf}; // TODO: perhaps it should be moved to some global common crate? pub fn account_id_to_cw_addr(account_id: &AccountId) -> Addr { @@ -75,3 +79,150 @@ impl DataWrapper { DataWrapper { data } } } + +fn find_toml_value<'a>(root: &'a toml::Value, key: &str) -> Option<&'a toml::Value> { + if let toml::Value::Table(table) = root { + for (k, v) in table { + if k == key { + return Some(v); + } + if v.is_table() { + if let Some(res) = find_toml_value(v, key) { + return Some(res); + } + } + } + } + None +} + +#[derive(Deserialize, Debug)] +#[serde(untagged)] +pub(crate) enum CommonConfigsWrapper { + // native, socks5, NR, etc. clients + NymClients(Box), + + // nym-api + NymApi(NymApiConfigLight), + + // anything else that might get get introduced + Unknown(UnknownConfigWrapper), +} + +impl CommonConfigsWrapper { + pub(crate) fn try_load>(path: P) -> anyhow::Result { + let content = fs::read_to_string(path)?; + Ok(toml::from_str(&content)?) + } + + pub(crate) fn try_get_id(&self) -> anyhow::Result<&str> { + match self { + CommonConfigsWrapper::NymClients(cfg) => cfg.try_get_id(), + CommonConfigsWrapper::NymApi(cfg) => Ok(&cfg.base.id), + CommonConfigsWrapper::Unknown(cfg) => cfg.try_get_id(), + } + } + + pub(crate) fn try_get_credentials_store(&self) -> anyhow::Result { + match self { + CommonConfigsWrapper::NymClients(cfg) => { + Ok(cfg.storage_paths.inner.credentials_database.clone()) + } + CommonConfigsWrapper::NymApi(cfg) => Ok(cfg + .network_monitor + .storage_paths + .credentials_database_path + .clone()), + CommonConfigsWrapper::Unknown(cfg) => cfg.try_get_credentials_store(), + } + } +} + +// ideally we would have just imported the full nym-api config structure, but that'd have been an overkill, +// because we'd have to import the whole crate +#[derive(Deserialize, Debug)] +pub(crate) struct NymApiConfigLight { + base: NymApiConfigBaseLight, + network_monitor: NymApiConfigNetworkMonitorLight, +} + +#[derive(Deserialize, Debug)] +struct NymApiConfigBaseLight { + id: String, +} + +#[derive(Deserialize, Debug)] +struct NymApiConfigNetworkMonitorLight { + storage_paths: NetworkMonitorPaths, +} + +#[derive(Deserialize, Debug)] +struct NetworkMonitorPaths { + credentials_database_path: PathBuf, +} + +// a hacky way of reading common data from client configs (native, socks5, etc.) +// it works because all clients follow the same structure for storage paths +// (or so I thought) +#[derive(Deserialize, Debug)] +pub(crate) struct ClientConfigCommonWrapper { + storage_paths: StoragePathsWrapper, + + // ... but they have different structure for `nym_client_core::config::Client` + // native client has it on the top layer, whilsts socks5 has it under 'core' table + #[serde(flatten)] + other: toml::Value, +} + +// wrapper to allow for any additional entries besides the common paths, like allow list for NR +#[derive(Deserialize, Debug)] +struct StoragePathsWrapper { + #[serde(flatten)] + inner: CommonClientPaths, +} + +impl ClientConfigCommonWrapper { + pub(crate) fn try_get_id(&self) -> anyhow::Result<&str> { + let id_val = find_toml_value(&self.other, "id") + .ok_or_else(|| anyhow!("no id field present in the config"))?; + if let toml::Value::String(id) = id_val { + Ok(id) + } else { + bail!("no id field present in the config") + } + } +} + +#[derive(Deserialize, Debug)] +pub(crate) struct UnknownConfigWrapper { + #[serde(flatten)] + inner: toml::Value, +} + +impl UnknownConfigWrapper { + fn find_value(&self, key: &str) -> Option<&toml::Value> { + find_toml_value(&self.inner, key) + } + + pub(crate) fn try_get_id(&self) -> anyhow::Result<&str> { + let id_val = self + .find_value("id") + .ok_or_else(|| anyhow!("no id field present in the config"))?; + if let toml::Value::String(id) = id_val { + Ok(id) + } else { + bail!("no id field present in the config") + } + } + + pub(crate) fn try_get_credentials_store(&self) -> anyhow::Result { + let id_val = self + .find_value("credentials_database_path") + .ok_or_else(|| anyhow!("no 'credentials_database_path' field present in the config"))?; + if let toml::Value::String(credentials_store) = id_val { + Ok(credentials_store.parse()?) + } else { + bail!("no 'credentials_database_path' field present in the config") + } + } +} diff --git a/common/commands/src/validator/account/balance.rs b/common/commands/src/validator/account/balance.rs index 8786470bd1..4e109ad7b3 100644 --- a/common/commands/src/validator/account/balance.rs +++ b/common/commands/src/validator/account/balance.rs @@ -4,7 +4,7 @@ use clap::Parser; use log::{error, info}; -use nym_validator_client::nyxd::AccountId; +use nym_validator_client::nyxd::{AccountId, CosmWasmClient}; use crate::context::QueryClient; use crate::utils::{pretty_coin, show_error}; diff --git a/common/commands/src/validator/account/pubkey.rs b/common/commands/src/validator/account/pubkey.rs index b1ed260568..6a7c3383d6 100644 --- a/common/commands/src/validator/account/pubkey.rs +++ b/common/commands/src/validator/account/pubkey.rs @@ -3,7 +3,7 @@ use clap::Parser; use log::{error, info}; -use nym_validator_client::nyxd::AccountId; +use nym_validator_client::nyxd::{AccountId, CosmWasmClient}; use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet; use crate::context::QueryClient; @@ -68,7 +68,7 @@ pub fn get_pubkey_from_mnemonic(address: AccountId, prefix: &str, mnemonic: bip3 pub async fn get_pubkey_from_chain(address: AccountId, client: &QueryClient) { info!("Getting public key for address {} from chain...", address); - match client.get_account_details(&address).await { + match client.get_account(&address).await { Ok(Some(account)) => { if let Ok(base_account) = account.try_get_base_account() { if let Some(pubkey) = base_account.pubkey { diff --git a/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs b/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs index 6efa068545..54b2516d6e 100644 --- a/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/delegate_to_mixnode.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::{Coin, MixId}; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { @@ -34,6 +34,7 @@ pub async fn delegate_to_mixnode(args: Args, client: SigningClient) { .get_mixnode_details_by_identity(identity_key) .await .expect("contract query failed") + .mixnode_details .expect("mixnode with the specified identity doesnt exist"); node_details.mix_id() } diff --git a/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs b/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs index 4e6d91926b..f29dae6db3 100644 --- a/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs +++ b/common/commands/src/validator/mixnet/delegators/query_for_delegations.rs @@ -10,6 +10,7 @@ use crate::utils::{pretty_cosmwasm_coin, show_error_passthrough}; use comfy_table::Table; use cosmwasm_std::Addr; use nym_mixnet_contract_common::{Delegation, PendingEpochEvent, PendingEpochEventKind}; +use nym_validator_client::nyxd::contract_traits::PagedMixnetQueryClient; #[derive(Debug, Parser)] pub struct Args {} @@ -21,12 +22,14 @@ pub async fn execute(_args: Args, client: SigningClientWithNyxd) { ); let delegations = client - .get_all_delegator_delegations(client.nyxd.address()) + .nyxd + .get_all_delegator_delegations(&client.nyxd.address()) .await .map_err(show_error_passthrough); let mixnet_contract_events = client - .get_all_nyxd_pending_epoch_events() + .nyxd + .get_all_pending_epoch_events() .await .map_err(show_error_passthrough); diff --git a/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs b/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs index 3f2ce6b197..327a3c0fd3 100644 --- a/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs +++ b/common/commands/src/validator/mixnet/delegators/rewards/claim_delegator_reward.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::MixId; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { @@ -29,6 +29,7 @@ pub async fn claim_delegator_reward(args: Args, client: SigningClient) { .get_mixnode_details_by_identity(identity_key) .await .expect("contract query failed") + .mixnode_details .expect("mixnode with the specified identity doesnt exist"); node_details.mix_id() } diff --git a/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs b/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs index 96733206f9..aa6b8dbe44 100644 --- a/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs +++ b/common/commands/src/validator/mixnet/delegators/rewards/vesting_claim_delegator_reward.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::MixId; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { @@ -29,6 +29,7 @@ pub async fn vesting_claim_delegator_reward(args: Args, client: SigningClient) { .get_mixnode_details_by_identity(identity_key) .await .expect("contract query failed") + .mixnode_details .expect("mixnode with the specified identity doesnt exist"); node_details.mix_id() } diff --git a/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs b/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs index 2e4ce7b8cd..cadb6c272f 100644 --- a/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs +++ b/common/commands/src/validator/mixnet/delegators/undelegate_from_mixnode.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::MixId; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { @@ -29,6 +29,7 @@ pub async fn undelegate_from_mixnode(args: Args, client: SigningClient) { .get_mixnode_details_by_identity(identity_key) .await .expect("contract query failed") + .mixnode_details .expect("mixnode with the specified identity doesnt exist"); node_details.mix_id() } 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 ec10a60193..45c4ffa5c3 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 @@ -5,8 +5,8 @@ use clap::Parser; use log::info; use nym_mixnet_contract_common::{Coin, MixId}; -use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; use crate::context::SigningClient; @@ -40,6 +40,7 @@ pub async fn vesting_delegate_to_mixnode(args: Args, client: SigningClient) { .get_mixnode_details_by_identity(identity_key) .await .expect("contract query failed") + .mixnode_details .expect("mixnode with the specified identity doesnt exist"); node_details.mix_id() } 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 4f8dac045e..13bae0b7a0 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 @@ -4,8 +4,8 @@ use clap::Parser; use log::info; use nym_mixnet_contract_common::MixId; -use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; use crate::context::SigningClient; @@ -34,6 +34,7 @@ pub async fn vesting_undelegate_from_mixnode(args: Args, client: SigningClient) .get_mixnode_details_by_identity(identity_key) .await .expect("contract query failed") + .mixnode_details .expect("mixnode with the specified identity doesnt exist"); node_details.mix_id() } diff --git a/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs index 76b6e93fa0..28fe160070 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/bond_gateway.rs @@ -7,7 +7,7 @@ use log::{info, warn}; use nym_contracts_common::signing::MessageSignature; use nym_mixnet_contract_common::Coin; use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT}; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/gateway/gateway_bonding_sign_payload.rs b/common/commands/src/validator/mixnet/operators/gateway/gateway_bonding_sign_payload.rs index ff4f8eaf32..3845a6c7ef 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/gateway_bonding_sign_payload.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/gateway_bonding_sign_payload.rs @@ -8,7 +8,7 @@ use cosmwasm_std::Coin; use nym_bin_common::output_format::OutputFormat; use nym_mixnet_contract_common::construct_gateway_bonding_sign_payload; use nym_network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT}; -use nym_validator_client::nyxd::traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider}; #[derive(Debug, Parser)] pub struct Args { @@ -62,7 +62,7 @@ pub async fn create_payload(args: Args, client: SigningClient) { let coin = Coin::new(args.amount, denom); - let nonce = match client.get_signing_nonce(client.address()).await { + let nonce = match client.get_signing_nonce(&client.address()).await { Ok(nonce) => nonce, Err(err) => { eprint!( @@ -73,9 +73,11 @@ pub async fn create_payload(args: Args, client: SigningClient) { } }; - let address = account_id_to_cw_addr(client.address()); + let address = account_id_to_cw_addr(&client.address()); let proxy = if args.with_vesting_account { - Some(account_id_to_cw_addr(client.vesting_contract_address())) + Some(account_id_to_cw_addr( + client.vesting_contract_address().unwrap(), + )) } else { None }; diff --git a/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs b/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs index cce3d26dfe..c0cf879147 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/settings/update_config.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::GatewayConfigUpdate; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { @@ -29,7 +29,7 @@ pub async fn update_config(args: Args, client: SigningClient) { info!("Update gateway config!"); let current_details = match client - .get_owned_gateway(client.address()) + .get_owned_gateway(&client.address()) .await .expect("failed to query the chain for gateway details") .gateway 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 e734ddc97d..346b652b2f 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 @@ -5,8 +5,8 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::GatewayConfigUpdate; -use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { @@ -30,7 +30,7 @@ pub async fn vesting_update_config(args: Args, client: SigningClient) { info!("Update vesting gateway config!"); let current_details = match client - .get_owned_gateway(client.address()) + .get_owned_gateway(&client.address()) .await .expect("failed to query the chain for gateway details") .gateway diff --git a/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs index 7603db1a6e..af9c6c8bb4 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/unbond_gateway.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[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 21f771c0b3..91c2817be8 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::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs b/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs index 8e79dd7bc7..1c4312424c 100644 --- a/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs +++ b/common/commands/src/validator/mixnet/operators/gateway/vesting_unbond_gateway.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args {} diff --git a/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs index 8fe6bb8040..862a797566 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/bond_mixnode.rs @@ -10,7 +10,7 @@ use nym_mixnet_contract_common::{Coin, MixNodeCostParams, Percent}; use nym_network_defaults::{ DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT, }; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; use nym_validator_client::nyxd::CosmWasmCoin; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/mixnode/decrease_pledge.rs b/common/commands/src/validator/mixnet/operators/mixnode/decrease_pledge.rs index 969e40024a..556021dcf9 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/decrease_pledge.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/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::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[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 f5ff9e3419..6acaca80d2 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 @@ -4,8 +4,8 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/families/create_family_join_permit_sign_payload.rs b/common/commands/src/validator/mixnet/operators/mixnode/families/create_family_join_permit_sign_payload.rs index a305df4839..c81dc35f69 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/families/create_family_join_permit_sign_payload.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/families/create_family_join_permit_sign_payload.rs @@ -10,7 +10,7 @@ use nym_bin_common::output_format::OutputFormat; use nym_crypto::asymmetric::identity; use nym_mixnet_contract_common::construct_family_join_permit; use nym_mixnet_contract_common::families::FamilyHead; -use nym_validator_client::nyxd::traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider}; #[derive(Debug, Parser)] pub struct Args { @@ -47,9 +47,10 @@ pub async fn create_family_join_permit_sign_payload(args: Args, client: QueryCli // make sure this mixnode is actually a family head if client - .get_node_family_by_head(mixnode.bond_information.identity()) + .get_node_family_by_head(mixnode.bond_information.identity().to_string()) .await .unwrap() + .family .is_none() { eprintln!("{} does not even seem to own a family!", args.address); @@ -69,7 +70,9 @@ pub async fn create_family_join_permit_sign_payload(args: Args, client: QueryCli // let address = account_id_to_cw_addr(&args.address); let proxy = if args.with_vesting_account { - Some(account_id_to_cw_addr(client.vesting_contract_address())) + Some(account_id_to_cw_addr( + client.vesting_contract_address().unwrap(), + )) } else { None }; 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 8b888e8bd6..411a8412b7 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 @@ -7,8 +7,8 @@ use log::info; 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::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { @@ -28,7 +28,7 @@ pub struct Args { pub async fn join_family(args: Args, client: SigningClient) { info!("Join family"); - let family_head = FamilyHead::new(&args.family_head.to_base58_string()); + let family_head = FamilyHead::new(args.family_head.to_base58_string()); let res = if args.with_vesting_account { client 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 9305c56918..27b5d71d7d 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 @@ -5,8 +5,8 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_crypto::asymmetric::identity; -use nym_validator_client::nyxd::traits::MixnetSigningClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_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 5cb06e7762..0673be508a 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 @@ -6,8 +6,8 @@ use clap::Parser; 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::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { @@ -23,7 +23,7 @@ pub struct Args { pub async fn leave_family(args: Args, client: SigningClient) { info!("Leave family"); - let family_head = FamilyHead::new(&args.family_head.to_base58_string()); + let family_head = FamilyHead::new(args.family_head.to_base58_string()); let res = if args.with_vesting_account { client diff --git a/common/commands/src/validator/mixnet/operators/mixnode/mixnode_bonding_sign_payload.rs b/common/commands/src/validator/mixnet/operators/mixnode/mixnode_bonding_sign_payload.rs index b44d159398..332de7614e 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/mixnode_bonding_sign_payload.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/mixnode_bonding_sign_payload.rs @@ -11,7 +11,7 @@ use nym_mixnet_contract_common::{construct_mixnode_bonding_sign_payload, MixNode use nym_network_defaults::{ DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT, }; -use nym_validator_client::nyxd::traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider}; use nym_validator_client::nyxd::CosmWasmCoin; #[derive(Debug, Parser)] @@ -88,7 +88,7 @@ pub async fn create_payload(args: Args, client: SigningClient) { }, }; - let nonce = match client.get_signing_nonce(client.address()).await { + let nonce = match client.get_signing_nonce(&client.address()).await { Ok(nonce) => nonce, Err(err) => { eprint!( @@ -99,9 +99,11 @@ pub async fn create_payload(args: Args, client: SigningClient) { } }; - let address = account_id_to_cw_addr(client.address()); + let address = account_id_to_cw_addr(&client.address()); let proxy = if args.with_vesting_account { - Some(account_id_to_cw_addr(client.vesting_contract_address())) + Some(account_id_to_cw_addr( + client.vesting_contract_address().unwrap(), + )) } else { None }; diff --git a/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.rs b/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.rs index 1fbf38eb0c..1989725907 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/pledge_more.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::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs b/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs index 729a5e8c03..a5f8d65236 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/rewards/claim_operator_reward.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args {} diff --git a/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs b/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs index 8c1d43e091..3b88c6cf5c 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/rewards/vesting_claim_operator_reward.rs @@ -4,7 +4,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs b/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs index 69fd8531be..733dea04a1 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/settings/update_config.rs @@ -5,7 +5,7 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::MixNodeConfigUpdate; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; #[derive(Debug, Parser)] pub struct Args { @@ -29,7 +29,7 @@ pub async fn update_config(args: Args, client: SigningClient) { info!("Update mix node config!"); let current_details = match client - .get_owned_mixnode(client.address()) + .get_owned_mixnode(&client.address()) .await .expect("failed to query the chain for mixnode details") .mixnode_details 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 5b956b929f..a495c8f4b5 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 @@ -5,8 +5,8 @@ use crate::context::SigningClient; use clap::Parser; use log::info; use nym_mixnet_contract_common::MixNodeConfigUpdate; -use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { @@ -30,7 +30,7 @@ pub async fn vesting_update_config(client: SigningClient, args: Args) { info!("Update vesting mix node config!"); let current_details = match client - .get_owned_mixnode(client.address()) + .get_owned_mixnode(&client.address()) .await .expect("failed to query the chain for mixnode details") .mixnode_details diff --git a/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs b/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs index ecfc3652e1..fe6f3df223 100644 --- a/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs +++ b/common/commands/src/validator/mixnet/operators/mixnode/unbond_mixnode.rs @@ -3,7 +3,7 @@ use clap::Parser; use log::info; -use nym_validator_client::nyxd::traits::MixnetSigningClient; +use nym_validator_client::nyxd::contract_traits::MixnetSigningClient; use crate::context::SigningClient; 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 9928dec18f..415f9fe2b3 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::{traits::VestingSigningClient, CosmWasmCoin}; +use nym_validator_client::nyxd::{contract_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 d38d7f61dc..bb83e1ffaa 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::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_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 0774fd89d6..9917e02860 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::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_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 49f717a5af..3e63846430 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::traits::VestingSigningClient; +use nym_validator_client::nyxd::contract_traits::VestingSigningClient; #[derive(Debug, Parser)] pub struct Args { diff --git a/common/commands/src/validator/mixnet/operators/name/delete.rs b/common/commands/src/validator/mixnet/operators/name/delete.rs index 558dbf190c..d93b432108 100644 --- a/common/commands/src/validator/mixnet/operators/name/delete.rs +++ b/common/commands/src/validator/mixnet/operators/name/delete.rs @@ -1,7 +1,7 @@ use clap::Parser; use log::{error, info}; use nym_name_service_common::NameId; -use nym_validator_client::nyxd::{error::NyxdError, traits::NameServiceSigningClient}; +use nym_validator_client::nyxd::{contract_traits::NameServiceSigningClient, error::NyxdError}; use tap::TapFallible; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/name/mod.rs b/common/commands/src/validator/mixnet/operators/name/mod.rs index a1e2733ac5..c4adb3301d 100644 --- a/common/commands/src/validator/mixnet/operators/name/mod.rs +++ b/common/commands/src/validator/mixnet/operators/name/mod.rs @@ -2,6 +2,7 @@ use clap::{Args, Subcommand}; pub mod delete; pub mod register; +pub mod register_sign_payload; #[derive(Debug, Args)] #[clap(args_conflicts_with_subcommands = true, subcommand_required = true)] @@ -16,4 +17,6 @@ pub enum MixnetOperatorsNameCommands { Register(register::Args), /// Delete name alias for a nym address Delete(delete::Args), + /// Create base58-encoded payload required for producing valiid register signature. + CreateNameRegisterPayload(register_sign_payload::Args), } diff --git a/common/commands/src/validator/mixnet/operators/name/register.rs b/common/commands/src/validator/mixnet/operators/name/register.rs index e246e4241a..54a4b1a20d 100644 --- a/common/commands/src/validator/mixnet/operators/name/register.rs +++ b/common/commands/src/validator/mixnet/operators/name/register.rs @@ -2,7 +2,8 @@ use clap::Parser; use log::{error, info}; use nym_contracts_common::signing::MessageSignature; use nym_name_service_common::{Address, Coin, NameDetails, NymName}; -use nym_validator_client::nyxd::{error::NyxdError, traits::NameServiceSigningClient}; +use nym_sphinx::addressing::clients::Recipient; +use nym_validator_client::nyxd::{contract_traits::NameServiceSigningClient, error::NyxdError}; use tap::TapFallible; use crate::context::SigningClient; @@ -11,11 +12,11 @@ use crate::context::SigningClient; pub struct Args { /// Name alias #[clap(long)] - pub name: String, + pub name: NymName, /// Nym address that the alias is pointing to #[clap(long)] - pub nym_address: String, + pub nym_address: Recipient, #[clap(long)] pub signature: MessageSignature, @@ -23,9 +24,6 @@ pub struct Args { /// Deposit to be made to the service provider directory, in curent DENOMINATION (e.g. 'unym') #[clap(long)] pub deposit: u128, - - #[clap(long)] - pub identity_key: String, } pub async fn register(args: Args, client: SigningClient) -> Result<(), NyxdError> { @@ -34,12 +32,12 @@ pub async fn register(args: Args, client: SigningClient) -> Result<(), NyxdError args.name, args.nym_address ); - let name = NymName::new(&args.name).expect("invalid name"); - let address = Address::new(&args.nym_address); + let address = Address::new(&args.nym_address.to_string()).expect("invalid address"); + let identity_key = address.client_id().to_string(); let name = NameDetails { - name, + name: args.name, address, - identity_key: args.identity_key, + identity_key, }; let denom = client.current_chain_details().mix_denom.base.as_str(); diff --git a/common/commands/src/validator/mixnet/operators/name/register_sign_payload.rs b/common/commands/src/validator/mixnet/operators/name/register_sign_payload.rs new file mode 100644 index 0000000000..e293f23c2e --- /dev/null +++ b/common/commands/src/validator/mixnet/operators/name/register_sign_payload.rs @@ -0,0 +1,65 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + context::SigningClient, + utils::{account_id_to_cw_addr, DataWrapper}, +}; + +use clap::Parser; +use cosmwasm_std::Coin; + +use nym_bin_common::output_format::OutputFormat; +use nym_name_service_common::{ + signing_types::construct_name_register_sign_payload, Address, NymName, +}; +use nym_sphinx::addressing::clients::Recipient; +use nym_validator_client::nyxd::{contract_traits::NameServiceQueryClient, error::NyxdError}; + +#[derive(Debug, Parser)] +pub struct Args { + // The name to register. + #[arg(long)] + pub name: NymName, + + /// The nym address the name should point to. + #[arg(long)] + pub nym_address: Recipient, + + #[arg(long)] + pub amount: u128, + + #[arg(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +pub async fn create_payload(args: Args, client: SigningClient) -> Result<(), NyxdError> { + let address = Address::new(&args.nym_address.to_string()).expect("invalid address"); + let identity_key = address.client_id().to_string(); + let name = nym_name_service_common::NameDetails { + name: args.name, + address, + identity_key, + }; + + let denom = client.current_chain_details().mix_denom.base.as_str(); + let deposit = Coin::new(args.amount, denom); + + let nonce = match client.get_name_signing_nonce(&client.address()).await { + Ok(nonce) => nonce, + Err(err) => { + eprint!( + "failed to query for the signing nonce of {}: {err}", + client.address() + ); + return Err(err); + } + }; + + let address = account_id_to_cw_addr(&client.address()); + let payload = construct_name_register_sign_payload(nonce, address, deposit, name); + let wrapper = DataWrapper::new(payload.to_base58_string().unwrap()); + println!("{}", args.output.format(&wrapper)); + + Ok(()) +} diff --git a/common/commands/src/validator/mixnet/operators/service/announce.rs b/common/commands/src/validator/mixnet/operators/service/announce.rs index a3e86bc674..b61652254e 100644 --- a/common/commands/src/validator/mixnet/operators/service/announce.rs +++ b/common/commands/src/validator/mixnet/operators/service/announce.rs @@ -2,7 +2,7 @@ use clap::Parser; use log::info; use nym_contracts_common::signing::MessageSignature; use nym_service_provider_directory_common::{Coin, NymAddress, ServiceDetails, ServiceType}; -use nym_validator_client::nyxd::traits::SpDirectorySigningClient; +use nym_validator_client::nyxd::contract_traits::SpDirectorySigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/mixnet/operators/service/announce_sign_payload.rs b/common/commands/src/validator/mixnet/operators/service/announce_sign_payload.rs index f8ac587f9b..f3dc4e5e66 100644 --- a/common/commands/src/validator/mixnet/operators/service/announce_sign_payload.rs +++ b/common/commands/src/validator/mixnet/operators/service/announce_sign_payload.rs @@ -15,7 +15,7 @@ use nym_service_provider_directory_common::{ ServiceType::NetworkRequester, }; use nym_sphinx::addressing::clients::Recipient; -use nym_validator_client::nyxd::traits::SpDirectoryQueryClient; +use nym_validator_client::nyxd::contract_traits::SpDirectoryQueryClient; #[derive(Debug, Parser)] pub struct Args { @@ -42,7 +42,7 @@ pub async fn create_payload(args: Args, client: SigningClient) { let denom = client.current_chain_details().mix_denom.base.as_str(); let deposit = Coin::new(args.amount, denom); - let nonce = match client.get_service_signing_nonce(client.address()).await { + let nonce = match client.get_service_signing_nonce(&client.address()).await { Ok(nonce) => nonce, Err(err) => { eprint!( @@ -53,7 +53,7 @@ pub async fn create_payload(args: Args, client: SigningClient) { } }; - let address = account_id_to_cw_addr(client.address()); + let address = account_id_to_cw_addr(&client.address()); let payload = construct_service_provider_announce_sign_payload(nonce, address, deposit, service); let wrapper = DataWrapper::new(payload.to_base58_string().unwrap()); diff --git a/common/commands/src/validator/mixnet/operators/service/delete.rs b/common/commands/src/validator/mixnet/operators/service/delete.rs index eb86f4028f..3d0ca4a676 100644 --- a/common/commands/src/validator/mixnet/operators/service/delete.rs +++ b/common/commands/src/validator/mixnet/operators/service/delete.rs @@ -1,7 +1,7 @@ use clap::Parser; use log::info; use nym_service_provider_directory_common::ServiceId; -use nym_validator_client::nyxd::traits::SpDirectorySigningClient; +use nym_validator_client::nyxd::contract_traits::SpDirectorySigningClient; use crate::context::SigningClient; diff --git a/common/commands/src/validator/transactions/get_transaction.rs b/common/commands/src/validator/transactions/get_transaction.rs index 6ad1bbbd2f..62d829d52b 100644 --- a/common/commands/src/validator/transactions/get_transaction.rs +++ b/common/commands/src/validator/transactions/get_transaction.rs @@ -7,6 +7,7 @@ use std::str::FromStr; use crate::context::QueryClient; use crate::utils::show_error; use cosmrs::tendermint::Hash; +use nym_validator_client::nyxd::CosmWasmClient; use serde_json::json; #[derive(Debug, Parser)] diff --git a/common/commands/src/validator/transactions/query_transactions.rs b/common/commands/src/validator/transactions/query_transactions.rs index b8b341ec57..f109d9a07a 100644 --- a/common/commands/src/validator/transactions/query_transactions.rs +++ b/common/commands/src/validator/transactions/query_transactions.rs @@ -5,6 +5,7 @@ use std::str::FromStr; use clap::Parser; use cosmrs::rpc::query::Query; +use nym_validator_client::nyxd::CosmWasmClient; use serde_json::json; use crate::context::QueryClient; diff --git a/common/commands/src/validator/vesting/balance.rs b/common/commands/src/validator/vesting/balance.rs index d5f5af20fe..ff8e2ee28c 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::{traits::VestingQueryClient, Coin}; +use nym_validator_client::nyxd::{contract_traits::VestingQueryClient, Coin, CosmWasmClient}; 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 524cdf67e1..151350bf2c 100644 --- a/common/commands/src/validator/vesting/create_vesting_schedule.rs +++ b/common/commands/src/validator/vesting/create_vesting_schedule.rs @@ -8,10 +8,10 @@ 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::contract_traits::VestingSigningClient; use nym_validator_client::nyxd::AccountId; use nym_validator_client::nyxd::{CosmosCoin, Denom}; -use nym_vesting_contract_common::messages::VestingSpecification; +use nym_vesting_contract_common::types::VestingSpecification; use nym_vesting_contract_common::PledgeCap; use crate::context::SigningClient; diff --git a/common/commands/src/validator/vesting/query_vesting_schedule.rs b/common/commands/src/validator/vesting/query_vesting_schedule.rs index 9c8facfb70..0baca858f1 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::{traits::VestingQueryClient, Coin}; +use nym_validator_client::nyxd::{contract_traits::VestingQueryClient, Coin, CosmWasmClient}; 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 a85e960a1a..2c2bf254b9 100644 --- a/common/commands/src/validator/vesting/withdraw_vested.rs +++ b/common/commands/src/validator/vesting/withdraw_vested.rs @@ -5,8 +5,8 @@ use clap::Parser; use log::info; use nym_validator_client::nyxd::{ - traits::{VestingQueryClient, VestingSigningClient}, - Coin, + contract_traits::{VestingQueryClient, VestingSigningClient}, + Coin, CosmWasmClient, }; use crate::context::SigningClient; @@ -39,7 +39,7 @@ pub async fn execute(args: Args, client: SigningClient) { .await .unwrap_or_else(|_| Coin::new(0u128, denom)); let liquid_account_balance = client - .get_balance(account_id, denom.to_string()) + .get_balance(&account_id, denom.to_string()) .await .unwrap_or(None) .unwrap_or_else(|| Coin::new(0u128, denom)); @@ -97,7 +97,7 @@ pub async fn execute(args: Args, client: SigningClient) { .unwrap_or_else(|_| Coin::new(0u128, denom)); let liquid_account_balance = client - .get_balance(account_id, denom.to_string()) + .get_balance(&account_id, denom.to_string()) .await .unwrap_or(None) .unwrap_or_else(|| Coin::new(0u128, denom)); diff --git a/common/config/Cargo.toml b/common/config/Cargo.toml index ed894e808b..d7ca649e60 100644 --- a/common/config/Cargo.toml +++ b/common/config/Cargo.toml @@ -12,7 +12,7 @@ handlebars = "3.5.5" log = { workspace = true } serde = { workspace = true, features = ["derive"] } toml = "0.7.4" -url = "2.2" +url = { workspace = true } nym-network-defaults = { path = "../network-defaults" } diff --git a/common/config/src/lib.rs b/common/config/src/lib.rs index fe91fc3d6d..a751ba2414 100644 --- a/common/config/src/lib.rs +++ b/common/config/src/lib.rs @@ -17,6 +17,7 @@ pub mod helpers; pub mod legacy_helpers; pub const NYM_DIR: &str = ".nym"; +pub const DEFAULT_NYM_APIS_DIR: &str = "nym-api"; pub const DEFAULT_CONFIG_DIR: &str = "config"; pub const DEFAULT_DATA_DIR: &str = "data"; pub const DEFAULT_CONFIG_FILENAME: &str = "config.toml"; diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/Cargo.toml b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/Cargo.toml index fc5e1b43d7..90cf90ba17 100644 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/Cargo.toml @@ -7,6 +7,9 @@ edition = "2021" [dependencies] cosmwasm-std = { workspace = true } -schemars = "0.8" -serde = { version = "1.0.103", default-features = false, features = ["derive"] } +cosmwasm-schema = { workspace = true } +cw2 = { workspace = true, optional = true } nym-multisig-contract-common = { path = "../multisig-contract" } + +[features] +schema = ["cw2"] \ No newline at end of file diff --git a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/deposit.rs b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/deposit.rs index d0572c3c91..c0e3a4acd3 100644 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/deposit.rs +++ b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/deposit.rs @@ -1,10 +1,9 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; +use cosmwasm_schema::cw_serde; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[cw_serde] pub struct DepositData { deposit_info: String, identity_key: String, 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 8fffcc81eb..8387ac4ec5 100644 --- a/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/coconut-bandwidth-contract/src/msg.rs @@ -1,39 +1,41 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use cosmwasm_std::Coin; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - use crate::{deposit::DepositData, spend_credential::SpendCredentialData}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Coin; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[cfg(feature = "schema")] +use crate::spend_credential::{PagedSpendCredentialResponse, SpendCredentialResponse}; +#[cfg(feature = "schema")] +use cosmwasm_schema::QueryResponses; + +#[cw_serde] pub struct InstantiateMsg { pub multisig_addr: String, pub pool_addr: String, pub mix_denom: String, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub enum ExecuteMsg { DepositFunds { data: DepositData }, SpendCredential { data: SpendCredentialData }, ReleaseFunds { funds: Coin }, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] +#[cfg_attr(feature = "schema", derive(QueryResponses))] pub enum QueryMsg { - GetSpentCredential { - blinded_serial_number: String, - }, + #[cfg_attr(feature = "schema", returns(SpendCredentialResponse))] + GetSpentCredential { blinded_serial_number: String }, + + #[cfg_attr(feature = "schema", returns(PagedSpendCredentialResponse))] GetAllSpentCredentials { limit: Option, start_after: Option, }, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct MigrateMsg {} 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 7cb47dc73b..835d6175de 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 @@ -1,14 +1,13 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use cosmwasm_schema::cw_serde; use cosmwasm_std::{from_binary, to_binary, Addr, Coin, CosmosMsg, StdResult, WasmMsg}; use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; use crate::msg::ExecuteMsg; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[cw_serde] pub struct SpendCredentialData { funds: Coin, blinded_serial_number: String, @@ -37,13 +36,16 @@ impl SpendCredentialData { } } -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[cw_serde] +#[derive(Copy)] pub enum SpendCredentialStatus { + #[serde(alias = "InProgress")] InProgress, + #[serde(alias = "Spent")] Spent, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[cw_serde] pub struct SpendCredential { funds: Coin, blinded_serial_number: String, @@ -74,10 +76,12 @@ impl SpendCredential { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[cw_serde] pub struct PagedSpendCredentialResponse { pub spend_credentials: Vec, pub per_page: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } @@ -95,7 +99,7 @@ impl PagedSpendCredentialResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[cw_serde] pub struct SpendCredentialResponse { pub spend_credential: Option, } diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/Cargo.toml b/common/cosmwasm-smart-contracts/coconut-dkg/Cargo.toml index 1f6d6c3f3f..271d5b5bb9 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/Cargo.toml +++ b/common/cosmwasm-smart-contracts/coconut-dkg/Cargo.toml @@ -6,10 +6,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } cw-utils = { workspace = true } -schemars = "0.8" -serde = { version = "1.0.103", default-features = false, features = ["derive"] } contracts-common = { path = "../contracts-common", package = "nym-contracts-common" } nym-multisig-contract-common = { path = "../multisig-contract" } + +[features] +schema = [] \ No newline at end of file diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/dealer.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/dealer.rs index 7b24be4869..3547345fa1 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/dealer.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/dealer.rs @@ -2,11 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, NodeIndex}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::Addr; -use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct DealerDetails { pub address: Addr, pub bte_public_key_with_proof: EncodedBTEPublicKeyWithProof, @@ -14,8 +13,8 @@ pub struct DealerDetails { pub assigned_index: NodeIndex, } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "snake_case")] +#[cw_serde] +#[derive(Copy)] pub enum DealerType { Current, Past, @@ -28,8 +27,7 @@ impl DealerType { } } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct DealerDetailsResponse { pub details: Option, pub dealer_type: DealerType, @@ -44,11 +42,12 @@ impl DealerDetailsResponse { } } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct PagedDealerResponse { pub dealers: Vec, pub per_page: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } @@ -66,8 +65,7 @@ impl PagedDealerResponse { } } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct ContractDealing { pub dealing: ContractSafeBytes, pub dealer: Addr, @@ -79,11 +77,12 @@ impl ContractDealing { } } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct PagedDealingsResponse { pub dealings: Vec, pub per_page: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs index 62891e8fe7..b986b77f41 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/msg.rs @@ -3,11 +3,19 @@ use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, EpochId, TimeConfiguration}; use crate::verification_key::VerificationKeyShare; +use cosmwasm_schema::cw_serde; use cosmwasm_std::Addr; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +#[cfg(feature = "schema")] +use crate::{ + dealer::{DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse}, + types::{Epoch, InitialReplacementData}, + verification_key::PagedVKSharesResponse, +}; +#[cfg(feature = "schema")] +use cosmwasm_schema::QueryResponses; + +#[cw_serde] pub struct InstantiateMsg { pub group_addr: String, pub multisig_addr: String, @@ -15,8 +23,7 @@ pub struct InstantiateMsg { pub mix_denom: String, } -#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub enum ExecuteMsg { RegisterDealer { bte_key_with_proof: EncodedBTEPublicKeyWithProof, @@ -35,6 +42,7 @@ pub enum ExecuteMsg { }, VerifyVerificationKeyShare { + // TODO: this should be using a String... owner: Addr, resharing: bool, }, @@ -44,28 +52,41 @@ pub enum ExecuteMsg { AdvanceEpochState {}, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] +#[cfg_attr(feature = "schema", derive(QueryResponses))] pub enum QueryMsg { + #[cfg_attr(feature = "schema", returns(Epoch))] GetCurrentEpochState {}, + + #[cfg_attr(feature = "schema", returns(u64))] GetCurrentEpochThreshold {}, + + #[cfg_attr(feature = "schema", returns(Option))] GetInitialDealers {}, - GetDealerDetails { - dealer_address: String, - }, + + #[cfg_attr(feature = "schema", returns(DealerDetailsResponse))] + GetDealerDetails { dealer_address: String }, + + #[cfg_attr(feature = "schema", returns(PagedDealerResponse))] GetCurrentDealers { limit: Option, start_after: Option, }, + + #[cfg_attr(feature = "schema", returns(PagedDealerResponse))] GetPastDealers { limit: Option, start_after: Option, }, + + #[cfg_attr(feature = "schema", returns(PagedDealingsResponse))] GetDealing { idx: u64, limit: Option, start_after: Option, }, + + #[cfg_attr(feature = "schema", returns(PagedVKSharesResponse))] GetVerificationKeys { epoch_id: EpochId, limit: Option, @@ -73,6 +94,5 @@ pub enum QueryMsg { }, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct MigrateMsg {} diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs index ee0b854eb9..b01f1101ab 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/types.rs @@ -1,8 +1,7 @@ -// Copyright 2022 - Nym Technologies SA +// Copyright 2022-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; +use cosmwasm_schema::cw_serde; use std::fmt::{Display, Formatter}; use std::str::FromStr; @@ -18,15 +17,14 @@ pub type EpochId = u64; // 2 public attributes, 2 private attributes, 1 fixed for coconut credential pub const TOTAL_DEALINGS: usize = 2 + 2 + 1; -#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)] +#[cw_serde] pub struct InitialReplacementData { pub initial_dealers: Vec, pub initial_height: u64, } -#[derive( - Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, JsonSchema, -)] +#[cw_serde] +#[derive(Copy)] pub struct TimeConfiguration { // The time sign-up is open for dealers to join pub public_key_submission_time_secs: u64, @@ -75,8 +73,8 @@ impl Default for TimeConfiguration { } } -#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] -#[serde(rename_all = "snake_case")] +#[cw_serde] +#[derive(Copy, Default)] pub struct Epoch { pub state: EpochState, pub epoch_id: EpochId, @@ -155,8 +153,8 @@ impl Epoch { // 8. InProgress -> all receivers have all their secrets derived and all is good // // Note: It's important that the variant ordering is not changed otherwise it would mess up the derived `PartialOrd` -#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] -#[serde(rename_all = "snake_case")] +#[cw_serde] +#[derive(Copy)] pub enum EpochState { PublicKeySubmission { resharing: bool }, DealingExchange { resharing: bool }, diff --git a/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs b/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs index 9b8338b6f1..7a3d02f2e8 100644 --- a/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs +++ b/common/cosmwasm-smart-contracts/coconut-dkg/src/verification_key.rs @@ -3,14 +3,14 @@ use crate::msg::ExecuteMsg; use crate::types::{EpochId, NodeIndex}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::{from_binary, to_binary, Addr, CosmosMsg, StdResult, Timestamp, WasmMsg}; use cw_utils::Expiration; use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg; -use serde::{Deserialize, Serialize}; pub type VerificationKeyShare = String; -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[cw_serde] pub struct ContractVKShare { pub share: VerificationKeyShare, pub announce_address: String, @@ -20,11 +20,12 @@ pub struct ContractVKShare { pub verified: bool, } -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct PagedVKSharesResponse { pub shares: Vec, pub per_page: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } diff --git a/common/cosmwasm-smart-contracts/contracts-common/Cargo.toml b/common/cosmwasm-smart-contracts/contracts-common/Cargo.toml index 512c3edc6b..fc55402be0 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/Cargo.toml +++ b/common/cosmwasm-smart-contracts/contracts-common/Cargo.toml @@ -10,9 +10,10 @@ repository = { workspace = true } [dependencies] bs58 = "0.4.0" cosmwasm-std = { workspace = true } +cosmwasm-schema = { workspace = true } schemars = "0.8" -serde = { version = "1.0", features = ["derive"] } -thiserror = "1" +serde = { workspace = true, features = ["derive"] } +thiserror = { workspace = true } [dev-dependencies] -serde_json = "1.0.0" +serde_json = { workspace = true } diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/dealings.rs b/common/cosmwasm-smart-contracts/contracts-common/src/dealings.rs index 87251d38e7..8c79ccbcfc 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/src/dealings.rs +++ b/common/cosmwasm-smart-contracts/contracts-common/src/dealings.rs @@ -1,8 +1,6 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -#[cfg(feature = "dkg")] -use nym_dkg::{error::DkgError, Dealing}; use schemars::JsonSchema; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{Display, Formatter}; diff --git a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs index 858907bafa..4dca6b173b 100644 --- a/common/cosmwasm-smart-contracts/contracts-common/src/types.rs +++ b/common/cosmwasm-smart-contracts/contracts-common/src/types.rs @@ -1,16 +1,17 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use cosmwasm_schema::cw_serde; use cosmwasm_std::Decimal; use cosmwasm_std::Uint128; -use schemars::JsonSchema; use serde::de::Error; -use serde::{Deserialize, Deserializer, Serialize}; +use serde::{Deserialize, Deserializer}; use std::fmt::{self, Display, Formatter}; use std::ops::Mul; use std::str::FromStr; use thiserror::Error; +/// Ed25519 public key strinfified into base58. pub type IdentityKey = String; pub type IdentityKeyRef<'a> = &'a str; @@ -32,9 +33,8 @@ pub enum ContractsCommonError { /// Percent represents a value between 0 and 100% /// (i.e. between 0.0 and 1.0) -#[derive( - Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Serialize, Deserialize, JsonSchema, -)] +#[cw_serde] +#[derive(Copy, Default, PartialOrd)] pub struct Percent(#[serde(deserialize_with = "de_decimal_percent")] Decimal); impl Percent { @@ -129,7 +129,7 @@ where // TODO: there's no reason this couldn't be used for proper binaries, but in that case // perhaps the struct should get renamed and moved to a "more" common crate -#[derive(Debug, Serialize, Deserialize)] +#[cw_serde] pub struct ContractBuildInformation { // VERGEN_BUILD_TIMESTAMP /// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`. diff --git a/common/cosmwasm-smart-contracts/ephemera/Cargo.toml b/common/cosmwasm-smart-contracts/ephemera/Cargo.toml new file mode 100644 index 0000000000..fda88a18f5 --- /dev/null +++ b/common/cosmwasm-smart-contracts/ephemera/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "nym-ephemera-common" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +cw-utils = { workspace = true } + +contracts-common = { path = "../contracts-common", package = "nym-contracts-common" } + +[features] +schema = [] \ No newline at end of file diff --git a/common/cosmwasm-smart-contracts/ephemera/src/lib.rs b/common/cosmwasm-smart-contracts/ephemera/src/lib.rs new file mode 100644 index 0000000000..f53ac15b7e --- /dev/null +++ b/common/cosmwasm-smart-contracts/ephemera/src/lib.rs @@ -0,0 +1,3 @@ +pub mod msg; +pub mod peers; +pub mod types; diff --git a/common/cosmwasm-smart-contracts/ephemera/src/msg.rs b/common/cosmwasm-smart-contracts/ephemera/src/msg.rs new file mode 100644 index 0000000000..5024a73b0b --- /dev/null +++ b/common/cosmwasm-smart-contracts/ephemera/src/msg.rs @@ -0,0 +1,33 @@ +// Copyright 2021 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(feature = "schema")] +use crate::peers::PagedPeerResponse; +use crate::types::JsonPeerInfo; +use cosmwasm_schema::cw_serde; +#[cfg(feature = "schema")] +use cosmwasm_schema::QueryResponses; + +#[cw_serde] +pub struct InstantiateMsg { + pub group_addr: String, + pub mix_denom: String, +} + +#[cw_serde] +pub enum ExecuteMsg { + RegisterPeer { peer_info: JsonPeerInfo }, +} + +#[cw_serde] +#[cfg_attr(feature = "schema", derive(QueryResponses))] +pub enum QueryMsg { + #[cfg_attr(feature = "schema", returns(PagedPeerResponse))] + GetPeers { + limit: Option, + start_after: Option, + }, +} + +#[cw_serde] +pub struct MigrateMsg {} diff --git a/common/cosmwasm-smart-contracts/ephemera/src/peers.rs b/common/cosmwasm-smart-contracts/ephemera/src/peers.rs new file mode 100644 index 0000000000..d2eafcad08 --- /dev/null +++ b/common/cosmwasm-smart-contracts/ephemera/src/peers.rs @@ -0,0 +1,25 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::types::JsonPeerInfo; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Addr; + +#[cw_serde] +pub struct PagedPeerResponse { + pub peers: Vec, + pub per_page: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. + pub start_next_after: Option, +} + +impl PagedPeerResponse { + pub fn new(peers: Vec, per_page: usize, start_next_after: Option) -> Self { + PagedPeerResponse { + peers, + per_page, + start_next_after, + } + } +} diff --git a/common/cosmwasm-smart-contracts/ephemera/src/types.rs b/common/cosmwasm-smart-contracts/ephemera/src/types.rs new file mode 100644 index 0000000000..cc68e6dc54 --- /dev/null +++ b/common/cosmwasm-smart-contracts/ephemera/src/types.rs @@ -0,0 +1,29 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Addr; + +#[cw_serde] +pub struct JsonPeerInfo { + /// The cosmos address of the peer, used in interacting with the chain. + pub cosmos_address: Addr, + /// The TCP/IP address of the peer. + /// Expected formats: + /// 1. `:` + /// 2. `/ip4//tcp/` - this is the format used by libp2p multiaddr + pub ip_address: String, + ///Serialized public key. + pub public_key: String, +} + +impl JsonPeerInfo { + #[must_use] + pub fn new(cosmos_address: Addr, ip_address: String, public_key: String) -> Self { + Self { + cosmos_address, + ip_address, + public_key, + } + } +} diff --git a/common/cosmwasm-smart-contracts/group-contract/src/msg.rs b/common/cosmwasm-smart-contracts/group-contract/src/msg.rs index 3f95227492..84674946b1 100644 --- a/common/cosmwasm-smart-contracts/group-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/group-contract/src/msg.rs @@ -46,3 +46,5 @@ pub enum QueryMsg { #[returns(cw_controllers::HooksResponse)] Hooks {}, } +#[cw_serde] +pub struct MigrateMsg {} diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml b/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml index 736cedf911..e82be77e29 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/mixnet-contract/Cargo.toml @@ -11,20 +11,22 @@ repository = { workspace = true } [dependencies] bs58 = "0.4.0" cosmwasm-std = { workspace = true } -serde = { version = "1.0", features = ["derive"] } +cosmwasm-schema = { workspace = true } +cw2 = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"] } serde_repr = "0.1" + +# we still have to preserve that import for `JsonSchema` for `Layer` type (since we can't use cw_serde macro due to custom serde impl) schemars = "0.8" thiserror = "1.0" contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" } -# use 0.4.1 as that's the version used by cosmwasm-std 1.0.0 -# (and ideally we don't want to pull the same dependency twice) -serde-json-wasm = "=0.4.1" +serde-json-wasm = { workspace = true } humantime-serde = "1.1.1" # TO CHECK WHETHER STILL NEEDED: log = { workspace = true } time = { version = "0.3.6", features = ["parsing", "formatting"] } -ts-rs = { version = "6.1.2", optional = true } +ts-rs = { workspace = true, optional = true } [dev-dependencies] rand_chacha = "0.3" @@ -33,4 +35,5 @@ time = { version = "0.3.5", features = ["serde", "macros"] } [features] default = [] contract-testing = [] +schema = ["cw2"] generate-ts = ['ts-rs'] diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs index 55294498b8..3a7cdd7702 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/delegation.rs @@ -1,15 +1,11 @@ // Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -// due to code generated by JsonSchema -#![allow(clippy::field_reassign_with_default)] - use crate::constants::TOKEN_SUPPLY; use crate::helpers::IntoBaseDecimal; use crate::{Addr, MixId}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, Decimal, StdResult}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; // just use a string representation of those so that we wouldn't need to bother with decoding bytes // and trying to figure out whether they're valid, etc @@ -37,7 +33,9 @@ pub fn generate_owner_storage_subkey( } } -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] +/// Information about tokens being delegated towards given mixnode in order to accrue rewards +/// with their work. +#[cw_serde] pub struct Delegation { /// Address of the owner of this delegation. pub owner: Addr, @@ -114,9 +112,13 @@ impl Delegation { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing paged list of all delegations made towards particular mixnode. +#[cw_serde] pub struct PagedMixNodeDelegationsResponse { + /// Each individual delegation made. pub delegations: Vec, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } @@ -129,9 +131,13 @@ impl PagedMixNodeDelegationsResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing paged list of all delegations made by the particular address. +#[cw_serde] pub struct PagedDelegatorDelegationsResponse { + /// Each individual delegation made. pub delegations: Vec, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option<(MixId, OwnerProxySubKey)>, } @@ -147,9 +153,13 @@ impl PagedDelegatorDelegationsResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing delegation details. +#[cw_serde] pub struct MixNodeDelegationResponse { + /// If the delegation exists, this field contains its detailed information. pub delegation: Option, + + /// Flag indicating whether the node towards which the delegation was made is still bonded in the network. pub mixnode_still_bonded: bool, } @@ -162,9 +172,13 @@ impl MixNodeDelegationResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing paged list of all delegations currently active. +#[cw_serde] pub struct PagedAllDelegationsResponse { + /// Each individual delegation made. pub delegations: Vec, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/error.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/error.rs index 0b572183ac..6007884cc8 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/error.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/error.rs @@ -165,6 +165,9 @@ pub enum MixnetContractError { #[error("Family with head {head} does not exist!")] FamilyDoesNotExist { head: String }, + #[error("Family with label {label} does not exist!")] + FamilyLabelDoesNotExist { label: String }, + #[error("Family with label '{0}' already exists")] FamilyWithLabelExists(String), diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/families.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/families.rs index 122e1690f6..5b44483c83 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/families.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/families.rs @@ -1,22 +1,34 @@ +// Copyright 2022-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + use crate::{IdentityKey, IdentityKeyRef}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::Addr; use schemars::JsonSchema; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{Display, Formatter}; use std::str::FromStr; +/// A group of mixnodes associated with particular staking entity. +/// When defined all nodes belonging to the same family will be prioritised to be put onto the same layer. #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/NodeFamily.ts") )] -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, JsonSchema)] +#[cw_serde] pub struct Family { + /// Owner of this family. head: FamilyHead, + + /// Optional proxy (i.e. vesting contract address) used when creating the family. proxy: Option, + + /// Human readable label for this family. label: String, } +/// Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58). #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( feature = "generate-ts", @@ -62,8 +74,8 @@ impl Display for FamilyHead { } impl FamilyHead { - pub fn new(identity: IdentityKeyRef<'_>) -> Self { - FamilyHead(identity.to_string()) + pub fn new>(identity: S) -> Self { + FamilyHead(identity.into()) } pub fn identity(&self) -> IdentityKeyRef<'_> { @@ -99,6 +111,66 @@ impl Family { } } +/// Response containing paged list of all families registered in the contract. +#[cw_serde] +pub struct PagedFamiliesResponse { + /// The families registered in the contract. + pub families: Vec, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. + pub start_next_after: Option, +} + +/// Response containing paged list of all family members (of ALL families) registered in the contract. +#[cw_serde] +pub struct PagedMembersResponse { + /// The members alongside their family heads. + pub members: Vec<(IdentityKey, FamilyHead)>, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. + pub start_next_after: Option, +} + +/// Response containing family information. +#[cw_serde] +pub struct FamilyByHeadResponse { + /// The family head used for the query. + pub head: FamilyHead, + + /// If applicable, the family associated with the provided head. + pub family: Option, +} + +/// Response containing family information. +#[cw_serde] +pub struct FamilyByLabelResponse { + /// The family label used for the query. + pub label: String, + + /// If applicable, the family associated with the provided label. + pub family: Option, +} + +/// Response containing family members information. +#[cw_serde] +pub struct FamilyMembersByHeadResponse { + /// The family head used for the query. + pub head: FamilyHead, + + /// All members belonging to the specified family. + pub members: Vec, +} + +/// Response containing family members information. +#[cw_serde] +pub struct FamilyMembersByLabelResponse { + /// The family label used for the query. + pub label: String, + + /// All members belonging to the specified family. + pub members: Vec, +} + #[cfg(test)] mod tests { use super::*; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs index c312d42bf7..fd29090374 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/gateway.rs @@ -1,34 +1,56 @@ // Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -// due to code generated by JsonSchema -#![allow(clippy::field_reassign_with_default)] - use crate::{IdentityKey, SphinxKey}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Coin}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::fmt::Display; -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, PartialOrd, Serialize, JsonSchema)] +/// Information provided by the node operator during bonding that are used to allow other entities to use the services of this node. +#[cw_serde] +#[derive(PartialOrd)] pub struct Gateway { + /// Network address of this gateway, for example 1.1.1.1 or foo.gateway.com pub host: String, + + /// Port used by this gateway for listening for mix packets. pub mix_port: u16, + + /// Port used by this gateway for listening for client requests. pub clients_port: u16, + + /// The physical, self-reported, location of this gateway. + // this field should be deprecated in favour of externally hosted information, like the mixnodes'. pub location: String, + + /// Base58-encoded x25519 public key used for sphinx key derivation. pub sphinx_key: SphinxKey, + /// Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients pub identity_key: IdentityKey, + + /// The self-reported semver version of this gateway. pub version: String, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Basic gateway information provided by the node operator. +#[cw_serde] pub struct GatewayBond { + /// Original amount pledged by the operator of this node. pub pledge_amount: Coin, + + /// Address of the owner of this gateway. pub owner: Addr, + + /// Block height at which this gateway has been bonded. pub block_height: u64, + + /// Information provided by the operator for the purposes of bonding. pub gateway: Gateway, + + /// Entity who bonded this gateway on behalf of the owner. + /// If exists, it's most likely the address of the vesting contract. pub proxy: Option, } @@ -117,7 +139,7 @@ impl Display for GatewayBond { feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/GatewayConfigUpdate.ts") )] -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[cw_serde] pub struct GatewayConfigUpdate { pub host: String, pub mix_port: u16, @@ -132,10 +154,17 @@ impl GatewayConfigUpdate { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing paged list of all gateway bonds in the contract. +#[cw_serde] pub struct PagedGatewayResponse { + /// The gateway bond information present in the contract. pub nodes: Vec, + + /// Maximum number of entries that could be included in a response. `per_page <= nodes.len()` + // this field is rather redundant and should be deprecated. pub per_page: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } @@ -153,15 +182,23 @@ impl PagedGatewayResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing details of a gateway belonging to the particular owner. +#[cw_serde] pub struct GatewayOwnershipResponse { + /// Validated address of the gateway owner. pub address: Addr, + + /// If the provided address owns a gateway, this field contains its details. pub gateway: Option, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing details of a gateway with the provided identity key. +#[cw_serde] pub struct GatewayBondResponse { + /// The identity key (base58-encoded ed25519 public key) of the gateway. pub identity: IdentityKey, + + /// If there exists a gateway with the provided identity key, this field contains its details. 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 5afdbd827e..6352c138c6 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/interval.rs @@ -2,20 +2,20 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::MixnetContractError; -use crate::pending_events::{PendingEpochEvent, PendingIntervalEvent}; -use crate::{ - EpochEventId, EpochId, IntervalEventId, IntervalId, MixId, PendingEpochEventData, - PendingIntervalEventData, -}; +use crate::MixId; +use cosmwasm_schema::cw_serde; +use cosmwasm_schema::schemars::gen::SchemaGenerator; +use cosmwasm_schema::schemars::schema::{InstanceType, Schema, SchemaObject}; +use cosmwasm_schema::schemars::JsonSchema; use cosmwasm_std::{Addr, Env}; -use schemars::gen::SchemaGenerator; -use schemars::schema::{InstanceType, Schema, SchemaObject}; -use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; use std::time::Duration; use time::OffsetDateTime; +pub type EpochId = u32; +pub type IntervalId = u32; + // internally, since version 0.3.6, time uses deserialize_any for deserialization, which can't be handled // by serde wasm. We could just downgrade to 0.3.5 and call it a day, but then it would break // when we decided to upgrade it at some point in the future. And then it would have been more problematic @@ -64,12 +64,15 @@ pub(crate) mod string_rfc3339_offset_date_time { } } -#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] +/// The status of the current rewarding epoch. +#[cw_serde] pub struct EpochStatus { // TODO: introduce mechanism to allow another validator to take over if no progress has been made in X blocks / Y seconds /// Specifies either, which validator is currently performing progression into the following epoch (if the epoch is currently being progressed), /// or which validator was responsible for progressing into the current epoch (if the epoch is currently in progress) pub being_advanced_by: Addr, + + /// The concrete state of the epoch. pub state: EpochState, } @@ -150,27 +153,35 @@ impl EpochStatus { } } -#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq)] +/// The state of the current rewarding epoch. +#[cw_serde] +#[derive(Copy)] pub enum EpochState { /// Represents the state of an epoch that's in progress (well, duh.) /// All actions are allowed to be issued. + #[serde(alias = "InProgress")] InProgress, /// Represents the state of an epoch when the rewarding entity has been decided on, /// and the mixnodes are in the process of being rewarded for their work in this epoch. + #[serde(alias = "Rewarding")] Rewarding { + /// The id of the last node that has already received its rewards. last_rewarded: MixId, + /// The id of the last node that's going to be rewarded before progressing into the next state. final_node_id: MixId, // total_rewarded: u32, }, /// Represents the state of an epoch when all mixnodes have already been rewarded for their work in this epoch /// and all issued actions should now get resolved before being allowed to advance into the next epoch. + #[serde(alias = "ReconcilingEvents")] ReconcilingEvents, /// Represents the state of an epoch when all mixnodes have already been rewarded for their work in this epoch, /// all issued actions got resolved and the epoch should now be advanced whilst assigning new rewarded set. + #[serde(alias = "AdvancingEpoch")] AdvancingEpoch, } @@ -191,6 +202,7 @@ impl Display for EpochState { } } +/// Specification of a rewarding interval. #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( feature = "generate-ts", @@ -198,7 +210,10 @@ impl Display for EpochState { )] #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct Interval { + /// Monotonously increasing id of this interval. id: IntervalId, + + /// Number of epochs in this interval. epochs_in_interval: u32, // TODO add a better TS type generation @@ -207,10 +222,17 @@ pub struct Interval { // note: the `ts-rs failed to parse this attribute. It will be ignored.` warning emitted during // compilation is fine (I guess). `ts-rs` can't handle `with` serde attribute, but that's okay // since we explicitly specified this field should correspond to typescript's string + /// The timestamp indicating the start of the current rewarding epoch. current_epoch_start: OffsetDateTime, + + /// Monotonously increasing id of the current epoch in this interval. current_epoch_id: EpochId, + + /// The duration of all epochs in this interval. #[cfg_attr(feature = "generate-ts", ts(type = "{ secs: number; nanos: number; }"))] epoch_length: Duration, + + /// The total amount of elapsed epochs since the first epoch of the first interval. total_elapsed_epochs: EpochId, } @@ -455,11 +477,19 @@ impl Display for Interval { } } -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)] +/// Information about the current rewarding interval. +#[cw_serde] pub struct CurrentIntervalResponse { + /// Detailed information about the underlying interval. pub interval: Interval, + + /// The current blocktime pub current_blocktime: u64, + + /// Flag indicating whether the current interval is over and it should be advanced. pub is_current_interval_over: bool, + + /// Flag indicating whether the current epoch is over and it should be advanced. pub is_current_epoch_over: bool, } @@ -489,87 +519,6 @@ impl CurrentIntervalResponse { } } -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct PendingEpochEventsResponse { - pub seconds_until_executable: i64, - pub events: Vec, - pub start_next_after: Option, -} - -impl PendingEpochEventsResponse { - pub fn new( - seconds_until_executable: i64, - events: Vec, - start_next_after: Option, - ) -> Self { - PendingEpochEventsResponse { - seconds_until_executable, - events, - start_next_after, - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct PendingIntervalEventsResponse { - pub seconds_until_executable: i64, - pub events: Vec, - pub start_next_after: Option, -} - -impl PendingIntervalEventsResponse { - pub fn new( - seconds_until_executable: i64, - events: Vec, - start_next_after: Option, - ) -> Self { - PendingIntervalEventsResponse { - seconds_until_executable, - events, - start_next_after, - } - } -} - -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct PendingEpochEventResponse { - pub event_id: EpochEventId, - pub event: Option, -} - -impl PendingEpochEventResponse { - pub fn new(event_id: EpochEventId, event: Option) -> Self { - PendingEpochEventResponse { event_id, event } - } -} - -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct PendingIntervalEventResponse { - pub event_id: IntervalEventId, - pub event: Option, -} - -impl PendingIntervalEventResponse { - pub fn new(event_id: IntervalEventId, event: Option) -> Self { - PendingIntervalEventResponse { event_id, event } - } -} - -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct NumberOfPendingEventsResponse { - pub epoch_events: u32, - pub interval_events: u32, -} - -impl NumberOfPendingEventsResponse { - pub fn new(epoch_events: u32, interval_events: u32) -> Self { - Self { - epoch_events, - interval_events, - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs index e2a4a67a56..50a5e90aec 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/lib.rs @@ -11,14 +11,14 @@ pub mod events; pub mod families; pub mod gateway; pub mod helpers; -mod interval; +pub mod interval; pub mod mixnode; -mod msg; +pub mod msg; pub mod pending_events; pub mod reward_params; pub mod rewarding; pub mod signing_types; -mod types; +pub mod types; pub use contracts_common::types::*; pub use cosmwasm_std::{Addr, Coin, Decimal, Fraction}; @@ -26,25 +26,32 @@ pub use delegation::{ Delegation, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, PagedMixNodeDelegationsResponse, }; +pub use families::{ + Family, FamilyByHeadResponse, FamilyByLabelResponse, FamilyHead, FamilyMembersByHeadResponse, + FamilyMembersByLabelResponse, PagedFamiliesResponse, PagedMembersResponse, +}; pub use gateway::{ Gateway, GatewayBond, GatewayBondResponse, GatewayConfigUpdate, GatewayOwnershipResponse, PagedGatewayResponse, }; pub use interval::{ - CurrentIntervalResponse, EpochState, EpochStatus, Interval, NumberOfPendingEventsResponse, - PendingEpochEventResponse, PendingEpochEventsResponse, PendingIntervalEventResponse, - PendingIntervalEventsResponse, + CurrentIntervalResponse, EpochId, EpochState, EpochStatus, Interval, IntervalId, }; pub use mixnode::{ Layer, MixNode, MixNodeBond, MixNodeConfigUpdate, MixNodeCostParams, MixNodeDetails, - MixNodeRewarding, MixOwnershipResponse, MixnodeDetailsResponse, PagedMixnodeBondsResponse, - RewardedSetNodeStatus, UnbondedMixnode, + MixNodeRewarding, MixOwnershipResponse, MixnodeDetailsByIdentityResponse, + MixnodeDetailsResponse, PagedMixnodeBondsResponse, RewardedSetNodeStatus, UnbondedMixnode, }; pub use msg::*; pub use pending_events::{ - PendingEpochEvent, PendingEpochEventData, PendingEpochEventKind, PendingIntervalEvent, - PendingIntervalEventData, PendingIntervalEventKind, + EpochEventId, IntervalEventId, NumberOfPendingEventsResponse, PendingEpochEvent, + PendingEpochEventData, PendingEpochEventKind, PendingEpochEventResponse, + PendingEpochEventsResponse, PendingIntervalEvent, PendingIntervalEventData, + PendingIntervalEventKind, PendingIntervalEventResponse, PendingIntervalEventsResponse, }; pub use reward_params::{IntervalRewardParams, IntervalRewardingParamsUpdate, RewardingParams}; +pub use rewarding::{ + EstimatedCurrentEpochRewardResponse, PagedRewardedSetResponse, PendingRewardResponse, +}; pub use signing_types::*; pub use types::*; diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs index 69f1a89209..a09d21dd2d 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/mixnode.rs @@ -11,19 +11,26 @@ use crate::reward_params::{NodeRewardParams, RewardingParams}; use crate::rewarding::helpers::truncate_reward; use crate::rewarding::RewardDistribution; use crate::{Delegation, EpochEventId, EpochId, IdentityKey, MixId, Percent, SphinxKey}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Coin, Decimal, StdResult, Uint128}; use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; +/// Current state of given node in the rewarded set. #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/RewardedSetNodeStatus.ts") )] -#[derive(Clone, Copy, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +#[cw_serde] +#[derive(Copy)] pub enum RewardedSetNodeStatus { + /// Node that is currently active, i.e. is expected to be used by clients for mixing packets. + #[serde(alias = "Active")] Active, + + /// Node that is currently in standby, i.e. it's present in the rewarded set but is not active. + #[serde(alias = "Standby")] Standby, } @@ -33,10 +40,16 @@ impl RewardedSetNodeStatus { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Full details associated with given mixnode. +#[cw_serde] pub struct MixNodeDetails { + /// Basic bond information of this mixnode, such as owner address, original pledge, etc. pub bond_information: MixNodeBond, + + /// Details used for computation of rewarding related data. pub rewarding_details: MixNodeRewarding, + + /// Adjustments to the mixnode that are ought to happen during future epoch transitions. #[serde(default)] pub pending_changes: PendingMixNodeChanges, } @@ -58,6 +71,10 @@ impl MixNodeDetails { self.bond_information.mix_id } + pub fn layer(&self) -> Layer { + self.bond_information.layer + } + pub fn is_unbonding(&self) -> bool { self.bond_information.is_unbonding } @@ -86,7 +103,7 @@ impl MixNodeDetails { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[cw_serde] pub struct MixNodeRewarding { /// Information provided by the operator that influence the cost function. pub cost_params: MixNodeCostParams, @@ -464,8 +481,8 @@ impl MixNodeRewarding { } } -// operator information + data assigned by the contract(s) -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Basic mixnode information provided by the node operator. +#[cw_serde] pub struct MixNodeBond { /// Unique id assigned to the bonded mixnode. pub mix_id: MixId, @@ -533,21 +550,24 @@ impl MixNodeBond { } } -// information provided by the operator -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Information provided by the node operator during bonding that are used to allow other entities to use the services of this node. +#[cw_serde] #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/Mixnode.ts") )] pub struct MixNode { - /// Network address of this mixnode, for example 1.1.1.1:1234 or foo.mixnode.com + /// Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com pub host: String, + /// Port used by this mixnode for listening for mix packets. pub mix_port: u16, + /// Port used by this mixnode for listening for verloc requests. pub verloc_port: u16, + /// Port used by this mixnode for its http(s) API pub http_api_port: u16, /// Base58-encoded x25519 public key used for sphinx key derivation. @@ -556,11 +576,15 @@ pub struct MixNode { /// Base58-encoded ed25519 EdDSA public key. pub identity_key: IdentityKey, + /// The self-reported semver version of this mixnode. pub version: String, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// The cost parameters, or the cost function, defined for the particular mixnode that influences +/// how the rewards should be split between the node operator and its delegators. +#[cw_serde] pub struct MixNodeCostParams { + /// The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator. pub profit_margin_percent: Percent, /// Operating cost of the associated mixnode per the entire interval. @@ -633,7 +657,8 @@ impl From for u8 { feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/PendingMixnodeChanges.ts") )] -#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[cw_serde] +#[derive(Default, Copy)] pub struct PendingMixNodeChanges { pub pledge_change: Option, // pub cost_params_change: Option, @@ -647,21 +672,27 @@ impl PendingMixNodeChanges { } } +/// Basic information of a node that used to be part of the mix network but has already unbonded. #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/UnbondedMixnode.ts") )] -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[cw_serde] pub struct UnbondedMixnode { + /// Base58-encoded ed25519 EdDSA public key. pub identity_key: IdentityKey, + /// Address of the owner of this mixnode. #[cfg_attr(feature = "generate-ts", ts(type = "string"))] pub owner: Addr, + /// Entity who bonded this mixnode on behalf of the owner. + /// If exists, it's most likely the address of the vesting contract. #[cfg_attr(feature = "generate-ts", ts(type = "string | null"))] pub proxy: Option, + /// Block height at which this mixnode has unbonded. #[cfg_attr(feature = "generate-ts", ts(type = "number"))] pub unbonding_height: u64, } @@ -671,7 +702,7 @@ pub struct UnbondedMixnode { feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/MixNodeConfigUpdate.ts") )] -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[cw_serde] pub struct MixNodeConfigUpdate { pub host: String, pub mix_port: u16, @@ -686,10 +717,17 @@ impl MixNodeConfigUpdate { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing paged list of all mixnode bonds in the contract. +#[cw_serde] pub struct PagedMixnodeBondsResponse { + /// The mixnode bond information present in the contract. pub nodes: Vec, + + /// Maximum number of entries that could be included in a response. `per_page <= nodes.len()` + // this field is rather redundant and should be deprecated. pub per_page: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } @@ -703,10 +741,19 @@ impl PagedMixnodeBondsResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing paged list of all mixnode details in the contract. +#[cw_serde] pub struct PagedMixnodesDetailsResponse { + /// All mixnode details stored in the contract. + /// Apart from the basic bond information it also contains details required for all future reward calculation + /// as well as any pending changes requested by the operator. pub nodes: Vec, + + /// Maximum number of entries that could be included in a response. `per_page <= nodes.len()` + // this field is rather redundant and should be deprecated. pub per_page: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } @@ -724,10 +771,17 @@ impl PagedMixnodesDetailsResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing paged list of all mixnodes that have ever unbonded. +#[cw_serde] pub struct PagedUnbondedMixnodesResponse { + /// The past ids of unbonded mixnodes alongside their basic information such as the owner or the identity key. pub nodes: Vec<(MixId, UnbondedMixnode)>, + + /// Maximum number of entries that could be included in a response. `per_page <= nodes.len()` + // this field is rather redundant and should be deprecated. pub per_page: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } @@ -745,33 +799,68 @@ impl PagedUnbondedMixnodesResponse { } } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing details of a mixnode belonging to the particular owner. +#[cw_serde] pub struct MixOwnershipResponse { + /// Validated address of the mixnode owner. pub address: Addr, + + /// If the provided address owns a mixnode, this field contains its detailed information. pub mixnode_details: Option, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing details of a mixnode with the provided id. +#[cw_serde] pub struct MixnodeDetailsResponse { + /// Id of the requested mixnode. pub mix_id: MixId, + + /// If there exists a mixnode with the provided id, this field contains its detailed information. pub mixnode_details: Option, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing details of a bonded mixnode with the provided identity key. +#[cw_serde] +pub struct MixnodeDetailsByIdentityResponse { + /// The identity key (base58-encoded ed25519 public key) of the mixnode. + pub identity_key: IdentityKey, + + /// If there exists a bonded mixnode with the provided identity key, this field contains its detailed information. + pub mixnode_details: Option, +} + +/// Response containing rewarding information of a mixnode with the provided id. +#[cw_serde] pub struct MixnodeRewardingDetailsResponse { + /// Id of the requested mixnode. pub mix_id: MixId, + + /// If there exists a mixnode with the provided id, this field contains its rewarding information. pub rewarding_details: Option, } -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing basic information of an unbonded mixnode with the provided id. +#[cw_serde] pub struct UnbondedMixnodeResponse { + /// Id of the requested mixnode. pub mix_id: MixId, + + /// If there existed a mixnode with the provided id, this field contains its basic information. pub unbonded_info: Option, } -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +/// Response containing the current state of the stake saturation of a mixnode with the provided id. +#[cw_serde] pub struct StakeSaturationResponse { + /// Id of the requested mixnode. pub mix_id: MixId, + + /// The current stake saturation of this node that is indirectly used in reward calculation formulas. + /// Note that it can't be larger than 1. pub current_saturation: Option, + + /// The current, absolute, stake saturation of this node. + /// Note that as the name suggests it can be larger than 1. + /// However, anything beyond that value has no effect on the total node reward. pub uncapped_saturation: Option, } diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs index 24603147a0..ca154e231b 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/msg.rs @@ -1,28 +1,54 @@ // Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::delegation::OwnerProxySubKey; +use crate::delegation::{self, OwnerProxySubKey}; use crate::error::MixnetContractError; use crate::families::FamilyHead; -use crate::gateway::GatewayConfigUpdate; +use crate::gateway::{Gateway, GatewayConfigUpdate}; use crate::helpers::IntoBaseDecimal; -use crate::mixnode::{MixNodeConfigUpdate, MixNodeCostParams}; +use crate::mixnode::{Layer, MixNode, MixNodeConfigUpdate, MixNodeCostParams}; +use crate::pending_events::{EpochEventId, IntervalEventId}; use crate::reward_params::{ IntervalRewardParams, IntervalRewardingParamsUpdate, Performance, RewardingParams, }; -use crate::{ - delegation, ContractStateParams, EpochEventId, IntervalEventId, Layer, LayerAssignment, MixId, - Percent, -}; -use crate::{Gateway, IdentityKey, MixNode}; -use contracts_common::signing::MessageSignature; +use crate::types::{ContractStateParams, LayerAssignment, MixId}; +use contracts_common::{signing::MessageSignature, IdentityKey, Percent}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, Decimal}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; use std::time::Duration; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cfg(feature = "schema")] +use crate::{ + delegation::{ + MixNodeDelegationResponse, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse, + PagedMixNodeDelegationsResponse, + }, + families::{ + FamilyByHeadResponse, FamilyByLabelResponse, FamilyMembersByHeadResponse, + FamilyMembersByLabelResponse, PagedFamiliesResponse, PagedMembersResponse, + }, + gateway::{GatewayBondResponse, GatewayOwnershipResponse, PagedGatewayResponse}, + interval::{CurrentIntervalResponse, EpochStatus}, + mixnode::{ + MixOwnershipResponse, MixnodeDetailsByIdentityResponse, MixnodeDetailsResponse, + MixnodeRewardingDetailsResponse, PagedMixnodeBondsResponse, PagedMixnodesDetailsResponse, + PagedUnbondedMixnodesResponse, StakeSaturationResponse, UnbondedMixnodeResponse, + }, + pending_events::{ + NumberOfPendingEventsResponse, PendingEpochEventResponse, PendingEpochEventsResponse, + PendingIntervalEventResponse, PendingIntervalEventsResponse, + }, + rewarding::{ + EstimatedCurrentEpochRewardResponse, PagedRewardedSetResponse, PendingRewardResponse, + }, + types::{ContractState, LayerDistribution}, +}; +#[cfg(feature = "schema")] +use contracts_common::{signing::Nonce, ContractBuildInformation}; +#[cfg(feature = "schema")] +use cosmwasm_schema::QueryResponses; + +#[cw_serde] pub struct InstantiateMsg { pub rewarding_validator_address: String, pub vesting_contract_address: String, @@ -33,8 +59,7 @@ pub struct InstantiateMsg { pub initial_rewarding_params: InitialRewardingParams, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct InitialRewardingParams { pub initial_reward_pool: Decimal, pub initial_staking_supply: Decimal, @@ -76,8 +101,7 @@ impl InitialRewardingParams { } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub enum ExecuteMsg { AssignNodeLayer { mix_id: MixId, @@ -365,177 +389,376 @@ impl ExecuteMsg { } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] +#[cfg_attr(feature = "schema", derive(QueryResponses))] pub enum QueryMsg { // families + /// Gets the list of families registered in this contract. + #[cfg_attr(feature = "schema", returns(PagedFamiliesResponse))] GetAllFamiliesPaged { + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, }, + + /// Gets the list of all family members registered in this contract. + #[cfg_attr(feature = "schema", returns(PagedMembersResponse))] GetAllMembersPaged { + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, }, - GetFamilyByHead { - head: String, - }, - GetFamilyByLabel { - label: String, - }, - GetFamilyMembersByHead { - head: String, - }, - GetFamilyMembersByLabel { - label: String, - }, + + /// Attempts to lookup family information given the family head. + #[cfg_attr(feature = "schema", returns(FamilyByHeadResponse))] + GetFamilyByHead { head: String }, + + /// Attempts to lookup family information given the family label. + #[cfg_attr(feature = "schema", returns(FamilyByLabelResponse))] + GetFamilyByLabel { label: String }, + + /// Attempts to retrieve family members given the family head. + #[cfg_attr(feature = "schema", returns(FamilyMembersByHeadResponse))] + GetFamilyMembersByHead { head: String }, + + /// Attempts to retrieve family members given the family label. + #[cfg_attr(feature = "schema", returns(FamilyMembersByLabelResponse))] + GetFamilyMembersByLabel { label: String }, + // state/sys-params-related + /// Gets build information of this contract, such as the commit hash used for the build or rustc version. + #[cfg_attr(feature = "schema", returns(ContractBuildInformation))] GetContractVersion {}, + + /// Gets the stored contract version information that's required by the CW2 spec interface for migrations. #[serde(rename = "get_cw2_contract_version")] + #[cfg_attr(feature = "schema", returns(cw2::ContractVersion))] GetCW2ContractVersion {}, + + /// Gets the address of the validator that's allowed to send rewarding transactions and transition the epoch. + #[cfg_attr(feature = "schema", returns(String))] GetRewardingValidatorAddress {}, + + /// Gets the contract parameters that could be adjusted in a transaction by the contract admin. + #[cfg_attr(feature = "schema", returns(ContractStateParams))] GetStateParams {}, + + /// Gets the current state of the contract. + #[cfg_attr(feature = "schema", returns(ContractState))] GetState {}, + + /// Gets the current parameters used for reward calculation. + #[cfg_attr(feature = "schema", returns(RewardingParams))] GetRewardingParams {}, + + /// Gets the status of the current rewarding epoch. + #[cfg_attr(feature = "schema", returns(EpochStatus))] GetEpochStatus {}, + + /// Get the details of the current rewarding interval. + #[cfg_attr(feature = "schema", returns(CurrentIntervalResponse))] GetCurrentIntervalDetails {}, + + /// Gets the current list of mixnodes in the rewarded set. + #[cfg_attr(feature = "schema", returns(PagedRewardedSetResponse))] GetRewardedSet { + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, }, // mixnode-related: + /// Gets the basic list of all currently bonded mixnodes. + #[cfg_attr(feature = "schema", returns(PagedMixnodeBondsResponse))] GetMixNodeBonds { + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, }, + + /// Gets the detailed list of all currently bonded mixnodes. + #[cfg_attr(feature = "schema", returns(PagedMixnodesDetailsResponse))] GetMixNodesDetailed { + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, }, + + /// Gets the basic list of all unbonded mixnodes. + #[cfg_attr(feature = "schema", returns(PagedUnbondedMixnodesResponse))] GetUnbondedMixNodes { + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, }, + + /// Gets the basic list of all unbonded mixnodes that belonged to a particular owner. + #[cfg_attr(feature = "schema", returns(PagedUnbondedMixnodesResponse))] GetUnbondedMixNodesByOwner { + /// The address of the owner of the the mixnodes used for the query. owner: String, + + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, }, + + /// Gets the basic list of all unbonded mixnodes that used the particular identity key. + #[cfg_attr(feature = "schema", returns(PagedUnbondedMixnodesResponse))] GetUnbondedMixNodesByIdentityKey { + /// The identity key (base58-encoded ed25519 public key) of the mixnode used for the query. identity_key: String, + + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, }, + + /// Gets the detailed mixnode information belonging to the particular owner. + #[cfg_attr(feature = "schema", returns(MixOwnershipResponse))] GetOwnedMixnode { + /// Address of the mixnode owner to use for the query. address: String, }, + + /// Gets the detailed mixnode information of a node with the provided id. + #[cfg_attr(feature = "schema", returns(MixnodeDetailsResponse))] GetMixnodeDetails { + /// Id of the node to query. mix_id: MixId, }, + + /// Gets the rewarding information of a mixnode with the provided id. + #[cfg_attr(feature = "schema", returns(MixnodeRewardingDetailsResponse))] GetMixnodeRewardingDetails { + /// Id of the node to query. mix_id: MixId, }, + + /// Gets the stake saturation of a mixnode with the provided id. + #[cfg_attr(feature = "schema", returns(StakeSaturationResponse))] GetStakeSaturation { + /// Id of the node to query. mix_id: MixId, }, + + /// Gets the basic information of an unbonded mixnode with the provided id. + #[cfg_attr(feature = "schema", returns(UnbondedMixnodeResponse))] GetUnbondedMixNodeInformation { + /// Id of the node to query. mix_id: MixId, }, + + /// Gets the detailed mixnode information of a node given its current identity key. + #[cfg_attr(feature = "schema", returns(MixnodeDetailsByIdentityResponse))] GetBondedMixnodeDetailsByIdentity { + /// The identity key (base58-encoded ed25519 public key) of the mixnode used for the query. mix_identity: IdentityKey, }, + + /// Gets the current layer configuration of the mix network. + #[cfg_attr(feature = "schema", returns(LayerDistribution))] GetLayerDistribution {}, + // gateway-related: + /// Gets the basic list of all currently bonded gateways. + #[cfg_attr(feature = "schema", returns(PagedGatewayResponse))] GetGateways { + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, + + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, }, + + /// Gets the gateway details of a node given its identity key. + #[cfg_attr(feature = "schema", returns(GatewayBondResponse))] GetGatewayBond { + /// The identity key (base58-encoded ed25519 public key) of the gateway used for the query. identity: IdentityKey, }, + + /// Gets the detailed gateway information belonging to the particular owner. + #[cfg_attr(feature = "schema", returns(GatewayOwnershipResponse))] GetOwnedGateway { + /// Address of the gateway owner to use for the query. address: String, }, // delegation-related: - // gets all [paged] delegations associated with particular mixnode + /// Gets all delegations associated with particular mixnode + #[cfg_attr(feature = "schema", returns(PagedMixNodeDelegationsResponse))] GetMixnodeDelegations { + /// Id of the node to query. mix_id: MixId, - // since `start_after` is user-provided input, we can't use `Addr` as we - // can't guarantee it's validated. - start_after: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. + start_after: Option, + + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, }, - // gets all [paged] delegations associated with particular delegator + + /// Gets all delegations associated with particular delegator + #[cfg_attr(feature = "schema", returns(PagedDelegatorDelegationsResponse))] GetDelegatorDelegations { // since `delegator` is user-provided input, we can't use `Addr` as we // can't guarantee it's validated. + /// The address of the owner of the delegations. delegator: String, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option<(MixId, OwnerProxySubKey)>, + + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, }, - // gets delegation associated with particular mixnode, delegator pair + + /// Gets delegation information associated with particular mixnode - delegator pair + #[cfg_attr(feature = "schema", returns(MixNodeDelegationResponse))] GetDelegationDetails { + /// Id of the node to query. mix_id: MixId, + + /// The address of the owner of the delegation. delegator: String, + + /// Entity who made the delegation on behalf of the owner. + /// If present, it's most likely the address of the vesting contract. proxy: Option, }, - // gets all delegations in the system + + /// Gets all delegations in the system + #[cfg_attr(feature = "schema", returns(PagedAllDelegationsResponse))] GetAllDelegations { + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, + + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, }, // rewards related + /// Gets the reward amount accrued by the node operator that has not yet been claimed. + #[cfg_attr(feature = "schema", returns(PendingRewardResponse))] GetPendingOperatorReward { + /// Address of the operator to use for the query. address: String, }, + + /// Gets the reward amount accrued by the particular mixnode that has not yet been claimed. + #[cfg_attr(feature = "schema", returns(PendingRewardResponse))] GetPendingMixNodeOperatorReward { + /// Id of the node to query. mix_id: MixId, }, + + /// Gets the reward amount accrued by the particular delegator that has not yet been claimed. + #[cfg_attr(feature = "schema", returns(PendingRewardResponse))] GetPendingDelegatorReward { + /// Address of the delegator to use for the query. address: String, + + /// Id of the node to query. mix_id: MixId, + + /// Entity who made the delegation on behalf of the owner. + /// If present, it's most likely the address of the vesting contract. proxy: Option, }, - // given the provided performance, estimate the reward at the end of the current epoch + + /// Given the provided node performance, attempt to estimate the operator reward for the current epoch. + #[cfg_attr(feature = "schema", returns(EstimatedCurrentEpochRewardResponse))] GetEstimatedCurrentEpochOperatorReward { + /// Id of the node to query. mix_id: MixId, + + /// The estimated performance for the current epoch of the given node. estimated_performance: Performance, }, + + /// Given the provided node performance, attempt to estimate the delegator reward for the current epoch. + #[cfg_attr(feature = "schema", returns(EstimatedCurrentEpochRewardResponse))] GetEstimatedCurrentEpochDelegatorReward { + /// Address of the delegator to use for the query. address: String, + + /// Id of the node to query. mix_id: MixId, + + /// Entity who made the delegation on behalf of the owner. + /// If present, it's most likely the address of the vesting contract. proxy: Option, + + /// The estimated performance for the current epoch of the given node. estimated_performance: Performance, }, // interval-related + /// Gets the list of all currently pending epoch events that will be resolved once the current epoch finishes. + #[cfg_attr(feature = "schema", returns(PendingEpochEventsResponse))] GetPendingEpochEvents { + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, }, + + /// Gets the list of all currently pending interval events that will be resolved once the current interval finishes. + #[cfg_attr(feature = "schema", returns(PendingIntervalEventsResponse))] GetPendingIntervalEvents { + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, + + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option, }, + + /// Gets detailed information about a pending epoch event given its id. + #[cfg_attr(feature = "schema", returns(PendingEpochEventResponse))] GetPendingEpochEvent { + /// The unique id associated with the event. event_id: EpochEventId, }, + + /// Gets detailed information about a pending interval event given its id. + #[cfg_attr(feature = "schema", returns(PendingIntervalEventResponse))] GetPendingIntervalEvent { + /// The unique id associated with the event. event_id: IntervalEventId, }, + + /// Gets the information about the number of currently pending epoch and interval events. + #[cfg_attr(feature = "schema", returns(NumberOfPendingEventsResponse))] GetNumberOfPendingEvents {}, // signing-related + /// Gets the signing nonce associated with the particular cosmos address. + #[cfg_attr(feature = "schema", returns(Nonce))] GetSigningNonce { + /// Cosmos address used for the query of the signing nonce. address: String, }, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct MigrateMsg { pub vesting_contract_address: 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 5f4db1ab90..fd681cbed4 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/pending_events.rs @@ -3,49 +3,101 @@ use crate::mixnode::MixNodeCostParams; use crate::reward_params::IntervalRewardingParamsUpdate; -use crate::{BlockHeight, EpochEventId, IntervalEventId, MixId}; +use crate::{BlockHeight, MixId}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Coin}; -use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub type EpochEventId = u32; +pub type IntervalEventId = u32; + +/// A request made at some point in the current epoch that's going to get resolved once the epoch rolls over. +#[cw_serde] pub struct PendingEpochEvent { + /// The unique id associated with the event. pub id: EpochEventId, + + /// The underlying event details, containing its type and information on how it should get resolved. pub event: PendingEpochEventData, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +/// Details of a particular pending epoch event. +#[cw_serde] pub struct PendingEpochEventData { + /// The block height at which the request has been made. pub created_at: BlockHeight, + + /// The underlying event data, containing its concrete type and information on how it should get resolved. pub kind: PendingEpochEventKind, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +/// Enum encompassing all possible epoch events. +#[cw_serde] pub enum PendingEpochEventKind { // can't just pass the `Delegation` struct here as it's impossible to determine // `cumulative_reward_ratio` ahead of time + /// Request to create a delegation towards particular mixnode. + /// Note that if such delegation already exists, it will get updated with the provided token amount. + #[serde(alias = "Delegate")] Delegate { + /// The address of the owner of the delegation. owner: Addr, + + /// The id of the mixnode used for the delegation. mix_id: MixId, + + /// The amount of tokens to use for the delegation. amount: Coin, + + /// Entity who made the delegation on behalf of the owner. + /// If present, it's most likely the address of the vesting contract. proxy: Option, }, + + /// Request to remove delegation from particular mixnode. + #[serde(alias = "Undelegate")] Undelegate { + /// The address of the owner of the delegation. owner: Addr, + + /// The id of the mixnode used for the delegation. mix_id: MixId, + + /// Entity who made the delegation on behalf of the owner. + /// If present, it's most likely the address of the vesting contract. proxy: Option, }, + + /// Request to pledge more tokens (by the node operator) towards its node. + #[serde(alias = "PledgeMore")] PledgeMore { + /// The id of the mixnode that will have its pledge updated. mix_id: MixId, + + /// The amount of additional tokens to use by the pledge. amount: Coin, }, + + /// Request to decrease amount of pledged tokens (by the node operator) from its node. + #[serde(alias = "DecreasePledge")] DecreasePledge { + /// The id of the mixnode that will have its pledge updated. mix_id: MixId, + + /// The amount of tokens that should be removed from the pledge. decrease_by: Coin, }, + + /// Request to unbond a mixnode and completely remove it from the network. + #[serde(alias = "UnbondMixnode")] UnbondMixnode { + /// The id of the mixnode that will get unbonded. mix_id: MixId, }, + + /// Request to update the current size of the active set. + #[serde(alias = "UpdateActiveSetSize")] UpdateActiveSetSize { + /// The new desired size of the active set. new_size: u32, }, } @@ -68,29 +120,53 @@ impl From<(EpochEventId, PendingEpochEventData)> for PendingEpochEvent { } } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +/// A request made at some point in the current interval that's going to get resolved once the interval rolls over. +#[cw_serde] pub struct PendingIntervalEvent { + /// The unique id associated with the event. pub id: IntervalEventId, + + /// The underlying event details, containing its type and information on how it should get resolved. pub event: PendingIntervalEventData, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +/// Details of a particular pending interval event. +#[cw_serde] pub struct PendingIntervalEventData { + /// The block height at which the request has been made. pub created_at: BlockHeight, + + /// The underlying event data, containing its concrete type and information on how it should get resolved. pub kind: PendingIntervalEventKind, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +/// Enum encompassing all possible interval events. +#[cw_serde] pub enum PendingIntervalEventKind { + /// Request to update cost parameters of given mixnode. + #[serde(alias = "PendingIntervalEventKind")] ChangeMixCostParams { + /// The id of the mixnode that will have its cost parameters updated. mix_id: MixId, + + /// The new updated cost function of this mixnode. new_costs: MixNodeCostParams, }, + + /// Request to update the underlying rewarding parameters used by the system + #[serde(alias = "UpdateRewardingParams")] UpdateRewardingParams { + /// The detailed specification of the update. update: IntervalRewardingParamsUpdate, }, + + /// Request to change the next interval configuration. + #[serde(alias = "UpdateIntervalConfig")] UpdateIntervalConfig { + /// The new number of epochs in intervals. epochs_in_interval: u32, + + /// The new epoch duration. epoch_duration_secs: u64, }, } @@ -112,3 +188,102 @@ impl From<(IntervalEventId, PendingIntervalEventData)> for PendingIntervalEvent } } } + +/// Response containing all currently pending epoch events that will be resolved once the current epoch finishes. +#[cw_serde] +pub struct PendingEpochEventsResponse { + /// Amount of seconds until the events would be eligible to be resolved. + /// It's equivalent to the time until the current epoch finishes. + pub seconds_until_executable: i64, + + /// The currently pending events. + pub events: Vec, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. + pub start_next_after: Option, +} + +impl PendingEpochEventsResponse { + pub fn new( + seconds_until_executable: i64, + events: Vec, + start_next_after: Option, + ) -> Self { + PendingEpochEventsResponse { + seconds_until_executable, + events, + start_next_after, + } + } +} + +/// Response containing all currently pending interval events that will be resolved once the current interval finishes. +#[cw_serde] +pub struct PendingIntervalEventsResponse { + /// Amount of seconds until the events would be eligible to be resolved. + /// It's equivalent to the time until the current interval finishes. + pub seconds_until_executable: i64, + + /// The currently pending events. + pub events: Vec, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. + pub start_next_after: Option, +} + +impl PendingIntervalEventsResponse { + pub fn new( + seconds_until_executable: i64, + events: Vec, + start_next_after: Option, + ) -> Self { + PendingIntervalEventsResponse { + seconds_until_executable, + events, + start_next_after, + } + } +} + +#[cw_serde] +pub struct PendingEpochEventResponse { + pub event_id: EpochEventId, + pub event: Option, +} + +impl PendingEpochEventResponse { + pub fn new(event_id: EpochEventId, event: Option) -> Self { + PendingEpochEventResponse { event_id, event } + } +} + +#[cw_serde] +pub struct PendingIntervalEventResponse { + pub event_id: IntervalEventId, + pub event: Option, +} + +impl PendingIntervalEventResponse { + pub fn new(event_id: IntervalEventId, event: Option) -> Self { + PendingIntervalEventResponse { event_id, event } + } +} + +/// Response containing number of currently pending epoch and interval events. +#[cw_serde] +pub struct NumberOfPendingEventsResponse { + /// The number of the currently pending epoch events. + pub epoch_events: u32, + + /// The number of the currently pending epoch events. + pub interval_events: u32, +} + +impl NumberOfPendingEventsResponse { + pub fn new(epoch_events: u32, interval_events: u32) -> Self { + Self { + epoch_events, + interval_events, + } + } +} diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs index 74777eb99f..4928bd6a1e 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/reward_params.rs @@ -3,9 +3,8 @@ use crate::helpers::IntoBaseDecimal; use crate::{error::MixnetContractError, Percent}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::Decimal; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; pub type Performance = Percent; @@ -15,7 +14,8 @@ pub type Performance = Percent; feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/IntervalRewardParams.ts") )] -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Serialize, JsonSchema)] +#[cw_serde] +#[derive(Copy)] pub struct IntervalRewardParams { /// Current value of the rewarding pool. /// It is expected to be constant throughout the interval. @@ -74,21 +74,26 @@ impl IntervalRewardParams { } } +/// Parameters used for reward calculation. #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/RewardingParams.ts") )] -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Serialize, JsonSchema)] +#[cw_serde] +#[derive(Copy)] pub struct RewardingParams { /// Parameters that should remain unchanged throughout an interval. pub interval: IntervalRewardParams, - // while the active set size can change between epochs to accommodate for bandwidth demands, + // while the rewarded set size can change between epochs to accommodate for bandwidth demands, // the active set size should be unchanged between epochs and should only be adjusted between // intervals. However, it makes more sense to keep both of those values together as they're // very strongly related to each other. + /// The expected number of mixnodes in the rewarded set (i.e. active + standby). pub rewarded_set_size: u32, + + /// The expected number of mixnodes in the active set. pub active_set_size: u32, } @@ -224,9 +229,14 @@ impl RewardingParams { } // TODO: possibly refactor this -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, PartialOrd, Serialize, JsonSchema)] +/// Parameters used for rewarding particular mixnode. +#[cw_serde] +#[derive(Copy)] pub struct NodeRewardParams { + /// Performance of the particular node in the current epoch. pub performance: Percent, + + /// Flag indicating whether the node has been in the active set during the epoch. pub in_active_set: bool, } @@ -239,33 +249,40 @@ impl NodeRewardParams { } } +/// Specification on how the rewarding params should be updated. #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/IntervalRewardingParamsUpdate.ts") )] -#[derive( - Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq, PartialOrd, Serialize, JsonSchema, -)] +#[cw_serde] +#[derive(Copy, Default)] pub struct IntervalRewardingParamsUpdate { #[cfg_attr(feature = "generate-ts", ts(type = "string | null"))] + /// Defines the new value of the reward pool. pub reward_pool: Option, #[cfg_attr(feature = "generate-ts", ts(type = "string | null"))] + /// Defines the new value of the staking supply. pub staking_supply: Option, #[cfg_attr(feature = "generate-ts", ts(type = "string | null"))] + /// Defines the new value of the staking supply scale factor. pub staking_supply_scale_factor: Option, #[cfg_attr(feature = "generate-ts", ts(type = "string | null"))] + /// Defines the new value of the sybil resistance percent. pub sybil_resistance_percent: Option, #[cfg_attr(feature = "generate-ts", ts(type = "string | null"))] + /// Defines the new value of the active set work factor. pub active_set_work_factor: Option, #[cfg_attr(feature = "generate-ts", ts(type = "string | null"))] + /// Defines the new value of the interval pool emission rate. pub interval_pool_emission: Option, + /// Defines the new size of the rewarded set. pub rewarded_set_size: Option, } 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 650e39a2ff..4ef9cd433a 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/mod.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/rewarding/mod.rs @@ -1,9 +1,9 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::{MixId, RewardedSetNodeStatus}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, Decimal}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; pub mod helpers; pub mod simulator; @@ -13,32 +13,52 @@ pub mod simulator; feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/RewardEstimate.ts") )] -#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +#[cw_serde] +#[derive(Copy, Default)] pub struct RewardEstimate { + /// The amount of **decimal** coins that are going to get distributed to the node, + /// i.e. the operator and all its delegators. #[cfg_attr(feature = "generate-ts", ts(type = "string"))] pub total_node_reward: Decimal, // note that operator reward includes the operating_cost, // i.e. say total_node_reward was `1nym` and operating_cost was `2nym` // in that case the operator reward would still be `1nym` as opposed to 0 + /// The share of the reward that is going to get distributed to the node operator. #[cfg_attr(feature = "generate-ts", ts(type = "string"))] pub operator: Decimal, + + /// The share of the reward that is going to get distributed among the node delegators. #[cfg_attr(feature = "generate-ts", ts(type = "string"))] pub delegates: Decimal, + + /// The operating cost of this node. Note: it's already included in the operator reward. #[cfg_attr(feature = "generate-ts", ts(type = "string"))] pub operating_cost: Decimal, } -#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +#[cw_serde] +#[derive(Copy, Default)] pub struct RewardDistribution { + /// The share of the reward that is going to get distributed to the node operator. pub operator: Decimal, + + /// The share of the reward that is going to get distributed among the node delegators. pub delegates: Decimal, } -#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +/// Response containing information about accrued rewards. +#[cw_serde] +#[derive(Default)] pub struct PendingRewardResponse { + /// The amount of tokens initially staked. pub amount_staked: Option, + + /// The amount of tokens that could be claimed. pub amount_earned: Option, + + /// The full pending rewards. Note that it's nearly identical to `amount_earned`, + /// however, it contains few additional decimal points for more accurate reward calculation. pub amount_earned_detailed: Option, /// The associated mixnode is still fully bonded, meaning it is neither unbonded @@ -46,14 +66,25 @@ pub struct PendingRewardResponse { pub mixnode_still_fully_bonded: bool, } -#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +/// Response containing estimation of node rewards for the current epoch. +#[cw_serde] pub struct EstimatedCurrentEpochRewardResponse { + /// The amount of tokens initially staked. pub original_stake: Option, + /// The current stake value given all past rewarding and compounding since the original staking was performed. pub current_stake_value: Option, + + /// The current stake value. Note that it's nearly identical to `current_stake_value`, + /// however, it contains few additional decimal points for more accurate reward calculation. pub current_stake_value_detailed_amount: Option, + /// The reward estimation for the current epoch, i.e. the amount of tokens that could be claimable + /// after the epoch finishes and the state of the network does not change. pub estimation: Option, + + /// The full reward estimation. Note that it's nearly identical to `estimation`, + /// however, it contains few additional decimal points for more accurate reward calculation. pub detailed_estimation_amount: Option, } @@ -68,3 +99,13 @@ impl EstimatedCurrentEpochRewardResponse { } } } + +/// Response containing paged list of all mixnodes in the rewarded set. +#[cw_serde] +pub struct PagedRewardedSetResponse { + /// Nodes in the current rewarded set. + pub nodes: Vec<(MixId, RewardedSetNodeStatus)>, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. + pub start_next_after: Option, +} diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/signing.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/signing.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs b/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs index e0a8109f98..e1ff854bb9 100644 --- a/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs +++ b/common/cosmwasm-smart-contracts/mixnet-contract/src/types.rs @@ -2,29 +2,26 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::MixnetContractError; -use crate::families::{Family, FamilyHead}; -use crate::{Layer, RewardedSetNodeStatus}; -use contracts_common::IdentityKey; +use crate::Layer; +use cosmwasm_schema::cw_serde; use cosmwasm_std::Addr; use cosmwasm_std::Coin; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; use std::ops::Index; // type aliases for better reasoning about available data pub type SphinxKey = String; pub type SphinxKeyRef<'a> = &'a str; -pub type EpochId = u32; -pub type IntervalId = u32; + pub type MixId = u32; pub type BlockHeight = u64; -pub type EpochEventId = u32; -pub type IntervalEventId = u32; -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Serialize, Deserialize, Debug, Clone, JsonSchema, PartialEq, Eq)] +/// Specifies layer assignment for the given mixnode. +#[cw_serde] pub struct LayerAssignment { + /// The id of the mixnode. mix_id: MixId, + + /// The layer to which it's going to be assigned layer: Layer, } @@ -42,10 +39,17 @@ impl LayerAssignment { } } -#[derive(Debug, Default, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)] +/// The current layer distribution of the mix network. +#[cw_serde] +#[derive(Copy, Default)] pub struct LayerDistribution { + /// Number of nodes on the first layer. pub layer1: u64, + + /// Number of nodes on the second layer. pub layer2: u64, + + /// Number of nodes on the third layer. pub layer3: u64, } @@ -118,19 +122,29 @@ impl Index for LayerDistribution { } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +/// The current state of the mixnet contract. +#[cw_serde] pub struct ContractState { - pub owner: Addr, // only the owner account can update state + /// Address of the contract owner. + pub owner: Addr, + + /// Address of "rewarding validator" (nym-api) that's allowed to send any rewarding-related transactions. pub rewarding_validator_address: Addr, /// Address of the vesting contract to which the mixnet contract would be sending all /// track-related messages. pub vesting_contract_address: Addr, + + /// The expected denom used for rewarding (and realistically any other operation). + /// Default: `unym` pub rewarding_denom: String, + + /// Contract parameters that could be adjusted in a transaction the contract admin. pub params: ContractStateParams, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +/// Contract parameters that could be adjusted in a transaction by the contract admin. +#[cw_serde] pub struct ContractStateParams { /// Minimum amount a delegator must stake in orders for his delegation to get accepted. pub minimum_mixnode_delegation: Option, @@ -141,21 +155,3 @@ pub struct ContractStateParams { /// Minimum amount a gateway must pledge to get into the system. pub minimum_gateway_pledge: Coin, } - -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] -pub struct PagedRewardedSetResponse { - pub nodes: Vec<(MixId, RewardedSetNodeStatus)>, - pub start_next_after: Option, -} - -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] -pub struct PagedFamiliesResponse { - pub families: Vec, - pub start_next_after: Option, -} - -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] -pub struct PagedMembersResponse { - pub members: Vec<(IdentityKey, FamilyHead)>, - pub start_next_after: Option, -} diff --git a/common/cosmwasm-smart-contracts/multisig-contract/Cargo.toml b/common/cosmwasm-smart-contracts/multisig-contract/Cargo.toml index 2a6753267e..d419e25086 100644 --- a/common/cosmwasm-smart-contracts/multisig-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/multisig-contract/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] cw-utils = { workspace = true } cw3 = { workspace = true } -cw4 = { workspace= true } +cw4 = { workspace = true } cw-storage-plus = { workspace = true } cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } diff --git a/common/cosmwasm-smart-contracts/name-service/Cargo.toml b/common/cosmwasm-smart-contracts/name-service/Cargo.toml index 23fb221e23..4af7d851f1 100644 --- a/common/cosmwasm-smart-contracts/name-service/Cargo.toml +++ b/common/cosmwasm-smart-contracts/name-service/Cargo.toml @@ -6,10 +6,14 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } +cw2 = { workspace = true, optional = true } cw-controllers = { workspace = true } cw-utils = { workspace = true } nym-contracts-common = { path = "../contracts-common", version = "0.5.0" } -schemars = "0.8" serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } + +[features] +schema = ["cw2"] \ No newline at end of file diff --git a/common/cosmwasm-smart-contracts/name-service/src/error.rs b/common/cosmwasm-smart-contracts/name-service/src/error.rs index 8c31308b66..24d62c23cf 100644 --- a/common/cosmwasm-smart-contracts/name-service/src/error.rs +++ b/common/cosmwasm-smart-contracts/name-service/src/error.rs @@ -49,13 +49,13 @@ pub enum NameServiceError { error_message: String, }, - #[error("Failed to recover ed25519 public key from its base58 representation - {0}")] + #[error("failed to recover ed25519 public key from its base58 representation: {0}")] MalformedEd25519IdentityKey(String), - #[error("Failed to recover ed25519 signature from its base58 representation - {0}")] + #[error("failed to recover ed25519 signature from its base58 representation: {0}")] MalformedEd25519Signature(String), - #[error("Provided ed25519 signature did not verify correctly")] + #[error("provided ed25519 signature did not verify correctly")] InvalidEd25519Signature, #[error("failed to verify message signature: {source}")] @@ -69,6 +69,15 @@ pub enum NameServiceError { #[error("name already registered: {name}")] NameAlreadyRegistered { name: NymName }, + + #[error("invalid nym address format: {0}")] + InvalidNymAddress(String), + + #[error("client identity in nym address does not match the provided identity key")] + IdentityKeyMismatch { + address: Address, + identity_key: String, + }, } pub type Result = std::result::Result; diff --git a/common/cosmwasm-smart-contracts/name-service/src/msg.rs b/common/cosmwasm-smart-contracts/name-service/src/msg.rs index 9a8e2b8ecf..cdac95c215 100644 --- a/common/cosmwasm-smart-contracts/name-service/src/msg.rs +++ b/common/cosmwasm-smart-contracts/name-service/src/msg.rs @@ -1,10 +1,22 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + use crate::{Address, NameDetails, NameId, NymName}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::Coin; use nym_contracts_common::signing::MessageSignature; -use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cfg(feature = "schema")] +use crate::{ + response::{ConfigResponse, NamesListResponse, PagedNamesListResponse}, + types::RegisteredName, +}; +#[cfg(feature = "schema")] +use cosmwasm_schema::QueryResponses; +#[cfg(feature = "schema")] +use nym_contracts_common::{signing::Nonce, ContractBuildInformation}; + +#[cw_serde] pub struct InstantiateMsg { pub deposit_required: Coin, } @@ -15,22 +27,23 @@ impl InstantiateMsg { } } -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct MigrateMsg {} -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub enum ExecuteMsg { /// Announcing a name pointing to a nym-address Register { name: NameDetails, owner_signature: MessageSignature, }, + /// Delete a name entry by id DeleteId { name_id: NameId }, + /// Delete a name entry by name DeleteName { name: NymName }, + /// Change the deposit required for announcing a name UpdateDepositRequired { deposit_required: Coin }, } @@ -61,33 +74,42 @@ impl ExecuteMsg { } } -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cw_serde] +#[cfg_attr(feature = "schema", derive(QueryResponses))] pub enum QueryMsg { /// Query the name by it's assigned id - NameId { - name_id: NameId, - }, - // Query the names by the registrator - ByOwner { - owner: String, - }, - ByName { - name: NymName, - }, - ByAddress { - address: Address, - }, + #[cfg_attr(feature = "schema", returns(RegisteredName))] + NameId { name_id: NameId }, + + /// Query the names by the registrator + #[cfg_attr(feature = "schema", returns(NamesListResponse))] + ByOwner { owner: String }, + + #[cfg_attr(feature = "schema", returns(RegisteredName))] + ByName { name: NymName }, + + #[cfg_attr(feature = "schema", returns(NamesListResponse))] + ByAddress { address: Address }, + + #[cfg_attr(feature = "schema", returns(PagedNamesListResponse))] All { limit: Option, start_after: Option, }, - SigningNonce { - address: String, - }, + + #[cfg_attr(feature = "schema", returns(Nonce))] + SigningNonce { address: String }, + + #[cfg_attr(feature = "schema", returns(ConfigResponse))] Config {}, + + /// Gets build information of this contract, such as the commit hash used for the build or rustc version. + #[cfg_attr(feature = "schema", returns(ContractBuildInformation))] GetContractVersion {}, + + /// Gets the stored contract version information that's required by the CW2 spec interface for migrations. #[serde(rename = "get_cw2_contract_version")] + #[cfg_attr(feature = "schema", returns(cw2::ContractVersion))] GetCW2ContractVersion {}, } diff --git a/common/cosmwasm-smart-contracts/name-service/src/response.rs b/common/cosmwasm-smart-contracts/name-service/src/response.rs index 8087195eee..162021d7c7 100644 --- a/common/cosmwasm-smart-contracts/name-service/src/response.rs +++ b/common/cosmwasm-smart-contracts/name-service/src/response.rs @@ -1,10 +1,11 @@ -use crate::{NameId, RegisteredName}; -use cosmwasm_std::Coin; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)] -#[serde(rename_all = "snake_case")] +use crate::{NameId, RegisteredName}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Coin; + +#[cw_serde] pub struct NamesListResponse { pub names: Vec, } @@ -23,11 +24,12 @@ impl From<&[RegisteredName]> for NamesListResponse { } } -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct PagedNamesListResponse { pub names: Vec, pub per_page: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } @@ -45,8 +47,7 @@ impl PagedNamesListResponse { } } -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct ConfigResponse { pub deposit_required: Coin, } diff --git a/common/cosmwasm-smart-contracts/name-service/src/types.rs b/common/cosmwasm-smart-contracts/name-service/src/types.rs index dd44340815..58746576a6 100644 --- a/common/cosmwasm-smart-contracts/name-service/src/types.rs +++ b/common/cosmwasm-smart-contracts/name-service/src/types.rs @@ -1,14 +1,21 @@ -use std::fmt::{Display, Formatter}; +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 +use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Coin}; use nym_contracts_common::IdentityKey; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; +use std::{ + fmt::{Display, Formatter}, + str::FromStr, +}; +use thiserror::Error; + +use crate::error::{NameServiceError, Result}; /// The directory of names are indexed by [`NameId`]. pub type NameId = u32; -#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)] +#[cw_serde] pub struct RegisteredName { /// Unique id assigned to the registerd name. pub id: NameId, @@ -33,7 +40,7 @@ impl RegisteredName { } } -#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)] +#[cw_serde] pub struct NameDetails { /// The name pointing to the nym address pub name: NymName, @@ -48,23 +55,54 @@ pub struct NameDetails { /// String representation of a nym address, which is of the form /// client_id.client_enc@gateway_id. /// NOTE: entirely unvalidated. -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub enum Address { - NymAddress(String), + NymAddress(NymAddressInner), // Possible extension: //Gateway(String) } +#[cw_serde] +pub struct NymAddressInner { + client_id: String, + client_enc: String, + gateway_id: String, +} + +// ADDRESS . ENCRYPTION @ GATEWAY_ID +impl std::fmt::Display for NymAddressInner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}.{}@{}", + self.client_id, self.client_enc, self.gateway_id + ) + } +} + impl Address { /// Create a new nym address. - pub fn new(address: &str) -> Self { - Self::NymAddress(address.to_string()) + pub fn new(address: &str) -> Result { + parse_nym_address(address) + .map(Self::NymAddress) + .ok_or_else(|| NameServiceError::InvalidNymAddress(address.to_string())) } - pub fn as_str(&self) -> &str { + pub fn client_id(&self) -> &str { match self { - Address::NymAddress(address) => address, + Address::NymAddress(address) => &address.client_id, + } + } + + pub fn client_enc(&self) -> &str { + match self { + Address::NymAddress(address) => &address.client_enc, + } + } + + pub fn gateway_id(&self) -> &str { + match self { + Address::NymAddress(address) => &address.gateway_id, } } @@ -78,17 +116,47 @@ impl Address { impl Display for Address { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.as_str()) + match self { + Address::NymAddress(address) => write!(f, "{}", address), + } } } +// A valid nym address is of the form client_id.client_enc@gateway_id +fn parse_nym_address(address: &str) -> Option { + let parts: Vec<&str> = address.split('@').collect(); + if parts.len() != 2 { + return None; + } + + let client_part = parts[0]; + let gateway_part = parts[1]; + + // The client part consists of two parts separated by a dot + let client_parts: Vec<&str> = client_part.split('.').collect(); + if client_parts.len() != 2 { + return None; + } + + // Check that the gateway part does not contain any dots + if gateway_part.contains('.') { + return None; + } + + Some(NymAddressInner { + client_id: client_parts[0].to_string(), + client_enc: client_parts[1].to_string(), + gateway_id: gateway_part.to_string(), + }) +} + /// Name stored and pointing a to a nym-address -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct NymName(String); -#[derive(Debug)] +#[derive(Debug, Error)] pub enum NymNameError { + #[error("invalid name")] InvalidName, } @@ -117,6 +185,14 @@ impl NymName { } } +impl FromStr for NymName { + type Err = NymNameError; + + fn from_str(s: &str) -> Result { + Self::new(s) + } +} + impl Display for NymName { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) diff --git a/common/cosmwasm-smart-contracts/service-provider-directory/Cargo.toml b/common/cosmwasm-smart-contracts/service-provider-directory/Cargo.toml index 3965734b2d..aed3912abd 100644 --- a/common/cosmwasm-smart-contracts/service-provider-directory/Cargo.toml +++ b/common/cosmwasm-smart-contracts/service-provider-directory/Cargo.toml @@ -6,10 +6,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +cosmwasm-schema = { workspace = true } cosmwasm-std = { workspace = true } +cw2 = { workspace = true, optional = true } cw-controllers = { workspace = true } cw-utils = { workspace = true } nym-contracts-common = { path = "../contracts-common", version = "0.5.0" } -schemars = "0.8" -serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true } + +[features] +schema = ["cw2"] \ No newline at end of file diff --git a/common/cosmwasm-smart-contracts/service-provider-directory/src/msg.rs b/common/cosmwasm-smart-contracts/service-provider-directory/src/msg.rs index f66ad787a5..0c7928e079 100644 --- a/common/cosmwasm-smart-contracts/service-provider-directory/src/msg.rs +++ b/common/cosmwasm-smart-contracts/service-provider-directory/src/msg.rs @@ -1,10 +1,22 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + use crate::{NymAddress, ServiceDetails, ServiceId}; +use cosmwasm_schema::cw_serde; use cosmwasm_std::Coin; use nym_contracts_common::signing::MessageSignature; -use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cfg(feature = "schema")] +use crate::{ + response::{ConfigResponse, PagedServicesListResponse, ServicesListResponse}, + types::Service, +}; +#[cfg(feature = "schema")] +use cosmwasm_schema::QueryResponses; +#[cfg(feature = "schema")] +use nym_contracts_common::{signing::Nonce, ContractBuildInformation}; + +#[cw_serde] pub struct InstantiateMsg { pub deposit_required: Coin, } @@ -15,12 +27,10 @@ impl InstantiateMsg { } } -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct MigrateMsg {} -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub enum ExecuteMsg { Announce { service: ServiceDetails, @@ -64,28 +74,37 @@ impl ExecuteMsg { } } -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cw_serde] +#[cfg_attr(feature = "schema", derive(QueryResponses))] pub enum QueryMsg { - ServiceId { - service_id: ServiceId, - }, - ByAnnouncer { - announcer: String, - }, - ByNymAddress { - nym_address: NymAddress, - }, + #[cfg_attr(feature = "schema", returns(Service))] + ServiceId { service_id: ServiceId }, + + #[cfg_attr(feature = "schema", returns(ServicesListResponse))] + ByAnnouncer { announcer: String }, + + #[cfg_attr(feature = "schema", returns(ServicesListResponse))] + ByNymAddress { nym_address: NymAddress }, + + #[cfg_attr(feature = "schema", returns(PagedServicesListResponse))] All { limit: Option, start_after: Option, }, - SigningNonce { - address: String, - }, + + #[cfg_attr(feature = "schema", returns(Nonce))] + SigningNonce { address: String }, + + #[cfg_attr(feature = "schema", returns(ConfigResponse))] Config {}, + + /// Gets build information of this contract, such as the commit hash used for the build or rustc version. + #[cfg_attr(feature = "schema", returns(ContractBuildInformation))] GetContractVersion {}, + + /// Gets the stored contract version information that's required by the CW2 spec interface for migrations. #[serde(rename = "get_cw2_contract_version")] + #[cfg_attr(feature = "schema", returns(cw2::ContractVersion))] GetCW2ContractVersion {}, } diff --git a/common/cosmwasm-smart-contracts/service-provider-directory/src/response.rs b/common/cosmwasm-smart-contracts/service-provider-directory/src/response.rs index 8b6bf16fbf..dd9392c2d9 100644 --- a/common/cosmwasm-smart-contracts/service-provider-directory/src/response.rs +++ b/common/cosmwasm-smart-contracts/service-provider-directory/src/response.rs @@ -1,17 +1,17 @@ -use crate::{Service, ServiceId}; -use cosmwasm_std::Coin; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +use crate::{Service, ServiceId}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Coin; + +#[cw_serde] pub struct ServiceInfoResponse { pub service_id: ServiceId, pub service: Option, } -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct ServicesListResponse { pub services: Vec, } @@ -30,11 +30,12 @@ impl From<&[Service]> for ServicesListResponse { } } -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct PagedServicesListResponse { pub services: Vec, pub per_page: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } @@ -52,8 +53,7 @@ impl PagedServicesListResponse { } } -#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct ConfigResponse { pub deposit_required: Coin, } diff --git a/common/cosmwasm-smart-contracts/service-provider-directory/src/signing_types.rs b/common/cosmwasm-smart-contracts/service-provider-directory/src/signing_types.rs index 742470b2cd..fb9752d10a 100644 --- a/common/cosmwasm-smart-contracts/service-provider-directory/src/signing_types.rs +++ b/common/cosmwasm-smart-contracts/service-provider-directory/src/signing_types.rs @@ -1,15 +1,17 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::ServiceDetails; +use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Coin}; use nym_contracts_common::signing::{ ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose, }; -use serde::Serialize; - -use crate::ServiceDetails; pub type SignableServiceProviderAnnounceMsg = SignableMessage>; -#[derive(Serialize)] +#[cw_serde] pub struct ServiceProviderAnnounce { service: ServiceDetails, } diff --git a/common/cosmwasm-smart-contracts/service-provider-directory/src/types.rs b/common/cosmwasm-smart-contracts/service-provider-directory/src/types.rs index c3ad296fa2..845c05b768 100644 --- a/common/cosmwasm-smart-contracts/service-provider-directory/src/types.rs +++ b/common/cosmwasm-smart-contracts/service-provider-directory/src/types.rs @@ -1,14 +1,15 @@ -use std::fmt::{Display, Formatter}; +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 +use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, Coin}; use nym_contracts_common::IdentityKey; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; /// The directory of services are indexed by [`ServiceId`]. pub type ServiceId = u32; -#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)] +#[cw_serde] pub struct Service { /// Unique id assigned to the anounced service. pub service_id: ServiceId, @@ -22,7 +23,7 @@ pub struct Service { pub deposit: Coin, } -#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, JsonSchema)] +#[cw_serde] pub struct ServiceDetails { /// The address of the service. pub nym_address: NymAddress, @@ -33,8 +34,7 @@ pub struct ServiceDetails { } /// The types of addresses supported. -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub enum NymAddress { /// String representation of a nym address, which is of the form /// client_id.client_enc@gateway_id. @@ -64,8 +64,7 @@ impl Display for NymAddress { } /// The type of services provider supported -#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub enum ServiceType { NetworkRequester, } diff --git a/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml b/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml index 516f8a8e35..3fd7e8a377 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml +++ b/common/cosmwasm-smart-contracts/vesting-contract/Cargo.toml @@ -9,11 +9,14 @@ repository = { workspace = true } [dependencies] cosmwasm-std = { workspace = true } +cosmwasm-schema = { workspace = true } +cw2 = { workspace = true, optional = true } mixnet-contract-common = { path = "../mixnet-contract", package = "nym-mixnet-contract-common", version = "0.6.0" } contracts-common = { path = "../contracts-common", package = "nym-contracts-common", version = "0.5.0" } serde = { version = "1.0", features = ["derive"] } -schemars = "0.8" -ts-rs = {version = "6.1.2", optional = true} +thiserror = "1.0" +ts-rs = { workspace = true, optional = true} [features] +schema = ["cw2"] generate-ts = ["ts-rs"] diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/account.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/account.rs new file mode 100644 index 0000000000..0d1e5b754e --- /dev/null +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/account.rs @@ -0,0 +1,124 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::{Period, PledgeCap, VestingContractError, VestingPeriod}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Coin, Timestamp}; + +// this shouldn't really be exposed like this, but we really don't want to migrate the contract just for this... +pub type VestingAccountStorageKey = u32; + +/// Vesting account information. +#[cw_serde] +pub struct Account { + /// Address of the owner of the vesting account. + pub owner_address: Addr, + + /// Optional address of an account allowed to perform staking on behalf of the owner. + pub staking_address: Option, + + /// The starting vesting time. + pub start_time: Timestamp, + + /// All vesting periods for this account. + pub periods: Vec, + + /// The initial amount of coins used creation of this account. + pub coin: Coin, + + /// The id/storage_key of this vesting account. + pub storage_key: VestingAccountStorageKey, + + /// Optional custom pledge cap of this vesting account. + #[serde(default)] + pub pledge_cap: Option, +} + +impl Account { + pub fn pledge_cap(&self) -> PledgeCap { + self.pledge_cap.clone().unwrap_or_default() + } + + pub fn coin(&self) -> Coin { + self.coin.clone() + } + + pub fn num_vesting_periods(&self) -> usize { + self.periods.len() + } + + pub fn period_duration(&self) -> Result { + self.periods + .get(0) + .ok_or(VestingContractError::UnpopulatedVestingPeriods { + owner: self.owner_address.clone(), + }) + .map(|p| p.period_seconds) + } + + pub fn storage_key(&self) -> u32 { + self.storage_key + } + + pub fn owner_address(&self) -> Addr { + self.owner_address.clone() + } + + pub fn staking_address(&self) -> Option<&Addr> { + self.staking_address.as_ref() + } + + pub fn periods(&self) -> Vec { + self.periods.clone() + } + + pub fn start_time(&self) -> Timestamp { + self.start_time + } + + pub fn tokens_per_period(&self) -> Result { + let amount = self.coin.amount.u128(); + if amount < self.num_vesting_periods() as u128 { + Err(VestingContractError::ImprobableVestingAmount(amount)) + } else { + // Remainder tokens will be lumped into the last period. + Ok(amount / self.num_vesting_periods() as u128) + } + } + + /// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet. + /// In case vesting is over it will always return NUM_VESTING_PERIODS. + pub fn get_current_vesting_period( + &self, + block_time: Timestamp, + ) -> Result { + let first_period = + self.periods + .first() + .ok_or(VestingContractError::UnpopulatedVestingPeriods { + owner: self.owner_address.clone(), + })?; + + let last_period = + self.periods + .last() + .ok_or(VestingContractError::UnpopulatedVestingPeriods { + owner: self.owner_address.clone(), + })?; + + if block_time.seconds() < first_period.start_time { + Ok(Period::Before) + } else if last_period.end_time() < block_time { + Ok(Period::After) + } else { + let mut index = 0; + for period in &self.periods { + if block_time < period.end_time() { + break; + } + index += 1; + } + Ok(Period::In(index)) + } + } +} diff --git a/contracts/vesting/src/errors.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/error.rs similarity index 92% rename from contracts/vesting/src/errors.rs rename to common/cosmwasm-smart-contracts/vesting-contract/src/error.rs index 2b92be0c8c..65825c6ad9 100644 --- a/contracts/vesting/src/errors.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/error.rs @@ -1,10 +1,13 @@ -use crate::storage::AccountStorageKey; +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::account::VestingAccountStorageKey; use cosmwasm_std::{Addr, Coin, OverflowError, StdError, Uint128}; use mixnet_contract_common::MixId; use thiserror::Error; #[derive(Error, Debug, PartialEq)] -pub enum ContractError { +pub enum VestingContractError { #[error("VESTING ({}): {0}", line!())] Std(#[from] StdError), @@ -27,7 +30,7 @@ pub enum ContractError { InsufficientSpendable(String, u128), #[error( - "VESTING ({}):Only delegation owner can perform delegation actions, {0} is not the delegation owner" + "VESTING ({}):Only delegation owner can perform delegation actions, {0} is not the delegation owner" , line!())] NotDelegate(String), @@ -91,7 +94,7 @@ pub enum ContractError { #[error("VESTING: {address} ({acc_id} has already performed {num} individual delegations towards {mix_id}. No further delegations are allowed. Please consider consolidating those delegations instead. The current cap is {cap}.")] TooManyDelegations { address: Addr, - acc_id: AccountStorageKey, + acc_id: VestingAccountStorageKey, mix_id: MixId, num: u32, cap: u32, diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs index 6c456674a5..31fab7a7ef 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/lib.rs @@ -4,83 +4,31 @@ #![warn(clippy::expect_used)] #![warn(clippy::unwrap_used)] -use contracts_common::Percent; -use cosmwasm_std::{Addr, Coin, Timestamp, Uint128}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Coin}; use mixnet_contract_common::MixId; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use std::str::FromStr; - -pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg}; +pub mod account; +pub mod error; pub mod events; pub mod messages; +pub mod types; -#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] -#[cfg_attr( - feature = "generate-ts", - ts(export_to = "ts-packages/types/src/types/rust/Period.ts") -)] -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)] -pub enum Period { - Before, - In(usize), - After, -} +pub use account::Account; +pub use error::VestingContractError; +pub use messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg}; +pub use types::*; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -pub struct PledgeData { - pub amount: Coin, - pub block_time: Timestamp, -} - -impl PledgeData { - pub fn amount(&self) -> Coin { - self.amount.clone() - } - - pub fn block_time(&self) -> Timestamp { - self.block_time - } - - pub fn new(amount: Coin, block_time: Timestamp) -> Self { - Self { amount, block_time } - } -} - -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -pub enum PledgeCap { - Percent(Percent), - Absolute(Uint128), // This has to be in unym -} - -impl FromStr for PledgeCap { - type Err = String; - - fn from_str(cap: &str) -> Result { - let cap = cap.replace('_', "").replace(',', "."); - match Percent::from_str(&cap) { - Ok(p) => Ok(PledgeCap::Percent(p)), - Err(_) => match cap.parse::() { - Ok(i) => Ok(PledgeCap::Absolute(Uint128::from(i))), - Err(_e) => Err(format!("Could not parse {cap} as Percent or Uint128")), - }, - } - } -} - -impl Default for PledgeCap { - fn default() -> Self { - #[allow(clippy::expect_used)] - PledgeCap::Percent(Percent::from_percentage_value(10).expect("This can never fail!")) - } -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +/// Details about the original vesting specification used when the account was created. +#[cw_serde] pub struct OriginalVestingResponse { + /// The original amount that was used for the creation of this vesting account pub amount: Coin, + + /// The number of vesting periods that the account was created with pub number_of_periods: usize, + + /// Duration of each vesting period in seconds pub period_duration: u64, } @@ -106,57 +54,73 @@ impl OriginalVestingResponse { } } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)] -pub struct VestingDelegation { - pub account_id: u32, - pub mix_id: MixId, - pub block_timestamp: u64, - pub amount: Uint128, -} - -impl VestingDelegation { - pub fn storage_key(&self) -> (u32, MixId, u64) { - (self.account_id, self.mix_id, self.block_timestamp) - } -} - -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)] +/// Response containing timestamps of all delegations made towards particular mixnode by given vesting account. +#[cw_serde] pub struct DelegationTimesResponse { + /// Address of this account's owner pub owner: Addr, + + /// Id associated with this account pub account_id: u32, + + /// Id of the mixnode towards which the delegation was made pub mix_id: MixId, + + /// All timestamps where a delegation was made pub delegation_timestamps: Vec, } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, JsonSchema)] +/// Response containing paged list of all vesting delegations made using vesting coins. +#[cw_serde] pub struct AllDelegationsResponse { + /// The actual vesting delegations made. pub delegations: Vec, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option<(u32, MixId, u64)>, } -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)] +/// Basic information regarding particular vesting account alongside the amount of vesting coins. +#[cw_serde] pub struct AccountVestingCoins { + /// Id associated with this account pub account_id: u32, + + /// Address of this account's owner pub owner: Addr, + + /// Coins that are still vesting belonging to this account. pub still_vesting: Coin, } -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)] +/// Response containing vesting coins held in this contract +#[cw_serde] pub struct VestingCoinsResponse { + /// The actual accounts, and their vesting coins, returned by the query. pub accounts: Vec, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)] +/// Basic information regarding particular vesting account +#[cw_serde] pub struct BaseVestingAccountInfo { + /// Id associated with this account pub account_id: u32, + + /// Address of this account's owner pub owner: Addr, // TODO: should this particular query/response expose anything else? } -#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)] +/// Response containing basic vesting account information +#[cw_serde] pub struct AccountsResponse { + /// The actual accounts returned by the query. pub accounts: Vec, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs index f4d7147e2c..6593926445 100644 --- a/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/messages.rs @@ -1,4 +1,9 @@ +// Copyright 2021-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::{PledgeCap, VestingSpecification}; use contracts_common::signing::MessageSignature; +use cosmwasm_schema::cw_serde; use cosmwasm_std::{Coin, Timestamp}; use mixnet_contract_common::families::FamilyHead; use mixnet_contract_common::{ @@ -6,57 +11,30 @@ use mixnet_contract_common::{ mixnode::{MixNodeConfigUpdate, MixNodeCostParams}, Gateway, IdentityKey, MixId, MixNode, }; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use crate::PledgeCap; +#[cfg(feature = "schema")] +use contracts_common::ContractBuildInformation; +#[cfg(feature = "schema")] +use cosmwasm_schema::QueryResponses; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cfg(feature = "schema")] +use crate::{ + account::Account, + types::{Period, PledgeData, VestingDelegation}, + AccountsResponse, AllDelegationsResponse, DelegationTimesResponse, OriginalVestingResponse, + VestingCoinsResponse, +}; + +#[cw_serde] pub struct InitMsg { pub mixnet_contract_address: String, pub mix_denom: String, } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub struct MigrateMsg {} -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, Default)] -pub struct VestingSpecification { - start_time: Option, - period_seconds: Option, - num_periods: Option, -} - -impl VestingSpecification { - pub fn new( - start_time: Option, - period_seconds: Option, - num_periods: Option, - ) -> Self { - Self { - start_time, - period_seconds, - num_periods, - } - } - - pub fn start_time(&self) -> Option { - self.start_time - } - - pub fn period_seconds(&self) -> u64 { - self.period_seconds.unwrap_or(3 * 30 * 86400) - } - - pub fn num_periods(&self) -> u64 { - self.num_periods.unwrap_or(8) - } -} - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +#[cw_serde] pub enum ExecuteMsg { // Families /// Only owner of the node can crate the family with node as head @@ -197,93 +175,226 @@ impl ExecuteMsg { } } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -#[serde(rename_all = "snake_case")] +/// Queries exposed by this contract. +#[cw_serde] +#[cfg_attr(feature = "schema", derive(QueryResponses))] pub enum QueryMsg { + /// Gets build information of this contract, such as the commit hash used for the build or rustc version. + #[cfg_attr(feature = "schema", returns(ContractBuildInformation))] GetContractVersion {}, + + /// Gets the stored contract version information that's required by the CW2 spec interface for migrations. #[serde(rename = "get_cw2_contract_version")] + #[cfg_attr(feature = "schema", returns(cw2::ContractVersion))] GetCW2ContractVersion {}, + + /// Gets the list of vesting accounts held in this contract. + #[cfg_attr(feature = "schema", returns(AccountsResponse))] GetAccountsPaged { + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_next_after: Option, + + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, }, + + /// Gets the list of coins that are still vesting for each account held in this contract. + #[cfg_attr(feature = "schema", returns(VestingCoinsResponse))] GetAccountsVestingCoinsPaged { + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_next_after: Option, + + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, }, + + /// Returns the amount of locked coins for the provided vesting account, + /// i.e. coins that are still vesting but have not been staked. + /// `locked_coins = vesting_coins - staked_coins` + #[cfg_attr(feature = "schema", returns(Coin))] LockedCoins { + /// Address of the vesting account in question. vesting_account_address: String, + + /// (deprecated) Optional argument specifying that the query should be performed against non-current block. block_time: Option, }, + + /// Returns the amount of spendable coins for the provided vesting account, + /// i.e. coins that could be withdrawn. + /// `spendable_coins = account_balance - locked_coins` + /// note: `account_balance` is the amount of coins still physically present in this contract, i.e. not withdrawn or staked. + #[cfg_attr(feature = "schema", returns(Coin))] SpendableCoins { + /// Address of the vesting account in question. vesting_account_address: String, + + /// (deprecated) Optional argument specifying that the query should be performed against non-current block. block_time: Option, }, + + /// Returns the amount of coins that have already vested for the provided vesting account address. + #[cfg_attr(feature = "schema", returns(Coin))] GetVestedCoins { + /// Address of the vesting account in question. vesting_account_address: String, + + /// (deprecated) Optional argument specifying that the query should be performed against non-current block. block_time: Option, }, + + /// Returns the amount of coins that are still vesting for the provided vesting account address. + #[cfg_attr(feature = "schema", returns(Coin))] GetVestingCoins { + /// Address of the vesting account in question. vesting_account_address: String, + + /// (deprecated) Optional argument specifying that the query should be performed against non-current block. block_time: Option, }, + + /// Returns the starting vesting time for the provided vesting account, + /// i.e. the beginning of the first vesting period. + #[cfg_attr(feature = "schema", returns(Timestamp))] GetStartTime { + /// Address of the vesting account in question. vesting_account_address: String, }, + + /// Returns the ending vesting time for the provided vesting account, + /// i.e. the end of the last vesting period. + #[cfg_attr(feature = "schema", returns(Timestamp))] GetEndTime { + /// Address of the vesting account in question. vesting_account_address: String, }, + + /// Returns the initial vesting specification used for the provided vesting account address. + #[cfg_attr(feature = "schema", returns(OriginalVestingResponse))] GetOriginalVesting { + /// Address of the vesting account in question. vesting_account_address: String, }, + + /// Returns the total amount of coins accrued through claimed staking rewards by the provided vesting account. + #[cfg_attr(feature = "schema", returns(Coin))] GetHistoricalVestingStakingReward { + /// Address of the vesting account in question. vesting_account_address: String, }, + + /// Returns the amount of spendable vesting coins for the provided vesting account, + /// i.e. coins that could be withdrawn that originated from the vesting specification. + #[cfg_attr(feature = "schema", returns(Coin))] GetSpendableVestedCoins { + /// Address of the vesting account in question. vesting_account_address: String, }, + + /// Returns the amount of spendable reward coins for the provided vesting account, + /// i.e. coins that could be withdrawn that originated from the claimed staking rewards. + #[cfg_attr(feature = "schema", returns(Coin))] GetSpendableRewardCoins { + /// Address of the vesting account in question. vesting_account_address: String, }, + + /// Returns the amount of coins that are currently delegated for the provided vesting account address. + #[cfg_attr(feature = "schema", returns(Coin))] GetDelegatedCoins { + /// Address of the vesting account in question. vesting_account_address: String, }, + + /// Returns the amount of coins that are currently pledged for the provided vesting account address. + #[cfg_attr(feature = "schema", returns(Coin))] GetPledgedCoins { + /// Address of the vesting account in question. vesting_account_address: String, }, + + /// Returns the amount of coins that are currently staked (i.e. delegations + pledges) for the provided vesting account address. + #[cfg_attr(feature = "schema", returns(Coin))] GetStakedCoins { + /// Address of the vesting account in question. vesting_account_address: String, }, + + /// Returns the amount of coins that got withdrawn for the provided vesting account address. + #[cfg_attr(feature = "schema", returns(Coin))] GetWithdrawnCoins { + /// Address of the vesting account in question. vesting_account_address: String, }, + + /// Returns detailed information associated with the account for the provided vesting account address. + #[cfg_attr(feature = "schema", returns(Account))] GetAccount { + /// Address of the vesting account in question. address: String, }, + + /// Returns pledge information (if applicable) for bonded mixnode for the provided vesting account address. + #[cfg_attr(feature = "schema", returns(Option))] GetMixnode { + /// Address of the vesting account in question. address: String, }, + + /// Returns pledge information (if applicable) for bonded gateway for the provided vesting account address. + #[cfg_attr(feature = "schema", returns(Option))] GetGateway { + /// Address of the vesting account in question. address: String, }, + + /// Returns the current vesting period for the provided vesting account address. + #[cfg_attr(feature = "schema", returns(Period))] GetCurrentVestingPeriod { + /// Address of the vesting account in question. address: String, }, + + /// Returns the information about particular vesting delegation. + #[cfg_attr(feature = "schema", returns(VestingDelegation))] GetDelegation { + /// Address of the vesting account in question. address: String, + + /// Id of the mixnode towards which the delegation has been made. mix_id: MixId, + + /// Block timestamp of the delegation. block_timestamp_secs: u64, }, + + /// Returns the total amount of coins delegated towards particular mixnode by the provided vesting account address. + #[cfg_attr(feature = "schema", returns(Coin))] GetTotalDelegationAmount { + /// Address of the vesting account in question. address: String, + + /// Id of the mixnode towards which the delegations have been made. mix_id: MixId, }, + + /// Returns timestamps of delegations made towards particular mixnode by the provided vesting account address. + #[cfg_attr(feature = "schema", returns(DelegationTimesResponse))] GetDelegationTimes { + /// Address of the vesting account in question. address: String, + + /// Id of the mixnode towards which the delegations have been made. mix_id: MixId, }, + + /// Returns all active delegations made with vesting tokens stored in this contract. + #[cfg_attr(feature = "schema", returns(AllDelegationsResponse))] GetAllDelegations { + /// Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response. start_after: Option<(u32, MixId, u64)>, + + /// Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default. limit: Option, }, } diff --git a/common/cosmwasm-smart-contracts/vesting-contract/src/types.rs b/common/cosmwasm-smart-contracts/vesting-contract/src/types.rs new file mode 100644 index 0000000000..e4c1644a94 --- /dev/null +++ b/common/cosmwasm-smart-contracts/vesting-contract/src/types.rs @@ -0,0 +1,171 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use contracts_common::Percent; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Coin, Timestamp, Uint128}; +use mixnet_contract_common::MixId; +use std::str::FromStr; + +#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] +#[cfg_attr( + feature = "generate-ts", + ts(export_to = "ts-packages/types/src/types/rust/Period.ts") +)] +#[cw_serde] +/// The vesting period. +pub enum Period { + /// Defines a pre-vesting period. + #[serde(alias = "Before")] + Before, + + /// Defines currently active vesting period. + #[serde(alias = "In")] + In(usize), + + /// Defines a post-vesting period. + #[serde(alias = "After")] + After, +} + +/// Information regarding pledge (i.e. mixnode or gateway bonding) made with vesting tokens. +#[cw_serde] +pub struct PledgeData { + /// The amount pledged. + pub amount: Coin, + + /// The block timestamp where the pledge occurred. + pub block_time: Timestamp, +} + +impl PledgeData { + pub fn amount(&self) -> Coin { + self.amount.clone() + } + + pub fn block_time(&self) -> Timestamp { + self.block_time + } + + pub fn new(amount: Coin, block_time: Timestamp) -> Self { + Self { amount, block_time } + } +} + +/// Defines cap for pleding/staking tokens. +#[cw_serde] +pub enum PledgeCap { + /// Specifies a percent-based pledge cap, i.e. only given % of tokens could be pledged/staked. + #[serde(alias = "Percent")] + Percent(Percent), + + /// Specifies an absolute pledge cap, i.e. an explicit value that could be pledged/staked. + #[serde(alias = "Absolute")] + Absolute(Uint128), // This has to be in unym +} + +impl FromStr for PledgeCap { + type Err = String; + + fn from_str(cap: &str) -> Result { + let cap = cap.replace('_', "").replace(',', "."); + match Percent::from_str(&cap) { + Ok(p) => Ok(PledgeCap::Percent(p)), + Err(_) => match cap.parse::() { + Ok(i) => Ok(PledgeCap::Absolute(Uint128::from(i))), + Err(_e) => Err(format!("Could not parse {cap} as Percent or Uint128")), + }, + } + } +} + +impl Default for PledgeCap { + fn default() -> Self { + #[allow(clippy::expect_used)] + PledgeCap::Percent(Percent::from_percentage_value(10).expect("This can never fail!")) + } +} + +/// Vesting period details. +#[cw_serde] +pub struct VestingPeriod { + /// The start time of this vesting period, as unix timestamp. + pub start_time: u64, + + /// The duration (in seconds) of the vesting period. + pub period_seconds: u64, +} + +impl VestingPeriod { + pub fn end_time(&self) -> Timestamp { + Timestamp::from_seconds(self.start_time + self.period_seconds) + } +} + +#[cw_serde] +#[derive(Default)] +pub struct VestingSpecification { + start_time: Option, + period_seconds: Option, + num_periods: Option, +} + +impl VestingSpecification { + pub fn new( + start_time: Option, + period_seconds: Option, + num_periods: Option, + ) -> Self { + Self { + start_time, + period_seconds, + num_periods, + } + } + + pub fn start_time(&self) -> Option { + self.start_time + } + + pub fn period_seconds(&self) -> u64 { + self.period_seconds.unwrap_or(3 * 30 * 86400) + } + + pub fn num_periods(&self) -> u64 { + self.num_periods.unwrap_or(8) + } + + pub fn populate_vesting_periods(&self, start_time: u64) -> Vec { + let mut periods = Vec::with_capacity(self.num_periods() as usize); + for i in 0..self.num_periods() { + let period = VestingPeriod { + start_time: start_time + i * self.period_seconds(), + period_seconds: self.period_seconds(), + }; + periods.push(period); + } + periods + } +} + +/// Details about particular vesting delegation. +#[cw_serde] +pub struct VestingDelegation { + /// The id of the vesting account that has made the delegation. + pub account_id: u32, + + /// The id of the mixnode towards which the delegation has been made. + pub mix_id: MixId, + + /// The block timestamp when the delegation has been made. + pub block_timestamp: u64, + + /// The raw amount delegated (interpreted to be in the same denom as the underlying vesting specification) + pub amount: Uint128, +} + +impl VestingDelegation { + pub fn storage_key(&self) -> (u32, MixId, u64) { + (self.account_id, self.mix_id, self.block_timestamp) + } +} diff --git a/clients/credential/Cargo.toml b/common/credential-utils/Cargo.toml similarity index 54% rename from clients/credential/Cargo.toml rename to common/credential-utils/Cargo.toml index 8ceb14bc98..1949107036 100644 --- a/clients/credential/Cargo.toml +++ b/common/credential-utils/Cargo.toml @@ -1,25 +1,18 @@ [package] -name = "nym-credential-client" +name = "nym-credential-utils" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.0", features = ["cargo", "derive"] } -log = "0.4" -serde = { workspace = true, features = ["derive"] } -thiserror = "1.0" -tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime - +log = { workspace = true } +thiserror = { workspace = true } +tokio = { workspace = true } nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } -nym-client-core = { path = "../../common/client-core" } -nym-config = { path = "../../common/config" } nym-credentials = { path = "../../common/credentials" } 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" } - +nym-config = { path = "../../common/config" } +nym-client-core = { path = "../../common/client-core" } diff --git a/common/credential-utils/src/errors.rs b/common/credential-utils/src/errors.rs new file mode 100644 index 0000000000..6407929a99 --- /dev/null +++ b/common/credential-utils/src/errors.rs @@ -0,0 +1,31 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use nym_credential_storage::error::StorageError; +use nym_credentials::error::Error as CredentialError; +use nym_validator_client::nyxd::error::NyxdError; +use std::num::ParseIntError; +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Error, Debug)] +pub enum Error { + #[error(transparent)] + IOError(#[from] std::io::Error), + + #[error(transparent)] + BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError), + + #[error(transparent)] + Nyxd(#[from] NyxdError), + + #[error(transparent)] + Credential(#[from] CredentialError), + + #[error("Could not use shared storage: {0}")] + SharedStorageError(#[from] StorageError), + + #[error("failed to parse credential value: {0}")] + MalformedCredentialValue(#[from] ParseIntError), +} diff --git a/common/credential-utils/src/lib.rs b/common/credential-utils/src/lib.rs new file mode 100644 index 0000000000..6e5fbc3158 --- /dev/null +++ b/common/credential-utils/src/lib.rs @@ -0,0 +1,5 @@ +pub mod errors; +pub mod recovery_storage; +pub mod utils; + +pub use errors::{Error, Result}; diff --git a/common/credential-utils/src/recovery_storage.rs b/common/credential-utils/src/recovery_storage.rs new file mode 100644 index 0000000000..1793553c62 --- /dev/null +++ b/common/credential-utils/src/recovery_storage.rs @@ -0,0 +1,64 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::errors::Result; +use log::error; +use nym_credentials::coconut::bandwidth::BandwidthVoucher; +use std::fs::{create_dir_all, read_dir, File}; +use std::io::{Read, Write}; +use std::path::PathBuf; + +pub struct RecoveryStorage { + recovery_dir: PathBuf, +} + +impl RecoveryStorage { + pub fn new(recovery_dir: PathBuf) -> Result { + create_dir_all(&recovery_dir)?; + Ok(Self { recovery_dir }) + } + + pub fn unconsumed_vouchers(&self) -> Result> { + let entries = read_dir(&self.recovery_dir)?; + + let mut paths = vec![]; + for entry in entries.flatten() { + let path = entry.path(); + if path.is_file() { + paths.push(path) + } + } + + let mut vouchers = vec![]; + for path in paths { + if let Ok(mut file) = File::open(&path) { + let mut buff = Vec::new(); + if file.read_to_end(&mut buff).is_ok() { + match BandwidthVoucher::try_from_bytes(&buff) { + Ok(voucher) => vouchers.push(voucher), + Err(err) => { + error!("failed to parse the voucher at {}: {err}", path.display()) + } + } + } + } + } + + Ok(vouchers) + } + + pub fn insert_voucher(&self, voucher: &BandwidthVoucher) -> Result { + let file_name = voucher.tx_hash().to_string(); + let file_path = self.recovery_dir.join(file_name); + let mut file = File::create(&file_path)?; + let buff = voucher.to_bytes(); + file.write_all(&buff)?; + + Ok(file_path) + } + + pub fn remove_voucher(&self, file_name: String) -> Result<()> { + let file_path = self.recovery_dir.join(file_name); + Ok(std::fs::remove_file(file_path)?) + } +} diff --git a/common/credential-utils/src/utils.rs b/common/credential-utils/src/utils.rs new file mode 100644 index 0000000000..2f92b53c90 --- /dev/null +++ b/common/credential-utils/src/utils.rs @@ -0,0 +1,139 @@ +use crate::errors::{Error, Result}; +use crate::recovery_storage::RecoveryStorage; +use log::*; +use nym_bandwidth_controller::acquire::state::State; +use nym_client_core::config::disk_persistence::CommonClientPaths; +use nym_config::DEFAULT_DATA_DIR; +use nym_credential_storage::persistent_storage::PersistentStorage; +use nym_validator_client::nyxd::contract_traits::{CoconutBandwidthSigningClient, DkgQueryClient}; +use nym_validator_client::nyxd::Coin; +use std::path::PathBuf; +use std::process::exit; +use std::time::{Duration, SystemTime}; + +const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute + +pub async fn issue_credential( + client: &C, + amount: Coin, + persistent_storage: &PersistentStorage, + recovery_storage_path: PathBuf, +) -> Result<()> +where + C: DkgQueryClient + CoconutBandwidthSigningClient + Send + Sync, +{ + let recovery_storage = setup_recovery_storage(recovery_storage_path).await; + + block_until_coconut_is_available(client).await?; + info!("Starting to deposit funds, don't kill the process"); + + if let Ok(recovered_amount) = + recover_credentials(client, &recovery_storage, persistent_storage).await + { + if recovered_amount != 0 { + info!( + "Recovered credentials in the amount of {}", + recovered_amount + ); + return Ok(()); + } + }; + + let state = nym_bandwidth_controller::acquire::deposit(client, amount.clone()).await?; + + if nym_bandwidth_controller::acquire::get_credential(&state, client, persistent_storage) + .await + .is_err() + { + warn!("Failed to obtain credential. Dumping recovery data.",); + match recovery_storage.insert_voucher(&state.voucher) { + Ok(file_path) => { + warn!("Dumped recovery data to {}. Try using recovery mode to convert it to a credential", file_path.to_str().unwrap()); + } + Err(e) => { + error!("Could not dump recovery data to file system due to {:?}, the deposit will be lost!", e) + } + } + + return Err(Error::Credential( + nym_credentials::error::Error::BandwidthCredentialError, + )); + } + + info!("Succeeded adding a credential with amount {amount}"); + + Ok(()) +} + +pub async fn setup_recovery_storage(recovery_dir: PathBuf) -> RecoveryStorage { + RecoveryStorage::new(recovery_dir).expect("") +} + +pub async fn setup_persistent_storage(client_home_directory: PathBuf) -> PersistentStorage { + let data_dir = client_home_directory.join(DEFAULT_DATA_DIR); + let paths = CommonClientPaths::new_default(data_dir); + let db_path = paths.credentials_database; + + nym_credential_storage::initialise_persistent_storage(db_path).await +} + +pub async fn block_until_coconut_is_available(client: &C) -> Result<()> +where + C: DkgQueryClient + Send + Sync, +{ + loop { + let epoch = client.get_current_epoch().await?; + let current_timestamp_secs = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("the system clock is set to 01/01/1970 (or earlier)") + .as_secs(); + if epoch.state.is_final() { + if current_timestamp_secs + SAFETY_BUFFER_SECS >= epoch.finish_timestamp.seconds() { + info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60); + exit(0); + } + + break; + } else { + // Use 1 additional second to not start the next iteration immediately and spam get_current_epoch queries + let secs_until_final = epoch + .final_timestamp_secs() + .saturating_sub(current_timestamp_secs) + + 1; + info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final); + tokio::time::sleep(Duration::from_secs(secs_until_final)).await; + } + } + + Ok(()) +} + +pub async fn recover_credentials( + client: &C, + recovery_storage: &RecoveryStorage, + shared_storage: &PersistentStorage, +) -> Result +where + C: DkgQueryClient + Send + Sync, +{ + let mut recovered_amount: u128 = 0; + for voucher in recovery_storage.unconsumed_vouchers()? { + let voucher_value = voucher.get_voucher_value(); + recovered_amount += voucher_value.parse::()?; + + let state = State::new(voucher); + let voucher = state.voucher.tx_hash(); + if let Err(e) = + nym_bandwidth_controller::acquire::get_credential(&state, client, shared_storage).await + { + error!("Could not recover deposit {voucher} due to {e}, try again later",) + } else { + info!("Converted deposit {voucher} to a credential, removing recovery data for it",); + if let Err(e) = recovery_storage.remove_voucher(voucher.to_string()) { + warn!("Could not remove recovery data: {e}"); + } + } + } + + Ok(recovered_amount) +} diff --git a/common/credentials/Cargo.toml b/common/credentials/Cargo.toml index 6bb10d5cb6..aff9cf01ab 100644 --- a/common/credentials/Cargo.toml +++ b/common/credentials/Cargo.toml @@ -8,13 +8,14 @@ edition = "2021" [dependencies] bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] } cosmrs = { workspace = true } -thiserror = "1.0" +thiserror = { workspace = true } +log = { workspace = true } # I guess temporarily until we get serde support in coconut up and running nym-coconut-interface = { path = "../coconut-interface" } nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "hashing"] } nym-api-requests = { path = "../../nym-api/nym-api-requests" } -nym-validator-client = { path = "../client-libs/validator-client" } +nym-validator-client = { path = "../client-libs/validator-client", default-features = false } [dev-dependencies] rand = "0.7.3" diff --git a/common/credentials/src/coconut/utils.rs b/common/credentials/src/coconut/utils.rs index 025297f167..b7fda42f73 100644 --- a/common/credentials/src/coconut/utils.rs +++ b/common/credentials/src/coconut/utils.rs @@ -1,6 +1,10 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use crate::coconut::bandwidth::{BandwidthVoucher, PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES}; +use crate::coconut::params::{NymApiCredentialEncryptionAlgorithm, NymApiCredentialHkdfAlgorithm}; +use crate::error::Error; +use log::{debug, warn}; use nym_api_requests::coconut::BlindSignRequestBody; use nym_coconut_interface::{ aggregate_signature_shares, aggregate_verification_keys, prove_bandwidth_credential, Attribute, @@ -11,10 +15,6 @@ use nym_crypto::shared_key::recompute_shared_key; use nym_crypto::symmetric::stream_cipher; use nym_validator_client::client::CoconutApiClient; -use crate::coconut::bandwidth::{BandwidthVoucher, PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES}; -use crate::coconut::params::{NymApiCredentialEncryptionAlgorithm, NymApiCredentialHkdfAlgorithm}; -use crate::error::Error; - pub async fn obtain_aggregate_verification_key( api_clients: &[CoconutApiClient], ) -> Result { @@ -107,7 +107,12 @@ pub async fn obtain_aggregate_signature( aggregate_verification_keys(&validators_partial_vks, Some(indices.as_ref()))?; for coconut_api_client in coconut_api_clients.iter() { - if let Ok(signature) = obtain_partial_credential( + debug!( + "attempting to obtain partial credential from {}", + coconut_api_client.api_client.api_url() + ); + + match obtain_partial_credential( params, attributes, &coconut_api_client.api_client, @@ -115,9 +120,17 @@ pub async fn obtain_aggregate_signature( ) .await { - let share = SignatureShare::new(signature, coconut_api_client.node_id); - shares.push(share) - } + Ok(signature) => { + let share = SignatureShare::new(signature, coconut_api_client.node_id); + shares.push(share) + } + Err(err) => { + warn!( + "failed to obtain partial credential from {}: {err}", + coconut_api_client.api_client.api_url() + ); + } + }; } if shares.len() < threshold as usize { return Err(Error::NotEnoughShares); diff --git a/common/crypto/Cargo.toml b/common/crypto/Cargo.toml index c6521d9056..992005f2d4 100644 --- a/common/crypto/Cargo.toml +++ b/common/crypto/Cargo.toml @@ -27,14 +27,17 @@ thiserror = "1.0.37" zeroize = { workspace = true, optional = true, features = ["zeroize_derive"] } # internal -nym-sphinx-types = { path = "../nymsphinx/types", version = "0.2.0" } +nym-sphinx-types = { path = "../nymsphinx/types", version = "0.2.0", default-features = false } nym-pemstore = { path = "../../common/pemstore", version = "0.3.0" } [dev-dependencies] rand_chacha = "0.2" [features] +default = ["sphinx"] serde = ["serde_crate", "serde_bytes", "ed25519-dalek/serde", "x25519-dalek/serde"] asymmetric = ["x25519-dalek", "ed25519-dalek", "zeroize"] hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array"] symmetric = ["aes", "ctr", "cipher", "generic-array"] +sphinx = ["nym-sphinx-types/sphinx"] +outfox = ["nym-sphinx-types/outfox"] diff --git a/common/crypto/src/asymmetric/encryption/mod.rs b/common/crypto/src/asymmetric/encryption/mod.rs index 5052e785a0..6c45d41d60 100644 --- a/common/crypto/src/asymmetric/encryption/mod.rs +++ b/common/crypto/src/asymmetric/encryption/mod.rs @@ -273,36 +273,42 @@ impl PemStorableKey for PrivateKey { } // compatibility with sphinx keys: +#[cfg(feature = "sphinx")] impl From for nym_sphinx_types::PublicKey { fn from(key: PublicKey) -> Self { nym_sphinx_types::PublicKey::from(key.to_bytes()) } } +#[cfg(feature = "sphinx")] impl<'a> From<&'a PublicKey> for nym_sphinx_types::PublicKey { fn from(key: &'a PublicKey) -> Self { nym_sphinx_types::PublicKey::from((*key).to_bytes()) } } +#[cfg(feature = "sphinx")] impl From for PublicKey { fn from(pub_key: nym_sphinx_types::PublicKey) -> Self { Self(x25519_dalek::PublicKey::from(*pub_key.as_bytes())) } } +#[cfg(feature = "sphinx")] impl From for nym_sphinx_types::PrivateKey { fn from(key: PrivateKey) -> Self { nym_sphinx_types::PrivateKey::from(key.to_bytes()) } } +#[cfg(feature = "sphinx")] impl<'a> From<&'a PrivateKey> for nym_sphinx_types::PrivateKey { fn from(key: &'a PrivateKey) -> Self { nym_sphinx_types::PrivateKey::from(key.to_bytes()) } } +#[cfg(feature = "sphinx")] impl From for PrivateKey { fn from(private_key: nym_sphinx_types::PrivateKey) -> Self { let private_key_bytes = private_key.to_bytes(); diff --git a/common/crypto/src/asymmetric/identity/mod.rs b/common/crypto/src/asymmetric/identity/mod.rs index c552206da3..efe11acf4f 100644 --- a/common/crypto/src/asymmetric/identity/mod.rs +++ b/common/crypto/src/asymmetric/identity/mod.rs @@ -5,6 +5,7 @@ pub use ed25519_dalek::ed25519::signature::Signature as SignatureTrait; pub use ed25519_dalek::SignatureError; pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH}; use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair}; +#[cfg(feature = "sphinx")] use nym_sphinx_types::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH}; use std::fmt::{self, Display, Formatter}; use std::str::FromStr; @@ -113,6 +114,7 @@ impl Display for PublicKey { } impl PublicKey { + #[cfg(feature = "sphinx")] pub fn derive_destination_address(&self) -> DestinationAddressBytes { let mut temporary_address = [0u8; DESTINATION_ADDRESS_LENGTH]; let public_key_bytes = self.to_bytes(); diff --git a/common/mixnode-common/Cargo.toml b/common/mixnode-common/Cargo.toml index 62fec9422a..b2fcf6e2a3 100644 --- a/common/mixnode-common/Cargo.toml +++ b/common/mixnode-common/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] bytes = "1.0" -futures = "0.3" +futures = { workspace = true } humantime-serde = "1.0" log = { workspace = true } rand = "0.8" @@ -21,8 +21,8 @@ tokio = { version = "1.24.1", features = [ "io-util", ] } tokio-util = { version = "0.7.4", features = ["codec"] } -url = "2.2" -thiserror = "1.0.37" +url = { workspace = true } +thiserror = { workspace = true } ## tracing tracing = { version = "0.1.37", optional = true } diff --git a/common/network-defaults/Cargo.toml b/common/network-defaults/Cargo.toml index f2878ad16a..bc5bddb497 100644 --- a/common/network-defaults/Cargo.toml +++ b/common/network-defaults/Cargo.toml @@ -12,6 +12,7 @@ cfg-if = { workspace = true } dotenvy = { workspace = true } hex-literal = "0.3.3" once_cell = { workspace = true } +schemars = { version = "0.8", features = ["preserve_order"] } serde = { workspace = true, features = ["derive"]} thiserror = { workspace = true } url = { workspace = true } diff --git a/common/network-defaults/src/lib.rs b/common/network-defaults/src/lib.rs index a84175af1e..fe353b16ae 100644 --- a/common/network-defaults/src/lib.rs +++ b/common/network-defaults/src/lib.rs @@ -2,45 +2,48 @@ // SPDX-License-Identifier: Apache-2.0 use crate::var_names::{DEPRECATED_API_VALIDATOR, DEPRECATED_NYMD_VALIDATOR, NYM_API, NYXD}; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use std::path::Path; use std::{ env::{var, VarError}, ffi::OsStr, ops::Not, - path::PathBuf, }; use url::Url; pub mod mainnet; pub mod var_names; -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] pub struct ChainDetails { pub bech32_account_prefix: String, pub mix_denom: DenomDetailsOwned, pub stake_denom: DenomDetailsOwned, } -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] pub struct NymContracts { pub mixnet_contract_address: Option, pub vesting_contract_address: Option, - pub bandwidth_claim_contract_address: Option, pub coconut_bandwidth_contract_address: Option, pub group_contract_address: Option, pub multisig_contract_address: Option, pub coconut_dkg_contract_address: Option, + pub ephemera_contract_address: Option, pub service_provider_directory_contract_address: Option, pub name_service_contract_address: Option, } // I wanted to use the simpler `NetworkDetails` name, but there's a clash // with `NetworkDetails` defined in all.rs... -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] pub struct NymNetworkDetails { + pub network_name: String, pub chain_details: ChainDetails, pub endpoints: Vec, pub contracts: NymContracts, + pub explorer_api: Option, } // by default we assume the same defaults as mainnet, i.e. same prefixes and denoms @@ -53,6 +56,7 @@ impl Default for NymNetworkDetails { impl NymNetworkDetails { pub fn new_empty() -> Self { NymNetworkDetails { + network_name: Default::default(), chain_details: ChainDetails { bech32_account_prefix: Default::default(), mix_denom: DenomDetailsOwned { @@ -68,6 +72,7 @@ impl NymNetworkDetails { }, endpoints: Default::default(), contracts: Default::default(), + explorer_api: Default::default(), } } @@ -81,6 +86,7 @@ impl NymNetworkDetails { } NymNetworkDetails::new_empty() + .with_network_name(var(var_names::NETWORK_NAME).expect("network name not set")) .with_bech32_account_prefix( var(var_names::BECH32_PREFIX).expect("bech32 prefix not set"), ) @@ -102,7 +108,7 @@ impl NymNetworkDetails { .parse() .expect("denomination exponent is not u32"), }) - .with_validator_endpoint(ValidatorDetails::new( + .with_additional_validator_endpoint(ValidatorDetails::new( var(var_names::NYXD).expect("nyxd validator not set"), Some(var(var_names::NYM_API).expect("nym api not set")), )) @@ -112,10 +118,6 @@ impl NymNetworkDetails { .with_vesting_contract(Some( var(var_names::VESTING_CONTRACT_ADDRESS).expect("vesting contract not set"), )) - .with_bandwidth_claim_contract(Some( - var(var_names::BANDWIDTH_CLAIM_CONTRACT_ADDRESS) - .expect("bandwidth claim contract not set"), - )) .with_coconut_bandwidth_contract(Some( var(var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS) .expect("coconut bandwidth contract not set"), @@ -129,10 +131,14 @@ impl NymNetworkDetails { .with_coconut_dkg_contract(Some( var(var_names::COCONUT_DKG_CONTRACT_ADDRESS).expect("coconut dkg contract not set"), )) + .with_ephemera_contract(Some( + var(var_names::EPHEMERA_CONTRACT_ADDRESS).expect("ephemera contract not set"), + )) .with_service_provider_directory_contract(get_optional_env( var_names::SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS, )) .with_name_service_contract(get_optional_env(var_names::NAME_SERVICE_CONTRACT_ADDRESS)) + .with_explorer_api(get_optional_env(var_names::EXPLORER_API)) } pub fn new_mainnet() -> Self { @@ -142,6 +148,7 @@ impl NymNetworkDetails { // Consider caching this process (lazy static) NymNetworkDetails { + network_name: mainnet::NETWORK_NAME.into(), chain_details: ChainDetails { bech32_account_prefix: mainnet::BECH32_PREFIX.into(), mix_denom: mainnet::MIX_DENOM.into(), @@ -151,9 +158,6 @@ impl NymNetworkDetails { contracts: NymContracts { mixnet_contract_address: parse_optional_str(mainnet::MIXNET_CONTRACT_ADDRESS), vesting_contract_address: parse_optional_str(mainnet::VESTING_CONTRACT_ADDRESS), - bandwidth_claim_contract_address: parse_optional_str( - mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS, - ), coconut_bandwidth_contract_address: parse_optional_str( mainnet::COCONUT_BANDWIDTH_CONTRACT_ADDRESS, ), @@ -162,12 +166,24 @@ impl NymNetworkDetails { coconut_dkg_contract_address: parse_optional_str( mainnet::COCONUT_DKG_CONTRACT_ADDRESS, ), + ephemera_contract_address: parse_optional_str(mainnet::EPHEMERA_CONTRACT_ADDRESS), service_provider_directory_contract_address: None, name_service_contract_address: None, }, + explorer_api: parse_optional_str(mainnet::EXPLORER_API), } } + pub fn default_gas_price_amount(&self) -> f64 { + GAS_PRICE_AMOUNT + } + + #[must_use] + pub fn with_network_name(mut self, network_name: String) -> Self { + self.network_name = network_name; + self + } + #[must_use] pub fn with_bech32_account_prefix>(mut self, prefix: S) -> Self { self.chain_details.bech32_account_prefix = prefix.into(); @@ -199,11 +215,17 @@ impl NymNetworkDetails { } #[must_use] - pub fn with_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self { + pub fn with_additional_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self { self.endpoints.push(endpoint); self } + #[must_use] + pub fn with_validator_endpoint(mut self, endpoint: ValidatorDetails) -> Self { + self.endpoints = vec![endpoint]; + self + } + #[must_use] pub fn with_mixnet_contract>(mut self, contract: Option) -> Self { self.contracts.mixnet_contract_address = contract.map(Into::into); @@ -216,12 +238,6 @@ impl NymNetworkDetails { self } - #[must_use] - pub fn with_bandwidth_claim_contract>(mut self, contract: Option) -> Self { - self.contracts.bandwidth_claim_contract_address = contract.map(Into::into); - self - } - #[must_use] pub fn with_coconut_bandwidth_contract>(mut self, contract: Option) -> Self { self.contracts.coconut_bandwidth_contract_address = contract.map(Into::into); @@ -246,6 +262,12 @@ impl NymNetworkDetails { self } + #[must_use] + pub fn with_ephemera_contract>(mut self, contract: Option) -> Self { + self.contracts.ephemera_contract_address = contract.map(Into::into); + self + } + #[must_use] pub fn with_service_provider_directory_contract>( mut self, @@ -260,6 +282,12 @@ impl NymNetworkDetails { self.contracts.name_service_contract_address = contract.map(Into::into); self } + + #[must_use] + pub fn with_explorer_api>(mut self, endpoint: Option) -> Self { + self.explorer_api = endpoint.map(Into::into); + self + } } #[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)] @@ -280,7 +308,7 @@ impl DenomDetails { } } -#[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq)] +#[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq, JsonSchema)] pub struct DenomDetailsOwned { pub base: String, pub display: String, @@ -308,7 +336,7 @@ impl DenomDetailsOwned { } } -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)] pub struct ValidatorDetails { // it is assumed those values are always valid since they're being provided in our defaults file pub nyxd_url: String, @@ -360,7 +388,7 @@ fn fix_deprecated_environmental_variables() { } } -pub fn setup_env(config_env_file: Option<&PathBuf>) { +pub fn setup_env>(config_env_file: Option

) { match std::env::var(var_names::CONFIGURED) { // if the configuration is not already set in the env vars Err(std::env::VarError::NotPresent) => { diff --git a/common/network-defaults/src/mainnet.rs b/common/network-defaults/src/mainnet.rs index dc35157435..bfb3f42531 100644 --- a/common/network-defaults/src/mainnet.rs +++ b/common/network-defaults/src/mainnet.rs @@ -4,6 +4,8 @@ use crate::var_names; use crate::{DenomDetails, ValidatorDetails}; +pub(crate) const NETWORK_NAME: &str = "mainnet"; + pub(crate) const BECH32_PREFIX: &str = "n"; pub const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6); @@ -13,18 +15,19 @@ pub const MIXNET_CONTRACT_ADDRESS: &str = "n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr"; pub const VESTING_CONTRACT_ADDRESS: &str = "n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw"; -pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = - "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const GROUP_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; +pub(crate) const EPHEMERA_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy"; pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/"; pub const NYXD_URL: &str = "https://rpc.nymtech.net"; pub const NYM_API: &str = "https://validator.nymtech.net/api/"; +pub const EXPLORER_API: &str = "https://explorer.nymtech.net/api/"; + pub(crate) fn validators() -> Vec { vec![ValidatorDetails::new(NYXD_URL, Some(NYM_API))] } @@ -56,6 +59,7 @@ pub fn read_var_if_not_default(var: &str) -> Option { pub fn export_to_env() { set_var_to_default(var_names::CONFIGURED, "true"); + set_var_to_default(var_names::NETWORK_NAME, NETWORK_NAME); set_var_to_default(var_names::BECH32_PREFIX, BECH32_PREFIX); set_var_to_default(var_names::MIX_DENOM, MIX_DENOM.base); set_var_to_default(var_names::MIX_DENOM_DISPLAY, MIX_DENOM.display); @@ -70,10 +74,6 @@ pub fn export_to_env() { var_names::VESTING_CONTRACT_ADDRESS, VESTING_CONTRACT_ADDRESS, ); - set_var_to_default( - var_names::BANDWIDTH_CLAIM_CONTRACT_ADDRESS, - BANDWIDTH_CLAIM_CONTRACT_ADDRESS, - ); set_var_to_default( var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS, COCONUT_BANDWIDTH_CONTRACT_ADDRESS, @@ -87,6 +87,10 @@ pub fn export_to_env() { var_names::COCONUT_DKG_CONTRACT_ADDRESS, COCONUT_DKG_CONTRACT_ADDRESS, ); + set_var_to_default( + var_names::EPHEMERA_CONTRACT_ADDRESS, + EPHEMERA_CONTRACT_ADDRESS, + ); set_var_to_default( var_names::REWARDING_VALIDATOR_ADDRESS, REWARDING_VALIDATOR_ADDRESS, @@ -97,10 +101,12 @@ pub fn export_to_env() { ); set_var_to_default(var_names::NYXD, NYXD_URL); set_var_to_default(var_names::NYM_API, NYM_API); + set_var_to_default(var_names::EXPLORER_API, EXPLORER_API); } pub fn export_to_env_if_not_set() { set_var_conditionally_to_default(var_names::CONFIGURED, "true"); + set_var_conditionally_to_default(var_names::NETWORK_NAME, NETWORK_NAME); set_var_conditionally_to_default(var_names::BECH32_PREFIX, BECH32_PREFIX); set_var_conditionally_to_default(var_names::MIX_DENOM, MIX_DENOM.base); set_var_conditionally_to_default(var_names::MIX_DENOM_DISPLAY, MIX_DENOM.display); @@ -115,10 +121,6 @@ pub fn export_to_env_if_not_set() { var_names::VESTING_CONTRACT_ADDRESS, VESTING_CONTRACT_ADDRESS, ); - set_var_conditionally_to_default( - var_names::BANDWIDTH_CLAIM_CONTRACT_ADDRESS, - BANDWIDTH_CLAIM_CONTRACT_ADDRESS, - ); set_var_conditionally_to_default( var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS, COCONUT_BANDWIDTH_CONTRACT_ADDRESS, @@ -132,6 +134,10 @@ pub fn export_to_env_if_not_set() { var_names::COCONUT_DKG_CONTRACT_ADDRESS, COCONUT_DKG_CONTRACT_ADDRESS, ); + set_var_conditionally_to_default( + var_names::EPHEMERA_CONTRACT_ADDRESS, + EPHEMERA_CONTRACT_ADDRESS, + ); set_var_conditionally_to_default( var_names::REWARDING_VALIDATOR_ADDRESS, REWARDING_VALIDATOR_ADDRESS, @@ -142,4 +148,5 @@ pub fn export_to_env_if_not_set() { ); set_var_conditionally_to_default(var_names::NYXD, NYXD_URL); set_var_conditionally_to_default(var_names::NYM_API, NYM_API); + set_var_conditionally_to_default(var_names::EXPLORER_API, EXPLORER_API); } diff --git a/common/network-defaults/src/var_names.rs b/common/network-defaults/src/var_names.rs index ee7b1c9a2a..d082045a03 100644 --- a/common/network-defaults/src/var_names.rs +++ b/common/network-defaults/src/var_names.rs @@ -4,6 +4,8 @@ // Environment variable that, if set, shows the environment is currently configured pub const CONFIGURED: &str = "CONFIGURED"; +pub const NETWORK_NAME: &str = "NETWORK_NAME"; + pub const BECH32_PREFIX: &str = "BECH32_PREFIX"; pub const MIX_DENOM: &str = "MIX_DENOM"; pub const MIX_DENOM_DISPLAY: &str = "MIX_DENOM_DISPLAY"; @@ -12,11 +14,11 @@ pub const STAKE_DENOM_DISPLAY: &str = "STAKE_DENOM_DISPLAY"; pub const DENOMS_EXPONENT: &str = "DENOMS_EXPONENT"; pub const MIXNET_CONTRACT_ADDRESS: &str = "MIXNET_CONTRACT_ADDRESS"; pub const VESTING_CONTRACT_ADDRESS: &str = "VESTING_CONTRACT_ADDRESS"; -pub const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = "BANDWIDTH_CLAIM_CONTRACT_ADDRESS"; pub const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "COCONUT_BANDWIDTH_CONTRACT_ADDRESS"; pub const GROUP_CONTRACT_ADDRESS: &str = "GROUP_CONTRACT_ADDRESS"; pub const MULTISIG_CONTRACT_ADDRESS: &str = "MULTISIG_CONTRACT_ADDRESS"; pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "COCONUT_DKG_CONTRACT_ADDRESS"; +pub const EPHEMERA_CONTRACT_ADDRESS: &str = "EPHEMERA_CONTRACT_ADDRESS"; pub const REWARDING_VALIDATOR_ADDRESS: &str = "REWARDING_VALIDATOR_ADDRESS"; pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "STATISTICS_SERVICE_DOMAIN_ADDRESS"; pub const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str = @@ -24,6 +26,7 @@ pub const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str = pub const NAME_SERVICE_CONTRACT_ADDRESS: &str = "NAME_SERVICE_CONTRACT_ADDRESS"; pub const NYXD: &str = "NYXD"; pub const NYM_API: &str = "NYM_API"; +pub const EXPLORER_API: &str = "EXPLORER_API"; pub const DKG_TIME_CONFIGURATION: &str = "DKG_TIME_CONFIGURATION"; diff --git a/common/node-tester-utils/Cargo.toml b/common/node-tester-utils/Cargo.toml index 39a93d4a9f..32ec845723 100644 --- a/common/node-tester-utils/Cargo.toml +++ b/common/node-tester-utils/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -futures = "0.3.28" +futures = { workspace = true } rand = "0.7.3" serde = { workspace = true } diff --git a/common/nonexhaustive-delayqueue/Cargo.toml b/common/nonexhaustive-delayqueue/Cargo.toml index 975eb631fe..f79284dd50 100644 --- a/common/nonexhaustive-delayqueue/Cargo.toml +++ b/common/nonexhaustive-delayqueue/Cargo.toml @@ -7,16 +7,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = { version = "1.24.1", features = [] } tokio-stream = "0.1.11" # this one seems to be a thing until `Stream` trait is stabilised in stdlib -tokio-util = { version = "0.7.4", features = ["time"] } -[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-timer] -git = "https://github.com/mmsinclair/wasm-timer" -rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6" +[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio] +workspace = true -[target."cfg(target_arch = \"wasm32\")".dependencies.slab] -version = "0.4.4" +[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-util] +version = "0.7.4" +features = ["time"] + +[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer] +workspace = true +features = ["tokio", "tokio-util"] -[target."cfg(target_arch = \"wasm32\")".dependencies.futures-core] -version = "0.3.0" diff --git a/common/nonexhaustive-delayqueue/src/lib.rs b/common/nonexhaustive-delayqueue/src/lib.rs index 4e2329db2b..ee8c9f214c 100644 --- a/common/nonexhaustive-delayqueue/src/lib.rs +++ b/common/nonexhaustive-delayqueue/src/lib.rs @@ -6,12 +6,8 @@ use std::task::{Context, Poll, Waker}; use std::time::Duration; use tokio_stream::Stream; -pub use tokio::time::error::Error as TimerError; - // this is a copy of tokio-util delay_queue with `Sleep` and `Instant` being replaced with // `wasm_timer` equivalents -#[cfg(target_arch = "wasm32")] -mod wasm_delay_queue; #[cfg(not(target_arch = "wasm32"))] type DelayQueue = tokio_util::time::DelayQueue; @@ -23,13 +19,13 @@ pub type QueueKey = tokio_util::time::delay_queue::Key; use tokio::time::Instant; #[cfg(target_arch = "wasm32")] -type DelayQueue = crate::wasm_delay_queue::DelayQueue; +type DelayQueue = wasmtimer::tokio_util::DelayQueue; #[cfg(target_arch = "wasm32")] -pub use crate::wasm_delay_queue::delay_queue::Expired; +pub use wasmtimer::tokio_util::delay_queue::Expired; #[cfg(target_arch = "wasm32")] -pub type QueueKey = crate::wasm_delay_queue::delay_queue::Key; +pub type QueueKey = wasmtimer::tokio_util::delay_queue::Key; #[cfg(target_arch = "wasm32")] -use wasm_timer::Instant; +use wasmtimer::std::Instant; /// A variant of tokio's `DelayQueue`, such that its `Stream` implementation will never return a 'None'. pub struct NonExhaustiveDelayQueue { diff --git a/common/nonexhaustive-delayqueue/src/wasm_delay_queue/delay_queue.rs b/common/nonexhaustive-delayqueue/src/wasm_delay_queue/delay_queue.rs deleted file mode 100644 index 6d18b7b12d..0000000000 --- a/common/nonexhaustive-delayqueue/src/wasm_delay_queue/delay_queue.rs +++ /dev/null @@ -1,1234 +0,0 @@ -//! A queue of delayed elements. -//! -//! See [`DelayQueue`] for more details. -//! -//! [`DelayQueue`]: struct@DelayQueue - -use crate::wasm_delay_queue::sleep_until; -use crate::wasm_delay_queue::wheel::{self, Wheel}; -use core::ops::{Index, IndexMut}; -use futures_core::{ready, Future}; -use slab::Slab; -use std::cmp; -use std::collections::HashMap; -use std::convert::From; -use std::fmt; -use std::fmt::Debug; -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{self, Poll, Waker}; -use std::time::Duration; -use wasm_timer::{Delay, Instant}; - -/// A queue of delayed elements. -/// -/// Once an element is inserted into the `DelayQueue`, it is yielded once the -/// specified deadline has been reached. -/// -/// # Usage -/// -/// Elements are inserted into `DelayQueue` using the [`insert`] or -/// [`insert_at`] methods. A deadline is provided with the item and a [`Key`] is -/// returned. The key is used to remove the entry or to change the deadline at -/// which it should be yielded back. -/// -/// Once delays have been configured, the `DelayQueue` is used via its -/// [`Stream`] implementation. [`poll_expired`] is called. If an entry has reached its -/// deadline, it is returned. If not, `Poll::Pending` is returned indicating that the -/// current task will be notified once the deadline has been reached. -/// -/// # `Stream` implementation -/// -/// Items are retrieved from the queue via [`DelayQueue::poll_expired`]. If no delays have -/// expired, no items are returned. In this case, `Poll::Pending` is returned and the -/// current task is registered to be notified once the next item's delay has -/// expired. -/// -/// If no items are in the queue, i.e. `is_empty()` returns `true`, then `poll` -/// returns `Poll::Ready(None)`. This indicates that the stream has reached an end. -/// However, if a new item is inserted *after*, `poll` will once again start -/// returning items or `Poll::Pending`. -/// -/// Items are returned ordered by their expirations. Items that are configured -/// to expire first will be returned first. There are no ordering guarantees -/// for items configured to expire at the same instant. Also note that delays are -/// rounded to the closest millisecond. -/// -/// # Implementation -/// -/// The [`DelayQueue`] is backed by a separate instance of a timer wheel similar to that used internally -/// by Tokio's standalone timer utilities such as [`sleep`]. Because of this, it offers the same -/// performance and scalability benefits. -/// -/// State associated with each entry is stored in a [`slab`]. This amortizes the cost of allocation, -/// and allows reuse of the memory allocated for expired entires. -/// -/// Capacity can be checked using [`capacity`] and allocated preemptively by using -/// the [`reserve`] method. -/// -/// # Usage -/// -/// Using `DelayQueue` to manage cache entries. -/// -/// ```rust,no_run -/// use tokio_util::time::{DelayQueue, delay_queue}; -/// -/// use futures::ready; -/// use std::collections::HashMap; -/// use std::task::{Context, Poll}; -/// use std::time::Duration; -/// # type CacheKey = String; -/// # type Value = String; -/// -/// struct Cache { -/// entries: HashMap, -/// expirations: DelayQueue, -/// } -/// -/// const TTL_SECS: u64 = 30; -/// -/// impl Cache { -/// fn insert(&mut self, key: CacheKey, value: Value) { -/// let delay = self.expirations -/// .insert(key.clone(), Duration::from_secs(TTL_SECS)); -/// -/// self.entries.insert(key, (value, delay)); -/// } -/// -/// fn get(&self, key: &CacheKey) -> Option<&Value> { -/// self.entries.get(key) -/// .map(|&(ref v, _)| v) -/// } -/// -/// fn remove(&mut self, key: &CacheKey) { -/// if let Some((_, cache_key)) = self.entries.remove(key) { -/// self.expirations.remove(&cache_key); -/// } -/// } -/// -/// fn poll_purge(&mut self, cx: &mut Context<'_>) -> Poll<()> { -/// while let Some(entry) = ready!(self.expirations.poll_expired(cx)) { -/// self.entries.remove(entry.get_ref()); -/// } -/// -/// Poll::Ready(()) -/// } -/// } -/// ``` -/// -/// [`insert`]: method@Self::insert -/// [`insert_at`]: method@Self::insert_at -/// [`Key`]: struct@Key -/// [`Stream`]: https://docs.rs/futures/0.1/futures/stream/trait.Stream.html -/// [`poll_expired`]: method@Self::poll_expired -/// [`Stream::poll_expired`]: method@Self::poll_expired -/// [`DelayQueue`]: struct@DelayQueue -/// [`sleep`]: fn@tokio::time::sleep -/// [`slab`]: slab -/// [`capacity`]: method@Self::capacity -/// [`reserve`]: method@Self::reserve -#[derive(Debug)] -pub struct DelayQueue { - /// Stores data associated with entries - slab: SlabStorage, - - /// Lookup structure tracking all delays in the queue - wheel: Wheel>, - - /// Delays that were inserted when already expired. These cannot be stored - /// in the wheel - expired: Stack, - - /// Delay expiring when the *first* item in the queue expires - delay: Option>>, - - /// Wheel polling state - wheel_now: u64, - - /// Instant at which the timer starts - start: Instant, - - /// Waker that is invoked when we potentially need to reset the timer. - /// Because we lazily create the timer when the first entry is created, we - /// need to awaken any poller that polled us before that point. - waker: Option, -} - -#[derive(Default)] -struct SlabStorage { - inner: Slab>, - - // A `compact` call requires a re-mapping of the `Key`s that were changed - // during the `compact` call of the `slab`. Since the keys that were given out - // cannot be changed retroactively we need to keep track of these re-mappings. - // The keys of `key_map` correspond to the old keys that were given out and - // the values to the `Key`s that were re-mapped by the `compact` call. - key_map: HashMap, - - // Index used to create new keys to hand out. - next_key_index: usize, - - // Whether `compact` has been called, necessary in order to decide whether - // to include keys in `key_map`. - compact_called: bool, -} - -impl SlabStorage { - pub(crate) fn with_capacity(capacity: usize) -> SlabStorage { - SlabStorage { - inner: Slab::with_capacity(capacity), - key_map: HashMap::new(), - next_key_index: 0, - compact_called: false, - } - } - - // Inserts data into the inner slab and re-maps keys if necessary - pub(crate) fn insert(&mut self, val: Data) -> Key { - let mut key = KeyInternal::new(self.inner.insert(val)); - let key_contained = self.key_map.contains_key(&key.into()); - - if key_contained { - // It's possible that a `compact` call creates capacity in `self.inner` in - // such a way that a `self.inner.insert` call creates a `key` which was - // previously given out during an `insert` call prior to the `compact` call. - // If `key` is contained in `self.key_map`, we have encountered this exact situation, - // We need to create a new key `key_to_give_out` and include the relation - // `key_to_give_out` -> `key` in `self.key_map`. - let key_to_give_out = self.create_new_key(); - assert!(!self.key_map.contains_key(&key_to_give_out.into())); - self.key_map.insert(key_to_give_out.into(), key); - key = key_to_give_out; - } else if self.compact_called { - // Include an identity mapping in `self.key_map` in order to allow us to - // panic if a key that was handed out is removed more than once. - self.key_map.insert(key.into(), key); - } - - key.into() - } - - // Re-map the key in case compact was previously called. - // Note: Since we include identity mappings in key_map after compact was called, - // we have information about all keys that were handed out. In the case in which - // compact was called and we try to remove a Key that was previously removed - // we can detect invalid keys if no key is found in `key_map`. This is necessary - // in order to prevent situations in which a previously removed key - // corresponds to a re-mapped key internally and which would then be incorrectly - // removed from the slab. - // - // Example to illuminate this problem: - // - // Let's assume our `key_map` is {1 -> 2, 2 -> 1} and we call remove(1). If we - // were to remove 1 again, we would not find it inside `key_map` anymore. - // If we were to imply from this that no re-mapping was necessary, we would - // incorrectly remove 1 from `self.slab.inner`, which corresponds to the - // handed-out key 2. - pub(crate) fn remove(&mut self, key: &Key) -> Data { - let remapped_key = if self.compact_called { - match self.key_map.remove(key) { - Some(key_internal) => key_internal, - None => panic!("invalid key"), - } - } else { - (*key).into() - }; - - self.inner.remove(remapped_key.index) - } - - pub(crate) fn shrink_to_fit(&mut self) { - self.inner.shrink_to_fit(); - self.key_map.shrink_to_fit(); - } - - pub(crate) fn compact(&mut self) { - if !self.compact_called { - for (key, _) in self.inner.iter() { - self.key_map.insert(Key::new(key), KeyInternal::new(key)); - } - } - - let mut remapping = HashMap::new(); - self.inner.compact(|_, from, to| { - remapping.insert(from, to); - true - }); - - // At this point `key_map` contains a mapping for every element. - for internal_key in self.key_map.values_mut() { - if let Some(new_internal_key) = remapping.get(&internal_key.index) { - *internal_key = KeyInternal::new(*new_internal_key); - } - } - - if self.key_map.capacity() > 2 * self.key_map.len() { - self.key_map.shrink_to_fit(); - } - - self.compact_called = true; - } - - // Tries to re-map a `Key` that was given out to the user to its - // corresponding internal key. - fn remap_key(&self, key: &Key) -> Option { - let key_map = &self.key_map; - if self.compact_called { - key_map.get(&*key).copied() - } else { - Some((*key).into()) - } - } - - fn create_new_key(&mut self) -> KeyInternal { - while self.key_map.contains_key(&Key::new(self.next_key_index)) { - self.next_key_index = self.next_key_index.wrapping_add(1); - } - - KeyInternal::new(self.next_key_index) - } - - pub(crate) fn len(&self) -> usize { - self.inner.len() - } - - pub(crate) fn capacity(&self) -> usize { - self.inner.capacity() - } - - pub(crate) fn clear(&mut self) { - self.inner.clear(); - self.key_map.clear(); - self.compact_called = false; - } - - pub(crate) fn reserve(&mut self, additional: usize) { - self.inner.reserve(additional); - - if self.compact_called { - self.key_map.reserve(additional); - } - } - - pub(crate) fn is_empty(&self) -> bool { - self.inner.is_empty() - } - - pub(crate) fn contains(&self, key: &Key) -> bool { - let remapped_key = self.remap_key(key); - - match remapped_key { - Some(internal_key) => self.inner.contains(internal_key.index), - None => false, - } - } -} - -impl fmt::Debug for SlabStorage -where - T: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - if fmt.alternate() { - fmt.debug_map().entries(self.inner.iter()).finish() - } else { - fmt.debug_struct("Slab") - .field("len", &self.len()) - .field("cap", &self.capacity()) - .finish() - } - } -} - -impl Index for SlabStorage { - type Output = Data; - - fn index(&self, key: Key) -> &Self::Output { - let remapped_key = self.remap_key(&key); - - match remapped_key { - Some(internal_key) => &self.inner[internal_key.index], - None => panic!("Invalid index {}", key.index), - } - } -} - -impl IndexMut for SlabStorage { - fn index_mut(&mut self, key: Key) -> &mut Data { - let remapped_key = self.remap_key(&key); - - match remapped_key { - Some(internal_key) => &mut self.inner[internal_key.index], - None => panic!("Invalid index {}", key.index), - } - } -} - -/// An entry in `DelayQueue` that has expired and been removed. -/// -/// Values are returned by [`DelayQueue::poll_expired`]. -/// -/// [`DelayQueue::poll_expired`]: method@DelayQueue::poll_expired -#[derive(Debug)] -pub struct Expired { - /// The data stored in the queue - data: T, - - /// The expiration time - deadline: Instant, - - /// The key associated with the entry - key: Key, -} - -/// Token to a value stored in a `DelayQueue`. -/// -/// Instances of `Key` are returned by [`DelayQueue::insert`]. See [`DelayQueue`] -/// documentation for more details. -/// -/// [`DelayQueue`]: struct@DelayQueue -/// [`DelayQueue::insert`]: method@DelayQueue::insert -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Key { - index: usize, -} - -// Whereas `Key` is given out to users that use `DelayQueue`, internally we use -// `KeyInternal` as the key type in order to make the logic of mapping between keys -// as a result of `compact` calls clearer. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -struct KeyInternal { - index: usize, -} - -#[derive(Debug)] -struct Stack { - /// Head of the stack - head: Option, - _p: PhantomData T>, -} - -#[derive(Debug)] -struct Data { - /// The data being stored in the queue and will be returned at the requested - /// instant. - inner: T, - - /// The instant at which the item is returned. - when: u64, - - /// Set to true when stored in the `expired` queue - expired: bool, - - /// Next entry in the stack - next: Option, - - /// Previous entry in the stack - prev: Option, -} - -/// Maximum number of entries the queue can handle -const MAX_ENTRIES: usize = (1 << 30) - 1; - -impl DelayQueue { - /// Creates a new, empty, `DelayQueue`. - /// - /// The queue will not allocate storage until items are inserted into it. - /// - /// # Examples - /// - /// ```rust - /// # use tokio_util::time::DelayQueue; - /// let delay_queue: DelayQueue = DelayQueue::new(); - /// ``` - pub fn new() -> DelayQueue { - DelayQueue::with_capacity(0) - } - - /// Creates a new, empty, `DelayQueue` with the specified capacity. - /// - /// The queue will be able to hold at least `capacity` elements without - /// reallocating. If `capacity` is 0, the queue will not allocate for - /// storage. - /// - /// # Examples - /// - /// ```rust - /// # use tokio_util::time::DelayQueue; - /// # use std::time::Duration; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut delay_queue = DelayQueue::with_capacity(10); - /// - /// // These insertions are done without further allocation - /// for i in 0..10 { - /// delay_queue.insert(i, Duration::from_secs(i)); - /// } - /// - /// // This will make the queue allocate additional storage - /// delay_queue.insert(11, Duration::from_secs(11)); - /// # } - /// ``` - pub fn with_capacity(capacity: usize) -> DelayQueue { - DelayQueue { - wheel: Wheel::new(), - slab: SlabStorage::with_capacity(capacity), - expired: Stack::default(), - delay: None, - wheel_now: 0, - start: Instant::now(), - waker: None, - } - } - - /// Inserts `value` into the queue set to expire at a specific instant in - /// time. - /// - /// This function is identical to `insert`, but takes an `Instant` instead - /// of a `Duration`. - /// - /// `value` is stored in the queue until `when` is reached. At which point, - /// `value` will be returned from [`poll_expired`]. If `when` has already been - /// reached, then `value` is immediately made available to poll. - /// - /// The return value represents the insertion and is used as an argument to - /// [`remove`] and [`reset`]. Note that [`Key`] is a token and is reused once - /// `value` is removed from the queue either by calling [`poll_expired`] after - /// `when` is reached or by calling [`remove`]. At this point, the caller - /// must take care to not use the returned [`Key`] again as it may reference - /// a different item in the queue. - /// - /// See [type] level documentation for more details. - /// - /// # Panics - /// - /// This function panics if `when` is too far in the future. - /// - /// # Examples - /// - /// Basic usage - /// - /// ```rust - /// use tokio::time::{Duration, Instant}; - /// use tokio_util::time::DelayQueue; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut delay_queue = DelayQueue::new(); - /// let key = delay_queue.insert_at( - /// "foo", Instant::now() + Duration::from_secs(5)); - /// - /// // Remove the entry - /// let item = delay_queue.remove(&key); - /// assert_eq!(*item.get_ref(), "foo"); - /// # } - /// ``` - /// - /// [`poll_expired`]: method@Self::poll_expired - /// [`remove`]: method@Self::remove - /// [`reset`]: method@Self::reset - /// [`Key`]: struct@Key - /// [type]: # - #[track_caller] - pub fn insert_at(&mut self, value: T, when: Instant) -> Key { - assert!(self.slab.len() < MAX_ENTRIES, "max entries exceeded"); - - // Normalize the deadline. Values cannot be set to expire in the past. - let when = self.normalize_deadline(when); - - // Insert the value in the store - let key = self.slab.insert(Data { - inner: value, - when, - expired: false, - next: None, - prev: None, - }); - - self.insert_idx(when, key); - - // Set a new delay if the current's deadline is later than the one of the new item - let should_set_delay = if let Some(ref delay) = self.delay { - let current_exp = self.normalize_deadline(delay.deadline()); - current_exp > when - } else { - true - }; - - if should_set_delay { - if let Some(waker) = self.waker.take() { - waker.wake(); - } - - let delay_time = self.start + Duration::from_millis(when); - if let Some(ref mut delay) = &mut self.delay { - delay.as_mut().reset_at(delay_time); - } else { - self.delay = Some(Box::pin(sleep_until(delay_time))); - } - } - - key - } - - /// Attempts to pull out the next value of the delay queue, registering the - /// current task for wakeup if the value is not yet available, and returning - /// `None` if the queue is exhausted. - pub fn poll_expired(&mut self, cx: &mut task::Context<'_>) -> Poll>> { - if !self - .waker - .as_ref() - .map(|w| w.will_wake(cx.waker())) - .unwrap_or(false) - { - self.waker = Some(cx.waker().clone()); - } - - let item = ready!(self.poll_idx(cx)); - Poll::Ready(item.map(|key| { - let data = self.slab.remove(&key); - debug_assert!(data.next.is_none()); - debug_assert!(data.prev.is_none()); - - Expired { - key, - data: data.inner, - deadline: self.start + Duration::from_millis(data.when), - } - })) - } - - /// Inserts `value` into the queue set to expire after the requested duration - /// elapses. - /// - /// This function is identical to `insert_at`, but takes a `Duration` - /// instead of an `Instant`. - /// - /// `value` is stored in the queue until `timeout` duration has - /// elapsed after `insert` was called. At that point, `value` will - /// be returned from [`poll_expired`]. If `timeout` is a `Duration` of - /// zero, then `value` is immediately made available to poll. - /// - /// The return value represents the insertion and is used as an - /// argument to [`remove`] and [`reset`]. Note that [`Key`] is a - /// token and is reused once `value` is removed from the queue - /// either by calling [`poll_expired`] after `timeout` has elapsed - /// or by calling [`remove`]. At this point, the caller must not - /// use the returned [`Key`] again as it may reference a different - /// item in the queue. - /// - /// See [type] level documentation for more details. - /// - /// # Panics - /// - /// This function panics if `timeout` is greater than the maximum - /// duration supported by the timer in the current `Runtime`. - /// - /// # Examples - /// - /// Basic usage - /// - /// ```rust - /// use tokio_util::time::DelayQueue; - /// use std::time::Duration; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut delay_queue = DelayQueue::new(); - /// let key = delay_queue.insert("foo", Duration::from_secs(5)); - /// - /// // Remove the entry - /// let item = delay_queue.remove(&key); - /// assert_eq!(*item.get_ref(), "foo"); - /// # } - /// ``` - /// - /// [`poll_expired`]: method@Self::poll_expired - /// [`remove`]: method@Self::remove - /// [`reset`]: method@Self::reset - /// [`Key`]: struct@Key - /// [type]: # - #[track_caller] - pub fn insert(&mut self, value: T, timeout: Duration) -> Key { - self.insert_at(value, Instant::now() + timeout) - } - - #[track_caller] - fn insert_idx(&mut self, when: u64, key: Key) { - use self::wheel::{InsertError, Stack}; - - // Register the deadline with the timer wheel - match self.wheel.insert(when, key, &mut self.slab) { - Ok(_) => {} - Err((_, InsertError::Elapsed)) => { - self.slab[key].expired = true; - // The delay is already expired, store it in the expired queue - self.expired.push(key, &mut self.slab); - } - Err((_, err)) => panic!("invalid deadline; err={:?}", err), - } - } - - /// Removes the key from the expired queue or the timer wheel - /// depending on its expiration status. - /// - /// # Panics - /// - /// Panics if the key is not contained in the expired queue or the wheel. - #[track_caller] - fn remove_key(&mut self, key: &Key) { - use crate::wasm_delay_queue::wheel::Stack; - - // Special case the `expired` queue - if self.slab[*key].expired { - self.expired.remove(key, &mut self.slab); - } else { - self.wheel.remove(key, &mut self.slab); - } - } - - /// Removes the item associated with `key` from the queue. - /// - /// There must be an item associated with `key`. The function returns the - /// removed item as well as the `Instant` at which it will the delay will - /// have expired. - /// - /// # Panics - /// - /// The function panics if `key` is not contained by the queue. - /// - /// # Examples - /// - /// Basic usage - /// - /// ```rust - /// use tokio_util::time::DelayQueue; - /// use std::time::Duration; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut delay_queue = DelayQueue::new(); - /// let key = delay_queue.insert("foo", Duration::from_secs(5)); - /// - /// // Remove the entry - /// let item = delay_queue.remove(&key); - /// assert_eq!(*item.get_ref(), "foo"); - /// # } - /// ``` - #[track_caller] - pub fn remove(&mut self, key: &Key) -> Expired { - let prev_deadline = self.next_deadline(); - - self.remove_key(key); - let data = self.slab.remove(key); - - let next_deadline = self.next_deadline(); - if prev_deadline != next_deadline { - match (next_deadline, &mut self.delay) { - (None, _) => self.delay = None, - (Some(deadline), Some(delay)) => delay.as_mut().reset_at(deadline), - (Some(deadline), None) => self.delay = Some(Box::pin(sleep_until(deadline))), - } - } - - Expired { - key: Key::new(key.index), - data: data.inner, - deadline: self.start + Duration::from_millis(data.when), - } - } - - /// Sets the delay of the item associated with `key` to expire at `when`. - /// - /// This function is identical to `reset` but takes an `Instant` instead of - /// a `Duration`. - /// - /// The item remains in the queue but the delay is set to expire at `when`. - /// If `when` is in the past, then the item is immediately made available to - /// the caller. - /// - /// # Panics - /// - /// This function panics if `when` is too far in the future or if `key` is - /// not contained by the queue. - /// - /// # Examples - /// - /// Basic usage - /// - /// ```rust - /// use tokio::time::{Duration, Instant}; - /// use tokio_util::time::DelayQueue; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut delay_queue = DelayQueue::new(); - /// let key = delay_queue.insert("foo", Duration::from_secs(5)); - /// - /// // "foo" is scheduled to be returned in 5 seconds - /// - /// delay_queue.reset_at(&key, Instant::now() + Duration::from_secs(10)); - /// - /// // "foo" is now scheduled to be returned in 10 seconds - /// # } - /// ``` - #[track_caller] - pub fn reset_at(&mut self, key: &Key, when: Instant) { - self.remove_key(key); - - // Normalize the deadline. Values cannot be set to expire in the past. - let when = self.normalize_deadline(when); - - self.slab[*key].when = when; - self.slab[*key].expired = false; - - self.insert_idx(when, *key); - - let next_deadline = self.next_deadline(); - if let (Some(ref mut delay), Some(deadline)) = (&mut self.delay, next_deadline) { - // This should awaken us if necessary (ie, if already expired) - delay.as_mut().reset_at(deadline); - } - } - - /// Shrink the capacity of the slab, which `DelayQueue` uses internally for storage allocation. - /// This function is not guaranteed to, and in most cases, won't decrease the capacity of the slab - /// to the number of elements still contained in it, because elements cannot be moved to a different - /// index. To decrease the capacity to the size of the slab use [`compact`]. - /// - /// This function can take O(n) time even when the capacity cannot be reduced or the allocation is - /// shrunk in place. Repeated calls run in O(1) though. - /// - /// [`compact`]: method@Self::compact - pub fn shrink_to_fit(&mut self) { - self.slab.shrink_to_fit(); - } - - /// Shrink the capacity of the slab, which `DelayQueue` uses internally for storage allocation, - /// to the number of elements that are contained in it. - /// - /// This methods runs in O(n). - /// - /// # Examples - /// - /// Basic usage - /// - /// ```rust - /// use tokio_util::time::DelayQueue; - /// use std::time::Duration; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut delay_queue = DelayQueue::with_capacity(10); - /// - /// let key1 = delay_queue.insert(5, Duration::from_secs(5)); - /// let key2 = delay_queue.insert(10, Duration::from_secs(10)); - /// let key3 = delay_queue.insert(15, Duration::from_secs(15)); - /// - /// delay_queue.remove(&key2); - /// - /// delay_queue.compact(); - /// assert_eq!(delay_queue.capacity(), 2); - /// # } - /// ``` - pub fn compact(&mut self) { - self.slab.compact(); - } - - /// Returns the next time to poll as determined by the wheel - fn next_deadline(&mut self) -> Option { - self.wheel - .poll_at() - .map(|poll_at| self.start + Duration::from_millis(poll_at)) - } - - /// Sets the delay of the item associated with `key` to expire after - /// `timeout`. - /// - /// This function is identical to `reset_at` but takes a `Duration` instead - /// of an `Instant`. - /// - /// The item remains in the queue but the delay is set to expire after - /// `timeout`. If `timeout` is zero, then the item is immediately made - /// available to the caller. - /// - /// # Panics - /// - /// This function panics if `timeout` is greater than the maximum supported - /// duration or if `key` is not contained by the queue. - /// - /// # Examples - /// - /// Basic usage - /// - /// ```rust - /// use tokio_util::time::DelayQueue; - /// use std::time::Duration; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut delay_queue = DelayQueue::new(); - /// let key = delay_queue.insert("foo", Duration::from_secs(5)); - /// - /// // "foo" is scheduled to be returned in 5 seconds - /// - /// delay_queue.reset(&key, Duration::from_secs(10)); - /// - /// // "foo"is now scheduled to be returned in 10 seconds - /// # } - /// ``` - #[track_caller] - pub fn reset(&mut self, key: &Key, timeout: Duration) { - self.reset_at(key, Instant::now() + timeout); - } - - /// Clears the queue, removing all items. - /// - /// After calling `clear`, [`poll_expired`] will return `Ok(Ready(None))`. - /// - /// Note that this method has no effect on the allocated capacity. - /// - /// [`poll_expired`]: method@Self::poll_expired - /// - /// # Examples - /// - /// ```rust - /// use tokio_util::time::DelayQueue; - /// use std::time::Duration; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut delay_queue = DelayQueue::new(); - /// - /// delay_queue.insert("foo", Duration::from_secs(5)); - /// - /// assert!(!delay_queue.is_empty()); - /// - /// delay_queue.clear(); - /// - /// assert!(delay_queue.is_empty()); - /// # } - /// ``` - pub fn clear(&mut self) { - self.slab.clear(); - self.expired = Stack::default(); - self.wheel = Wheel::new(); - self.delay = None; - } - - /// Returns the number of elements the queue can hold without reallocating. - /// - /// # Examples - /// - /// ```rust - /// use tokio_util::time::DelayQueue; - /// - /// let delay_queue: DelayQueue = DelayQueue::with_capacity(10); - /// assert_eq!(delay_queue.capacity(), 10); - /// ``` - pub fn capacity(&self) -> usize { - self.slab.capacity() - } - - /// Returns the number of elements currently in the queue. - /// - /// # Examples - /// - /// ```rust - /// use tokio_util::time::DelayQueue; - /// use std::time::Duration; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut delay_queue: DelayQueue = DelayQueue::with_capacity(10); - /// assert_eq!(delay_queue.len(), 0); - /// delay_queue.insert(3, Duration::from_secs(5)); - /// assert_eq!(delay_queue.len(), 1); - /// # } - /// ``` - pub fn len(&self) -> usize { - self.slab.len() - } - - /// Reserves capacity for at least `additional` more items to be queued - /// without allocating. - /// - /// `reserve` does nothing if the queue already has sufficient capacity for - /// `additional` more values. If more capacity is required, a new segment of - /// memory will be allocated and all existing values will be copied into it. - /// As such, if the queue is already very large, a call to `reserve` can end - /// up being expensive. - /// - /// The queue may reserve more than `additional` extra space in order to - /// avoid frequent reallocations. - /// - /// # Panics - /// - /// Panics if the new capacity exceeds the maximum number of entries the - /// queue can contain. - /// - /// # Examples - /// - /// ``` - /// use tokio_util::time::DelayQueue; - /// use std::time::Duration; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut delay_queue = DelayQueue::new(); - /// - /// delay_queue.insert("hello", Duration::from_secs(10)); - /// delay_queue.reserve(10); - /// - /// assert!(delay_queue.capacity() >= 11); - /// # } - /// ``` - #[track_caller] - pub fn reserve(&mut self, additional: usize) { - assert!( - self.slab.capacity() + additional <= MAX_ENTRIES, - "max queue capacity exceeded" - ); - self.slab.reserve(additional); - } - - /// Returns `true` if there are no items in the queue. - /// - /// Note that this function returns `false` even if all items have not yet - /// expired and a call to `poll` will return `Poll::Pending`. - /// - /// # Examples - /// - /// ``` - /// use tokio_util::time::DelayQueue; - /// use std::time::Duration; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut delay_queue = DelayQueue::new(); - /// assert!(delay_queue.is_empty()); - /// - /// delay_queue.insert("hello", Duration::from_secs(5)); - /// assert!(!delay_queue.is_empty()); - /// # } - /// ``` - pub fn is_empty(&self) -> bool { - self.slab.is_empty() - } - - /// Polls the queue, returning the index of the next slot in the slab that - /// should be returned. - /// - /// A slot should be returned when the associated deadline has been reached. - fn poll_idx(&mut self, cx: &mut task::Context<'_>) -> Poll> { - use self::wheel::Stack; - - let expired = self.expired.pop(&mut self.slab); - - if expired.is_some() { - return Poll::Ready(expired); - } - - loop { - if let Some(ref mut delay) = self.delay { - ready!(Pin::new(&mut *delay).poll(cx)).ok(); - - let now = crate::wasm_delay_queue::ms( - delay.deadline() - self.start, - crate::wasm_delay_queue::Round::Down, - ); - - self.wheel_now = now; - } - - // We poll the wheel to get the next value out before finding the next deadline. - let wheel_idx = self.wheel.poll(self.wheel_now, &mut self.slab); - - self.delay = self.next_deadline().map(|when| Box::pin(sleep_until(when))); - - if let Some(idx) = wheel_idx { - return Poll::Ready(Some(idx)); - } - - if self.delay.is_none() { - return Poll::Ready(None); - } - } - } - - fn normalize_deadline(&self, when: Instant) -> u64 { - let when = if when < self.start { - 0 - } else { - crate::wasm_delay_queue::ms(when - self.start, crate::wasm_delay_queue::Round::Up) - }; - - cmp::max(when, self.wheel.elapsed()) - } -} - -// We never put `T` in a `Pin`... -impl Unpin for DelayQueue {} - -impl Default for DelayQueue { - fn default() -> DelayQueue { - DelayQueue::new() - } -} - -impl futures_core::Stream for DelayQueue { - // DelayQueue seems much more specific, where a user may care that it - // has reached capacity, so return those errors instead of panicking. - type Item = Expired; - - fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { - DelayQueue::poll_expired(self.get_mut(), cx) - } -} - -impl wheel::Stack for Stack { - type Owned = Key; - type Borrowed = Key; - type Store = SlabStorage; - - fn is_empty(&self) -> bool { - self.head.is_none() - } - - fn push(&mut self, item: Self::Owned, store: &mut Self::Store) { - // Ensure the entry is not already in a stack. - debug_assert!(store[item].next.is_none()); - debug_assert!(store[item].prev.is_none()); - - // Remove the old head entry - let old = self.head.take(); - - if let Some(idx) = old { - store[idx].prev = Some(item); - } - - store[item].next = old; - self.head = Some(item); - } - - fn pop(&mut self, store: &mut Self::Store) -> Option { - if let Some(key) = self.head { - self.head = store[key].next; - - if let Some(idx) = self.head { - store[idx].prev = None; - } - - store[key].next = None; - debug_assert!(store[key].prev.is_none()); - - Some(key) - } else { - None - } - } - - #[track_caller] - fn remove(&mut self, item: &Self::Borrowed, store: &mut Self::Store) { - let key = *item; - assert!(store.contains(item)); - - // Ensure that the entry is in fact contained by the stack - debug_assert!({ - // This walks the full linked list even if an entry is found. - let mut next = self.head; - let mut contains = false; - - while let Some(idx) = next { - let data = &store[idx]; - - if idx == *item { - debug_assert!(!contains); - contains = true; - } - - next = data.next; - } - - contains - }); - - if let Some(next) = store[key].next { - store[next].prev = store[key].prev; - } - - if let Some(prev) = store[key].prev { - store[prev].next = store[key].next; - } else { - self.head = store[key].next; - } - - store[key].next = None; - store[key].prev = None; - } - - fn when(item: &Self::Borrowed, store: &Self::Store) -> u64 { - store[*item].when - } -} - -impl Default for Stack { - fn default() -> Stack { - Stack { - head: None, - _p: PhantomData, - } - } -} - -impl Key { - pub(crate) fn new(index: usize) -> Key { - Key { index } - } -} - -impl KeyInternal { - pub(crate) fn new(index: usize) -> KeyInternal { - KeyInternal { index } - } -} - -impl From for KeyInternal { - fn from(item: Key) -> Self { - KeyInternal::new(item.index) - } -} - -impl From for Key { - fn from(item: KeyInternal) -> Self { - Key::new(item.index) - } -} - -impl Expired { - /// Returns a reference to the inner value. - pub fn get_ref(&self) -> &T { - &self.data - } - - /// Returns a mutable reference to the inner value. - pub fn get_mut(&mut self) -> &mut T { - &mut self.data - } - - /// Consumes `self` and returns the inner value. - pub fn into_inner(self) -> T { - self.data - } - - /// Returns the deadline that the expiration was set to. - pub fn deadline(&self) -> Instant { - self.deadline - } - - /// Returns the key that the expiration is indexed by. - pub fn key(&self) -> Key { - self.key - } -} diff --git a/common/nonexhaustive-delayqueue/src/wasm_delay_queue/mod.rs b/common/nonexhaustive-delayqueue/src/wasm_delay_queue/mod.rs deleted file mode 100644 index fa8b62f561..0000000000 --- a/common/nonexhaustive-delayqueue/src/wasm_delay_queue/mod.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::time::Duration; - -mod wheel; - -pub mod delay_queue; - -#[doc(inline)] -pub use delay_queue::DelayQueue; - -// ===== Internal utils ===== - -enum Round { - Up, - Down, -} - -/// Convert a `Duration` to milliseconds, rounding up and saturating at -/// `u64::MAX`. -/// -/// The saturating is fine because `u64::MAX` milliseconds are still many -/// million years. -#[inline] -fn ms(duration: Duration, round: Round) -> u64 { - const NANOS_PER_MILLI: u32 = 1_000_000; - const MILLIS_PER_SEC: u64 = 1_000; - - // Round up. - let millis = match round { - Round::Up => (duration.subsec_nanos() + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI, - Round::Down => duration.subsec_millis(), - }; - - duration - .as_secs() - .saturating_mul(MILLIS_PER_SEC) - .saturating_add(u64::from(millis)) -} - -#[inline] -fn sleep_until(deadline: wasm_timer::Instant) -> wasm_timer::Delay { - wasm_timer::Delay::new_at(deadline) -} diff --git a/common/nonexhaustive-delayqueue/src/wasm_delay_queue/wheel/level.rs b/common/nonexhaustive-delayqueue/src/wasm_delay_queue/wheel/level.rs deleted file mode 100644 index f84d80e265..0000000000 --- a/common/nonexhaustive-delayqueue/src/wasm_delay_queue/wheel/level.rs +++ /dev/null @@ -1,253 +0,0 @@ -use crate::wasm_delay_queue::wheel::Stack; - -use std::fmt; - -/// Wheel for a single level in the timer. This wheel contains 64 slots. -pub(crate) struct Level { - level: usize, - - /// Bit field tracking which slots currently contain entries. - /// - /// Using a bit field to track slots that contain entries allows avoiding a - /// scan to find entries. This field is updated when entries are added or - /// removed from a slot. - /// - /// The least-significant bit represents slot zero. - occupied: u64, - - /// Slots - slot: [T; LEVEL_MULT], -} - -/// Indicates when a slot must be processed next. -#[derive(Debug)] -pub(crate) struct Expiration { - /// The level containing the slot. - pub(crate) level: usize, - - /// The slot index. - pub(crate) slot: usize, - - /// The instant at which the slot needs to be processed. - pub(crate) deadline: u64, -} - -/// Level multiplier. -/// -/// Being a power of 2 is very important. -const LEVEL_MULT: usize = 64; - -impl Level { - pub(crate) fn new(level: usize) -> Level { - // Rust's derived implementations for arrays require that the value - // contained by the array be `Copy`. So, here we have to manually - // initialize every single slot. - macro_rules! s { - () => { - T::default() - }; - } - - Level { - level, - occupied: 0, - slot: [ - // It does not look like the necessary traits are - // derived for [T; 64]. - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - s!(), - ], - } - } - - /// Finds the slot that needs to be processed next and returns the slot and - /// `Instant` at which this slot must be processed. - pub(crate) fn next_expiration(&self, now: u64) -> Option { - // Use the `occupied` bit field to get the index of the next slot that - // needs to be processed. - let slot = match self.next_occupied_slot(now) { - Some(slot) => slot, - None => return None, - }; - - // From the slot index, calculate the `Instant` at which it needs to be - // processed. This value *must* be in the future with respect to `now`. - - let level_range = level_range(self.level); - let slot_range = slot_range(self.level); - - // TODO: This can probably be simplified w/ power of 2 math - let level_start = now - (now % level_range); - let deadline = level_start + slot as u64 * slot_range; - - debug_assert!( - deadline >= now, - "deadline={}; now={}; level={}; slot={}; occupied={:b}", - deadline, - now, - self.level, - slot, - self.occupied - ); - - Some(Expiration { - level: self.level, - slot, - deadline, - }) - } - - fn next_occupied_slot(&self, now: u64) -> Option { - if self.occupied == 0 { - return None; - } - - // Get the slot for now using Maths - let now_slot = (now / slot_range(self.level)) as usize; - let occupied = self.occupied.rotate_right(now_slot as u32); - let zeros = occupied.trailing_zeros() as usize; - let slot = (zeros + now_slot) % 64; - - Some(slot) - } - - pub(crate) fn add_entry(&mut self, when: u64, item: T::Owned, store: &mut T::Store) { - let slot = slot_for(when, self.level); - - self.slot[slot].push(item, store); - self.occupied |= occupied_bit(slot); - } - - pub(crate) fn remove_entry(&mut self, when: u64, item: &T::Borrowed, store: &mut T::Store) { - let slot = slot_for(when, self.level); - - self.slot[slot].remove(item, store); - - if self.slot[slot].is_empty() { - // The bit is currently set - debug_assert!(self.occupied & occupied_bit(slot) != 0); - - // Unset the bit - self.occupied ^= occupied_bit(slot); - } - } - - pub(crate) fn pop_entry_slot(&mut self, slot: usize, store: &mut T::Store) -> Option { - let ret = self.slot[slot].pop(store); - - if ret.is_some() && self.slot[slot].is_empty() { - // The bit is currently set - debug_assert!(self.occupied & occupied_bit(slot) != 0); - - self.occupied ^= occupied_bit(slot); - } - - ret - } -} - -impl fmt::Debug for Level { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("Level") - .field("occupied", &self.occupied) - .finish() - } -} - -fn occupied_bit(slot: usize) -> u64 { - 1 << slot -} - -fn slot_range(level: usize) -> u64 { - LEVEL_MULT.pow(level as u32) as u64 -} - -fn level_range(level: usize) -> u64 { - LEVEL_MULT as u64 * slot_range(level) -} - -/// Convert a duration (milliseconds) and a level to a slot position -fn slot_for(duration: u64, level: usize) -> usize { - ((duration >> (level * 6)) % LEVEL_MULT as u64) as usize -} - -#[cfg(all(test, not(loom)))] -mod test { - use super::*; - - #[test] - fn test_slot_for() { - for pos in 0..64 { - assert_eq!(pos as usize, slot_for(pos, 0)); - } - - for level in 1..5 { - for pos in level..64 { - let a = pos * 64_usize.pow(level as u32); - assert_eq!(pos as usize, slot_for(a as u64, level)); - } - } - } -} diff --git a/common/nonexhaustive-delayqueue/src/wasm_delay_queue/wheel/mod.rs b/common/nonexhaustive-delayqueue/src/wasm_delay_queue/wheel/mod.rs deleted file mode 100644 index ffa05ab71b..0000000000 --- a/common/nonexhaustive-delayqueue/src/wasm_delay_queue/wheel/mod.rs +++ /dev/null @@ -1,315 +0,0 @@ -mod level; -pub(crate) use self::level::Expiration; -use self::level::Level; - -mod stack; -pub(crate) use self::stack::Stack; - -use std::borrow::Borrow; -use std::fmt::Debug; -use std::usize; - -/// Timing wheel implementation. -/// -/// This type provides the hashed timing wheel implementation that backs `Timer` -/// and `DelayQueue`. -/// -/// The structure is generic over `T: Stack`. This allows handling timeout data -/// being stored on the heap or in a slab. In order to support the latter case, -/// the slab must be passed into each function allowing the implementation to -/// lookup timer entries. -/// -/// See `Timer` documentation for some implementation notes. -#[derive(Debug)] -pub(crate) struct Wheel { - /// The number of milliseconds elapsed since the wheel started. - elapsed: u64, - - /// Timer wheel. - /// - /// Levels: - /// - /// * 1 ms slots / 64 ms range - /// * 64 ms slots / ~ 4 sec range - /// * ~ 4 sec slots / ~ 4 min range - /// * ~ 4 min slots / ~ 4 hr range - /// * ~ 4 hr slots / ~ 12 day range - /// * ~ 12 day slots / ~ 2 yr range - levels: Vec>, -} - -/// Number of levels. Each level has 64 slots. By using 6 levels with 64 slots -/// each, the timer is able to track time up to 2 years into the future with a -/// precision of 1 millisecond. -const NUM_LEVELS: usize = 6; - -/// The maximum duration of a delay -const MAX_DURATION: u64 = (1 << (6 * NUM_LEVELS)) - 1; - -#[derive(Debug)] -pub(crate) enum InsertError { - Elapsed, - Invalid, -} - -impl Wheel -where - T: Stack, -{ - /// Create a new timing wheel - pub(crate) fn new() -> Wheel { - let levels = (0..NUM_LEVELS).map(Level::new).collect(); - - Wheel { elapsed: 0, levels } - } - - /// Return the number of milliseconds that have elapsed since the timing - /// wheel's creation. - pub(crate) fn elapsed(&self) -> u64 { - self.elapsed - } - - /// Insert an entry into the timing wheel. - /// - /// # Arguments - /// - /// * `when`: is the instant at which the entry should be fired. It is - /// represented as the number of milliseconds since the creation - /// of the timing wheel. - /// - /// * `item`: The item to insert into the wheel. - /// - /// * `store`: The slab or `()` when using heap storage. - /// - /// # Return - /// - /// Returns `Ok` when the item is successfully inserted, `Err` otherwise. - /// - /// `Err(Elapsed)` indicates that `when` represents an instant that has - /// already passed. In this case, the caller should fire the timeout - /// immediately. - /// - /// `Err(Invalid)` indicates an invalid `when` argument as been supplied. - pub(crate) fn insert( - &mut self, - when: u64, - item: T::Owned, - store: &mut T::Store, - ) -> Result<(), (T::Owned, InsertError)> { - if when <= self.elapsed { - return Err((item, InsertError::Elapsed)); - } else if when - self.elapsed > MAX_DURATION { - return Err((item, InsertError::Invalid)); - } - - // Get the level at which the entry should be stored - let level = self.level_for(when); - - self.levels[level].add_entry(when, item, store); - - debug_assert!({ - self.levels[level] - .next_expiration(self.elapsed) - .map(|e| e.deadline >= self.elapsed) - .unwrap_or(true) - }); - - Ok(()) - } - - /// Remove `item` from the timing wheel. - #[track_caller] - pub(crate) fn remove(&mut self, item: &T::Borrowed, store: &mut T::Store) { - let when = T::when(item, store); - - assert!( - self.elapsed <= when, - "elapsed={}; when={}", - self.elapsed, - when - ); - - let level = self.level_for(when); - - self.levels[level].remove_entry(when, item, store); - } - - /// Instant at which to poll - pub(crate) fn poll_at(&self) -> Option { - self.next_expiration().map(|expiration| expiration.deadline) - } - - /// Advances the timer up to the instant represented by `now`. - pub(crate) fn poll(&mut self, now: u64, store: &mut T::Store) -> Option { - loop { - let expiration = self.next_expiration().and_then(|expiration| { - if expiration.deadline > now { - None - } else { - Some(expiration) - } - }); - - match expiration { - Some(ref expiration) => { - if let Some(item) = self.poll_expiration(expiration, store) { - return Some(item); - } - - self.set_elapsed(expiration.deadline); - } - None => { - // in this case the poll did not indicate an expiration - // _and_ we were not able to find a next expiration in - // the current list of timers. advance to the poll's - // current time and do nothing else. - self.set_elapsed(now); - return None; - } - } - } - } - - /// Returns the instant at which the next timeout expires. - fn next_expiration(&self) -> Option { - // Check all levels - for level in 0..NUM_LEVELS { - if let Some(expiration) = self.levels[level].next_expiration(self.elapsed) { - // There cannot be any expirations at a higher level that happen - // before this one. - debug_assert!(self.no_expirations_before(level + 1, expiration.deadline)); - - return Some(expiration); - } - } - - None - } - - /// Used for debug assertions - fn no_expirations_before(&self, start_level: usize, before: u64) -> bool { - let mut res = true; - - for l2 in start_level..NUM_LEVELS { - if let Some(e2) = self.levels[l2].next_expiration(self.elapsed) { - if e2.deadline < before { - res = false; - } - } - } - - res - } - - /// iteratively find entries that are between the wheel's current - /// time and the expiration time. for each in that population either - /// return it for notification (in the case of the last level) or tier - /// it down to the next level (in all other cases). - pub(crate) fn poll_expiration( - &mut self, - expiration: &Expiration, - store: &mut T::Store, - ) -> Option { - while let Some(item) = self.pop_entry(expiration, store) { - if expiration.level == 0 { - debug_assert_eq!(T::when(item.borrow(), store), expiration.deadline); - - return Some(item); - } else { - let when = T::when(item.borrow(), store); - - let next_level = expiration.level - 1; - - self.levels[next_level].add_entry(when, item, store); - } - } - - None - } - - fn set_elapsed(&mut self, when: u64) { - assert!( - self.elapsed <= when, - "elapsed={:?}; when={:?}", - self.elapsed, - when - ); - - if when > self.elapsed { - self.elapsed = when; - } - } - - fn pop_entry(&mut self, expiration: &Expiration, store: &mut T::Store) -> Option { - self.levels[expiration.level].pop_entry_slot(expiration.slot, store) - } - - fn level_for(&self, when: u64) -> usize { - level_for(self.elapsed, when) - } -} - -fn level_for(elapsed: u64, when: u64) -> usize { - const SLOT_MASK: u64 = (1 << 6) - 1; - - // Mask in the trailing bits ignored by the level calculation in order to cap - // the possible leading zeros - let masked = elapsed ^ when | SLOT_MASK; - - let leading_zeros = masked.leading_zeros() as usize; - let significant = 63 - leading_zeros; - significant / 6 -} - -#[cfg(all(test, not(loom)))] -mod test { - use super::*; - - #[test] - fn test_level_for() { - for pos in 0..64 { - assert_eq!( - 0, - level_for(0, pos), - "level_for({}) -- binary = {:b}", - pos, - pos - ); - } - - for level in 1..5 { - for pos in level..64 { - let a = pos * 64_usize.pow(level as u32); - assert_eq!( - level, - level_for(0, a as u64), - "level_for({}) -- binary = {:b}", - a, - a - ); - - if pos > level { - let a = a - 1; - assert_eq!( - level, - level_for(0, a as u64), - "level_for({}) -- binary = {:b}", - a, - a - ); - } - - if pos < 64 { - let a = a + 1; - assert_eq!( - level, - level_for(0, a as u64), - "level_for({}) -- binary = {:b}", - a, - a - ); - } - } - } - } -} diff --git a/common/nonexhaustive-delayqueue/src/wasm_delay_queue/wheel/stack.rs b/common/nonexhaustive-delayqueue/src/wasm_delay_queue/wheel/stack.rs deleted file mode 100644 index c87adcafda..0000000000 --- a/common/nonexhaustive-delayqueue/src/wasm_delay_queue/wheel/stack.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::borrow::Borrow; -use std::cmp::Eq; -use std::hash::Hash; - -/// Abstracts the stack operations needed to track timeouts. -pub(crate) trait Stack: Default { - /// Type of the item stored in the stack - type Owned: Borrow; - - /// Borrowed item - type Borrowed: Eq + Hash; - - /// Item storage, this allows a slab to be used instead of just the heap - type Store; - - /// Returns `true` if the stack is empty - fn is_empty(&self) -> bool; - - /// Push an item onto the stack - fn push(&mut self, item: Self::Owned, store: &mut Self::Store); - - /// Pop an item from the stack - fn pop(&mut self, store: &mut Self::Store) -> Option; - - fn remove(&mut self, item: &Self::Borrowed, store: &mut Self::Store); - - fn when(item: &Self::Borrowed, store: &Self::Store) -> u64; -} diff --git a/common/nymcoconut/src/proofs/mod.rs b/common/nymcoconut/src/proofs/mod.rs index 84c7211aa2..42f923466d 100644 --- a/common/nymcoconut/src/proofs/mod.rs +++ b/common/nymcoconut/src/proofs/mod.rs @@ -354,7 +354,7 @@ impl ProofKappaZeta { let witness_blinder = params.random_scalar(); let witness_serial_number = params.random_scalar(); let witness_binding_number = params.random_scalar(); - let witness_attributes = vec![witness_serial_number, witness_binding_number]; + let witness_attributes = [witness_serial_number, witness_binding_number]; let beta_bytes = verification_key .beta_g2 @@ -417,7 +417,7 @@ impl ProofKappaZeta { .map(|beta_i| beta_i.to_bytes()) .collect::>(); - let response_attributes = vec![self.response_serial_number, self.response_binding_number]; + let response_attributes = [self.response_serial_number, self.response_binding_number]; // re-compute witnesses commitments // Aw = (c * kappa) + (rt * g2) + ((1 - c) * alpha) + (rm[0] * beta[0]) + ... + (rm[i] * beta[i]) let commitment_kappa = kappa * self.challenge diff --git a/common/nymsphinx/Cargo.toml b/common/nymsphinx/Cargo.toml index df93301876..955051b267 100644 --- a/common/nymsphinx/Cargo.toml +++ b/common/nymsphinx/Cargo.toml @@ -40,3 +40,8 @@ path = "framing" [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio] version = "1.24.1" features = ["sync"] + +[features] +default = ["sphinx"] +sphinx = ["nym-crypto/sphinx", "nym-sphinx-params/sphinx", "nym-sphinx-types/sphinx"] +outfox = ["nym-crypto/outfox", "nym-sphinx-params/outfox", "nym-sphinx-types/outfox"] diff --git a/common/nymsphinx/acknowledgements/Cargo.toml b/common/nymsphinx/acknowledgements/Cargo.toml index 95b3d8b30c..da9977d1f3 100644 --- a/common/nymsphinx/acknowledgements/Cargo.toml +++ b/common/nymsphinx/acknowledgements/Cargo.toml @@ -19,7 +19,7 @@ nym-pemstore = { path = "../../pemstore" } nym-sphinx-addressing = { path = "../addressing" } nym-sphinx-params = { path = "../params" } nym-sphinx-routing = { path = "../routing" } -nym-sphinx-types = { path = "../types" } +nym-sphinx-types = { path = "../types", features = ["sphinx"] } nym-topology = { path = "../../topology" } [features] diff --git a/common/nymsphinx/acknowledgements/src/identifier.rs b/common/nymsphinx/acknowledgements/src/identifier.rs index aab5a96146..abe1d58368 100644 --- a/common/nymsphinx/acknowledgements/src/identifier.rs +++ b/common/nymsphinx/acknowledgements/src/identifier.rs @@ -17,7 +17,7 @@ pub fn prepare_identifier( let id_ciphertext = encrypt::(key.inner(), &iv, &serialized_id); // IV || ID_CIPHERTEXT - iv.into_iter().chain(id_ciphertext.into_iter()).collect() + iv.into_iter().chain(id_ciphertext).collect() } pub fn recover_identifier( diff --git a/common/nymsphinx/acknowledgements/src/surb_ack.rs b/common/nymsphinx/acknowledgements/src/surb_ack.rs index e623d4e738..732f84b9ba 100644 --- a/common/nymsphinx/acknowledgements/src/surb_ack.rs +++ b/common/nymsphinx/acknowledgements/src/surb_ack.rs @@ -122,7 +122,7 @@ impl SurbAck { .first_hop_address .as_zero_padded_bytes(MAX_NODE_ADDRESS_UNPADDED_LEN) .into_iter() - .chain(self.surb_ack_packet.to_bytes()?.into_iter()) + .chain(self.surb_ack_packet.to_bytes()?) .collect(); Ok((self.expected_total_delay, surb_bytes)) } diff --git a/common/nymsphinx/addressing/Cargo.toml b/common/nymsphinx/addressing/Cargo.toml index f9c646e04b..c6bc2856e3 100644 --- a/common/nymsphinx/addressing/Cargo.toml +++ b/common/nymsphinx/addressing/Cargo.toml @@ -9,7 +9,7 @@ repository = { workspace = true } [dependencies] nym-crypto = { path = "../../crypto", features = ["asymmetric"] } # all addresses are expressed in terms on their crypto keys -nym-sphinx-types = { path = "../types" } # we need to be able to refer to some types defined inside sphinx crate +nym-sphinx-types = { path = "../types", features = ["sphinx"] } # we need to be able to refer to some types defined inside sphinx crate serde = "1.0" # implementing serialization/deserialization for some types, like `Recipient` thiserror = "1.0.37" diff --git a/common/nymsphinx/anonymous-replies/src/reply_surb.rs b/common/nymsphinx/anonymous-replies/src/reply_surb.rs index 2bf5799912..d38b16fc13 100644 --- a/common/nymsphinx/anonymous-replies/src/reply_surb.rs +++ b/common/nymsphinx/anonymous-replies/src/reply_surb.rs @@ -130,7 +130,7 @@ impl ReplySurb { self.encryption_key .to_bytes() .into_iter() - .chain(self.surb.to_bytes().into_iter()) + .chain(self.surb.to_bytes()) .collect() } diff --git a/common/nymsphinx/anonymous-replies/src/requests.rs b/common/nymsphinx/anonymous-replies/src/requests.rs index f4effbf7d6..9dd4c84dc0 100644 --- a/common/nymsphinx/anonymous-replies/src/requests.rs +++ b/common/nymsphinx/anonymous-replies/src/requests.rs @@ -275,7 +275,7 @@ impl RepliableMessageContent { .to_be_bytes() .into_iter() .chain(reply_surbs.into_iter().flat_map(|s| s.to_bytes())) - .chain(message.into_iter()) + .chain(message) .collect() } RepliableMessageContent::AdditionalSurbs { reply_surbs } => { @@ -465,7 +465,7 @@ impl ReplyMessageContent { ReplyMessageContent::SurbRequest { recipient, amount } => recipient .to_bytes() .into_iter() - .chain(amount.to_be_bytes().into_iter()) + .chain(amount.to_be_bytes()) .collect(), } } diff --git a/common/nymsphinx/chunking/src/fragment.rs b/common/nymsphinx/chunking/src/fragment.rs index 74996f29ab..6dccfa053a 100644 --- a/common/nymsphinx/chunking/src/fragment.rs +++ b/common/nymsphinx/chunking/src/fragment.rs @@ -204,7 +204,7 @@ impl Fragment { self.header .to_bytes() .into_iter() - .chain(self.payload.into_iter()) + .chain(self.payload) .collect() } diff --git a/common/nymsphinx/cover/src/lib.rs b/common/nymsphinx/cover/src/lib.rs index 05f4cffe33..66142a4cee 100644 --- a/common/nymsphinx/cover/src/lib.rs +++ b/common/nymsphinx/cover/src/lib.rs @@ -115,7 +115,7 @@ where let packet_payload: Vec<_> = ack_bytes .into_iter() .chain(ephemeral_keypair.public_key().to_bytes().iter().cloned()) - .chain(cover_content.into_iter()) + .chain(cover_content) .collect(); let route = diff --git a/common/nymsphinx/forwarding/Cargo.toml b/common/nymsphinx/forwarding/Cargo.toml index 6509098398..4060cbf076 100644 --- a/common/nymsphinx/forwarding/Cargo.toml +++ b/common/nymsphinx/forwarding/Cargo.toml @@ -10,6 +10,6 @@ repository = { workspace = true } [dependencies] nym-sphinx-addressing = { path = "../addressing" } nym-sphinx-params = { path = "../params" } -nym-sphinx-types = { path = "../types" } +nym-sphinx-types = { path = "../types", features = ["sphinx", "outfox"] } nym-outfox = { path = "../../../nym-outfox" } thiserror = "1" diff --git a/common/nymsphinx/forwarding/src/packet.rs b/common/nymsphinx/forwarding/src/packet.rs index 2276e9a5cd..13026a3aaf 100644 --- a/common/nymsphinx/forwarding/src/packet.rs +++ b/common/nymsphinx/forwarding/src/packet.rs @@ -106,8 +106,8 @@ impl MixPacket { pub fn into_bytes(self) -> Result, MixPacketFormattingError> { Ok(std::iter::once(self.packet_type as u8) - .chain(self.next_hop.as_bytes().into_iter()) - .chain(self.packet.to_bytes()?.into_iter()) + .chain(self.next_hop.as_bytes()) + .chain(self.packet.to_bytes()?) .collect()) } } diff --git a/common/nymsphinx/framing/Cargo.toml b/common/nymsphinx/framing/Cargo.toml index b510926555..3915f204a2 100644 --- a/common/nymsphinx/framing/Cargo.toml +++ b/common/nymsphinx/framing/Cargo.toml @@ -12,5 +12,6 @@ bytes = "1.0" tokio-util = { version = "0.7.4", features = ["codec"] } thiserror = "1.0.37" -nym-sphinx-types = { path = "../types" } -nym-sphinx-params = { path = "../params" } +nym-sphinx-types = { path = "../types", features = ["sphinx", "outfox"] } +nym-sphinx-params = { path = "../params", features = ["sphinx", "outfox"] } + diff --git a/common/nymsphinx/params/Cargo.toml b/common/nymsphinx/params/Cargo.toml index bc9b40b9a8..9d9a5a24e2 100644 --- a/common/nymsphinx/params/Cargo.toml +++ b/common/nymsphinx/params/Cargo.toml @@ -13,3 +13,8 @@ serde = { workspace = true, features = ["derive"] } nym-crypto = { path = "../../crypto", features = ["hashing", "symmetric"] } nym-sphinx-types = { path = "../types" } + +[features] +default = ["sphinx"] +sphinx = ["nym-crypto/sphinx", "nym-sphinx-types/outfox"] +outfox = ["nym-crypto/outfox", "nym-sphinx-types/outfox"] diff --git a/common/nymsphinx/params/src/packet_sizes.rs b/common/nymsphinx/params/src/packet_sizes.rs index a6613f3024..58d9681efc 100644 --- a/common/nymsphinx/params/src/packet_sizes.rs +++ b/common/nymsphinx/params/src/packet_sizes.rs @@ -1,11 +1,11 @@ // Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::{PacketType, FRAG_ID_LEN}; -use nym_sphinx_types::header::HEADER_SIZE; -use nym_sphinx_types::{ - MIN_PACKET_SIZE, MIX_PARAMS_LEN, OUTFOX_PACKET_OVERHEAD, PAYLOAD_OVERHEAD_SIZE, -}; +use crate::PacketType; +#[cfg(feature = "sphinx")] +use nym_sphinx_types::{header::HEADER_SIZE, PAYLOAD_OVERHEAD_SIZE}; +#[cfg(feature = "outfox")] +use nym_sphinx_types::{MIN_PACKET_SIZE, MIX_PARAMS_LEN, OUTFOX_PACKET_OVERHEAD}; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::convert::TryFrom; @@ -14,6 +14,7 @@ use std::str::FromStr; use thiserror::Error; // each sphinx packet contains mandatory header and payload padding + markers +#[cfg(feature = "sphinx")] const SPHINX_PACKET_OVERHEAD: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE; // it's up to the smart people to figure those values out : ) @@ -22,15 +23,23 @@ const SPHINX_PACKET_OVERHEAD: usize = HEADER_SIZE + PAYLOAD_OVERHEAD_SIZE; // TODO: I'm not entirely sure if we can easily extract `::NonceSize` // into a const usize before relevant stuff is stabilised in rust... +#[cfg(feature = "sphinx")] const ACK_IV_SIZE: usize = 16; -const ACK_PACKET_SIZE: usize = ACK_IV_SIZE + FRAG_ID_LEN + SPHINX_PACKET_OVERHEAD; +#[cfg(feature = "sphinx")] +const ACK_PACKET_SIZE: usize = ACK_IV_SIZE + crate::FRAG_ID_LEN + SPHINX_PACKET_OVERHEAD; +#[cfg(feature = "sphinx")] const REGULAR_PACKET_SIZE: usize = 2 * 1024 + SPHINX_PACKET_OVERHEAD; +#[cfg(feature = "sphinx")] const EXTENDED_PACKET_SIZE_8: usize = 8 * 1024 + SPHINX_PACKET_OVERHEAD; +#[cfg(feature = "sphinx")] const EXTENDED_PACKET_SIZE_16: usize = 16 * 1024 + SPHINX_PACKET_OVERHEAD; +#[cfg(feature = "sphinx")] const EXTENDED_PACKET_SIZE_32: usize = 32 * 1024 + SPHINX_PACKET_OVERHEAD; +#[cfg(feature = "outfox")] const OUTFOX_ACK_PACKET_SIZE: usize = MIN_PACKET_SIZE + OUTFOX_PACKET_OVERHEAD; +#[cfg(feature = "outfox")] const OUTFOX_REGULAR_PACKET_SIZE: usize = 2 * 1024 + OUTFOX_PACKET_OVERHEAD; #[derive(Debug, Error)] @@ -153,38 +162,55 @@ impl TryFrom for PacketSize { impl PacketSize { pub const fn size(self) -> usize { + #[allow(unreachable_patterns)] match self { + #[cfg(feature = "sphinx")] PacketSize::RegularPacket => REGULAR_PACKET_SIZE, + #[cfg(feature = "sphinx")] PacketSize::AckPacket => ACK_PACKET_SIZE, + #[cfg(feature = "sphinx")] PacketSize::ExtendedPacket8 => EXTENDED_PACKET_SIZE_8, + #[cfg(feature = "sphinx")] PacketSize::ExtendedPacket16 => EXTENDED_PACKET_SIZE_16, + #[cfg(feature = "sphinx")] PacketSize::ExtendedPacket32 => EXTENDED_PACKET_SIZE_32, + #[cfg(feature = "outfox")] PacketSize::OutfoxRegularPacket => OUTFOX_REGULAR_PACKET_SIZE, + #[cfg(feature = "outfox")] PacketSize::OutfoxAckPacket => OUTFOX_ACK_PACKET_SIZE, + _ => 0, } } pub const fn header_size(&self) -> usize { + #[allow(unreachable_patterns)] match self { + #[cfg(feature = "sphinx")] PacketSize::RegularPacket | PacketSize::AckPacket | PacketSize::ExtendedPacket8 | PacketSize::ExtendedPacket16 | PacketSize::ExtendedPacket32 => HEADER_SIZE, + #[cfg(feature = "outfox")] PacketSize::OutfoxRegularPacket | PacketSize::OutfoxAckPacket => MIX_PARAMS_LEN, + _ => 0, } } pub const fn payload_overhead(&self) -> usize { + #[allow(unreachable_patterns)] match self { + #[cfg(feature = "sphinx")] PacketSize::RegularPacket | PacketSize::AckPacket | PacketSize::ExtendedPacket8 | PacketSize::ExtendedPacket16 | PacketSize::ExtendedPacket32 => PAYLOAD_OVERHEAD_SIZE, + #[cfg(feature = "outfox")] PacketSize::OutfoxRegularPacket | PacketSize::OutfoxAckPacket => { OUTFOX_PACKET_OVERHEAD - MIX_PARAMS_LEN // Mix params are calculated into the total overhead so we take them out here } + _ => 0, } } @@ -242,11 +268,16 @@ impl PacketSize { plaintext_size: usize, packet_type: PacketType, ) -> Result { + #[allow(unreachable_patterns)] let overhead = match packet_type { + #[cfg(feature = "sphinx")] PacketType::Mix => SPHINX_PACKET_OVERHEAD, #[allow(deprecated)] + #[cfg(feature = "sphinx")] PacketType::Vpn => SPHINX_PACKET_OVERHEAD, + #[cfg(feature = "outfox")] PacketType::Outfox => OUTFOX_PACKET_OVERHEAD, + _ => 0, }; let packet_size = plaintext_size + overhead; Self::get_type(packet_size) diff --git a/common/nymsphinx/routing/Cargo.toml b/common/nymsphinx/routing/Cargo.toml index c0f7cf7628..37b8d5a4dc 100644 --- a/common/nymsphinx/routing/Cargo.toml +++ b/common/nymsphinx/routing/Cargo.toml @@ -11,4 +11,6 @@ repository = { workspace = true } thiserror = { workspace = true } nym-sphinx-addressing = { path = "../addressing" } -nym-sphinx-types = { path = "../types" } \ No newline at end of file +nym-sphinx-types = { path = "../types", features = ["sphinx"] } + +[features] \ No newline at end of file diff --git a/common/nymsphinx/src/preparer/payload.rs b/common/nymsphinx/src/preparer/payload.rs index 5999026252..bfd1715d57 100644 --- a/common/nymsphinx/src/preparer/payload.rs +++ b/common/nymsphinx/src/preparer/payload.rs @@ -50,8 +50,8 @@ impl NymPayloadBuilder { Ok(NymPayload( surb_ack_bytes .into_iter() - .chain(variant_data.into_iter()) - .chain(fragment_data.into_iter()) + .chain(variant_data) + .chain(fragment_data) .collect(), )) } @@ -61,10 +61,7 @@ impl NymPayloadBuilder { packet_encryption_key: &SurbEncryptionKey, ) -> Result { let key_digest = packet_encryption_key.compute_digest(); - self.build::( - packet_encryption_key.inner(), - key_digest.into_iter(), - ) + self.build::(packet_encryption_key.inner(), key_digest) } pub fn build_regular( diff --git a/common/nymsphinx/types/Cargo.toml b/common/nymsphinx/types/Cargo.toml index 2c52ef03d9..ae348113a0 100644 --- a/common/nymsphinx/types/Cargo.toml +++ b/common/nymsphinx/types/Cargo.toml @@ -8,6 +8,11 @@ license = { workspace = true } repository = { workspace = true } [dependencies] -sphinx-packet = { version = "0.1.0" } -nym-outfox = { path = "../../../nym-outfox" } +sphinx-packet = { version = "0.1.0", optional = true } +nym-outfox = { path = "../../../nym-outfox", optional = true } thiserror = "1" + +[features] +default = ["sphinx"] +sphinx = ["sphinx-packet"] +outfox = ["nym-outfox"] diff --git a/common/nymsphinx/types/src/lib.rs b/common/nymsphinx/types/src/lib.rs index bd3063f939..d75060e9b0 100644 --- a/common/nymsphinx/types/src/lib.rs +++ b/common/nymsphinx/types/src/lib.rs @@ -1,12 +1,15 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +#[cfg(feature = "outfox")] pub use nym_outfox::{ constants::MIN_PACKET_SIZE, constants::MIX_PARAMS_LEN, constants::OUTFOX_PACKET_OVERHEAD, error::OutfoxError, }; // re-exporting types and constants available in sphinx +#[cfg(feature = "outfox")] use nym_outfox::packet::{OutfoxPacket, OutfoxProcessedPacket}; +#[cfg(feature = "sphinx")] pub use sphinx_packet::{ constants::{ self, DESTINATION_ADDRESS_LENGTH, IDENTIFIER_LENGTH, MAX_PATH_LENGTH, NODE_ADDRESS_LENGTH, @@ -20,6 +23,7 @@ pub use sphinx_packet::{ surb::{SURBMaterial, SURB}, Error as SphinxError, ProcessedPacket, }; +#[cfg(feature = "sphinx")] use sphinx_packet::{SphinxPacket, SphinxPacketBuilder}; use std::{array::TryFromSliceError, fmt}; use thiserror::Error; @@ -27,9 +31,11 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum NymPacketError { #[error("Sphinx error: {0}")] + #[cfg(feature = "sphinx")] Sphinx(#[from] sphinx_packet::Error), #[error("Outfox error: {0}")] + #[cfg(feature = "outfox")] Outfox(#[from] nym_outfox::error::OutfoxError), #[error("{0}")] @@ -38,31 +44,40 @@ pub enum NymPacketError { #[allow(clippy::large_enum_variant)] pub enum NymPacket { + #[cfg(feature = "sphinx")] Sphinx(SphinxPacket), + #[cfg(feature = "outfox")] Outfox(OutfoxPacket), } pub enum NymProcessedPacket { + #[cfg(feature = "sphinx")] Sphinx(ProcessedPacket), + #[cfg(feature = "outfox")] Outfox(OutfoxProcessedPacket), } impl fmt::Debug for NymPacket { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + #[allow(unreachable_patterns)] match &self { + #[cfg(feature = "sphinx")] NymPacket::Sphinx(packet) => f .debug_struct("NymPacket::Sphinx") .field("len", &packet.len()) .finish(), + #[cfg(feature = "outfox")] NymPacket::Outfox(packet) => f .debug_struct("NymPacket::Outfox") .field("len", &packet.len()) .finish(), + _ => write!(f, ""), } } } impl NymPacket { + #[cfg(feature = "sphinx")] pub fn sphinx_build>( size: usize, message: M, @@ -76,10 +91,12 @@ impl NymPacket { .build_packet(message, route, destination, delays)?, )) } + #[cfg(feature = "sphinx")] pub fn sphinx_from_bytes(bytes: &[u8]) -> Result { Ok(NymPacket::Sphinx(SphinxPacket::from_bytes(bytes)?)) } + #[cfg(feature = "outfox")] pub fn outfox_build>( payload: M, route: &[Node], @@ -94,14 +111,19 @@ impl NymPacket { )?)) } + #[cfg(feature = "outfox")] pub fn outfox_from_bytes(bytes: &[u8]) -> Result { Ok(NymPacket::Outfox(OutfoxPacket::try_from(bytes)?)) } pub fn len(&self) -> usize { + #[allow(unreachable_patterns)] match self { + #[cfg(feature = "sphinx")] NymPacket::Sphinx(packet) => packet.len(), + #[cfg(feature = "outfox")] NymPacket::Outfox(packet) => packet.len(), + _ => 0, } } @@ -110,12 +132,17 @@ impl NymPacket { } pub fn to_bytes(&self) -> Result, NymPacketError> { + #[allow(unreachable_patterns)] match self { + #[cfg(feature = "sphinx")] NymPacket::Sphinx(packet) => Ok(packet.to_bytes()), + #[cfg(feature = "outfox")] NymPacket::Outfox(packet) => Ok(packet.to_bytes()?), + _ => Ok(vec![]), } } + #[cfg(feature = "sphinx")] pub fn process( self, node_secret_key: &PrivateKey, @@ -124,6 +151,7 @@ impl NymPacket { NymPacket::Sphinx(packet) => { Ok(NymProcessedPacket::Sphinx(packet.process(node_secret_key)?)) } + #[cfg(feature = "outfox")] NymPacket::Outfox(mut packet) => { let next_address = packet.decode_next_layer(node_secret_key)?; Ok(NymProcessedPacket::Outfox(OutfoxProcessedPacket::new( diff --git a/common/socks5-client-core/Cargo.toml b/common/socks5-client-core/Cargo.toml index 9ca792f8b1..6a5221bba0 100644 --- a/common/socks5-client-core/Cargo.toml +++ b/common/socks5-client-core/Cargo.toml @@ -7,22 +7,27 @@ edition = "2021" [dependencies] dirs = "4.0" +futures = { workspace = true } log = { workspace = true } pin-project = "1.0" rand = { version = "0.7.3", features = ["wasm-bindgen"] } +reqwest = { workspace = true } +schemars = { version = "0.8", features = ["preserve_order"] } serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization -thiserror = "1.0.34" tap = "1.0.1" +thiserror = "1.0.34" tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } -futures = "0.3" +url = { workspace = true } -nym-client-core = { path = "../client-core", features = ["fs-surb-storage"] } nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } +nym-client-core = { path = "../client-core", features = ["fs-surb-storage"] } nym-config = { path = "../config" } +nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common" } nym-credential-storage = { path = "../credential-storage" } +nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" } nym-network-defaults = { path = "../network-defaults" } -nym-socks5-proxy-helpers = { path = "../socks5/proxy-helpers" } nym-service-providers-common = { path = "../../service-providers/common" } +nym-socks5-proxy-helpers = { path = "../socks5/proxy-helpers" } nym-socks5-requests = { path = "../socks5/requests" } nym-sphinx = { path = "../nymsphinx" } nym-task = { path = "../task" } diff --git a/common/socks5-client-core/src/lib.rs b/common/socks5-client-core/src/lib.rs index 2070eeaf71..74d27b69ed 100644 --- a/common/socks5-client-core/src/lib.rs +++ b/common/socks5-client-core/src/lib.rs @@ -24,6 +24,7 @@ use nym_credential_storage::storage::Storage as CredentialStorage; use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::params::PacketType; use nym_task::{TaskClient, TaskManager}; + use std::error::Error; pub mod config; diff --git a/common/socks5/proxy-helpers/Cargo.toml b/common/socks5/proxy-helpers/Cargo.toml index 88157a22c4..6ebaaa03c9 100644 --- a/common/socks5/proxy-helpers/Cargo.toml +++ b/common/socks5/proxy-helpers/Cargo.toml @@ -12,7 +12,7 @@ tokio = { version = "1.24.1", features = [ "net", "io-util", "sync", "macros", " tokio-util = { version = "0.7.4", features = [ "io" ] } # reason for getting this guy is to to able to port to tokio 1.X more quickly by being able to use # their `read_buf` [from the util crate] replacement rather than having to rethink/reimplement `AvailableReader` with the new AsyncRead trait definition. # In the long run, the dependency should probably get removed in favour of pure-tokio implementation, but for time being it's fine. -futures = "0.3" +futures = { workspace = true } log = { workspace = true } # internal diff --git a/common/socks5/requests/src/lib.rs b/common/socks5/requests/src/lib.rs index 58c23dcd9d..c131f132a0 100644 --- a/common/socks5/requests/src/lib.rs +++ b/common/socks5/requests/src/lib.rs @@ -73,7 +73,7 @@ impl SocketDataHeader { .to_be_bytes() .into_iter() .chain(std::iter::once(self.local_socket_closed as u8)) - .chain(self.seq.to_be_bytes().into_iter()) + .chain(self.seq.to_be_bytes()) } pub fn try_from_response_bytes( @@ -107,8 +107,8 @@ impl SocketDataHeader { pub fn into_response_bytes_iter(self) -> impl Iterator { std::iter::once(self.local_socket_closed as u8) - .chain(self.connection_id.to_be_bytes().into_iter()) - .chain(self.seq.to_be_bytes().into_iter()) + .chain(self.connection_id.to_be_bytes()) + .chain(self.seq.to_be_bytes()) } } @@ -170,9 +170,7 @@ impl SocketData { } pub fn into_request_bytes_iter(self) -> impl Iterator { - self.header - .into_request_bytes_iter() - .chain(self.data.into_iter()) + self.header.into_request_bytes_iter().chain(self.data) } pub fn try_from_response_bytes(b: &[u8]) -> Result { @@ -190,9 +188,7 @@ impl SocketData { } pub fn into_response_bytes_iter(self) -> impl Iterator { - self.header - .into_response_bytes_iter() - .chain(self.data.into_iter()) + self.header.into_response_bytes_iter().chain(self.data) } } diff --git a/common/socks5/requests/src/request.rs b/common/socks5/requests/src/request.rs index 0e29fb49d9..530d27e11f 100644 --- a/common/socks5/requests/src/request.rs +++ b/common/socks5/requests/src/request.rs @@ -113,7 +113,7 @@ impl Serializable for Socks5Request { fn into_bytes(self) -> Vec { if let Some(version) = self.protocol_version.as_u8() { std::iter::once(version) - .chain(self.content.into_bytes().into_iter()) + .chain(self.content.into_bytes()) .collect() } else { std::iter::once(Self::LEGACY_TYPE_TAG) @@ -335,12 +335,12 @@ impl Socks5RequestContent { let remote_address_bytes_len = remote_address_bytes.len() as u16; let iter = std::iter::once(RequestFlag::Connect as u8) - .chain(req.conn_id.to_be_bytes().into_iter()) - .chain(remote_address_bytes_len.to_be_bytes().into_iter()) - .chain(remote_address_bytes.into_iter()); + .chain(req.conn_id.to_be_bytes()) + .chain(remote_address_bytes_len.to_be_bytes()) + .chain(remote_address_bytes); if let Some(return_address) = req.return_address { - iter.chain(return_address.to_bytes().into_iter()).collect() + iter.chain(return_address.to_bytes()).collect() } else { iter.collect() } @@ -358,7 +358,7 @@ impl Socks5RequestContent { }) .unwrap_or_default(); std::iter::once(RequestFlag::Query as u8) - .chain(query_bytes.into_iter()) + .chain(query_bytes) .collect() } } @@ -495,7 +495,7 @@ mod request_deserialization_tests { let request_bytes: Vec<_> = request_bytes_prefix .iter() .cloned() - .chain(recipient_bytes.into_iter()) + .chain(recipient_bytes) .collect(); assert!(Socks5RequestContent::try_from_bytes(&request_bytes) .unwrap_err() @@ -530,10 +530,7 @@ mod request_deserialization_tests { let recipient = Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f").unwrap(); let recipient_bytes = recipient.to_bytes(); - let request_bytes: Vec<_> = request_bytes - .into_iter() - .chain(recipient_bytes.into_iter()) - .collect(); + let request_bytes: Vec<_> = request_bytes.into_iter().chain(recipient_bytes).collect(); let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap(); match request { @@ -577,10 +574,7 @@ mod request_deserialization_tests { let recipient = Recipient::try_from_base58_string("CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@4sBbL1ngf1vtNqykydQKTFh26sQCw888GpUqvPvyNB4f").unwrap(); let recipient_bytes = recipient.to_bytes(); - let request_bytes: Vec<_> = request_bytes - .into_iter() - .chain(recipient_bytes.into_iter()) - .collect(); + let request_bytes: Vec<_> = request_bytes.into_iter().chain(recipient_bytes).collect(); let request = Socks5RequestContent::try_from_bytes(&request_bytes).unwrap(); match request { diff --git a/common/socks5/requests/src/response.rs b/common/socks5/requests/src/response.rs index c0ff60fa39..0415035ac5 100644 --- a/common/socks5/requests/src/response.rs +++ b/common/socks5/requests/src/response.rs @@ -84,7 +84,7 @@ impl Serializable for Socks5Response { fn into_bytes(self) -> Vec { if let Some(version) = self.protocol_version.as_u8() { std::iter::once(version) - .chain(self.content.into_bytes().into_iter()) + .chain(self.content.into_bytes()) .collect() } else { self.content.into_bytes() @@ -192,7 +192,7 @@ impl Socks5ResponseContent { } Socks5ResponseContent::ConnectionError(res) => { std::iter::once(ResponseFlag::ConnectionError as u8) - .chain(res.into_bytes().into_iter()) + .chain(res.into_bytes()) .collect() } Socks5ResponseContent::Query(query) => { @@ -204,7 +204,7 @@ impl Socks5ResponseContent { }) .unwrap_or_default(); std::iter::once(ResponseFlag::Query as u8) - .chain(query_bytes.into_iter()) + .chain(query_bytes) .collect() } } @@ -290,7 +290,7 @@ impl ConnectionError { .to_be_bytes() .iter() .copied() - .chain(self.network_requester_error.into_bytes().into_iter()) + .chain(self.network_requester_error.into_bytes()) .collect() } } @@ -339,7 +339,7 @@ mod tests { let bytes: Vec = 42u64 .to_be_bytes() .into_iter() - .chain([0, 159, 146, 150].into_iter()) + .chain([0, 159, 146, 150]) .collect(); let err = ConnectionError::try_from_bytes(&bytes).err().unwrap(); assert!(matches!( diff --git a/common/statistics/Cargo.toml b/common/statistics/Cargo.toml index 29573f1e06..1e762b88f1 100644 --- a/common/statistics/Cargo.toml +++ b/common/statistics/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] async-trait = { workspace = true } log = { workspace = true } -reqwest = { version = "0.11", features = ["json"] } +reqwest = { workspace = true, features = ["json"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1" sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "chrono"]} diff --git a/common/task/Cargo.toml b/common/task/Cargo.toml index 7e94b5442b..500cdb2a53 100644 --- a/common/task/Cargo.toml +++ b/common/task/Cargo.toml @@ -8,10 +8,10 @@ license.workspace = true repository.workspace = true [dependencies] -futures = "0.3" +futures = { workspace = true } log = { workspace = true } -thiserror = "1.0.37" -tokio = { version = "1.24.1", features = ["macros", "sync"] } +thiserror = { workspace = true } +tokio = { workspace = true, features = ["macros", "sync"] } [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio] version = "1.24.1" diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index 1d41a93ee4..bfe2c98573 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -19,10 +19,10 @@ thiserror = "1.0.37" async-trait = { workspace = true, optional = true } ## internal -nym-crypto = { path = "../crypto" } +nym-crypto = { path = "../crypto", features = ["sphinx", "outfox"] } nym-mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" } nym-sphinx-addressing = { path = "../nymsphinx/addressing" } -nym-sphinx-types = { path = "../nymsphinx/types" } +nym-sphinx-types = { path = "../nymsphinx/types", features = ["sphinx", "outfox"] } nym-sphinx-routing = { path = "../nymsphinx/routing" } nym-bin-common = { path = "../bin-common" } diff --git a/common/topology/src/error.rs b/common/topology/src/error.rs index bb36b42886..3f2523576f 100644 --- a/common/topology/src/error.rs +++ b/common/topology/src/error.rs @@ -41,6 +41,7 @@ pub enum NymTopologyError { PayloadBuilder, #[error("Outfox: {0}")] + #[cfg(feature = "outfox")] Outfox(#[from] nym_sphinx_types::OutfoxError), #[error("{0}")] diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index 45eb2d9d7d..428efd1056 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -131,7 +131,7 @@ impl NymTopology { } pub fn mixes_in_layer(&self, layer: MixLayer) -> Vec { - assert!(vec![1, 2, 3].contains(&layer)); + assert!([1, 2, 3].contains(&layer)); self.mixes.get(&layer).unwrap().to_owned() } diff --git a/common/types/Cargo.toml b/common/types/Cargo.toml index c99bbf3e30..4df4087661 100644 --- a/common/types/Cargo.toml +++ b/common/types/Cargo.toml @@ -10,14 +10,14 @@ rust-version = "1.58" eyre = "0.6.5" log = { workspace = true } itertools = "0.10" -reqwest = "0.11.9" +reqwest = { workspace = true } schemars = "0.8" serde = { version = "1.0", features = ["derive"] } serde_json = { workspace = true } strum = { version = "0.23", features = ["derive"] } -thiserror = "1.0" -url = "2.2" -ts-rs = "6.1.2" +thiserror = { workspace = true } +url = { workspace = true } +ts-rs = { workspace = true } cosmwasm-std = { workspace = true } cosmrs = { workspace = true } @@ -27,8 +27,7 @@ nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mix nym-vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" } nym-config = { path = "../../common/config" } nym-coconut-interface = { path = "../../common/coconut-interface" } -# Used for Type conversion, can be extracted but its a lot of work -nym-vesting-contract = { path = "../../contracts/vesting" } + [dev-dependencies] tempfile = "3.3.0" diff --git a/common/types/src/delegation.rs b/common/types/src/delegation.rs index 399bf7b91b..550f17a261 100644 --- a/common/types/src/delegation.rs +++ b/common/types/src/delegation.rs @@ -41,7 +41,7 @@ impl Delegation { feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/DelegationWithEverything.ts") )] -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] pub struct DelegationWithEverything { pub owner: String, pub mix_id: MixId, diff --git a/common/types/src/error.rs b/common/types/src/error.rs index 15b938da9a..4546731ff4 100644 --- a/common/types/src/error.rs +++ b/common/types/src/error.rs @@ -1,3 +1,4 @@ +use nym_validator_client::error::TendermintRpcError; use nym_validator_client::nym_api::error::NymAPIError; use nym_validator_client::{nyxd::error::NyxdError, ValidatorClientError}; use serde::{Serialize, Serializer}; @@ -18,6 +19,11 @@ pub enum TypesError { source: cosmwasm_std::StdError, }, #[error("{source}")] + TendermintRpcError { + #[from] + source: TendermintRpcError, + }, + #[error("{source}")] ErrorReport { #[from] source: eyre::Report, @@ -92,6 +98,7 @@ impl From for TypesError { ValidatorClientError::MalformedUrlProvided(e) => e.into(), ValidatorClientError::NyxdError(e) => e.into(), ValidatorClientError::NoAPIUrlAvailable => TypesError::NoNymApiUrlConfigured, + ValidatorClientError::TendermintErrorRpc(err) => err.into(), } } } diff --git a/common/types/src/mixnode.rs b/common/types/src/mixnode.rs index 145f9c409f..02246aad68 100644 --- a/common/types/src/mixnode.rs +++ b/common/types/src/mixnode.rs @@ -20,7 +20,7 @@ use std::net::IpAddr; feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/MixNodeDetails.ts") )] -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] pub struct MixNodeDetails { pub bond_information: MixNodeBond, pub rewarding_details: MixNodeRewarding, @@ -49,7 +49,7 @@ impl MixNodeDetails { feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/MixNodeBond.ts") )] -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] pub struct MixNodeBond { pub mix_id: MixId, pub owner: String, @@ -85,7 +85,7 @@ impl MixNodeBond { feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/MixNodeRewarding.ts") )] -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] pub struct MixNodeRewarding { pub cost_params: MixNodeCostParams, @@ -131,7 +131,7 @@ impl MixNodeRewarding { feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/MixNodeCostParams.ts") )] -#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] pub struct MixNodeCostParams { #[cfg_attr(feature = "generate-ts", ts(type = "string"))] pub profit_margin_percent: Percent, diff --git a/common/types/src/pending_events.rs b/common/types/src/pending_events.rs index 23ecdf4f19..f5789890bf 100644 --- a/common/types/src/pending_events.rs +++ b/common/types/src/pending_events.rs @@ -127,7 +127,7 @@ impl PendingEpochEventData { feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/PendingIntervalEvent.ts") )] -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] pub struct PendingIntervalEvent { pub id: IntervalEventId, pub created_at: BlockHeight, @@ -155,7 +155,7 @@ impl PendingIntervalEvent { feature = "generate-ts", ts(export_to = "ts-packages/types/src/types/rust/PendingIntervalEventData.ts") )] -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] pub enum PendingIntervalEventData { ChangeMixCostParams { mix_id: MixId, diff --git a/common/types/src/vesting.rs b/common/types/src/vesting.rs index 9be8ef03da..1071b21324 100644 --- a/common/types/src/vesting.rs +++ b/common/types/src/vesting.rs @@ -1,10 +1,10 @@ use crate::currency::{DecCoin, RegisteredCoins}; use crate::error::TypesError; +use nym_vesting_contract_common::account::Account as ContractVestingAccount; +use nym_vesting_contract_common::types::VestingPeriod as ContractVestingPeriod; use nym_vesting_contract_common::OriginalVestingResponse as ContractOriginalVestingResponse; use nym_vesting_contract_common::PledgeData as ContractPledgeData; use serde::{Deserialize, Serialize}; -use vesting_contract::vesting::Account as ContractVestingAccount; -use vesting_contract::vesting::VestingPeriod as ContractVestingPeriod; #[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))] #[cfg_attr( diff --git a/common/wasm-utils/Cargo.toml b/common/wasm-utils/Cargo.toml index 2964e32110..fb4b25c5d8 100644 --- a/common/wasm-utils/Cargo.toml +++ b/common/wasm-utils/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -futures = "0.3" +futures = { workspace = true } js-sys = "^0.3.51" wasm-bindgen = "=0.2.83" wasm-bindgen-futures = "0.4" diff --git a/common/wasm-utils/src/storage/mod.rs b/common/wasm-utils/src/storage/mod.rs index 1a5b601ce6..eb17949849 100644 --- a/common/wasm-utils/src/storage/mod.rs +++ b/common/wasm-utils/src/storage/mod.rs @@ -5,7 +5,6 @@ use crate::console_log; use crate::storage::cipher_export::StoredExportedStoreCipher; use crate::storage::error::StorageError; use futures::TryFutureExt; -use indexed_db_futures::IdbDatabase; use nym_store_cipher::{ Aes256Gcm, Algorithm, EncryptedData, KdfInfo, KeySizeUser, Params, StoreCipher, Unsigned, Version, diff --git a/contracts/CHANGELOG.md b/contracts/CHANGELOG.md index ffa2dba525..dbad30e829 100644 --- a/contracts/CHANGELOG.md +++ b/contracts/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog -## Unreleased +## [Unreleased] + +## [v1.5.0] (2023-08-16) +- Generate json schema for all used contracts #3693 ([#3693]) + +[#3233]: https://github.com/nymtech/nym/pull/3693 + ## [v1.4.0] (2023-04-25) - Allow mixnode operators to decrease their bond amount without having to rebond (will require a lot of testing EXACT reward values to make sure the "unit delegation" isn't broken afterwards) ([#3233]) diff --git a/contracts/Cargo.lock b/contracts/Cargo.lock index 0c3efd0a24..812afbaa8d 100644 --- a/contracts/Cargo.lock +++ b/contracts/Cargo.lock @@ -2,16 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array 0.14.6", -] - [[package]] name = "aes" version = "0.7.5" @@ -19,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if", - "cipher 0.3.0", + "cipher", "cpufeatures", "ctr", "opaque-debug 0.3.0", @@ -48,12 +38,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - [[package]] name = "autocfg" version = "1.1.0" @@ -102,27 +86,13 @@ dependencies = [ "opaque-debug 0.2.3", ] -[[package]] -name = "blake3" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "digest 0.10.7", -] - [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -131,9 +101,15 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bs58" version = "0.4.0" @@ -189,48 +165,13 @@ dependencies = [ "keystream", ] -[[package]] -name = "chacha20" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" -dependencies = [ - "cfg-if", - "cipher 0.4.4", - "cpufeatures", -] - -[[package]] -name = "chacha20poly1305" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" -dependencies = [ - "aead", - "chacha20", - "cipher 0.4.4", - "poly1305", - "zeroize", -] - [[package]] name = "cipher" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array 0.14.6", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", - "zeroize", + "generic-array 0.14.7", ] [[package]] @@ -264,17 +205,11 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - [[package]] name = "cosmwasm-crypto" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75836a10cb9654c54e77ee56da94d592923092a10b369cdb0dbd56acefc16340" +checksum = "0d076a08ec01ed23c4396aca98ec73a38fa1fee5f310465add52b4108181c7a8" dependencies = [ "digest 0.10.7", "ed25519-zebra", @@ -285,18 +220,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" +checksum = "dec361f3c09d7b41221948fc17be9b3c96cb58e55a02f82da36f888a651f2584" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc" +checksum = "bb6b2fb76758ef59cddc77f2e2ae91c22f77da49037e9f182e9c2833f0e959b1" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -307,9 +242,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c" +checksum = "2bfa39422f0d9f1c9a6fd3711573258495314dfa3aae738ea825ecd9964bc659" dependencies = [ "proc-macro2", "quote", @@ -318,11 +253,12 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb" +checksum = "1f6dc2ee23313add5ecacc3ccac217b9967ad9d2d11bd56e5da6aa65a9da6138" dependencies = [ "base64 0.13.1", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -330,17 +266,16 @@ dependencies = [ "hex", "schemars", "serde", - "serde-json-wasm 0.5.1", + "serde-json-wasm", "sha2 0.10.6", "thiserror", - "uint", ] [[package]] name = "cosmwasm-storage" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3737a3aac48f5ed883b5b73bfb731e77feebd8fc6b43419844ec2971072164d" +checksum = "7ade8cae79dc08a06bcf119c0854ffaed11bd8cb1013c6b04abfe1f51f36211e" dependencies = [ "cosmwasm-std", "serde", @@ -355,62 +290,13 @@ dependencies = [ "libc", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "rand_core 0.6.4", "subtle 2.4.1", "zeroize", @@ -422,8 +308,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", - "rand_core 0.6.4", + "generic-array 0.14.7", "typenum", ] @@ -443,7 +328,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "subtle 2.4.1", ] @@ -453,7 +338,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" dependencies = [ - "cipher 0.3.0", + "cipher", ] [[package]] @@ -471,9 +356,9 @@ dependencies = [ [[package]] name = "cw-controllers" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071" +checksum = "d5d8edce4b78785f36413f67387e4be7d0cb7d032b5d4164bcc024f9c3f3f2ea" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -505,9 +390,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" +checksum = "3f0e92a069d62067f3472c62e30adedb4cab1754725c0f2a682b3128d2bf3c79" dependencies = [ "cosmwasm-std", "schemars", @@ -531,22 +416,23 @@ dependencies = [ [[package]] name = "cw2" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" +checksum = "29ac2dc7a55ad64173ca1e0a46697c31b7a5c51342f55a1e84a724da4eb99908" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", "serde", + "thiserror", ] [[package]] name = "cw20" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" +checksum = "011c45920f8200bd5d32d4fe52502506f64f2f75651ab408054d4cfc75ca3a9b" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -575,9 +461,9 @@ dependencies = [ [[package]] name = "cw3" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa" +checksum = "171af3d9127de6805a7dd819fb070c7d2f6c3ea85f4193f42cef259f0a7f33d5" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -590,9 +476,9 @@ dependencies = [ [[package]] name = "cw3-fixed-multisig" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e2415adb201e5e89dab34edf59d7dc166bc558526de009a49ae66276c9119a" +checksum = "705875c68f95870e9707e7b72b50631ddc58c07517d6cc43758306e38921118e" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -609,6 +495,7 @@ dependencies = [ name = "cw3-flex-multisig" version = "1.0.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-multi-test", "cw-storage-plus", @@ -622,14 +509,13 @@ dependencies = [ "cw4-group", "nym-group-contract-common", "nym-multisig-contract-common", - "schemars", ] [[package]] name = "cw4" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b" +checksum = "a398696307efadaaa2d0850076f865fa706c959d493cb4203314f72be6b77a64" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -691,7 +577,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] @@ -778,7 +664,7 @@ dependencies = [ "der", "digest 0.10.7", "ff", - "generic-array 0.14.6", + "generic-array 0.14.7", "group", "pkcs8", "rand_core 0.6.4", @@ -974,9 +860,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1002,10 +888,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -1120,15 +1004,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array 0.14.6", -] - [[package]] name = "instant" version = "0.1.12" @@ -1226,9 +1101,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "libz-sys" @@ -1275,15 +1150,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - [[package]] name = "mixnet-vesting-integration-tests" version = "0.1.0" @@ -1302,35 +1168,25 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", "libm", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "nym-coconut-bandwidth" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", "cw-controllers", "cw-storage-plus", "nym-coconut-bandwidth-contract-common", "nym-multisig-contract-common", - "schemars", "serde", "thiserror", ] @@ -1339,16 +1195,17 @@ dependencies = [ name = "nym-coconut-bandwidth-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw2", "nym-multisig-contract-common", - "schemars", - "serde", ] [[package]] name = "nym-coconut-dkg" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cosmwasm-storage", "cw-controllers", @@ -1360,7 +1217,6 @@ dependencies = [ "nym-coconut-dkg-common", "nym-group-contract-common", "rusty-fork", - "schemars", "serde", "thiserror", ] @@ -1369,12 +1225,11 @@ dependencies = [ name = "nym-coconut-dkg-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-utils", "nym-contracts-common", "nym-multisig-contract-common", - "schemars", - "serde", ] [[package]] @@ -1382,6 +1237,7 @@ name = "nym-contracts-common" version = "0.5.0" dependencies = [ "bs58", + "cosmwasm-schema", "cosmwasm-std", "schemars", "serde", @@ -1403,6 +1259,36 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nym-ephemera" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-controllers", + "cw-multi-test", + "cw-storage-plus", + "cw4", + "cw4-group", + "lazy_static", + "nym-ephemera-common", + "nym-group-contract-common", + "rusty-fork", + "serde", + "thiserror", +] + +[[package]] +name = "nym-ephemera-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "nym-contracts-common", +] + [[package]] name = "nym-group-contract-common" version = "0.1.0" @@ -1416,7 +1302,7 @@ dependencies = [ [[package]] name = "nym-mixnet-contract" -version = "1.4.0" +version = "1.5.0" dependencies = [ "bs58", "cosmwasm-derive", @@ -1430,7 +1316,6 @@ dependencies = [ "nym-mixnet-contract-common", "nym-vesting-contract-common", "rand_chacha 0.2.2", - "schemars", "semver", "serde", "thiserror", @@ -1443,13 +1328,15 @@ name = "nym-mixnet-contract-common" version = "0.6.0" dependencies = [ "bs58", + "cosmwasm-schema", "cosmwasm-std", + "cw2", "humantime-serde", "log", "nym-contracts-common", "schemars", "serde", - "serde-json-wasm 0.4.1", + "serde-json-wasm", "serde_repr", "thiserror", "time", @@ -1476,6 +1363,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bs58", + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-multi-test", @@ -1485,6 +1373,7 @@ dependencies = [ "nym-contracts-common", "nym-crypto", "nym-name-service-common", + "nym-sphinx-addressing", "rand 0.8.5", "rand_chacha 0.2.2", "rstest", @@ -1498,32 +1387,16 @@ dependencies = [ name = "nym-name-service-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-utils", + "cw2", "nym-contracts-common", - "schemars", "serde", "thiserror", ] -[[package]] -name = "nym-outfox" -version = "0.1.0" -dependencies = [ - "blake3", - "chacha20", - "chacha20poly1305", - "curve25519-dalek", - "getrandom 0.2.10", - "log", - "rand 0.7.3", - "rayon", - "sphinx-packet", - "thiserror", - "zeroize", -] - [[package]] name = "nym-pemstore" version = "0.3.0" @@ -1537,6 +1410,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bs58", + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-multi-test", @@ -1558,11 +1432,21 @@ dependencies = [ name = "nym-service-provider-directory-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-utils", + "cw2", "nym-contracts-common", - "schemars", + "thiserror", +] + +[[package]] +name = "nym-sphinx-addressing" +version = "0.1.0" +dependencies = [ + "nym-crypto", + "nym-sphinx-types", "serde", "thiserror", ] @@ -1571,18 +1455,18 @@ dependencies = [ name = "nym-sphinx-types" version = "0.2.0" dependencies = [ - "nym-outfox", "sphinx-packet", "thiserror", ] [[package]] name = "nym-vesting-contract" -version = "1.4.0" +version = "1.4.1" dependencies = [ "base64 0.21.0", "cosmwasm-crypto", "cosmwasm-derive", + "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "cw2", @@ -1591,7 +1475,6 @@ dependencies = [ "nym-mixnet-contract-common", "nym-vesting-contract-common", "rand_chacha 0.3.1", - "schemars", "semver", "serde", "serde_json", @@ -1603,11 +1486,13 @@ dependencies = [ name = "nym-vesting-contract-common" version = "0.7.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", + "cw2", "nym-contracts-common", "nym-mixnet-contract-common", - "schemars", "serde", + "thiserror", ] [[package]] @@ -1673,17 +1558,6 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" -[[package]] -name = "poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" -dependencies = [ - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash", -] - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1842,28 +1716,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - [[package]] name = "redox_syscall" version = "0.3.5" @@ -1996,12 +1848,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "sec1" version = "0.3.0" @@ -2010,7 +1856,7 @@ checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" dependencies = [ "base16ct", "der", - "generic-array 0.14.6", + "generic-array 0.14.7", "pkcs8", "subtle 2.4.1", "zeroize", @@ -2033,18 +1879,9 @@ dependencies = [ [[package]] name = "serde-json-wasm" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" -dependencies = [ - "serde", -] - -[[package]] -name = "serde-json-wasm" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" dependencies = [ "serde", ] @@ -2170,12 +2007,6 @@ dependencies = [ "der", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "subtle" version = "1.0.0" @@ -2300,18 +2131,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-bidi" version = "0.3.12" @@ -2333,16 +2152,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle 2.4.1", -] - [[package]] name = "url" version = "2.3.1" diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index 1b20380ad7..8063d2f543 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -3,6 +3,7 @@ members = [ "coconut-bandwidth", "coconut-dkg", "coconut-test", + "ephemera", "mixnet", "mixnet-vesting-integration-tests", "multisig/cw3-flex-multisig", @@ -32,17 +33,17 @@ incremental = false overflow-checks = true [workspace.dependencies] -cosmwasm-crypto = "=1.2.5" -cosmwasm-derive = "=1.2.5" -cosmwasm-schema = "=1.2.5" -cosmwasm-std = "=1.2.5" -cosmwasm-storage = "=1.2.5" -cw-controllers = "=1.0.1" +cosmwasm-crypto = "=1.3.0" +cosmwasm-derive = "=1.3.0" +cosmwasm-schema = "=1.3.0" +cosmwasm-std = "=1.3.0" +cosmwasm-storage = "=1.3.0" +cw-controllers = "=1.1.0" cw-multi-test = "=0.16.4" -cw-storage-plus = "=1.0.1" +cw-storage-plus = "=1.1.0" 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" +cw2 = "=1.1.0" +cw3 = "=1.1.0" +cw3-fixed-multisig = "=1.1.0" +cw4 = "=1.1.0" +cw20 = "=1.1.0" diff --git a/contracts/Makefile b/contracts/Makefile new file mode 100644 index 0000000000..941959bf0c --- /dev/null +++ b/contracts/Makefile @@ -0,0 +1,25 @@ +schema: coconut-bandwidth-schema coconut-dkg-schema mixnet-schema name-service-schema service-provider-directory-schema vesting-schema multisig-schema group-schema + +coconut-bandwidth-schema: + $(MAKE) -C coconut-bandwidth generate-schema + +coconut-dkg-schema: + $(MAKE) -C coconut-dkg generate-schema + +mixnet-schema: + $(MAKE) -C mixnet generate-schema + +name-service-schema: + $(MAKE) -C name-service generate-schema + +service-provider-directory-schema: + $(MAKE) -C service-provider-directory generate-schema + +vesting-schema: + $(MAKE) -C vesting generate-schema + +multisig-schema: + $(MAKE) -C multisig/cw3-flex-multisig generate-schema + +group-schema: + $(MAKE) -C multisig/cw4-group generate-schema diff --git a/contracts/coconut-bandwidth/.cargo/config b/contracts/coconut-bandwidth/.cargo/config new file mode 100644 index 0000000000..2fb2c1afdb --- /dev/null +++ b/contracts/coconut-bandwidth/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --bin schema --features=schema-gen" \ No newline at end of file diff --git a/contracts/coconut-bandwidth/Cargo.toml b/contracts/coconut-bandwidth/Cargo.toml index 69bb7088d5..5ed8e5f98b 100644 --- a/contracts/coconut-bandwidth/Cargo.toml +++ b/contracts/coconut-bandwidth/Cargo.toml @@ -5,6 +5,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "schema" +required-features = ["schema-gen"] + [lib] crate-type = ["cdylib", "rlib"] @@ -13,10 +17,13 @@ nym-coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-co nym-multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" } cosmwasm-std = { workspace = true } +cosmwasm-schema = { workspace = true, optional = true } cosmwasm-storage = { workspace = true } cw-storage-plus = { workspace = true } cw-controllers = { workspace = true } -schemars = "0.8" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = "1.0.23" + +[features] +schema-gen = ["nym-coconut-bandwidth-contract-common/schema", "cosmwasm-schema"] \ No newline at end of file diff --git a/contracts/coconut-bandwidth/Makefile b/contracts/coconut-bandwidth/Makefile new file mode 100644 index 0000000000..f138e2c879 --- /dev/null +++ b/contracts/coconut-bandwidth/Makefile @@ -0,0 +1,2 @@ +generate-schema: + cargo schema \ No newline at end of file diff --git a/contracts/coconut-bandwidth/schema/nym-coconut-bandwidth.json b/contracts/coconut-bandwidth/schema/nym-coconut-bandwidth.json new file mode 100644 index 0000000000..1b210e85f9 --- /dev/null +++ b/contracts/coconut-bandwidth/schema/nym-coconut-bandwidth.json @@ -0,0 +1,382 @@ +{ + "contract_name": "nym-coconut-bandwidth", + "contract_version": "0.1.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "mix_denom", + "multisig_addr", + "pool_addr" + ], + "properties": { + "mix_denom": { + "type": "string" + }, + "multisig_addr": { + "type": "string" + }, + "pool_addr": { + "type": "string" + } + }, + "additionalProperties": false + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "deposit_funds" + ], + "properties": { + "deposit_funds": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/DepositData" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "spend_credential" + ], + "properties": { + "spend_credential": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/SpendCredentialData" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "release_funds" + ], + "properties": { + "release_funds": { + "type": "object", + "required": [ + "funds" + ], + "properties": { + "funds": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "DepositData": { + "type": "object", + "required": [ + "deposit_info", + "encryption_key", + "identity_key" + ], + "properties": { + "deposit_info": { + "type": "string" + }, + "encryption_key": { + "type": "string" + }, + "identity_key": { + "type": "string" + } + }, + "additionalProperties": false + }, + "SpendCredentialData": { + "type": "object", + "required": [ + "blinded_serial_number", + "funds", + "gateway_cosmos_address" + ], + "properties": { + "blinded_serial_number": { + "type": "string" + }, + "funds": { + "$ref": "#/definitions/Coin" + }, + "gateway_cosmos_address": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "get_spent_credential" + ], + "properties": { + "get_spent_credential": { + "type": "object", + "required": [ + "blinded_serial_number" + ], + "properties": { + "blinded_serial_number": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_all_spent_credentials" + ], + "properties": { + "get_all_spent_credentials": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "migrate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false + }, + "sudo": null, + "responses": { + "get_all_spent_credentials": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedSpendCredentialResponse", + "type": "object", + "required": [ + "per_page", + "spend_credentials" + ], + "properties": { + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "spend_credentials": { + "type": "array", + "items": { + "$ref": "#/definitions/SpendCredential" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "SpendCredential": { + "type": "object", + "required": [ + "blinded_serial_number", + "funds", + "gateway_cosmos_address", + "status" + ], + "properties": { + "blinded_serial_number": { + "type": "string" + }, + "funds": { + "$ref": "#/definitions/Coin" + }, + "gateway_cosmos_address": { + "$ref": "#/definitions/Addr" + }, + "status": { + "$ref": "#/definitions/SpendCredentialStatus" + } + }, + "additionalProperties": false + }, + "SpendCredentialStatus": { + "type": "string", + "enum": [ + "in_progress", + "spent" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_spent_credential": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SpendCredentialResponse", + "type": "object", + "properties": { + "spend_credential": { + "anyOf": [ + { + "$ref": "#/definitions/SpendCredential" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "SpendCredential": { + "type": "object", + "required": [ + "blinded_serial_number", + "funds", + "gateway_cosmos_address", + "status" + ], + "properties": { + "blinded_serial_number": { + "type": "string" + }, + "funds": { + "$ref": "#/definitions/Coin" + }, + "gateway_cosmos_address": { + "$ref": "#/definitions/Addr" + }, + "status": { + "$ref": "#/definitions/SpendCredentialStatus" + } + }, + "additionalProperties": false + }, + "SpendCredentialStatus": { + "type": "string", + "enum": [ + "in_progress", + "spent" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + } + } +} diff --git a/contracts/coconut-bandwidth/schema/raw/execute.json b/contracts/coconut-bandwidth/schema/raw/execute.json new file mode 100644 index 0000000000..027b423f7a --- /dev/null +++ b/contracts/coconut-bandwidth/schema/raw/execute.json @@ -0,0 +1,130 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "deposit_funds" + ], + "properties": { + "deposit_funds": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/DepositData" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "spend_credential" + ], + "properties": { + "spend_credential": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "$ref": "#/definitions/SpendCredentialData" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "release_funds" + ], + "properties": { + "release_funds": { + "type": "object", + "required": [ + "funds" + ], + "properties": { + "funds": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "DepositData": { + "type": "object", + "required": [ + "deposit_info", + "encryption_key", + "identity_key" + ], + "properties": { + "deposit_info": { + "type": "string" + }, + "encryption_key": { + "type": "string" + }, + "identity_key": { + "type": "string" + } + }, + "additionalProperties": false + }, + "SpendCredentialData": { + "type": "object", + "required": [ + "blinded_serial_number", + "funds", + "gateway_cosmos_address" + ], + "properties": { + "blinded_serial_number": { + "type": "string" + }, + "funds": { + "$ref": "#/definitions/Coin" + }, + "gateway_cosmos_address": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/coconut-bandwidth/schema/raw/instantiate.json b/contracts/coconut-bandwidth/schema/raw/instantiate.json new file mode 100644 index 0000000000..172dc76d79 --- /dev/null +++ b/contracts/coconut-bandwidth/schema/raw/instantiate.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "mix_denom", + "multisig_addr", + "pool_addr" + ], + "properties": { + "mix_denom": { + "type": "string" + }, + "multisig_addr": { + "type": "string" + }, + "pool_addr": { + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/coconut-bandwidth/schema/raw/migrate.json b/contracts/coconut-bandwidth/schema/raw/migrate.json new file mode 100644 index 0000000000..7fbe8c5708 --- /dev/null +++ b/contracts/coconut-bandwidth/schema/raw/migrate.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false +} diff --git a/contracts/coconut-bandwidth/schema/raw/query.json b/contracts/coconut-bandwidth/schema/raw/query.json new file mode 100644 index 0000000000..afdfae331d --- /dev/null +++ b/contracts/coconut-bandwidth/schema/raw/query.json @@ -0,0 +1,56 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "get_spent_credential" + ], + "properties": { + "get_spent_credential": { + "type": "object", + "required": [ + "blinded_serial_number" + ], + "properties": { + "blinded_serial_number": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_all_spent_credentials" + ], + "properties": { + "get_all_spent_credentials": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/coconut-bandwidth/schema/raw/response_to_get_all_spent_credentials.json b/contracts/coconut-bandwidth/schema/raw/response_to_get_all_spent_credentials.json new file mode 100644 index 0000000000..733da87081 --- /dev/null +++ b/contracts/coconut-bandwidth/schema/raw/response_to_get_all_spent_credentials.json @@ -0,0 +1,86 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedSpendCredentialResponse", + "type": "object", + "required": [ + "per_page", + "spend_credentials" + ], + "properties": { + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "spend_credentials": { + "type": "array", + "items": { + "$ref": "#/definitions/SpendCredential" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "SpendCredential": { + "type": "object", + "required": [ + "blinded_serial_number", + "funds", + "gateway_cosmos_address", + "status" + ], + "properties": { + "blinded_serial_number": { + "type": "string" + }, + "funds": { + "$ref": "#/definitions/Coin" + }, + "gateway_cosmos_address": { + "$ref": "#/definitions/Addr" + }, + "status": { + "$ref": "#/definitions/SpendCredentialStatus" + } + }, + "additionalProperties": false + }, + "SpendCredentialStatus": { + "type": "string", + "enum": [ + "in_progress", + "spent" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/coconut-bandwidth/schema/raw/response_to_get_spent_credential.json b/contracts/coconut-bandwidth/schema/raw/response_to_get_spent_credential.json new file mode 100644 index 0000000000..9af4def85b --- /dev/null +++ b/contracts/coconut-bandwidth/schema/raw/response_to_get_spent_credential.json @@ -0,0 +1,74 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SpendCredentialResponse", + "type": "object", + "properties": { + "spend_credential": { + "anyOf": [ + { + "$ref": "#/definitions/SpendCredential" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "SpendCredential": { + "type": "object", + "required": [ + "blinded_serial_number", + "funds", + "gateway_cosmos_address", + "status" + ], + "properties": { + "blinded_serial_number": { + "type": "string" + }, + "funds": { + "$ref": "#/definitions/Coin" + }, + "gateway_cosmos_address": { + "$ref": "#/definitions/Addr" + }, + "status": { + "$ref": "#/definitions/SpendCredentialStatus" + } + }, + "additionalProperties": false + }, + "SpendCredentialStatus": { + "type": "string", + "enum": [ + "in_progress", + "spent" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/coconut-bandwidth/src/bin/schema.rs b/contracts/coconut-bandwidth/src/bin/schema.rs new file mode 100644 index 0000000000..4956eb33f4 --- /dev/null +++ b/contracts/coconut-bandwidth/src/bin/schema.rs @@ -0,0 +1,16 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::write_api; +use nym_coconut_bandwidth_contract_common::msg::{ + ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, +}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + migrate: MigrateMsg, + } +} diff --git a/contracts/coconut-bandwidth/src/state.rs b/contracts/coconut-bandwidth/src/state.rs index 74b969341c..855d9fa07a 100644 --- a/contracts/coconut-bandwidth/src/state.rs +++ b/contracts/coconut-bandwidth/src/state.rs @@ -4,12 +4,11 @@ use cosmwasm_std::Addr; use cw_controllers::Admin; use cw_storage_plus::Item; -use schemars::JsonSchema; use serde::{Deserialize, Serialize}; pub const ADMIN: Admin = Admin::new("admin"); -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, Debug)] +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] pub struct Config { pub multisig_addr: Addr, pub pool_addr: Addr, diff --git a/contracts/coconut-dkg/.cargo/config b/contracts/coconut-dkg/.cargo/config new file mode 100644 index 0000000000..2f698e22b3 --- /dev/null +++ b/contracts/coconut-dkg/.cargo/config @@ -0,0 +1,5 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +wasm-debug = "build --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --bin schema --features=schema-gen" \ No newline at end of file diff --git a/contracts/coconut-dkg/Cargo.toml b/contracts/coconut-dkg/Cargo.toml index f877df138d..794c1f3a0f 100644 --- a/contracts/coconut-dkg/Cargo.toml +++ b/contracts/coconut-dkg/Cargo.toml @@ -5,19 +5,22 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "schema" +required-features = ["schema-gen"] + [lib] crate-type = ["cdylib", "rlib"] [dependencies] nym-coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut-dkg" } +cosmwasm-schema = { workspace = true, optional = true } cosmwasm-std = { workspace = true } cosmwasm-storage = { workspace = true } cw-storage-plus = { workspace = true } cw-controllers = { workspace = true } cw4 = { workspace = true } - -schemars = "0.8" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = "1.0.23" @@ -27,3 +30,7 @@ cw4-group = { path = "../multisig/cw4-group" } nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" } lazy_static = "1.4" rusty-fork = "0.3" + +[features] +schema-gen = ["nym-coconut-dkg-common/schema", "cosmwasm-schema"] + diff --git a/contracts/coconut-dkg/Makefile b/contracts/coconut-dkg/Makefile new file mode 100644 index 0000000000..f138e2c879 --- /dev/null +++ b/contracts/coconut-dkg/Makefile @@ -0,0 +1,2 @@ +generate-schema: + cargo schema \ No newline at end of file diff --git a/contracts/coconut-dkg/schema/nym-coconut-dkg.json b/contracts/coconut-dkg/schema/nym-coconut-dkg.json new file mode 100644 index 0000000000..035f1bfe8f --- /dev/null +++ b/contracts/coconut-dkg/schema/nym-coconut-dkg.json @@ -0,0 +1,1023 @@ +{ + "contract_name": "nym-coconut-dkg", + "contract_version": "0.1.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "group_addr", + "mix_denom", + "multisig_addr" + ], + "properties": { + "group_addr": { + "type": "string" + }, + "mix_denom": { + "type": "string" + }, + "multisig_addr": { + "type": "string" + }, + "time_configuration": { + "anyOf": [ + { + "$ref": "#/definitions/TimeConfiguration" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "TimeConfiguration": { + "type": "object", + "required": [ + "dealing_exchange_time_secs", + "in_progress_time_secs", + "public_key_submission_time_secs", + "verification_key_finalization_time_secs", + "verification_key_submission_time_secs", + "verification_key_validation_time_secs" + ], + "properties": { + "dealing_exchange_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "in_progress_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "public_key_submission_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_finalization_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_submission_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_validation_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "register_dealer" + ], + "properties": { + "register_dealer": { + "type": "object", + "required": [ + "announce_address", + "bte_key_with_proof", + "resharing" + ], + "properties": { + "announce_address": { + "type": "string" + }, + "bte_key_with_proof": { + "type": "string" + }, + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "commit_dealing" + ], + "properties": { + "commit_dealing": { + "type": "object", + "required": [ + "dealing_bytes", + "resharing" + ], + "properties": { + "dealing_bytes": { + "$ref": "#/definitions/ContractSafeBytes" + }, + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "commit_verification_key_share" + ], + "properties": { + "commit_verification_key_share": { + "type": "object", + "required": [ + "resharing", + "share" + ], + "properties": { + "resharing": { + "type": "boolean" + }, + "share": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "verify_verification_key_share" + ], + "properties": { + "verify_verification_key_share": { + "type": "object", + "required": [ + "owner", + "resharing" + ], + "properties": { + "owner": { + "$ref": "#/definitions/Addr" + }, + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "surpassed_threshold" + ], + "properties": { + "surpassed_threshold": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "advance_epoch_state" + ], + "properties": { + "advance_epoch_state": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "ContractSafeBytes": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "get_current_epoch_state" + ], + "properties": { + "get_current_epoch_state": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_current_epoch_threshold" + ], + "properties": { + "get_current_epoch_threshold": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_initial_dealers" + ], + "properties": { + "get_initial_dealers": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_dealer_details" + ], + "properties": { + "get_dealer_details": { + "type": "object", + "required": [ + "dealer_address" + ], + "properties": { + "dealer_address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_current_dealers" + ], + "properties": { + "get_current_dealers": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_past_dealers" + ], + "properties": { + "get_past_dealers": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_dealing" + ], + "properties": { + "get_dealing": { + "type": "object", + "required": [ + "idx" + ], + "properties": { + "idx": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_verification_keys" + ], + "properties": { + "get_verification_keys": { + "type": "object", + "required": [ + "epoch_id" + ], + "properties": { + "epoch_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "migrate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false + }, + "sudo": null, + "responses": { + "get_current_dealers": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedDealerResponse", + "type": "object", + "required": [ + "dealers", + "per_page" + ], + "properties": { + "dealers": { + "type": "array", + "items": { + "$ref": "#/definitions/DealerDetails" + } + }, + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "DealerDetails": { + "type": "object", + "required": [ + "address", + "announce_address", + "assigned_index", + "bte_public_key_with_proof" + ], + "properties": { + "address": { + "$ref": "#/definitions/Addr" + }, + "announce_address": { + "type": "string" + }, + "assigned_index": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "bte_public_key_with_proof": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "get_current_epoch_state": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Epoch", + "type": "object", + "required": [ + "epoch_id", + "finish_timestamp", + "state", + "time_configuration" + ], + "properties": { + "epoch_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "finish_timestamp": { + "$ref": "#/definitions/Timestamp" + }, + "state": { + "$ref": "#/definitions/EpochState" + }, + "time_configuration": { + "$ref": "#/definitions/TimeConfiguration" + } + }, + "additionalProperties": false, + "definitions": { + "EpochState": { + "oneOf": [ + { + "type": "string", + "enum": [ + "in_progress" + ] + }, + { + "type": "object", + "required": [ + "public_key_submission" + ], + "properties": { + "public_key_submission": { + "type": "object", + "required": [ + "resharing" + ], + "properties": { + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "dealing_exchange" + ], + "properties": { + "dealing_exchange": { + "type": "object", + "required": [ + "resharing" + ], + "properties": { + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "verification_key_submission" + ], + "properties": { + "verification_key_submission": { + "type": "object", + "required": [ + "resharing" + ], + "properties": { + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "verification_key_validation" + ], + "properties": { + "verification_key_validation": { + "type": "object", + "required": [ + "resharing" + ], + "properties": { + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "verification_key_finalization" + ], + "properties": { + "verification_key_finalization": { + "type": "object", + "required": [ + "resharing" + ], + "properties": { + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "TimeConfiguration": { + "type": "object", + "required": [ + "dealing_exchange_time_secs", + "in_progress_time_secs", + "public_key_submission_time_secs", + "verification_key_finalization_time_secs", + "verification_key_submission_time_secs", + "verification_key_validation_time_secs" + ], + "properties": { + "dealing_exchange_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "in_progress_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "public_key_submission_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_finalization_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_submission_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_validation_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "get_current_epoch_threshold": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "uint64", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "get_dealer_details": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DealerDetailsResponse", + "type": "object", + "required": [ + "dealer_type" + ], + "properties": { + "dealer_type": { + "$ref": "#/definitions/DealerType" + }, + "details": { + "anyOf": [ + { + "$ref": "#/definitions/DealerDetails" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "DealerDetails": { + "type": "object", + "required": [ + "address", + "announce_address", + "assigned_index", + "bte_public_key_with_proof" + ], + "properties": { + "address": { + "$ref": "#/definitions/Addr" + }, + "announce_address": { + "type": "string" + }, + "assigned_index": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "bte_public_key_with_proof": { + "type": "string" + } + }, + "additionalProperties": false + }, + "DealerType": { + "type": "string", + "enum": [ + "current", + "past", + "unknown" + ] + } + } + }, + "get_dealing": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedDealingsResponse", + "type": "object", + "required": [ + "dealings", + "per_page" + ], + "properties": { + "dealings": { + "type": "array", + "items": { + "$ref": "#/definitions/ContractDealing" + } + }, + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "ContractDealing": { + "type": "object", + "required": [ + "dealer", + "dealing" + ], + "properties": { + "dealer": { + "$ref": "#/definitions/Addr" + }, + "dealing": { + "$ref": "#/definitions/ContractSafeBytes" + } + }, + "additionalProperties": false + }, + "ContractSafeBytes": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + } + } + }, + "get_initial_dealers": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_InitialReplacementData", + "anyOf": [ + { + "$ref": "#/definitions/InitialReplacementData" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "InitialReplacementData": { + "type": "object", + "required": [ + "initial_dealers", + "initial_height" + ], + "properties": { + "initial_dealers": { + "type": "array", + "items": { + "$ref": "#/definitions/Addr" + } + }, + "initial_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "get_past_dealers": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedDealerResponse", + "type": "object", + "required": [ + "dealers", + "per_page" + ], + "properties": { + "dealers": { + "type": "array", + "items": { + "$ref": "#/definitions/DealerDetails" + } + }, + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "DealerDetails": { + "type": "object", + "required": [ + "address", + "announce_address", + "assigned_index", + "bte_public_key_with_proof" + ], + "properties": { + "address": { + "$ref": "#/definitions/Addr" + }, + "announce_address": { + "type": "string" + }, + "assigned_index": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "bte_public_key_with_proof": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "get_verification_keys": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedVKSharesResponse", + "type": "object", + "required": [ + "per_page", + "shares" + ], + "properties": { + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "shares": { + "type": "array", + "items": { + "$ref": "#/definitions/ContractVKShare" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "ContractVKShare": { + "type": "object", + "required": [ + "announce_address", + "epoch_id", + "node_index", + "owner", + "share", + "verified" + ], + "properties": { + "announce_address": { + "type": "string" + }, + "epoch_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "node_index": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "owner": { + "$ref": "#/definitions/Addr" + }, + "share": { + "type": "string" + }, + "verified": { + "type": "boolean" + } + }, + "additionalProperties": false + } + } + } + } +} diff --git a/contracts/coconut-dkg/schema/raw/execute.json b/contracts/coconut-dkg/schema/raw/execute.json new file mode 100644 index 0000000000..5e87d1dc07 --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/execute.json @@ -0,0 +1,150 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "register_dealer" + ], + "properties": { + "register_dealer": { + "type": "object", + "required": [ + "announce_address", + "bte_key_with_proof", + "resharing" + ], + "properties": { + "announce_address": { + "type": "string" + }, + "bte_key_with_proof": { + "type": "string" + }, + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "commit_dealing" + ], + "properties": { + "commit_dealing": { + "type": "object", + "required": [ + "dealing_bytes", + "resharing" + ], + "properties": { + "dealing_bytes": { + "$ref": "#/definitions/ContractSafeBytes" + }, + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "commit_verification_key_share" + ], + "properties": { + "commit_verification_key_share": { + "type": "object", + "required": [ + "resharing", + "share" + ], + "properties": { + "resharing": { + "type": "boolean" + }, + "share": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "verify_verification_key_share" + ], + "properties": { + "verify_verification_key_share": { + "type": "object", + "required": [ + "owner", + "resharing" + ], + "properties": { + "owner": { + "$ref": "#/definitions/Addr" + }, + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "surpassed_threshold" + ], + "properties": { + "surpassed_threshold": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "advance_epoch_state" + ], + "properties": { + "advance_epoch_state": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "ContractSafeBytes": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + } + } +} diff --git a/contracts/coconut-dkg/schema/raw/instantiate.json b/contracts/coconut-dkg/schema/raw/instantiate.json new file mode 100644 index 0000000000..af468be6c6 --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/instantiate.json @@ -0,0 +1,78 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "group_addr", + "mix_denom", + "multisig_addr" + ], + "properties": { + "group_addr": { + "type": "string" + }, + "mix_denom": { + "type": "string" + }, + "multisig_addr": { + "type": "string" + }, + "time_configuration": { + "anyOf": [ + { + "$ref": "#/definitions/TimeConfiguration" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "TimeConfiguration": { + "type": "object", + "required": [ + "dealing_exchange_time_secs", + "in_progress_time_secs", + "public_key_submission_time_secs", + "verification_key_finalization_time_secs", + "verification_key_submission_time_secs", + "verification_key_validation_time_secs" + ], + "properties": { + "dealing_exchange_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "in_progress_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "public_key_submission_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_finalization_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_submission_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_validation_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/coconut-dkg/schema/raw/migrate.json b/contracts/coconut-dkg/schema/raw/migrate.json new file mode 100644 index 0000000000..7fbe8c5708 --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/migrate.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false +} diff --git a/contracts/coconut-dkg/schema/raw/query.json b/contracts/coconut-dkg/schema/raw/query.json new file mode 100644 index 0000000000..c165069598 --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/query.json @@ -0,0 +1,198 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "get_current_epoch_state" + ], + "properties": { + "get_current_epoch_state": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_current_epoch_threshold" + ], + "properties": { + "get_current_epoch_threshold": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_initial_dealers" + ], + "properties": { + "get_initial_dealers": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_dealer_details" + ], + "properties": { + "get_dealer_details": { + "type": "object", + "required": [ + "dealer_address" + ], + "properties": { + "dealer_address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_current_dealers" + ], + "properties": { + "get_current_dealers": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_past_dealers" + ], + "properties": { + "get_past_dealers": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_dealing" + ], + "properties": { + "get_dealing": { + "type": "object", + "required": [ + "idx" + ], + "properties": { + "idx": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "get_verification_keys" + ], + "properties": { + "get_verification_keys": { + "type": "object", + "required": [ + "epoch_id" + ], + "properties": { + "epoch_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/coconut-dkg/schema/raw/response_to_get_current_dealers.json b/contracts/coconut-dkg/schema/raw/response_to_get_current_dealers.json new file mode 100644 index 0000000000..e08ceedf09 --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/response_to_get_current_dealers.json @@ -0,0 +1,66 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedDealerResponse", + "type": "object", + "required": [ + "dealers", + "per_page" + ], + "properties": { + "dealers": { + "type": "array", + "items": { + "$ref": "#/definitions/DealerDetails" + } + }, + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "DealerDetails": { + "type": "object", + "required": [ + "address", + "announce_address", + "assigned_index", + "bte_public_key_with_proof" + ], + "properties": { + "address": { + "$ref": "#/definitions/Addr" + }, + "announce_address": { + "type": "string" + }, + "assigned_index": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "bte_public_key_with_proof": { + "type": "string" + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/coconut-dkg/schema/raw/response_to_get_current_epoch_state.json b/contracts/coconut-dkg/schema/raw/response_to_get_current_epoch_state.json new file mode 100644 index 0000000000..8af981b9db --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/response_to_get_current_epoch_state.json @@ -0,0 +1,201 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Epoch", + "type": "object", + "required": [ + "epoch_id", + "finish_timestamp", + "state", + "time_configuration" + ], + "properties": { + "epoch_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "finish_timestamp": { + "$ref": "#/definitions/Timestamp" + }, + "state": { + "$ref": "#/definitions/EpochState" + }, + "time_configuration": { + "$ref": "#/definitions/TimeConfiguration" + } + }, + "additionalProperties": false, + "definitions": { + "EpochState": { + "oneOf": [ + { + "type": "string", + "enum": [ + "in_progress" + ] + }, + { + "type": "object", + "required": [ + "public_key_submission" + ], + "properties": { + "public_key_submission": { + "type": "object", + "required": [ + "resharing" + ], + "properties": { + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "dealing_exchange" + ], + "properties": { + "dealing_exchange": { + "type": "object", + "required": [ + "resharing" + ], + "properties": { + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "verification_key_submission" + ], + "properties": { + "verification_key_submission": { + "type": "object", + "required": [ + "resharing" + ], + "properties": { + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "verification_key_validation" + ], + "properties": { + "verification_key_validation": { + "type": "object", + "required": [ + "resharing" + ], + "properties": { + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "verification_key_finalization" + ], + "properties": { + "verification_key_finalization": { + "type": "object", + "required": [ + "resharing" + ], + "properties": { + "resharing": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "TimeConfiguration": { + "type": "object", + "required": [ + "dealing_exchange_time_secs", + "in_progress_time_secs", + "public_key_submission_time_secs", + "verification_key_finalization_time_secs", + "verification_key_submission_time_secs", + "verification_key_validation_time_secs" + ], + "properties": { + "dealing_exchange_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "in_progress_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "public_key_submission_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_finalization_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_submission_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "verification_key_validation_time_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/coconut-dkg/schema/raw/response_to_get_current_epoch_threshold.json b/contracts/coconut-dkg/schema/raw/response_to_get_current_epoch_threshold.json new file mode 100644 index 0000000000..7b729a7b96 --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/response_to_get_current_epoch_threshold.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "uint64", + "type": "integer", + "format": "uint64", + "minimum": 0.0 +} diff --git a/contracts/coconut-dkg/schema/raw/response_to_get_dealer_details.json b/contracts/coconut-dkg/schema/raw/response_to_get_dealer_details.json new file mode 100644 index 0000000000..474a247a99 --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/response_to_get_dealer_details.json @@ -0,0 +1,64 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DealerDetailsResponse", + "type": "object", + "required": [ + "dealer_type" + ], + "properties": { + "dealer_type": { + "$ref": "#/definitions/DealerType" + }, + "details": { + "anyOf": [ + { + "$ref": "#/definitions/DealerDetails" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "DealerDetails": { + "type": "object", + "required": [ + "address", + "announce_address", + "assigned_index", + "bte_public_key_with_proof" + ], + "properties": { + "address": { + "$ref": "#/definitions/Addr" + }, + "announce_address": { + "type": "string" + }, + "assigned_index": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "bte_public_key_with_proof": { + "type": "string" + } + }, + "additionalProperties": false + }, + "DealerType": { + "type": "string", + "enum": [ + "current", + "past", + "unknown" + ] + } + } +} diff --git a/contracts/coconut-dkg/schema/raw/response_to_get_dealing.json b/contracts/coconut-dkg/schema/raw/response_to_get_dealing.json new file mode 100644 index 0000000000..8fc350b74c --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/response_to_get_dealing.json @@ -0,0 +1,64 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedDealingsResponse", + "type": "object", + "required": [ + "dealings", + "per_page" + ], + "properties": { + "dealings": { + "type": "array", + "items": { + "$ref": "#/definitions/ContractDealing" + } + }, + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "ContractDealing": { + "type": "object", + "required": [ + "dealer", + "dealing" + ], + "properties": { + "dealer": { + "$ref": "#/definitions/Addr" + }, + "dealing": { + "$ref": "#/definitions/ContractSafeBytes" + } + }, + "additionalProperties": false + }, + "ContractSafeBytes": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + } + } +} diff --git a/contracts/coconut-dkg/schema/raw/response_to_get_initial_dealers.json b/contracts/coconut-dkg/schema/raw/response_to_get_initial_dealers.json new file mode 100644 index 0000000000..cd8c3df351 --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/response_to_get_initial_dealers.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_InitialReplacementData", + "anyOf": [ + { + "$ref": "#/definitions/InitialReplacementData" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "InitialReplacementData": { + "type": "object", + "required": [ + "initial_dealers", + "initial_height" + ], + "properties": { + "initial_dealers": { + "type": "array", + "items": { + "$ref": "#/definitions/Addr" + } + }, + "initial_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/coconut-dkg/schema/raw/response_to_get_past_dealers.json b/contracts/coconut-dkg/schema/raw/response_to_get_past_dealers.json new file mode 100644 index 0000000000..e08ceedf09 --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/response_to_get_past_dealers.json @@ -0,0 +1,66 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedDealerResponse", + "type": "object", + "required": [ + "dealers", + "per_page" + ], + "properties": { + "dealers": { + "type": "array", + "items": { + "$ref": "#/definitions/DealerDetails" + } + }, + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "DealerDetails": { + "type": "object", + "required": [ + "address", + "announce_address", + "assigned_index", + "bte_public_key_with_proof" + ], + "properties": { + "address": { + "$ref": "#/definitions/Addr" + }, + "announce_address": { + "type": "string" + }, + "assigned_index": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "bte_public_key_with_proof": { + "type": "string" + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/coconut-dkg/schema/raw/response_to_get_verification_keys.json b/contracts/coconut-dkg/schema/raw/response_to_get_verification_keys.json new file mode 100644 index 0000000000..d0fc6e1deb --- /dev/null +++ b/contracts/coconut-dkg/schema/raw/response_to_get_verification_keys.json @@ -0,0 +1,76 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedVKSharesResponse", + "type": "object", + "required": [ + "per_page", + "shares" + ], + "properties": { + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "shares": { + "type": "array", + "items": { + "$ref": "#/definitions/ContractVKShare" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "ContractVKShare": { + "type": "object", + "required": [ + "announce_address", + "epoch_id", + "node_index", + "owner", + "share", + "verified" + ], + "properties": { + "announce_address": { + "type": "string" + }, + "epoch_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "node_index": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "owner": { + "$ref": "#/definitions/Addr" + }, + "share": { + "type": "string" + }, + "verified": { + "type": "boolean" + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/coconut-dkg/src/bin/schema.rs b/contracts/coconut-dkg/src/bin/schema.rs new file mode 100644 index 0000000000..7a66216cfa --- /dev/null +++ b/contracts/coconut-dkg/src/bin/schema.rs @@ -0,0 +1,14 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::write_api; +use nym_coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + migrate: MigrateMsg, + } +} diff --git a/contracts/coconut-dkg/src/state/mod.rs b/contracts/coconut-dkg/src/state/mod.rs index 533bd98a0a..90abdc3711 100644 --- a/contracts/coconut-dkg/src/state/mod.rs +++ b/contracts/coconut-dkg/src/state/mod.rs @@ -5,14 +5,13 @@ use cosmwasm_std::Addr; use cw4::Cw4Contract; use cw_controllers::Admin; use cw_storage_plus::Item; -use schemars::JsonSchema; use serde::{Deserialize, Serialize}; // unique items pub const STATE: Item = Item::new("state"); pub const MULTISIG: Admin = Admin::new("multisig"); -#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] pub struct State { pub mix_denom: String, pub multisig_addr: Addr, diff --git a/contracts/ephemera/.cargo/config b/contracts/ephemera/.cargo/config new file mode 100644 index 0000000000..2f698e22b3 --- /dev/null +++ b/contracts/ephemera/.cargo/config @@ -0,0 +1,5 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +wasm-debug = "build --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --bin schema --features=schema-gen" \ No newline at end of file diff --git a/contracts/ephemera/Cargo.toml b/contracts/ephemera/Cargo.toml new file mode 100644 index 0000000000..376eec374e --- /dev/null +++ b/contracts/ephemera/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "nym-ephemera" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +name = "schema" +required-features = ["schema-gen"] + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +nym-ephemera-common = { path = "../../common/cosmwasm-smart-contracts/ephemera" } + +cosmwasm-schema = { workspace = true, optional = true } +cosmwasm-std = { workspace = true } +cosmwasm-storage = { workspace = true } +cw-storage-plus = { workspace = true } +cw-controllers = { workspace = true } +cw4 = { workspace = true } + +serde = { version = "1.0.103", default-features = false, features = ["derive"] } +thiserror = "1.0.23" + +[dev-dependencies] +cw-multi-test = { workspace = true } +cw4-group = { path = "../multisig/cw4-group" } +nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" } +lazy_static = "1.4" +rusty-fork = "0.3" + +[features] +schema-gen = ["nym-ephemera-common/schema", "cosmwasm-schema"] diff --git a/contracts/ephemera/Makefile b/contracts/ephemera/Makefile new file mode 100644 index 0000000000..f138e2c879 --- /dev/null +++ b/contracts/ephemera/Makefile @@ -0,0 +1,2 @@ +generate-schema: + cargo schema \ No newline at end of file diff --git a/contracts/ephemera/schema/nym-ephemera.json b/contracts/ephemera/schema/nym-ephemera.json new file mode 100644 index 0000000000..4a0efed6bd --- /dev/null +++ b/contracts/ephemera/schema/nym-ephemera.json @@ -0,0 +1,194 @@ +{ + "contract_name": "nym-ephemera", + "contract_version": "0.1.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "group_addr", + "mix_denom" + ], + "properties": { + "group_addr": { + "type": "string" + }, + "mix_denom": { + "type": "string" + } + }, + "additionalProperties": false + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "register_peer" + ], + "properties": { + "register_peer": { + "type": "object", + "required": [ + "peer_info" + ], + "properties": { + "peer_info": { + "$ref": "#/definitions/JsonPeerInfo" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "JsonPeerInfo": { + "type": "object", + "required": [ + "cosmos_address", + "ip_address", + "public_key" + ], + "properties": { + "cosmos_address": { + "description": "The cosmos address of the peer, used in interacting with the chain.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "ip_address": { + "description": "The TCP/IP address of the peer. Expected formats: 1. `:` 2. `/ip4//tcp/` - this is the format used by libp2p multiaddr", + "type": "string" + }, + "public_key": { + "description": "Serialized public key.", + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "get_peers" + ], + "properties": { + "get_peers": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "migrate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false + }, + "sudo": null, + "responses": { + "get_peers": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedPeerResponse", + "type": "object", + "required": [ + "peers", + "per_page" + ], + "properties": { + "peers": { + "type": "array", + "items": { + "$ref": "#/definitions/JsonPeerInfo" + } + }, + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "JsonPeerInfo": { + "type": "object", + "required": [ + "cosmos_address", + "ip_address", + "public_key" + ], + "properties": { + "cosmos_address": { + "description": "The cosmos address of the peer, used in interacting with the chain.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "ip_address": { + "description": "The TCP/IP address of the peer. Expected formats: 1. `:` 2. `/ip4//tcp/` - this is the format used by libp2p multiaddr", + "type": "string" + }, + "public_key": { + "description": "Serialized public key.", + "type": "string" + } + }, + "additionalProperties": false + } + } + } + } +} diff --git a/contracts/ephemera/schema/raw/execute.json b/contracts/ephemera/schema/raw/execute.json new file mode 100644 index 0000000000..becf9117ca --- /dev/null +++ b/contracts/ephemera/schema/raw/execute.json @@ -0,0 +1,60 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "register_peer" + ], + "properties": { + "register_peer": { + "type": "object", + "required": [ + "peer_info" + ], + "properties": { + "peer_info": { + "$ref": "#/definitions/JsonPeerInfo" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "JsonPeerInfo": { + "type": "object", + "required": [ + "cosmos_address", + "ip_address", + "public_key" + ], + "properties": { + "cosmos_address": { + "description": "The cosmos address of the peer, used in interacting with the chain.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "ip_address": { + "description": "The TCP/IP address of the peer. Expected formats: 1. `:` 2. `/ip4//tcp/` - this is the format used by libp2p multiaddr", + "type": "string" + }, + "public_key": { + "description": "Serialized public key.", + "type": "string" + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/ephemera/schema/raw/instantiate.json b/contracts/ephemera/schema/raw/instantiate.json new file mode 100644 index 0000000000..907c7aa20f --- /dev/null +++ b/contracts/ephemera/schema/raw/instantiate.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "group_addr", + "mix_denom" + ], + "properties": { + "group_addr": { + "type": "string" + }, + "mix_denom": { + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/ephemera/schema/raw/migrate.json b/contracts/ephemera/schema/raw/migrate.json new file mode 100644 index 0000000000..7fbe8c5708 --- /dev/null +++ b/contracts/ephemera/schema/raw/migrate.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false +} diff --git a/contracts/ephemera/schema/raw/query.json b/contracts/ephemera/schema/raw/query.json new file mode 100644 index 0000000000..77c2991f6b --- /dev/null +++ b/contracts/ephemera/schema/raw/query.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "get_peers" + ], + "properties": { + "get_peers": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/ephemera/schema/raw/response_to_get_peers.json b/contracts/ephemera/schema/raw/response_to_get_peers.json new file mode 100644 index 0000000000..3b3c85bb5b --- /dev/null +++ b/contracts/ephemera/schema/raw/response_to_get_peers.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedPeerResponse", + "type": "object", + "required": [ + "peers", + "per_page" + ], + "properties": { + "peers": { + "type": "array", + "items": { + "$ref": "#/definitions/JsonPeerInfo" + } + }, + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "JsonPeerInfo": { + "type": "object", + "required": [ + "cosmos_address", + "ip_address", + "public_key" + ], + "properties": { + "cosmos_address": { + "description": "The cosmos address of the peer, used in interacting with the chain.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "ip_address": { + "description": "The TCP/IP address of the peer. Expected formats: 1. `:` 2. `/ip4//tcp/` - this is the format used by libp2p multiaddr", + "type": "string" + }, + "public_key": { + "description": "Serialized public key.", + "type": "string" + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/ephemera/src/bin/schema.rs b/contracts/ephemera/src/bin/schema.rs new file mode 100644 index 0000000000..8df2100755 --- /dev/null +++ b/contracts/ephemera/src/bin/schema.rs @@ -0,0 +1,14 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::write_api; +use nym_ephemera_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + migrate: MigrateMsg, + } +} diff --git a/contracts/ephemera/src/contract.rs b/contracts/ephemera/src/contract.rs new file mode 100644 index 0000000000..0cfa78a410 --- /dev/null +++ b/contracts/ephemera/src/contract.rs @@ -0,0 +1,97 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::error::ContractError; +use crate::peers::queries::query_peers_paged; +use crate::peers::transactions::try_register_peer; +use crate::state::{State, STATE}; +use cosmwasm_std::{ + entry_point, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, +}; +use cw4::Cw4Contract; +use nym_ephemera_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; + +/// Instantiate the contract. +/// +/// `deps` contains Storage, API and Querier +/// `env` contains block, message and contract info +/// `msg` is the contract initialization message, sort of like a constructor call. +#[entry_point] +pub fn instantiate( + deps: DepsMut<'_>, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + let group_addr = Cw4Contract(deps.api.addr_validate(&msg.group_addr).map_err(|_| { + ContractError::InvalidGroup { + addr: msg.group_addr.clone(), + } + })?); + + let state = State { + group_addr, + mix_denom: msg.mix_denom, + }; + STATE.save(deps.storage, &state)?; + + Ok(Response::default()) +} + +/// Handle an incoming message +#[entry_point] +pub fn execute( + deps: DepsMut<'_>, + _env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::RegisterPeer { peer_info } => try_register_peer(deps, info, peer_info), + } +} + +#[entry_point] +pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result { + let response = match msg { + QueryMsg::GetPeers { limit, start_after } => { + to_binary(&query_peers_paged(deps, start_after, limit)?)? + } + }; + + Ok(response) +} + +#[entry_point] +pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { + Ok(Default::default()) +} + +#[cfg(test)] +mod tests { + use super::*; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; + use cosmwasm_std::Addr; + + #[test] + fn initialize_contract() { + let mut deps = mock_dependencies(); + let env = mock_env(); + + let init_msg = InstantiateMsg { + group_addr: "group_addr".to_string(), + mix_denom: "uatom".to_string(), + }; + + let sender = mock_info("sender", &[]); + let res = instantiate(deps.as_mut(), env, sender, init_msg); + assert!(res.is_ok()); + + let expected_state = State { + group_addr: Cw4Contract::new(Addr::unchecked("group_addr")), + mix_denom: "uatom".to_string(), + }; + let state = STATE.load(deps.as_ref().storage).unwrap(); + assert_eq!(state, expected_state); + } +} diff --git a/contracts/ephemera/src/error.rs b/contracts/ephemera/src/error.rs new file mode 100644 index 0000000000..2ee74c6ab0 --- /dev/null +++ b/contracts/ephemera/src/error.rs @@ -0,0 +1,21 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_std::StdError; +use thiserror::Error; + +/// Custom errors for contract failure conditions. +#[derive(Error, Debug, PartialEq)] +pub enum ContractError { + #[error(transparent)] + Std(#[from] StdError), + + #[error("Group contract invalid address '{addr}'")] + InvalidGroup { addr: String }, + + #[error("This potential ephemera peer is not in the ephemera group")] + Unauthorized, + + #[error("This sender is already registered")] + AlreadyRegistered, +} diff --git a/contracts/ephemera/src/lib.rs b/contracts/ephemera/src/lib.rs new file mode 100644 index 0000000000..fa30231382 --- /dev/null +++ b/contracts/ephemera/src/lib.rs @@ -0,0 +1,8 @@ +// Copyright 2022 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod contract; +pub mod error; +mod peers; +mod state; +mod support; diff --git a/contracts/ephemera/src/peers/mod.rs b/contracts/ephemera/src/peers/mod.rs new file mode 100644 index 0000000000..e82ae1570a --- /dev/null +++ b/contracts/ephemera/src/peers/mod.rs @@ -0,0 +1,6 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod queries; +pub mod storage; +pub mod transactions; diff --git a/contracts/ephemera/src/peers/queries.rs b/contracts/ephemera/src/peers/queries.rs new file mode 100644 index 0000000000..fc24e6d583 --- /dev/null +++ b/contracts/ephemera/src/peers/queries.rs @@ -0,0 +1,164 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::peers::storage::{PEERS, PEERS_PAGE_DEFAULT_LIMIT, PEERS_PAGE_MAX_LIMIT}; +use cosmwasm_std::{Deps, Order, StdResult}; +use cw_storage_plus::Bound; +use nym_ephemera_common::peers::PagedPeerResponse; + +pub fn query_peers_paged( + deps: Deps<'_>, + start_after: Option, + limit: Option, +) -> StdResult { + let limit = limit + .unwrap_or(PEERS_PAGE_DEFAULT_LIMIT) + .min(PEERS_PAGE_MAX_LIMIT) as usize; + + let addr = start_after + .map(|addr| deps.api.addr_validate(&addr)) + .transpose()?; + + let start = addr.map(Bound::exclusive); + + let peers = PEERS + .range(deps.storage, start, None, Order::Ascending) + .take(limit) + .map(|res| res.map(|item| item.1)) + .collect::>>()?; + + let start_next_after = peers + .last() + .map(|peer_info| peer_info.cosmos_address.clone()); + + Ok(PagedPeerResponse::new(peers, limit, start_next_after)) +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::peers::storage::{PEERS_PAGE_DEFAULT_LIMIT, PEERS_PAGE_MAX_LIMIT}; + use crate::support::tests::fixtures::peer_fixture; + use crate::support::tests::helpers::init_contract; + use cosmwasm_std::DepsMut; + + fn fill_peers(deps: DepsMut<'_>, size: usize) { + for n in 0..size { + let peer = peer_fixture(&format!("peer{}", n)); + PEERS + .save(deps.storage, peer.cosmos_address.clone(), &peer) + .unwrap(); + } + } + + fn remove_peers(deps: DepsMut<'_>, size: usize) { + for n in 0..size { + let peer = peer_fixture(&format!("peer{}", n)); + PEERS.remove(deps.storage, peer.cosmos_address); + } + } + + #[test] + fn peers_empty_on_init() { + let deps = init_contract(); + + let page1 = query_peers_paged(deps.as_ref(), None, None).unwrap(); + assert_eq!(0, page1.peers.len() as u32); + } + + #[test] + fn peers_paged_retrieval_obeys_limits() { + let mut deps = init_contract(); + let limit = 2; + + fill_peers(deps.as_mut(), 1000); + + let page1 = query_peers_paged(deps.as_ref(), None, Option::from(limit)).unwrap(); + assert_eq!(limit, page1.peers.len() as u32); + + remove_peers(deps.as_mut(), 1000); + } + + #[test] + fn peers_paged_retrieval_has_default_limit() { + let mut deps = init_contract(); + + fill_peers(deps.as_mut(), 1000); + + // query without explicitly setting a limit + let page1 = query_peers_paged(deps.as_ref(), None, None).unwrap(); + + assert_eq!(PEERS_PAGE_DEFAULT_LIMIT, page1.peers.len() as u32); + + remove_peers(deps.as_mut(), 1000); + } + + #[test] + fn peers_paged_retrieval_has_max_limit() { + let mut deps = init_contract(); + + // query with a crazily high limit in an attempt to use too many resources + let crazy_limit = 1000 * PEERS_PAGE_MAX_LIMIT; + + fill_peers(deps.as_mut(), 1000); + + let page1 = query_peers_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap(); + + // we default to a decent sized upper bound instead + let expected_limit = PEERS_PAGE_MAX_LIMIT; + assert_eq!(expected_limit, page1.peers.len() as u32); + + remove_peers(deps.as_mut(), 1000); + } + + #[test] + fn peers_pagination_works() { + let mut deps = init_contract(); + + let per_page = 2; + + fill_peers(deps.as_mut(), 1); + let page1 = query_peers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); + + // page should have 1 result on it + assert_eq!(1, page1.peers.len()); + remove_peers(deps.as_mut(), 1); + + fill_peers(deps.as_mut(), 2); + // page1 should have 2 results on it + let page1 = query_peers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); + assert_eq!(2, page1.peers.len()); + remove_peers(deps.as_mut(), 2); + + fill_peers(deps.as_mut(), 3); + // page1 still has 2 results + let page1 = query_peers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); + assert_eq!(2, page1.peers.len()); + + // retrieving the next page should start after the last key on this page + let start_after = page1.start_next_after.unwrap(); + let page2 = query_peers_paged( + deps.as_ref(), + Option::from(start_after.to_string()), + Option::from(per_page), + ) + .unwrap(); + + assert_eq!(1, page2.peers.len()); + remove_peers(deps.as_mut(), 3); + + fill_peers(deps.as_mut(), 4); + let page1 = query_peers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap(); + let start_after = page1.start_next_after.unwrap(); + let page2 = query_peers_paged( + deps.as_ref(), + Option::from(start_after.to_string()), + Option::from(per_page), + ) + .unwrap(); + + // now we have 2 pages, with 2 results on the second page + assert_eq!(2, page2.peers.len()); + remove_peers(deps.as_mut(), 4); + } +} diff --git a/contracts/ephemera/src/peers/storage.rs b/contracts/ephemera/src/peers/storage.rs new file mode 100644 index 0000000000..b3a1734109 --- /dev/null +++ b/contracts/ephemera/src/peers/storage.rs @@ -0,0 +1,11 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_std::Addr; +use cw_storage_plus::Map; +use nym_ephemera_common::types::JsonPeerInfo; + +pub(crate) const PEERS_PAGE_MAX_LIMIT: u32 = 75; +pub(crate) const PEERS_PAGE_DEFAULT_LIMIT: u32 = 50; + +pub(crate) const PEERS: Map<'_, Addr, JsonPeerInfo> = Map::new("prs"); diff --git a/contracts/ephemera/src/peers/transactions.rs b/contracts/ephemera/src/peers/transactions.rs new file mode 100644 index 0000000000..6aa587bcb2 --- /dev/null +++ b/contracts/ephemera/src/peers/transactions.rs @@ -0,0 +1,63 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::error::ContractError; +use crate::peers::storage::PEERS; +use crate::state::STATE; +use cosmwasm_std::{DepsMut, MessageInfo, Response}; +use nym_ephemera_common::types::JsonPeerInfo; + +pub fn try_register_peer( + deps: DepsMut<'_>, + info: MessageInfo, + peer_info: JsonPeerInfo, +) -> Result { + if PEERS.may_load(deps.storage, info.sender.clone())?.is_none() { + if STATE + .load(deps.storage)? + .group_addr + .is_voting_member(&deps.querier, &info.sender, None)? + .is_some() + { + PEERS.save(deps.storage, info.sender, &peer_info)?; + Ok(Default::default()) + } else { + Err(ContractError::Unauthorized {}) + } + } else { + Err(ContractError::AlreadyRegistered) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::support::tests::fixtures::peer_fixture; + use crate::support::tests::helpers; + use crate::support::tests::helpers::GROUP_MEMBERS; + use cosmwasm_std::testing::mock_info; + use cw4::Member; + + #[test] + fn peer_registration() { + let mut deps = helpers::init_contract(); + let peer_info = peer_fixture("owner"); + let info = mock_info("owner", &[]); + + let ret = try_register_peer(deps.as_mut(), info.clone(), peer_info.clone()).unwrap_err(); + assert_eq!(ret, ContractError::Unauthorized); + + GROUP_MEMBERS.lock().unwrap().push(( + Member { + addr: "owner".to_string(), + weight: 10, + }, + 1, + )); + + try_register_peer(deps.as_mut(), info.clone(), peer_info.clone()).unwrap(); + + let ret = try_register_peer(deps.as_mut(), info, peer_info).unwrap_err(); + assert_eq!(ret, ContractError::AlreadyRegistered); + } +} diff --git a/contracts/ephemera/src/state/mod.rs b/contracts/ephemera/src/state/mod.rs new file mode 100644 index 0000000000..6dec1262d2 --- /dev/null +++ b/contracts/ephemera/src/state/mod.rs @@ -0,0 +1,15 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cw4::Cw4Contract; +use cw_storage_plus::Item; +use serde::{Deserialize, Serialize}; + +// unique items +pub const STATE: Item = Item::new("state"); + +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +pub struct State { + pub mix_denom: String, + pub group_addr: Cw4Contract, +} diff --git a/contracts/ephemera/src/support/mod.rs b/contracts/ephemera/src/support/mod.rs new file mode 100644 index 0000000000..070bbc8eed --- /dev/null +++ b/contracts/ephemera/src/support/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(test)] +pub mod tests; diff --git a/contracts/ephemera/src/support/tests/fixtures.rs b/contracts/ephemera/src/support/tests/fixtures.rs new file mode 100644 index 0000000000..bf1f469b6d --- /dev/null +++ b/contracts/ephemera/src/support/tests/fixtures.rs @@ -0,0 +1,15 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_std::Addr; +use nym_ephemera_common::types::JsonPeerInfo; + +pub const TEST_MIX_DENOM: &str = "unym"; + +pub fn peer_fixture(cosmos_address: &str) -> JsonPeerInfo { + JsonPeerInfo { + cosmos_address: Addr::unchecked(cosmos_address), + ip_address: "127.0.0.1".to_string(), + public_key: "random_key".to_string(), + } +} diff --git a/contracts/ephemera/src/support/tests/helpers.rs b/contracts/ephemera/src/support/tests/helpers.rs new file mode 100644 index 0000000000..df665eba1e --- /dev/null +++ b/contracts/ephemera/src/support/tests/helpers.rs @@ -0,0 +1,74 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::contract::instantiate; +use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; +use cosmwasm_std::{ + from_binary, to_binary, ContractResult, Empty, MemoryStorage, OwnedDeps, QuerierResult, + SystemResult, WasmQuery, +}; +use cw4::{Cw4QueryMsg, Member, MemberListResponse, MemberResponse}; +use lazy_static::lazy_static; +use nym_ephemera_common::msg::InstantiateMsg; +use std::sync::Mutex; + +use super::fixtures::TEST_MIX_DENOM; + +pub const ADMIN_ADDRESS: &str = "admin address"; +pub const GROUP_CONTRACT: &str = "group contract address"; + +lazy_static! { + pub static ref GROUP_MEMBERS: Mutex> = Mutex::new(vec![]); +} + +fn querier_handler(query: &WasmQuery) -> QuerierResult { + let bin = match query { + WasmQuery::Smart { contract_addr, msg } => { + if contract_addr != GROUP_CONTRACT { + panic!("Not supported"); + } + match from_binary(msg) { + Ok(Cw4QueryMsg::Member { addr, at_height }) => { + let weight = GROUP_MEMBERS.lock().unwrap().iter().find_map(|(m, h)| { + if m.addr == addr { + if let Some(height) = at_height { + if height != *h { + return None; + } + } + Some(m.weight) + } else { + None + } + }); + to_binary(&MemberResponse { weight }).unwrap() + } + Ok(Cw4QueryMsg::ListMembers { .. }) => { + let members = GROUP_MEMBERS + .lock() + .unwrap() + .iter() + .map(|m| m.0.clone()) + .collect(); + to_binary(&MemberListResponse { members }).unwrap() + } + _ => panic!("Not supported"), + } + } + _ => panic!("Not supported"), + }; + SystemResult::Ok(ContractResult::Ok(bin)) +} + +pub fn init_contract() -> OwnedDeps> { + let mut deps = mock_dependencies(); + deps.querier.update_wasm(querier_handler); + let msg = InstantiateMsg { + group_addr: GROUP_CONTRACT.to_string(), + mix_denom: TEST_MIX_DENOM.to_string(), + }; + let env = mock_env(); + let info = mock_info(ADMIN_ADDRESS, &[]); + instantiate(deps.as_mut(), env, info, msg).unwrap(); + deps +} diff --git a/contracts/ephemera/src/support/tests/mod.rs b/contracts/ephemera/src/support/tests/mod.rs new file mode 100644 index 0000000000..44f93cb286 --- /dev/null +++ b/contracts/ephemera/src/support/tests/mod.rs @@ -0,0 +1,5 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +pub mod fixtures; +pub mod helpers; diff --git a/contracts/mixnet-vesting-integration-tests/src/decrease_mixnode_pledge.rs b/contracts/mixnet-vesting-integration-tests/src/decrease_mixnode_pledge.rs index c2ec5bd71b..a9930e23f7 100644 --- a/contracts/mixnet-vesting-integration-tests/src/decrease_mixnode_pledge.rs +++ b/contracts/mixnet-vesting-integration-tests/src/decrease_mixnode_pledge.rs @@ -9,8 +9,7 @@ use nym_contracts_common::Percent; use nym_mixnet_contract_common::error::MixnetContractError; use nym_mixnet_contract_common::{ContractStateParams, MixNodeCostParams}; use nym_mixnet_contract_common::{MixOwnershipResponse, QueryMsg as MixnetQueryMsg}; -use nym_vesting_contract_common::ExecuteMsg as VestingExecuteMsg; -use vesting_contract::errors::ContractError as VestingContractError; +use nym_vesting_contract_common::{ExecuteMsg as VestingExecuteMsg, VestingContractError}; #[test] fn decrease_mixnode_pledge_from_vesting_account_with_minimum_pledge() { diff --git a/contracts/mixnet/.cargo/config b/contracts/mixnet/.cargo/config index 336b618a17..2fb2c1afdb 100644 --- a/contracts/mixnet/.cargo/config +++ b/contracts/mixnet/.cargo/config @@ -1,4 +1,4 @@ [alias] -wasm = "build --release --target wasm32-unknown-unknown" +wasm = "build --release --lib --target wasm32-unknown-unknown" unit-test = "test --lib" -schema = "run --example schema" +schema = "run --bin schema --features=schema-gen" \ No newline at end of file diff --git a/contracts/mixnet/Cargo.toml b/contracts/mixnet/Cargo.toml index acdbfd4617..a50d9c789a 100644 --- a/contracts/mixnet/Cargo.toml +++ b/contracts/mixnet/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-mixnet-contract" -version = "1.4.0" +version = "1.5.0" description = "Nym mixnet contract" edition = { workspace = true } authors = { workspace = true } @@ -17,6 +17,10 @@ exclude = [ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "schema" +required-features = ["schema-gen"] + [lib] name = "mixnet_contract" crate-type = ["cdylib", "rlib"] @@ -26,6 +30,7 @@ mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet- vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.7.0" } nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.5.0" } +cosmwasm-schema = { workspace = true, optional = true } cosmwasm-std = { workspace = true } cosmwasm-storage = { workspace = true } cosmwasm-derive = { workspace = true } @@ -33,14 +38,12 @@ cw2 = { workspace = true } cw-storage-plus = { workspace = true } bs58 = "0.4.0" -schemars = "0.8" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { version = "1.0.23" } time = { version = "0.3", features = ["macros"] } semver = { version = "1.0.16", default-features = false } [dev-dependencies] -cosmwasm-schema = { workspace = true } rand_chacha = "0.2" nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] } @@ -50,6 +53,9 @@ vergen = { version = "=7.4.3", default-features = false, features = ["build", "g [features] default = [] contract-testing = ["mixnet-contract-common/contract-testing"] +schema-gen = ["mixnet-contract-common/schema", "cosmwasm-schema"] [profile.release] overflow-checks = true + + diff --git a/contracts/mixnet/Makefile b/contracts/mixnet/Makefile index 7741d05913..01452be701 100644 --- a/contracts/mixnet/Makefile +++ b/contracts/mixnet/Makefile @@ -3,3 +3,6 @@ opt: wasm wasm: RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown + +generate-schema: + cargo schema \ No newline at end of file diff --git a/contracts/mixnet/examples/schema.rs b/contracts/mixnet/examples/schema.rs deleted file mode 100644 index 46e555aff3..0000000000 --- a/contracts/mixnet/examples/schema.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2021 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; -use mixnet_contract_common::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; -use std::env::current_dir; -use std::fs::create_dir_all; - -fn main() { - let mut out_dir = current_dir().unwrap(); - out_dir.push("schema"); - create_dir_all(&out_dir).unwrap(); - remove_schemas(&out_dir).unwrap(); - - export_schema(&schema_for!(InstantiateMsg), &out_dir); - export_schema(&schema_for!(ExecuteMsg), &out_dir); - export_schema(&schema_for!(QueryMsg), &out_dir); - export_schema(&schema_for!(MigrateMsg), &out_dir); -} diff --git a/contracts/mixnet/schema/nym-mixnet-contract.json b/contracts/mixnet/schema/nym-mixnet-contract.json new file mode 100644 index 0000000000..37cbc9ba65 --- /dev/null +++ b/contracts/mixnet/schema/nym-mixnet-contract.json @@ -0,0 +1,8507 @@ +{ + "contract_name": "nym-mixnet-contract", + "contract_version": "1.5.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "epoch_duration", + "epochs_in_interval", + "initial_rewarding_params", + "rewarding_denom", + "rewarding_validator_address", + "vesting_contract_address" + ], + "properties": { + "epoch_duration": { + "$ref": "#/definitions/Duration" + }, + "epochs_in_interval": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "initial_rewarding_params": { + "$ref": "#/definitions/InitialRewardingParams" + }, + "rewarding_denom": { + "type": "string" + }, + "rewarding_validator_address": { + "type": "string" + }, + "vesting_contract_address": { + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Duration": { + "type": "object", + "required": [ + "nanos", + "secs" + ], + "properties": { + "nanos": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "InitialRewardingParams": { + "type": "object", + "required": [ + "active_set_size", + "active_set_work_factor", + "initial_reward_pool", + "initial_staking_supply", + "interval_pool_emission", + "rewarded_set_size", + "staking_supply_scale_factor", + "sybil_resistance" + ], + "properties": { + "active_set_size": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "active_set_work_factor": { + "$ref": "#/definitions/Decimal" + }, + "initial_reward_pool": { + "$ref": "#/definitions/Decimal" + }, + "initial_staking_supply": { + "$ref": "#/definitions/Decimal" + }, + "interval_pool_emission": { + "$ref": "#/definitions/Percent" + }, + "rewarded_set_size": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "staking_supply_scale_factor": { + "$ref": "#/definitions/Percent" + }, + "sybil_resistance": { + "$ref": "#/definitions/Percent" + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + } + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "assign_node_layer" + ], + "properties": { + "assign_node_layer": { + "type": "object", + "required": [ + "layer", + "mix_id" + ], + "properties": { + "layer": { + "$ref": "#/definitions/Layer" + }, + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Only owner of the node can crate the family with node as head", + "type": "object", + "required": [ + "create_family" + ], + "properties": { + "create_family": { + "type": "object", + "required": [ + "label" + ], + "properties": { + "label": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Family head needs to sign the joining node IdentityKey", + "type": "object", + "required": [ + "join_family" + ], + "properties": { + "join_family": { + "type": "object", + "required": [ + "family_head", + "join_permit" + ], + "properties": { + "family_head": { + "$ref": "#/definitions/FamilyHead" + }, + "join_permit": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "leave_family" + ], + "properties": { + "leave_family": { + "type": "object", + "required": [ + "family_head" + ], + "properties": { + "family_head": { + "$ref": "#/definitions/FamilyHead" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "kick_family_member" + ], + "properties": { + "kick_family_member": { + "type": "object", + "required": [ + "member" + ], + "properties": { + "member": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "create_family_on_behalf" + ], + "properties": { + "create_family_on_behalf": { + "type": "object", + "required": [ + "label", + "owner_address" + ], + "properties": { + "label": { + "type": "string" + }, + "owner_address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Family head needs to sign the joining node IdentityKey, MixNode needs to provide its signature proving that it wants to join the family", + "type": "object", + "required": [ + "join_family_on_behalf" + ], + "properties": { + "join_family_on_behalf": { + "type": "object", + "required": [ + "family_head", + "join_permit", + "member_address" + ], + "properties": { + "family_head": { + "$ref": "#/definitions/FamilyHead" + }, + "join_permit": { + "$ref": "#/definitions/MessageSignature" + }, + "member_address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "leave_family_on_behalf" + ], + "properties": { + "leave_family_on_behalf": { + "type": "object", + "required": [ + "family_head", + "member_address" + ], + "properties": { + "family_head": { + "$ref": "#/definitions/FamilyHead" + }, + "member_address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "kick_family_member_on_behalf" + ], + "properties": { + "kick_family_member_on_behalf": { + "type": "object", + "required": [ + "head_address", + "member" + ], + "properties": { + "head_address": { + "type": "string" + }, + "member": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_rewarding_validator_address" + ], + "properties": { + "update_rewarding_validator_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_contract_state_params" + ], + "properties": { + "update_contract_state_params": { + "type": "object", + "required": [ + "updated_parameters" + ], + "properties": { + "updated_parameters": { + "$ref": "#/definitions/ContractStateParams" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_active_set_size" + ], + "properties": { + "update_active_set_size": { + "type": "object", + "required": [ + "active_set_size", + "force_immediately" + ], + "properties": { + "active_set_size": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "force_immediately": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_rewarding_params" + ], + "properties": { + "update_rewarding_params": { + "type": "object", + "required": [ + "force_immediately", + "updated_params" + ], + "properties": { + "force_immediately": { + "type": "boolean" + }, + "updated_params": { + "$ref": "#/definitions/IntervalRewardingParamsUpdate" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_interval_config" + ], + "properties": { + "update_interval_config": { + "type": "object", + "required": [ + "epoch_duration_secs", + "epochs_in_interval", + "force_immediately" + ], + "properties": { + "epoch_duration_secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "epochs_in_interval": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "force_immediately": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "begin_epoch_transition" + ], + "properties": { + "begin_epoch_transition": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "advance_current_epoch" + ], + "properties": { + "advance_current_epoch": { + "type": "object", + "required": [ + "expected_active_set_size", + "new_rewarded_set" + ], + "properties": { + "expected_active_set_size": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "new_rewarded_set": { + "type": "array", + "items": { + "$ref": "#/definitions/LayerAssignment" + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "reconcile_epoch_events" + ], + "properties": { + "reconcile_epoch_events": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "bond_mixnode" + ], + "properties": { + "bond_mixnode": { + "type": "object", + "required": [ + "cost_params", + "mix_node", + "owner_signature" + ], + "properties": { + "cost_params": { + "$ref": "#/definitions/MixNodeCostParams" + }, + "mix_node": { + "$ref": "#/definitions/MixNode" + }, + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "bond_mixnode_on_behalf" + ], + "properties": { + "bond_mixnode_on_behalf": { + "type": "object", + "required": [ + "cost_params", + "mix_node", + "owner", + "owner_signature" + ], + "properties": { + "cost_params": { + "$ref": "#/definitions/MixNodeCostParams" + }, + "mix_node": { + "$ref": "#/definitions/MixNode" + }, + "owner": { + "type": "string" + }, + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "pledge_more" + ], + "properties": { + "pledge_more": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "pledge_more_on_behalf" + ], + "properties": { + "pledge_more_on_behalf": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "decrease_pledge" + ], + "properties": { + "decrease_pledge": { + "type": "object", + "required": [ + "decrease_by" + ], + "properties": { + "decrease_by": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "decrease_pledge_on_behalf" + ], + "properties": { + "decrease_pledge_on_behalf": { + "type": "object", + "required": [ + "decrease_by", + "owner" + ], + "properties": { + "decrease_by": { + "$ref": "#/definitions/Coin" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "unbond_mixnode" + ], + "properties": { + "unbond_mixnode": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "unbond_mixnode_on_behalf" + ], + "properties": { + "unbond_mixnode_on_behalf": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_mixnode_cost_params" + ], + "properties": { + "update_mixnode_cost_params": { + "type": "object", + "required": [ + "new_costs" + ], + "properties": { + "new_costs": { + "$ref": "#/definitions/MixNodeCostParams" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_mixnode_cost_params_on_behalf" + ], + "properties": { + "update_mixnode_cost_params_on_behalf": { + "type": "object", + "required": [ + "new_costs", + "owner" + ], + "properties": { + "new_costs": { + "$ref": "#/definitions/MixNodeCostParams" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_mixnode_config" + ], + "properties": { + "update_mixnode_config": { + "type": "object", + "required": [ + "new_config" + ], + "properties": { + "new_config": { + "$ref": "#/definitions/MixNodeConfigUpdate" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_mixnode_config_on_behalf" + ], + "properties": { + "update_mixnode_config_on_behalf": { + "type": "object", + "required": [ + "new_config", + "owner" + ], + "properties": { + "new_config": { + "$ref": "#/definitions/MixNodeConfigUpdate" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "bond_gateway" + ], + "properties": { + "bond_gateway": { + "type": "object", + "required": [ + "gateway", + "owner_signature" + ], + "properties": { + "gateway": { + "$ref": "#/definitions/Gateway" + }, + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "bond_gateway_on_behalf" + ], + "properties": { + "bond_gateway_on_behalf": { + "type": "object", + "required": [ + "gateway", + "owner", + "owner_signature" + ], + "properties": { + "gateway": { + "$ref": "#/definitions/Gateway" + }, + "owner": { + "type": "string" + }, + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "unbond_gateway" + ], + "properties": { + "unbond_gateway": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "unbond_gateway_on_behalf" + ], + "properties": { + "unbond_gateway_on_behalf": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_gateway_config" + ], + "properties": { + "update_gateway_config": { + "type": "object", + "required": [ + "new_config" + ], + "properties": { + "new_config": { + "$ref": "#/definitions/GatewayConfigUpdate" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_gateway_config_on_behalf" + ], + "properties": { + "update_gateway_config_on_behalf": { + "type": "object", + "required": [ + "new_config", + "owner" + ], + "properties": { + "new_config": { + "$ref": "#/definitions/GatewayConfigUpdate" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "delegate_to_mixnode" + ], + "properties": { + "delegate_to_mixnode": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "delegate_to_mixnode_on_behalf" + ], + "properties": { + "delegate_to_mixnode_on_behalf": { + "type": "object", + "required": [ + "delegate", + "mix_id" + ], + "properties": { + "delegate": { + "type": "string" + }, + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "undelegate_from_mixnode" + ], + "properties": { + "undelegate_from_mixnode": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "undelegate_from_mixnode_on_behalf" + ], + "properties": { + "undelegate_from_mixnode_on_behalf": { + "type": "object", + "required": [ + "delegate", + "mix_id" + ], + "properties": { + "delegate": { + "type": "string" + }, + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "reward_mixnode" + ], + "properties": { + "reward_mixnode": { + "type": "object", + "required": [ + "mix_id", + "performance" + ], + "properties": { + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "performance": { + "$ref": "#/definitions/Percent" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "withdraw_operator_reward" + ], + "properties": { + "withdraw_operator_reward": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "withdraw_operator_reward_on_behalf" + ], + "properties": { + "withdraw_operator_reward_on_behalf": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "withdraw_delegator_reward_on_behalf" + ], + "properties": { + "withdraw_delegator_reward_on_behalf": { + "type": "object", + "required": [ + "mix_id", + "owner" + ], + "properties": { + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "ContractStateParams": { + "description": "Contract parameters that could be adjusted in a transaction by the contract admin.", + "type": "object", + "required": [ + "minimum_gateway_pledge", + "minimum_mixnode_pledge" + ], + "properties": { + "minimum_gateway_pledge": { + "description": "Minimum amount a gateway must pledge to get into the system.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "minimum_mixnode_delegation": { + "description": "Minimum amount a delegator must stake in orders for his delegation to get accepted.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "minimum_mixnode_pledge": { + "description": "Minimum amount a mixnode must pledge to get into the system.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + } + }, + "additionalProperties": false + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + }, + "Gateway": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "clients_port", + "host", + "identity_key", + "location", + "mix_port", + "sphinx_key", + "version" + ], + "properties": { + "clients_port": { + "description": "Port used by this gateway for listening for client requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "description": "Network address of this gateway, for example 1.1.1.1 or foo.gateway.com", + "type": "string" + }, + "identity_key": { + "description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients", + "type": "string" + }, + "location": { + "description": "The physical, self-reported, location of this gateway.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this gateway for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "version": { + "description": "The self-reported semver version of this gateway.", + "type": "string" + } + }, + "additionalProperties": false + }, + "GatewayConfigUpdate": { + "type": "object", + "required": [ + "clients_port", + "host", + "location", + "mix_port", + "version" + ], + "properties": { + "clients_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mix_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "type": "string" + } + }, + "additionalProperties": false + }, + "IntervalRewardingParamsUpdate": { + "description": "Specification on how the rewarding params should be updated.", + "type": "object", + "properties": { + "active_set_work_factor": { + "description": "Defines the new value of the active set work factor.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "interval_pool_emission": { + "description": "Defines the new value of the interval pool emission rate.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + }, + "reward_pool": { + "description": "Defines the new value of the reward pool.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "rewarded_set_size": { + "description": "Defines the new size of the rewarded set.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "staking_supply": { + "description": "Defines the new value of the staking supply.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "staking_supply_scale_factor": { + "description": "Defines the new value of the staking supply scale factor.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + }, + "sybil_resistance_percent": { + "description": "Defines the new value of the sybil resistance percent.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Layer": { + "type": "string", + "enum": [ + "One", + "Two", + "Three" + ] + }, + "LayerAssignment": { + "description": "Specifies layer assignment for the given mixnode.", + "type": "object", + "required": [ + "layer", + "mix_id" + ], + "properties": { + "layer": { + "description": "The layer to which it's going to be assigned", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "MessageSignature": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeConfigUpdate": { + "type": "object", + "required": [ + "host", + "http_api_port", + "mix_port", + "verloc_port", + "version" + ], + "properties": { + "host": { + "type": "string" + }, + "http_api_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "mix_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "verloc_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "description": "Gets the list of families registered in this contract.", + "type": "object", + "required": [ + "get_all_families_paged" + ], + "properties": { + "get_all_families_paged": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the list of all family members registered in this contract.", + "type": "object", + "required": [ + "get_all_members_paged" + ], + "properties": { + "get_all_members_paged": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Attempts to lookup family information given the family head.", + "type": "object", + "required": [ + "get_family_by_head" + ], + "properties": { + "get_family_by_head": { + "type": "object", + "required": [ + "head" + ], + "properties": { + "head": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Attempts to lookup family information given the family label.", + "type": "object", + "required": [ + "get_family_by_label" + ], + "properties": { + "get_family_by_label": { + "type": "object", + "required": [ + "label" + ], + "properties": { + "label": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Attempts to retrieve family members given the family head.", + "type": "object", + "required": [ + "get_family_members_by_head" + ], + "properties": { + "get_family_members_by_head": { + "type": "object", + "required": [ + "head" + ], + "properties": { + "head": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Attempts to retrieve family members given the family label.", + "type": "object", + "required": [ + "get_family_members_by_label" + ], + "properties": { + "get_family_members_by_label": { + "type": "object", + "required": [ + "label" + ], + "properties": { + "label": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets build information of this contract, such as the commit hash used for the build or rustc version.", + "type": "object", + "required": [ + "get_contract_version" + ], + "properties": { + "get_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.", + "type": "object", + "required": [ + "get_cw2_contract_version" + ], + "properties": { + "get_cw2_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the address of the validator that's allowed to send rewarding transactions and transition the epoch.", + "type": "object", + "required": [ + "get_rewarding_validator_address" + ], + "properties": { + "get_rewarding_validator_address": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the contract parameters that could be adjusted in a transaction by the contract admin.", + "type": "object", + "required": [ + "get_state_params" + ], + "properties": { + "get_state_params": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the current state of the contract.", + "type": "object", + "required": [ + "get_state" + ], + "properties": { + "get_state": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the current parameters used for reward calculation.", + "type": "object", + "required": [ + "get_rewarding_params" + ], + "properties": { + "get_rewarding_params": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the status of the current rewarding epoch.", + "type": "object", + "required": [ + "get_epoch_status" + ], + "properties": { + "get_epoch_status": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Get the details of the current rewarding interval.", + "type": "object", + "required": [ + "get_current_interval_details" + ], + "properties": { + "get_current_interval_details": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the current list of mixnodes in the rewarded set.", + "type": "object", + "required": [ + "get_rewarded_set" + ], + "properties": { + "get_rewarded_set": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the basic list of all currently bonded mixnodes.", + "type": "object", + "required": [ + "get_mix_node_bonds" + ], + "properties": { + "get_mix_node_bonds": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the detailed list of all currently bonded mixnodes.", + "type": "object", + "required": [ + "get_mix_nodes_detailed" + ], + "properties": { + "get_mix_nodes_detailed": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the basic list of all unbonded mixnodes.", + "type": "object", + "required": [ + "get_unbonded_mix_nodes" + ], + "properties": { + "get_unbonded_mix_nodes": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the basic list of all unbonded mixnodes that belonged to a particular owner.", + "type": "object", + "required": [ + "get_unbonded_mix_nodes_by_owner" + ], + "properties": { + "get_unbonded_mix_nodes_by_owner": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "The address of the owner of the the mixnodes used for the query.", + "type": "string" + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the basic list of all unbonded mixnodes that used the particular identity key.", + "type": "object", + "required": [ + "get_unbonded_mix_nodes_by_identity_key" + ], + "properties": { + "get_unbonded_mix_nodes_by_identity_key": { + "type": "object", + "required": [ + "identity_key" + ], + "properties": { + "identity_key": { + "description": "The identity key (base58-encoded ed25519 public key) of the mixnode used for the query.", + "type": "string" + }, + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the detailed mixnode information belonging to the particular owner.", + "type": "object", + "required": [ + "get_owned_mixnode" + ], + "properties": { + "get_owned_mixnode": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Address of the mixnode owner to use for the query.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the detailed mixnode information of a node with the provided id.", + "type": "object", + "required": [ + "get_mixnode_details" + ], + "properties": { + "get_mixnode_details": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "Id of the node to query.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the rewarding information of a mixnode with the provided id.", + "type": "object", + "required": [ + "get_mixnode_rewarding_details" + ], + "properties": { + "get_mixnode_rewarding_details": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "Id of the node to query.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the stake saturation of a mixnode with the provided id.", + "type": "object", + "required": [ + "get_stake_saturation" + ], + "properties": { + "get_stake_saturation": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "Id of the node to query.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the basic information of an unbonded mixnode with the provided id.", + "type": "object", + "required": [ + "get_unbonded_mix_node_information" + ], + "properties": { + "get_unbonded_mix_node_information": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "Id of the node to query.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the detailed mixnode information of a node given its current identity key.", + "type": "object", + "required": [ + "get_bonded_mixnode_details_by_identity" + ], + "properties": { + "get_bonded_mixnode_details_by_identity": { + "type": "object", + "required": [ + "mix_identity" + ], + "properties": { + "mix_identity": { + "description": "The identity key (base58-encoded ed25519 public key) of the mixnode used for the query.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the current layer configuration of the mix network.", + "type": "object", + "required": [ + "get_layer_distribution" + ], + "properties": { + "get_layer_distribution": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the basic list of all currently bonded gateways.", + "type": "object", + "required": [ + "get_gateways" + ], + "properties": { + "get_gateways": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the gateway details of a node given its identity key.", + "type": "object", + "required": [ + "get_gateway_bond" + ], + "properties": { + "get_gateway_bond": { + "type": "object", + "required": [ + "identity" + ], + "properties": { + "identity": { + "description": "The identity key (base58-encoded ed25519 public key) of the gateway used for the query.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the detailed gateway information belonging to the particular owner.", + "type": "object", + "required": [ + "get_owned_gateway" + ], + "properties": { + "get_owned_gateway": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Address of the gateway owner to use for the query.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets all delegations associated with particular mixnode", + "type": "object", + "required": [ + "get_mixnode_delegations" + ], + "properties": { + "get_mixnode_delegations": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "mix_id": { + "description": "Id of the node to query.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets all delegations associated with particular delegator", + "type": "object", + "required": [ + "get_delegator_delegations" + ], + "properties": { + "get_delegator_delegations": { + "type": "object", + "required": [ + "delegator" + ], + "properties": { + "delegator": { + "description": "The address of the owner of the delegations.", + "type": "string" + }, + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "array", + "null" + ], + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "string" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets delegation information associated with particular mixnode - delegator pair", + "type": "object", + "required": [ + "get_delegation_details" + ], + "properties": { + "get_delegation_details": { + "type": "object", + "required": [ + "delegator", + "mix_id" + ], + "properties": { + "delegator": { + "description": "The address of the owner of the delegation.", + "type": "string" + }, + "mix_id": { + "description": "Id of the node to query.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets all delegations in the system", + "type": "object", + "required": [ + "get_all_delegations" + ], + "properties": { + "get_all_delegations": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "array", + "null" + ], + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "string" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the reward amount accrued by the node operator that has not yet been claimed.", + "type": "object", + "required": [ + "get_pending_operator_reward" + ], + "properties": { + "get_pending_operator_reward": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Address of the operator to use for the query.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the reward amount accrued by the particular mixnode that has not yet been claimed.", + "type": "object", + "required": [ + "get_pending_mix_node_operator_reward" + ], + "properties": { + "get_pending_mix_node_operator_reward": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "Id of the node to query.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the reward amount accrued by the particular delegator that has not yet been claimed.", + "type": "object", + "required": [ + "get_pending_delegator_reward" + ], + "properties": { + "get_pending_delegator_reward": { + "type": "object", + "required": [ + "address", + "mix_id" + ], + "properties": { + "address": { + "description": "Address of the delegator to use for the query.", + "type": "string" + }, + "mix_id": { + "description": "Id of the node to query.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Given the provided node performance, attempt to estimate the operator reward for the current epoch.", + "type": "object", + "required": [ + "get_estimated_current_epoch_operator_reward" + ], + "properties": { + "get_estimated_current_epoch_operator_reward": { + "type": "object", + "required": [ + "estimated_performance", + "mix_id" + ], + "properties": { + "estimated_performance": { + "description": "The estimated performance for the current epoch of the given node.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + }, + "mix_id": { + "description": "Id of the node to query.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Given the provided node performance, attempt to estimate the delegator reward for the current epoch.", + "type": "object", + "required": [ + "get_estimated_current_epoch_delegator_reward" + ], + "properties": { + "get_estimated_current_epoch_delegator_reward": { + "type": "object", + "required": [ + "address", + "estimated_performance", + "mix_id" + ], + "properties": { + "address": { + "description": "Address of the delegator to use for the query.", + "type": "string" + }, + "estimated_performance": { + "description": "The estimated performance for the current epoch of the given node.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + }, + "mix_id": { + "description": "Id of the node to query.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the list of all currently pending epoch events that will be resolved once the current epoch finishes.", + "type": "object", + "required": [ + "get_pending_epoch_events" + ], + "properties": { + "get_pending_epoch_events": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the list of all currently pending interval events that will be resolved once the current interval finishes.", + "type": "object", + "required": [ + "get_pending_interval_events" + ], + "properties": { + "get_pending_interval_events": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets detailed information about a pending epoch event given its id.", + "type": "object", + "required": [ + "get_pending_epoch_event" + ], + "properties": { + "get_pending_epoch_event": { + "type": "object", + "required": [ + "event_id" + ], + "properties": { + "event_id": { + "description": "The unique id associated with the event.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets detailed information about a pending interval event given its id.", + "type": "object", + "required": [ + "get_pending_interval_event" + ], + "properties": { + "get_pending_interval_event": { + "type": "object", + "required": [ + "event_id" + ], + "properties": { + "event_id": { + "description": "The unique id associated with the event.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the information about the number of currently pending epoch and interval events.", + "type": "object", + "required": [ + "get_number_of_pending_events" + ], + "properties": { + "get_number_of_pending_events": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the signing nonce associated with the particular cosmos address.", + "type": "object", + "required": [ + "get_signing_nonce" + ], + "properties": { + "get_signing_nonce": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Cosmos address used for the query of the signing nonce.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + } + }, + "migrate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "properties": { + "vesting_contract_address": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "sudo": null, + "responses": { + "get_all_delegations": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedAllDelegationsResponse", + "description": "Response containing paged list of all delegations currently active.", + "type": "object", + "required": [ + "delegations" + ], + "properties": { + "delegations": { + "description": "Each individual delegation made.", + "type": "array", + "items": { + "$ref": "#/definitions/Delegation" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "array", + "null" + ], + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "string" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Delegation": { + "description": "Information about tokens being delegated towards given mixnode in order to accrue rewards with their work.", + "type": "object", + "required": [ + "amount", + "cumulative_reward_ratio", + "height", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "Original delegation amount. Note that it is never mutated as delegation accumulates rewards.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "cumulative_reward_ratio": { + "description": "Value of the \"unit delegation\" associated with the mixnode at the time of delegation.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "height": { + "description": "Block height where this delegation occurred.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "Id of the MixNode that this delegation was performed against.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of the owner of this delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Proxy address used to delegate the funds on behalf of another address", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_all_families_paged": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedFamiliesResponse", + "description": "Response containing paged list of all families registered in the contract.", + "type": "object", + "required": [ + "families" + ], + "properties": { + "families": { + "description": "The families registered in the contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Family" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "Family": { + "description": "A group of mixnodes associated with particular staking entity. When defined all nodes belonging to the same family will be prioritised to be put onto the same layer.", + "type": "object", + "required": [ + "head", + "label" + ], + "properties": { + "head": { + "description": "Owner of this family.", + "allOf": [ + { + "$ref": "#/definitions/FamilyHead" + } + ] + }, + "label": { + "description": "Human readable label for this family.", + "type": "string" + }, + "proxy": { + "description": "Optional proxy (i.e. vesting contract address) used when creating the family.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + } + } + }, + "get_all_members_paged": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedMembersResponse", + "description": "Response containing paged list of all family members (of ALL families) registered in the contract.", + "type": "object", + "required": [ + "members" + ], + "properties": { + "members": { + "description": "The members alongside their family heads.", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/FamilyHead" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + } + } + }, + "get_bonded_mixnode_details_by_identity": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MixnodeDetailsByIdentityResponse", + "description": "Response containing details of a bonded mixnode with the provided identity key.", + "type": "object", + "required": [ + "identity_key" + ], + "properties": { + "identity_key": { + "description": "The identity key (base58-encoded ed25519 public key) of the mixnode.", + "type": "string" + }, + "mixnode_details": { + "description": "If there exists a bonded mixnode with the provided identity key, this field contains its detailed information.", + "anyOf": [ + { + "$ref": "#/definitions/MixNodeDetails" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Layer": { + "type": "string", + "enum": [ + "One", + "Two", + "Three" + ] + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeBond": { + "description": "Basic mixnode information provided by the node operator.", + "type": "object", + "required": [ + "bonding_height", + "is_unbonding", + "layer", + "mix_id", + "mix_node", + "original_pledge", + "owner" + ], + "properties": { + "bonding_height": { + "description": "Block height at which this mixnode has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "is_unbonding": { + "description": "Flag to indicate whether this node is in the process of unbonding, that will conclude upon the epoch finishing.", + "type": "boolean" + }, + "layer": { + "description": "Layer assigned to this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] + }, + "mix_id": { + "description": "Unique id assigned to the bonded mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mix_node": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/MixNode" + } + ] + }, + "original_pledge": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeDetails": { + "description": "Full details associated with given mixnode.", + "type": "object", + "required": [ + "bond_information", + "rewarding_details" + ], + "properties": { + "bond_information": { + "description": "Basic bond information of this mixnode, such as owner address, original pledge, etc.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeBond" + } + ] + }, + "pending_changes": { + "description": "Adjustments to the mixnode that are ought to happen during future epoch transitions.", + "default": { + "pledge_change": null + }, + "allOf": [ + { + "$ref": "#/definitions/PendingMixNodeChanges" + } + ] + }, + "rewarding_details": { + "description": "Details used for computation of rewarding related data.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeRewarding" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeRewarding": { + "type": "object", + "required": [ + "cost_params", + "delegates", + "last_rewarded_epoch", + "operator", + "total_unit_reward", + "unique_delegations", + "unit_delegation" + ], + "properties": { + "cost_params": { + "description": "Information provided by the operator that influence the cost function.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + }, + "delegates": { + "description": "Total delegation and compounded reward earned by all node delegators.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "last_rewarded_epoch": { + "description": "Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt to reward it multiple times in the same epoch.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "operator": { + "description": "Total pledge and compounded reward earned by the node operator.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "total_unit_reward": { + "description": "Cumulative reward earned by the \"unit delegation\" since the block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "unique_delegations": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unit_delegation": { + "description": "Value of the theoretical \"unit delegation\" that has delegated to this mixnode at block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + }, + "additionalProperties": false + }, + "PendingMixNodeChanges": { + "type": "object", + "properties": { + "pledge_change": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_c_w2_contract_version": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractVersion", + "type": "object", + "required": [ + "contract", + "version" + ], + "properties": { + "contract": { + "description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing", + "type": "string" + }, + "version": { + "description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)", + "type": "string" + } + }, + "additionalProperties": false + }, + "get_contract_version": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractBuildInformation", + "type": "object", + "required": [ + "build_timestamp", + "build_version", + "commit_branch", + "commit_sha", + "commit_timestamp", + "rustc_version" + ], + "properties": { + "build_timestamp": { + "description": "Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.", + "type": "string" + }, + "build_version": { + "description": "Provides the build version, for example `0.1.0-9-g46f83e1`.", + "type": "string" + }, + "commit_branch": { + "description": "Provides the name of the git branch that was used for the build, for example `master`.", + "type": "string" + }, + "commit_sha": { + "description": "Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`.", + "type": "string" + }, + "commit_timestamp": { + "description": "Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`.", + "type": "string" + }, + "rustc_version": { + "description": "Provides the rustc version that was used for the build, for example `1.52.0-nightly`.", + "type": "string" + } + }, + "additionalProperties": false + }, + "get_current_interval_details": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CurrentIntervalResponse", + "description": "Information about the current rewarding interval.", + "type": "object", + "required": [ + "current_blocktime", + "interval", + "is_current_epoch_over", + "is_current_interval_over" + ], + "properties": { + "current_blocktime": { + "description": "The current blocktime", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "interval": { + "description": "Detailed information about the underlying interval.", + "allOf": [ + { + "$ref": "#/definitions/Interval" + } + ] + }, + "is_current_epoch_over": { + "description": "Flag indicating whether the current epoch is over and it should be advanced.", + "type": "boolean" + }, + "is_current_interval_over": { + "description": "Flag indicating whether the current interval is over and it should be advanced.", + "type": "boolean" + } + }, + "additionalProperties": false, + "definitions": { + "Duration": { + "type": "object", + "required": [ + "nanos", + "secs" + ], + "properties": { + "nanos": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "Interval": { + "type": "object", + "required": [ + "current_epoch_id", + "current_epoch_start", + "epoch_length", + "epochs_in_interval", + "id", + "total_elapsed_epochs" + ], + "properties": { + "current_epoch_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "current_epoch_start": { + "type": "string" + }, + "epoch_length": { + "$ref": "#/definitions/Duration" + }, + "epochs_in_interval": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "total_elapsed_epochs": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } + } + }, + "get_delegation_details": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MixNodeDelegationResponse", + "description": "Response containing delegation details.", + "type": "object", + "required": [ + "mixnode_still_bonded" + ], + "properties": { + "delegation": { + "description": "If the delegation exists, this field contains its detailed information.", + "anyOf": [ + { + "$ref": "#/definitions/Delegation" + }, + { + "type": "null" + } + ] + }, + "mixnode_still_bonded": { + "description": "Flag indicating whether the node towards which the delegation was made is still bonded in the network.", + "type": "boolean" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Delegation": { + "description": "Information about tokens being delegated towards given mixnode in order to accrue rewards with their work.", + "type": "object", + "required": [ + "amount", + "cumulative_reward_ratio", + "height", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "Original delegation amount. Note that it is never mutated as delegation accumulates rewards.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "cumulative_reward_ratio": { + "description": "Value of the \"unit delegation\" associated with the mixnode at the time of delegation.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "height": { + "description": "Block height where this delegation occurred.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "Id of the MixNode that this delegation was performed against.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of the owner of this delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Proxy address used to delegate the funds on behalf of another address", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_delegator_delegations": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedDelegatorDelegationsResponse", + "description": "Response containing paged list of all delegations made by the particular address.", + "type": "object", + "required": [ + "delegations" + ], + "properties": { + "delegations": { + "description": "Each individual delegation made.", + "type": "array", + "items": { + "$ref": "#/definitions/Delegation" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "array", + "null" + ], + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "string" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Delegation": { + "description": "Information about tokens being delegated towards given mixnode in order to accrue rewards with their work.", + "type": "object", + "required": [ + "amount", + "cumulative_reward_ratio", + "height", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "Original delegation amount. Note that it is never mutated as delegation accumulates rewards.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "cumulative_reward_ratio": { + "description": "Value of the \"unit delegation\" associated with the mixnode at the time of delegation.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "height": { + "description": "Block height where this delegation occurred.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "Id of the MixNode that this delegation was performed against.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of the owner of this delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Proxy address used to delegate the funds on behalf of another address", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_epoch_status": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "EpochStatus", + "description": "The status of the current rewarding epoch.", + "type": "object", + "required": [ + "being_advanced_by", + "state" + ], + "properties": { + "being_advanced_by": { + "description": "Specifies either, which validator is currently performing progression into the following epoch (if the epoch is currently being progressed), or which validator was responsible for progressing into the current epoch (if the epoch is currently in progress)", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "state": { + "description": "The concrete state of the epoch.", + "allOf": [ + { + "$ref": "#/definitions/EpochState" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "EpochState": { + "description": "The state of the current rewarding epoch.", + "oneOf": [ + { + "description": "Represents the state of an epoch that's in progress (well, duh.) All actions are allowed to be issued.", + "type": "string", + "enum": [ + "in_progress" + ] + }, + { + "description": "Represents the state of an epoch when the rewarding entity has been decided on, and the mixnodes are in the process of being rewarded for their work in this epoch.", + "type": "object", + "required": [ + "rewarding" + ], + "properties": { + "rewarding": { + "type": "object", + "required": [ + "final_node_id", + "last_rewarded" + ], + "properties": { + "final_node_id": { + "description": "The id of the last node that's going to be rewarded before progressing into the next state.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "last_rewarded": { + "description": "The id of the last node that has already received its rewards.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Represents the state of an epoch when all mixnodes have already been rewarded for their work in this epoch and all issued actions should now get resolved before being allowed to advance into the next epoch.", + "type": "string", + "enum": [ + "reconciling_events" + ] + }, + { + "description": "Represents the state of an epoch when all mixnodes have already been rewarded for their work in this epoch, all issued actions got resolved and the epoch should now be advanced whilst assigning new rewarded set.", + "type": "string", + "enum": [ + "advancing_epoch" + ] + } + ] + } + } + }, + "get_estimated_current_epoch_delegator_reward": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "EstimatedCurrentEpochRewardResponse", + "description": "Response containing estimation of node rewards for the current epoch.", + "type": "object", + "properties": { + "current_stake_value": { + "description": "The current stake value given all past rewarding and compounding since the original staking was performed.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "current_stake_value_detailed_amount": { + "description": "The current stake value. Note that it's nearly identical to `current_stake_value`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "detailed_estimation_amount": { + "description": "The full reward estimation. Note that it's nearly identical to `estimation`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "estimation": { + "description": "The reward estimation for the current epoch, i.e. the amount of tokens that could be claimable after the epoch finishes and the state of the network does not change.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "original_stake": { + "description": "The amount of tokens initially staked.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_estimated_current_epoch_operator_reward": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "EstimatedCurrentEpochRewardResponse", + "description": "Response containing estimation of node rewards for the current epoch.", + "type": "object", + "properties": { + "current_stake_value": { + "description": "The current stake value given all past rewarding and compounding since the original staking was performed.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "current_stake_value_detailed_amount": { + "description": "The current stake value. Note that it's nearly identical to `current_stake_value`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "detailed_estimation_amount": { + "description": "The full reward estimation. Note that it's nearly identical to `estimation`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "estimation": { + "description": "The reward estimation for the current epoch, i.e. the amount of tokens that could be claimable after the epoch finishes and the state of the network does not change.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "original_stake": { + "description": "The amount of tokens initially staked.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_family_by_head": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FamilyByHeadResponse", + "description": "Response containing family information.", + "type": "object", + "required": [ + "head" + ], + "properties": { + "family": { + "description": "If applicable, the family associated with the provided head.", + "anyOf": [ + { + "$ref": "#/definitions/Family" + }, + { + "type": "null" + } + ] + }, + "head": { + "description": "The family head used for the query.", + "allOf": [ + { + "$ref": "#/definitions/FamilyHead" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Family": { + "description": "A group of mixnodes associated with particular staking entity. When defined all nodes belonging to the same family will be prioritised to be put onto the same layer.", + "type": "object", + "required": [ + "head", + "label" + ], + "properties": { + "head": { + "description": "Owner of this family.", + "allOf": [ + { + "$ref": "#/definitions/FamilyHead" + } + ] + }, + "label": { + "description": "Human readable label for this family.", + "type": "string" + }, + "proxy": { + "description": "Optional proxy (i.e. vesting contract address) used when creating the family.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + } + } + }, + "get_family_by_label": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FamilyByLabelResponse", + "description": "Response containing family information.", + "type": "object", + "required": [ + "label" + ], + "properties": { + "family": { + "description": "If applicable, the family associated with the provided label.", + "anyOf": [ + { + "$ref": "#/definitions/Family" + }, + { + "type": "null" + } + ] + }, + "label": { + "description": "The family label used for the query.", + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "Family": { + "description": "A group of mixnodes associated with particular staking entity. When defined all nodes belonging to the same family will be prioritised to be put onto the same layer.", + "type": "object", + "required": [ + "head", + "label" + ], + "properties": { + "head": { + "description": "Owner of this family.", + "allOf": [ + { + "$ref": "#/definitions/FamilyHead" + } + ] + }, + "label": { + "description": "Human readable label for this family.", + "type": "string" + }, + "proxy": { + "description": "Optional proxy (i.e. vesting contract address) used when creating the family.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + } + } + }, + "get_family_members_by_head": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FamilyMembersByHeadResponse", + "description": "Response containing family members information.", + "type": "object", + "required": [ + "head", + "members" + ], + "properties": { + "head": { + "description": "The family head used for the query.", + "allOf": [ + { + "$ref": "#/definitions/FamilyHead" + } + ] + }, + "members": { + "description": "All members belonging to the specified family.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "definitions": { + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + } + } + }, + "get_family_members_by_label": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FamilyMembersByLabelResponse", + "description": "Response containing family members information.", + "type": "object", + "required": [ + "label", + "members" + ], + "properties": { + "label": { + "description": "The family label used for the query.", + "type": "string" + }, + "members": { + "description": "All members belonging to the specified family.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "get_gateway_bond": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GatewayBondResponse", + "description": "Response containing details of a gateway with the provided identity key.", + "type": "object", + "required": [ + "identity" + ], + "properties": { + "gateway": { + "description": "If there exists a gateway with the provided identity key, this field contains its details.", + "anyOf": [ + { + "$ref": "#/definitions/GatewayBond" + }, + { + "type": "null" + } + ] + }, + "identity": { + "description": "The identity key (base58-encoded ed25519 public key) of the gateway.", + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Gateway": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "clients_port", + "host", + "identity_key", + "location", + "mix_port", + "sphinx_key", + "version" + ], + "properties": { + "clients_port": { + "description": "Port used by this gateway for listening for client requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "description": "Network address of this gateway, for example 1.1.1.1 or foo.gateway.com", + "type": "string" + }, + "identity_key": { + "description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients", + "type": "string" + }, + "location": { + "description": "The physical, self-reported, location of this gateway.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this gateway for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "version": { + "description": "The self-reported semver version of this gateway.", + "type": "string" + } + }, + "additionalProperties": false + }, + "GatewayBond": { + "description": "Basic gateway information provided by the node operator.", + "type": "object", + "required": [ + "block_height", + "gateway", + "owner", + "pledge_amount" + ], + "properties": { + "block_height": { + "description": "Block height at which this gateway has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "gateway": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/Gateway" + } + ] + }, + "owner": { + "description": "Address of the owner of this gateway.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "pledge_amount": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "proxy": { + "description": "Entity who bonded this gateway on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_gateways": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedGatewayResponse", + "description": "Response containing paged list of all gateway bonds in the contract.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "The gateway bond information present in the contract.", + "type": "array", + "items": { + "$ref": "#/definitions/GatewayBond" + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Gateway": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "clients_port", + "host", + "identity_key", + "location", + "mix_port", + "sphinx_key", + "version" + ], + "properties": { + "clients_port": { + "description": "Port used by this gateway for listening for client requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "description": "Network address of this gateway, for example 1.1.1.1 or foo.gateway.com", + "type": "string" + }, + "identity_key": { + "description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients", + "type": "string" + }, + "location": { + "description": "The physical, self-reported, location of this gateway.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this gateway for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "version": { + "description": "The self-reported semver version of this gateway.", + "type": "string" + } + }, + "additionalProperties": false + }, + "GatewayBond": { + "description": "Basic gateway information provided by the node operator.", + "type": "object", + "required": [ + "block_height", + "gateway", + "owner", + "pledge_amount" + ], + "properties": { + "block_height": { + "description": "Block height at which this gateway has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "gateway": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/Gateway" + } + ] + }, + "owner": { + "description": "Address of the owner of this gateway.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "pledge_amount": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "proxy": { + "description": "Entity who bonded this gateway on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_layer_distribution": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LayerDistribution", + "description": "The current layer distribution of the mix network.", + "type": "object", + "required": [ + "layer1", + "layer2", + "layer3" + ], + "properties": { + "layer1": { + "description": "Number of nodes on the first layer.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "layer2": { + "description": "Number of nodes on the second layer.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "layer3": { + "description": "Number of nodes on the third layer.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "get_mix_node_bonds": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedMixnodeBondsResponse", + "description": "Response containing paged list of all mixnode bonds in the contract.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "The mixnode bond information present in the contract.", + "type": "array", + "items": { + "$ref": "#/definitions/MixNodeBond" + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Layer": { + "type": "string", + "enum": [ + "One", + "Two", + "Three" + ] + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeBond": { + "description": "Basic mixnode information provided by the node operator.", + "type": "object", + "required": [ + "bonding_height", + "is_unbonding", + "layer", + "mix_id", + "mix_node", + "original_pledge", + "owner" + ], + "properties": { + "bonding_height": { + "description": "Block height at which this mixnode has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "is_unbonding": { + "description": "Flag to indicate whether this node is in the process of unbonding, that will conclude upon the epoch finishing.", + "type": "boolean" + }, + "layer": { + "description": "Layer assigned to this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] + }, + "mix_id": { + "description": "Unique id assigned to the bonded mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mix_node": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/MixNode" + } + ] + }, + "original_pledge": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_mix_nodes_detailed": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedMixnodesDetailsResponse", + "description": "Response containing paged list of all mixnode details in the contract.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "All mixnode details stored in the contract. Apart from the basic bond information it also contains details required for all future reward calculation as well as any pending changes requested by the operator.", + "type": "array", + "items": { + "$ref": "#/definitions/MixNodeDetails" + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Layer": { + "type": "string", + "enum": [ + "One", + "Two", + "Three" + ] + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeBond": { + "description": "Basic mixnode information provided by the node operator.", + "type": "object", + "required": [ + "bonding_height", + "is_unbonding", + "layer", + "mix_id", + "mix_node", + "original_pledge", + "owner" + ], + "properties": { + "bonding_height": { + "description": "Block height at which this mixnode has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "is_unbonding": { + "description": "Flag to indicate whether this node is in the process of unbonding, that will conclude upon the epoch finishing.", + "type": "boolean" + }, + "layer": { + "description": "Layer assigned to this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] + }, + "mix_id": { + "description": "Unique id assigned to the bonded mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mix_node": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/MixNode" + } + ] + }, + "original_pledge": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeDetails": { + "description": "Full details associated with given mixnode.", + "type": "object", + "required": [ + "bond_information", + "rewarding_details" + ], + "properties": { + "bond_information": { + "description": "Basic bond information of this mixnode, such as owner address, original pledge, etc.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeBond" + } + ] + }, + "pending_changes": { + "description": "Adjustments to the mixnode that are ought to happen during future epoch transitions.", + "default": { + "pledge_change": null + }, + "allOf": [ + { + "$ref": "#/definitions/PendingMixNodeChanges" + } + ] + }, + "rewarding_details": { + "description": "Details used for computation of rewarding related data.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeRewarding" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeRewarding": { + "type": "object", + "required": [ + "cost_params", + "delegates", + "last_rewarded_epoch", + "operator", + "total_unit_reward", + "unique_delegations", + "unit_delegation" + ], + "properties": { + "cost_params": { + "description": "Information provided by the operator that influence the cost function.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + }, + "delegates": { + "description": "Total delegation and compounded reward earned by all node delegators.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "last_rewarded_epoch": { + "description": "Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt to reward it multiple times in the same epoch.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "operator": { + "description": "Total pledge and compounded reward earned by the node operator.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "total_unit_reward": { + "description": "Cumulative reward earned by the \"unit delegation\" since the block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "unique_delegations": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unit_delegation": { + "description": "Value of the theoretical \"unit delegation\" that has delegated to this mixnode at block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + }, + "additionalProperties": false + }, + "PendingMixNodeChanges": { + "type": "object", + "properties": { + "pledge_change": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_mixnode_delegations": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedMixNodeDelegationsResponse", + "description": "Response containing paged list of all delegations made towards particular mixnode.", + "type": "object", + "required": [ + "delegations" + ], + "properties": { + "delegations": { + "description": "Each individual delegation made.", + "type": "array", + "items": { + "$ref": "#/definitions/Delegation" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Delegation": { + "description": "Information about tokens being delegated towards given mixnode in order to accrue rewards with their work.", + "type": "object", + "required": [ + "amount", + "cumulative_reward_ratio", + "height", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "Original delegation amount. Note that it is never mutated as delegation accumulates rewards.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "cumulative_reward_ratio": { + "description": "Value of the \"unit delegation\" associated with the mixnode at the time of delegation.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "height": { + "description": "Block height where this delegation occurred.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "Id of the MixNode that this delegation was performed against.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of the owner of this delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Proxy address used to delegate the funds on behalf of another address", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_mixnode_details": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MixnodeDetailsResponse", + "description": "Response containing details of a mixnode with the provided id.", + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "Id of the requested mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mixnode_details": { + "description": "If there exists a mixnode with the provided id, this field contains its detailed information.", + "anyOf": [ + { + "$ref": "#/definitions/MixNodeDetails" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Layer": { + "type": "string", + "enum": [ + "One", + "Two", + "Three" + ] + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeBond": { + "description": "Basic mixnode information provided by the node operator.", + "type": "object", + "required": [ + "bonding_height", + "is_unbonding", + "layer", + "mix_id", + "mix_node", + "original_pledge", + "owner" + ], + "properties": { + "bonding_height": { + "description": "Block height at which this mixnode has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "is_unbonding": { + "description": "Flag to indicate whether this node is in the process of unbonding, that will conclude upon the epoch finishing.", + "type": "boolean" + }, + "layer": { + "description": "Layer assigned to this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] + }, + "mix_id": { + "description": "Unique id assigned to the bonded mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mix_node": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/MixNode" + } + ] + }, + "original_pledge": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeDetails": { + "description": "Full details associated with given mixnode.", + "type": "object", + "required": [ + "bond_information", + "rewarding_details" + ], + "properties": { + "bond_information": { + "description": "Basic bond information of this mixnode, such as owner address, original pledge, etc.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeBond" + } + ] + }, + "pending_changes": { + "description": "Adjustments to the mixnode that are ought to happen during future epoch transitions.", + "default": { + "pledge_change": null + }, + "allOf": [ + { + "$ref": "#/definitions/PendingMixNodeChanges" + } + ] + }, + "rewarding_details": { + "description": "Details used for computation of rewarding related data.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeRewarding" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeRewarding": { + "type": "object", + "required": [ + "cost_params", + "delegates", + "last_rewarded_epoch", + "operator", + "total_unit_reward", + "unique_delegations", + "unit_delegation" + ], + "properties": { + "cost_params": { + "description": "Information provided by the operator that influence the cost function.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + }, + "delegates": { + "description": "Total delegation and compounded reward earned by all node delegators.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "last_rewarded_epoch": { + "description": "Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt to reward it multiple times in the same epoch.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "operator": { + "description": "Total pledge and compounded reward earned by the node operator.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "total_unit_reward": { + "description": "Cumulative reward earned by the \"unit delegation\" since the block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "unique_delegations": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unit_delegation": { + "description": "Value of the theoretical \"unit delegation\" that has delegated to this mixnode at block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + }, + "additionalProperties": false + }, + "PendingMixNodeChanges": { + "type": "object", + "properties": { + "pledge_change": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_mixnode_rewarding_details": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MixnodeRewardingDetailsResponse", + "description": "Response containing rewarding information of a mixnode with the provided id.", + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "Id of the requested mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "rewarding_details": { + "description": "If there exists a mixnode with the provided id, this field contains its rewarding information.", + "anyOf": [ + { + "$ref": "#/definitions/MixNodeRewarding" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeRewarding": { + "type": "object", + "required": [ + "cost_params", + "delegates", + "last_rewarded_epoch", + "operator", + "total_unit_reward", + "unique_delegations", + "unit_delegation" + ], + "properties": { + "cost_params": { + "description": "Information provided by the operator that influence the cost function.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + }, + "delegates": { + "description": "Total delegation and compounded reward earned by all node delegators.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "last_rewarded_epoch": { + "description": "Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt to reward it multiple times in the same epoch.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "operator": { + "description": "Total pledge and compounded reward earned by the node operator.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "total_unit_reward": { + "description": "Cumulative reward earned by the \"unit delegation\" since the block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "unique_delegations": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unit_delegation": { + "description": "Value of the theoretical \"unit delegation\" that has delegated to this mixnode at block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_number_of_pending_events": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "NumberOfPendingEventsResponse", + "description": "Response containing number of currently pending epoch and interval events.", + "type": "object", + "required": [ + "epoch_events", + "interval_events" + ], + "properties": { + "epoch_events": { + "description": "The number of the currently pending epoch events.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "interval_events": { + "description": "The number of the currently pending epoch events.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "get_owned_gateway": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GatewayOwnershipResponse", + "description": "Response containing details of a gateway belonging to the particular owner.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Validated address of the gateway owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "gateway": { + "description": "If the provided address owns a gateway, this field contains its details.", + "anyOf": [ + { + "$ref": "#/definitions/GatewayBond" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Gateway": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "clients_port", + "host", + "identity_key", + "location", + "mix_port", + "sphinx_key", + "version" + ], + "properties": { + "clients_port": { + "description": "Port used by this gateway for listening for client requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "description": "Network address of this gateway, for example 1.1.1.1 or foo.gateway.com", + "type": "string" + }, + "identity_key": { + "description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients", + "type": "string" + }, + "location": { + "description": "The physical, self-reported, location of this gateway.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this gateway for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "version": { + "description": "The self-reported semver version of this gateway.", + "type": "string" + } + }, + "additionalProperties": false + }, + "GatewayBond": { + "description": "Basic gateway information provided by the node operator.", + "type": "object", + "required": [ + "block_height", + "gateway", + "owner", + "pledge_amount" + ], + "properties": { + "block_height": { + "description": "Block height at which this gateway has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "gateway": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/Gateway" + } + ] + }, + "owner": { + "description": "Address of the owner of this gateway.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "pledge_amount": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "proxy": { + "description": "Entity who bonded this gateway on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_owned_mixnode": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MixOwnershipResponse", + "description": "Response containing details of a mixnode belonging to the particular owner.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Validated address of the mixnode owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "mixnode_details": { + "description": "If the provided address owns a mixnode, this field contains its detailed information.", + "anyOf": [ + { + "$ref": "#/definitions/MixNodeDetails" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Layer": { + "type": "string", + "enum": [ + "One", + "Two", + "Three" + ] + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeBond": { + "description": "Basic mixnode information provided by the node operator.", + "type": "object", + "required": [ + "bonding_height", + "is_unbonding", + "layer", + "mix_id", + "mix_node", + "original_pledge", + "owner" + ], + "properties": { + "bonding_height": { + "description": "Block height at which this mixnode has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "is_unbonding": { + "description": "Flag to indicate whether this node is in the process of unbonding, that will conclude upon the epoch finishing.", + "type": "boolean" + }, + "layer": { + "description": "Layer assigned to this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] + }, + "mix_id": { + "description": "Unique id assigned to the bonded mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mix_node": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/MixNode" + } + ] + }, + "original_pledge": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeDetails": { + "description": "Full details associated with given mixnode.", + "type": "object", + "required": [ + "bond_information", + "rewarding_details" + ], + "properties": { + "bond_information": { + "description": "Basic bond information of this mixnode, such as owner address, original pledge, etc.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeBond" + } + ] + }, + "pending_changes": { + "description": "Adjustments to the mixnode that are ought to happen during future epoch transitions.", + "default": { + "pledge_change": null + }, + "allOf": [ + { + "$ref": "#/definitions/PendingMixNodeChanges" + } + ] + }, + "rewarding_details": { + "description": "Details used for computation of rewarding related data.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeRewarding" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeRewarding": { + "type": "object", + "required": [ + "cost_params", + "delegates", + "last_rewarded_epoch", + "operator", + "total_unit_reward", + "unique_delegations", + "unit_delegation" + ], + "properties": { + "cost_params": { + "description": "Information provided by the operator that influence the cost function.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + }, + "delegates": { + "description": "Total delegation and compounded reward earned by all node delegators.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "last_rewarded_epoch": { + "description": "Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt to reward it multiple times in the same epoch.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "operator": { + "description": "Total pledge and compounded reward earned by the node operator.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "total_unit_reward": { + "description": "Cumulative reward earned by the \"unit delegation\" since the block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "unique_delegations": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unit_delegation": { + "description": "Value of the theoretical \"unit delegation\" that has delegated to this mixnode at block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + }, + "additionalProperties": false + }, + "PendingMixNodeChanges": { + "type": "object", + "properties": { + "pledge_change": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_pending_delegator_reward": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingRewardResponse", + "description": "Response containing information about accrued rewards.", + "type": "object", + "required": [ + "mixnode_still_fully_bonded" + ], + "properties": { + "amount_earned": { + "description": "The amount of tokens that could be claimed.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "amount_earned_detailed": { + "description": "The full pending rewards. Note that it's nearly identical to `amount_earned`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "amount_staked": { + "description": "The amount of tokens initially staked.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "mixnode_still_fully_bonded": { + "description": "The associated mixnode is still fully bonded, meaning it is neither unbonded nor in the process of unbonding that would have finished at the epoch transition.", + "type": "boolean" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_pending_epoch_event": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingEpochEventResponse", + "type": "object", + "required": [ + "event_id" + ], + "properties": { + "event": { + "anyOf": [ + { + "$ref": "#/definitions/PendingEpochEventData" + }, + { + "type": "null" + } + ] + }, + "event_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "PendingEpochEventData": { + "description": "Details of a particular pending epoch event.", + "type": "object", + "required": [ + "created_at", + "kind" + ], + "properties": { + "created_at": { + "description": "The block height at which the request has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "kind": { + "description": "The underlying event data, containing its concrete type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingEpochEventKind" + } + ] + } + }, + "additionalProperties": false + }, + "PendingEpochEventKind": { + "description": "Enum encompassing all possible epoch events.", + "oneOf": [ + { + "description": "Request to create a delegation towards particular mixnode. Note that if such delegation already exists, it will get updated with the provided token amount.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "The amount of tokens to use for the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode used for the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "The address of the owner of the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to remove delegation from particular mixnode.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "mix_id", + "owner" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode used for the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "The address of the owner of the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to pledge more tokens (by the node operator) towards its node.", + "type": "object", + "required": [ + "pledge_more" + ], + "properties": { + "pledge_more": { + "type": "object", + "required": [ + "amount", + "mix_id" + ], + "properties": { + "amount": { + "description": "The amount of additional tokens to use by the pledge.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode that will have its pledge updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to decrease amount of pledged tokens (by the node operator) from its node.", + "type": "object", + "required": [ + "decrease_pledge" + ], + "properties": { + "decrease_pledge": { + "type": "object", + "required": [ + "decrease_by", + "mix_id" + ], + "properties": { + "decrease_by": { + "description": "The amount of tokens that should be removed from the pledge.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode that will have its pledge updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to unbond a mixnode and completely remove it from the network.", + "type": "object", + "required": [ + "unbond_mixnode" + ], + "properties": { + "unbond_mixnode": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode that will get unbonded.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to update the current size of the active set.", + "type": "object", + "required": [ + "update_active_set_size" + ], + "properties": { + "update_active_set_size": { + "type": "object", + "required": [ + "new_size" + ], + "properties": { + "new_size": { + "description": "The new desired size of the active set.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_pending_epoch_events": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingEpochEventsResponse", + "description": "Response containing all currently pending epoch events that will be resolved once the current epoch finishes.", + "type": "object", + "required": [ + "events", + "seconds_until_executable" + ], + "properties": { + "events": { + "description": "The currently pending events.", + "type": "array", + "items": { + "$ref": "#/definitions/PendingEpochEvent" + } + }, + "seconds_until_executable": { + "description": "Amount of seconds until the events would be eligible to be resolved. It's equivalent to the time until the current epoch finishes.", + "type": "integer", + "format": "int64" + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "PendingEpochEvent": { + "description": "A request made at some point in the current epoch that's going to get resolved once the epoch rolls over.", + "type": "object", + "required": [ + "event", + "id" + ], + "properties": { + "event": { + "description": "The underlying event details, containing its type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingEpochEventData" + } + ] + }, + "id": { + "description": "The unique id associated with the event.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "PendingEpochEventData": { + "description": "Details of a particular pending epoch event.", + "type": "object", + "required": [ + "created_at", + "kind" + ], + "properties": { + "created_at": { + "description": "The block height at which the request has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "kind": { + "description": "The underlying event data, containing its concrete type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingEpochEventKind" + } + ] + } + }, + "additionalProperties": false + }, + "PendingEpochEventKind": { + "description": "Enum encompassing all possible epoch events.", + "oneOf": [ + { + "description": "Request to create a delegation towards particular mixnode. Note that if such delegation already exists, it will get updated with the provided token amount.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "The amount of tokens to use for the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode used for the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "The address of the owner of the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to remove delegation from particular mixnode.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "mix_id", + "owner" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode used for the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "The address of the owner of the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to pledge more tokens (by the node operator) towards its node.", + "type": "object", + "required": [ + "pledge_more" + ], + "properties": { + "pledge_more": { + "type": "object", + "required": [ + "amount", + "mix_id" + ], + "properties": { + "amount": { + "description": "The amount of additional tokens to use by the pledge.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode that will have its pledge updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to decrease amount of pledged tokens (by the node operator) from its node.", + "type": "object", + "required": [ + "decrease_pledge" + ], + "properties": { + "decrease_pledge": { + "type": "object", + "required": [ + "decrease_by", + "mix_id" + ], + "properties": { + "decrease_by": { + "description": "The amount of tokens that should be removed from the pledge.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode that will have its pledge updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to unbond a mixnode and completely remove it from the network.", + "type": "object", + "required": [ + "unbond_mixnode" + ], + "properties": { + "unbond_mixnode": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode that will get unbonded.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to update the current size of the active set.", + "type": "object", + "required": [ + "update_active_set_size" + ], + "properties": { + "update_active_set_size": { + "type": "object", + "required": [ + "new_size" + ], + "properties": { + "new_size": { + "description": "The new desired size of the active set.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_pending_interval_event": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingIntervalEventResponse", + "type": "object", + "required": [ + "event_id" + ], + "properties": { + "event": { + "anyOf": [ + { + "$ref": "#/definitions/PendingIntervalEventData" + }, + { + "type": "null" + } + ] + }, + "event_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "IntervalRewardingParamsUpdate": { + "description": "Specification on how the rewarding params should be updated.", + "type": "object", + "properties": { + "active_set_work_factor": { + "description": "Defines the new value of the active set work factor.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "interval_pool_emission": { + "description": "Defines the new value of the interval pool emission rate.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + }, + "reward_pool": { + "description": "Defines the new value of the reward pool.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "rewarded_set_size": { + "description": "Defines the new size of the rewarded set.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "staking_supply": { + "description": "Defines the new value of the staking supply.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "staking_supply_scale_factor": { + "description": "Defines the new value of the staking supply scale factor.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + }, + "sybil_resistance_percent": { + "description": "Defines the new value of the sybil resistance percent.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "PendingIntervalEventData": { + "description": "Details of a particular pending interval event.", + "type": "object", + "required": [ + "created_at", + "kind" + ], + "properties": { + "created_at": { + "description": "The block height at which the request has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "kind": { + "description": "The underlying event data, containing its concrete type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingIntervalEventKind" + } + ] + } + }, + "additionalProperties": false + }, + "PendingIntervalEventKind": { + "description": "Enum encompassing all possible interval events.", + "oneOf": [ + { + "description": "Request to update cost parameters of given mixnode.", + "type": "object", + "required": [ + "change_mix_cost_params" + ], + "properties": { + "change_mix_cost_params": { + "type": "object", + "required": [ + "mix_id", + "new_costs" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode that will have its cost parameters updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "new_costs": { + "description": "The new updated cost function of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to update the underlying rewarding parameters used by the system", + "type": "object", + "required": [ + "update_rewarding_params" + ], + "properties": { + "update_rewarding_params": { + "type": "object", + "required": [ + "update" + ], + "properties": { + "update": { + "description": "The detailed specification of the update.", + "allOf": [ + { + "$ref": "#/definitions/IntervalRewardingParamsUpdate" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to change the next interval configuration.", + "type": "object", + "required": [ + "update_interval_config" + ], + "properties": { + "update_interval_config": { + "type": "object", + "required": [ + "epoch_duration_secs", + "epochs_in_interval" + ], + "properties": { + "epoch_duration_secs": { + "description": "The new epoch duration.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "epochs_in_interval": { + "description": "The new number of epochs in intervals.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_pending_interval_events": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingIntervalEventsResponse", + "description": "Response containing all currently pending interval events that will be resolved once the current interval finishes.", + "type": "object", + "required": [ + "events", + "seconds_until_executable" + ], + "properties": { + "events": { + "description": "The currently pending events.", + "type": "array", + "items": { + "$ref": "#/definitions/PendingIntervalEvent" + } + }, + "seconds_until_executable": { + "description": "Amount of seconds until the events would be eligible to be resolved. It's equivalent to the time until the current interval finishes.", + "type": "integer", + "format": "int64" + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "IntervalRewardingParamsUpdate": { + "description": "Specification on how the rewarding params should be updated.", + "type": "object", + "properties": { + "active_set_work_factor": { + "description": "Defines the new value of the active set work factor.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "interval_pool_emission": { + "description": "Defines the new value of the interval pool emission rate.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + }, + "reward_pool": { + "description": "Defines the new value of the reward pool.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "rewarded_set_size": { + "description": "Defines the new size of the rewarded set.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "staking_supply": { + "description": "Defines the new value of the staking supply.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "staking_supply_scale_factor": { + "description": "Defines the new value of the staking supply scale factor.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + }, + "sybil_resistance_percent": { + "description": "Defines the new value of the sybil resistance percent.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "PendingIntervalEvent": { + "description": "A request made at some point in the current interval that's going to get resolved once the interval rolls over.", + "type": "object", + "required": [ + "event", + "id" + ], + "properties": { + "event": { + "description": "The underlying event details, containing its type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingIntervalEventData" + } + ] + }, + "id": { + "description": "The unique id associated with the event.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "PendingIntervalEventData": { + "description": "Details of a particular pending interval event.", + "type": "object", + "required": [ + "created_at", + "kind" + ], + "properties": { + "created_at": { + "description": "The block height at which the request has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "kind": { + "description": "The underlying event data, containing its concrete type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingIntervalEventKind" + } + ] + } + }, + "additionalProperties": false + }, + "PendingIntervalEventKind": { + "description": "Enum encompassing all possible interval events.", + "oneOf": [ + { + "description": "Request to update cost parameters of given mixnode.", + "type": "object", + "required": [ + "change_mix_cost_params" + ], + "properties": { + "change_mix_cost_params": { + "type": "object", + "required": [ + "mix_id", + "new_costs" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode that will have its cost parameters updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "new_costs": { + "description": "The new updated cost function of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to update the underlying rewarding parameters used by the system", + "type": "object", + "required": [ + "update_rewarding_params" + ], + "properties": { + "update_rewarding_params": { + "type": "object", + "required": [ + "update" + ], + "properties": { + "update": { + "description": "The detailed specification of the update.", + "allOf": [ + { + "$ref": "#/definitions/IntervalRewardingParamsUpdate" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to change the next interval configuration.", + "type": "object", + "required": [ + "update_interval_config" + ], + "properties": { + "update_interval_config": { + "type": "object", + "required": [ + "epoch_duration_secs", + "epochs_in_interval" + ], + "properties": { + "epoch_duration_secs": { + "description": "The new epoch duration.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "epochs_in_interval": { + "description": "The new number of epochs in intervals.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_pending_mix_node_operator_reward": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingRewardResponse", + "description": "Response containing information about accrued rewards.", + "type": "object", + "required": [ + "mixnode_still_fully_bonded" + ], + "properties": { + "amount_earned": { + "description": "The amount of tokens that could be claimed.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "amount_earned_detailed": { + "description": "The full pending rewards. Note that it's nearly identical to `amount_earned`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "amount_staked": { + "description": "The amount of tokens initially staked.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "mixnode_still_fully_bonded": { + "description": "The associated mixnode is still fully bonded, meaning it is neither unbonded nor in the process of unbonding that would have finished at the epoch transition.", + "type": "boolean" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_pending_operator_reward": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingRewardResponse", + "description": "Response containing information about accrued rewards.", + "type": "object", + "required": [ + "mixnode_still_fully_bonded" + ], + "properties": { + "amount_earned": { + "description": "The amount of tokens that could be claimed.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "amount_earned_detailed": { + "description": "The full pending rewards. Note that it's nearly identical to `amount_earned`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "amount_staked": { + "description": "The amount of tokens initially staked.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "mixnode_still_fully_bonded": { + "description": "The associated mixnode is still fully bonded, meaning it is neither unbonded nor in the process of unbonding that would have finished at the epoch transition.", + "type": "boolean" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_rewarded_set": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedRewardedSetResponse", + "description": "Response containing paged list of all mixnodes in the rewarded set.", + "type": "object", + "required": [ + "nodes" + ], + "properties": { + "nodes": { + "description": "Nodes in the current rewarded set.", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "$ref": "#/definitions/RewardedSetNodeStatus" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "RewardedSetNodeStatus": { + "description": "Current state of given node in the rewarded set.", + "oneOf": [ + { + "description": "Node that is currently active, i.e. is expected to be used by clients for mixing packets.", + "type": "string", + "enum": [ + "active" + ] + }, + { + "description": "Node that is currently in standby, i.e. it's present in the rewarded set but is not active.", + "type": "string", + "enum": [ + "standby" + ] + } + ] + } + } + }, + "get_rewarding_params": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "RewardingParams", + "description": "Parameters used for reward calculation.", + "type": "object", + "required": [ + "active_set_size", + "interval", + "rewarded_set_size" + ], + "properties": { + "active_set_size": { + "description": "The expected number of mixnodes in the active set.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "interval": { + "description": "Parameters that should remain unchanged throughout an interval.", + "allOf": [ + { + "$ref": "#/definitions/IntervalRewardParams" + } + ] + }, + "rewarded_set_size": { + "description": "The expected number of mixnodes in the rewarded set (i.e. active + standby).", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "IntervalRewardParams": { + "description": "Parameters required by the mix-mining reward distribution that do not change during an interval.", + "type": "object", + "required": [ + "active_set_work_factor", + "epoch_reward_budget", + "interval_pool_emission", + "reward_pool", + "stake_saturation_point", + "staking_supply", + "staking_supply_scale_factor", + "sybil_resistance" + ], + "properties": { + "active_set_work_factor": { + "description": "Current active set work factor. It is not really expected to be changing very often. As a matter of fact, unless there's a very specific reason, it should remain constant.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "epoch_reward_budget": { + "description": "Current value of the computed reward budget per epoch, per node. It is expected to be constant throughout the interval.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "interval_pool_emission": { + "description": "Current maximum interval pool emission. Assuming all nodes in the rewarded set are fully saturated and have 100% performance, this % of the reward pool would get distributed in rewards to all operators and its delegators. It is not really expected to be changing very often. As a matter of fact, unless there's a very specific reason, it should remain constant.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + }, + "reward_pool": { + "description": "Current value of the rewarding pool. It is expected to be constant throughout the interval.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "stake_saturation_point": { + "description": "Current value of the stake saturation point. It is expected to be constant throughout the interval.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "staking_supply": { + "description": "Current value of the staking supply. It is expected to be constant throughout the interval.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "staking_supply_scale_factor": { + "description": "Defines the percentage of stake needed to reach saturation for all of the nodes in the rewarded set. Also known as `beta`.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + }, + "sybil_resistance": { + "description": "Current value of the sybil resistance percent (`alpha`). It is not really expected to be changing very often. As a matter of fact, unless there's a very specific reason, it should remain constant.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + } + }, + "get_rewarding_validator_address": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "String", + "type": "string" + }, + "get_signing_nonce": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "uint32", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "get_stake_saturation": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "StakeSaturationResponse", + "description": "Response containing the current state of the stake saturation of a mixnode with the provided id.", + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "current_saturation": { + "description": "The current stake saturation of this node that is indirectly used in reward calculation formulas. Note that it can't be larger than 1.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "mix_id": { + "description": "Id of the requested mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "uncapped_saturation": { + "description": "The current, absolute, stake saturation of this node. Note that as the name suggests it can be larger than 1. However, anything beyond that value has no effect on the total node reward.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + } + } + }, + "get_state": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractState", + "description": "The current state of the mixnet contract.", + "type": "object", + "required": [ + "owner", + "params", + "rewarding_denom", + "rewarding_validator_address", + "vesting_contract_address" + ], + "properties": { + "owner": { + "description": "Address of the contract owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "params": { + "description": "Contract parameters that could be adjusted in a transaction the contract admin.", + "allOf": [ + { + "$ref": "#/definitions/ContractStateParams" + } + ] + }, + "rewarding_denom": { + "description": "The expected denom used for rewarding (and realistically any other operation). Default: `unym`", + "type": "string" + }, + "rewarding_validator_address": { + "description": "Address of \"rewarding validator\" (nym-api) that's allowed to send any rewarding-related transactions.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "vesting_contract_address": { + "description": "Address of the vesting contract to which the mixnet contract would be sending all track-related messages.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "ContractStateParams": { + "description": "Contract parameters that could be adjusted in a transaction by the contract admin.", + "type": "object", + "required": [ + "minimum_gateway_pledge", + "minimum_mixnode_pledge" + ], + "properties": { + "minimum_gateway_pledge": { + "description": "Minimum amount a gateway must pledge to get into the system.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "minimum_mixnode_delegation": { + "description": "Minimum amount a delegator must stake in orders for his delegation to get accepted.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "minimum_mixnode_pledge": { + "description": "Minimum amount a mixnode must pledge to get into the system.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_state_params": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractStateParams", + "description": "Contract parameters that could be adjusted in a transaction by the contract admin.", + "type": "object", + "required": [ + "minimum_gateway_pledge", + "minimum_mixnode_pledge" + ], + "properties": { + "minimum_gateway_pledge": { + "description": "Minimum amount a gateway must pledge to get into the system.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "minimum_mixnode_delegation": { + "description": "Minimum amount a delegator must stake in orders for his delegation to get accepted.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "minimum_mixnode_pledge": { + "description": "Minimum amount a mixnode must pledge to get into the system.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_unbonded_mix_node_information": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "UnbondedMixnodeResponse", + "description": "Response containing basic information of an unbonded mixnode with the provided id.", + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "Id of the requested mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unbonded_info": { + "description": "If there existed a mixnode with the provided id, this field contains its basic information.", + "anyOf": [ + { + "$ref": "#/definitions/UnbondedMixnode" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "UnbondedMixnode": { + "description": "Basic information of a node that used to be part of the mix network but has already unbonded.", + "type": "object", + "required": [ + "identity_key", + "owner", + "unbonding_height" + ], + "properties": { + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "unbonding_height": { + "description": "Block height at which this mixnode has unbonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "get_unbonded_mix_nodes": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedUnbondedMixnodesResponse", + "description": "Response containing paged list of all mixnodes that have ever unbonded.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "The past ids of unbonded mixnodes alongside their basic information such as the owner or the identity key.", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "$ref": "#/definitions/UnbondedMixnode" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "UnbondedMixnode": { + "description": "Basic information of a node that used to be part of the mix network but has already unbonded.", + "type": "object", + "required": [ + "identity_key", + "owner", + "unbonding_height" + ], + "properties": { + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "unbonding_height": { + "description": "Block height at which this mixnode has unbonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "get_unbonded_mix_nodes_by_identity_key": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedUnbondedMixnodesResponse", + "description": "Response containing paged list of all mixnodes that have ever unbonded.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "The past ids of unbonded mixnodes alongside their basic information such as the owner or the identity key.", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "$ref": "#/definitions/UnbondedMixnode" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "UnbondedMixnode": { + "description": "Basic information of a node that used to be part of the mix network but has already unbonded.", + "type": "object", + "required": [ + "identity_key", + "owner", + "unbonding_height" + ], + "properties": { + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "unbonding_height": { + "description": "Block height at which this mixnode has unbonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "get_unbonded_mix_nodes_by_owner": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedUnbondedMixnodesResponse", + "description": "Response containing paged list of all mixnodes that have ever unbonded.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "The past ids of unbonded mixnodes alongside their basic information such as the owner or the identity key.", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "$ref": "#/definitions/UnbondedMixnode" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "UnbondedMixnode": { + "description": "Basic information of a node that used to be part of the mix network but has already unbonded.", + "type": "object", + "required": [ + "identity_key", + "owner", + "unbonding_height" + ], + "properties": { + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "unbonding_height": { + "description": "Block height at which this mixnode has unbonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + } + } +} diff --git a/contracts/mixnet/schema/execute_msg.json b/contracts/mixnet/schema/raw/execute.json similarity index 73% rename from contracts/mixnet/schema/execute_msg.json rename to contracts/mixnet/schema/raw/execute.json index b50d74a447..c4f1817fc9 100644 --- a/contracts/mixnet/schema/execute_msg.json +++ b/contracts/mixnet/schema/raw/execute.json @@ -23,7 +23,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -38,17 +39,14 @@ "create_family": { "type": "object", "required": [ - "label", - "owner_signature" + "label" ], "properties": { "label": { "type": "string" - }, - "owner_signature": { - "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -64,16 +62,17 @@ "type": "object", "required": [ "family_head", - "signature" + "join_permit" ], "properties": { "family_head": { - "type": "string" + "$ref": "#/definitions/FamilyHead" }, - "signature": { - "type": "string" + "join_permit": { + "$ref": "#/definitions/MessageSignature" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -87,17 +86,14 @@ "leave_family": { "type": "object", "required": [ - "family_head", - "signature" + "family_head" ], "properties": { "family_head": { - "type": "string" - }, - "signature": { - "type": "string" + "$ref": "#/definitions/FamilyHead" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -111,17 +107,14 @@ "kick_family_member": { "type": "object", "required": [ - "member", - "signature" + "member" ], "properties": { "member": { "type": "string" - }, - "signature": { - "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -136,8 +129,7 @@ "type": "object", "required": [ "label", - "owner_address", - "owner_signature" + "owner_address" ], "properties": { "label": { @@ -145,17 +137,15 @@ }, "owner_address": { "type": "string" - }, - "owner_signature": { - "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { - "description": "Family head needs to sign the joining node IdentityKey", + "description": "Family head needs to sign the joining node IdentityKey, MixNode needs to provide its signature proving that it wants to join the family", "type": "object", "required": [ "join_family_on_behalf" @@ -165,20 +155,21 @@ "type": "object", "required": [ "family_head", - "member_address", - "signature" + "join_permit", + "member_address" ], "properties": { "family_head": { - "type": "string" + "$ref": "#/definitions/FamilyHead" + }, + "join_permit": { + "$ref": "#/definitions/MessageSignature" }, "member_address": { "type": "string" - }, - "signature": { - "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -193,20 +184,17 @@ "type": "object", "required": [ "family_head", - "member_address", - "signature" + "member_address" ], "properties": { "family_head": { - "type": "string" + "$ref": "#/definitions/FamilyHead" }, "member_address": { "type": "string" - }, - "signature": { - "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -221,8 +209,7 @@ "type": "object", "required": [ "head_address", - "member", - "signature" + "member" ], "properties": { "head_address": { @@ -230,11 +217,9 @@ }, "member": { "type": "string" - }, - "signature": { - "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -254,7 +239,8 @@ "address": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -274,7 +260,8 @@ "updated_parameters": { "$ref": "#/definitions/ContractStateParams" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -300,7 +287,8 @@ "force_immediately": { "type": "boolean" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -324,7 +312,8 @@ "updated_params": { "$ref": "#/definitions/IntervalRewardingParamsUpdate" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -356,7 +345,21 @@ "force_immediately": { "type": "boolean" } - } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "begin_epoch_transition" + ], + "properties": { + "begin_epoch_transition": { + "type": "object", + "additionalProperties": false } }, "additionalProperties": false @@ -385,7 +388,8 @@ "$ref": "#/definitions/LayerAssignment" } } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -407,7 +411,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -433,9 +438,10 @@ "$ref": "#/definitions/MixNode" }, "owner_signature": { - "type": "string" + "$ref": "#/definitions/MessageSignature" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -465,9 +471,10 @@ "type": "string" }, "owner_signature": { - "type": "string" + "$ref": "#/definitions/MessageSignature" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -479,7 +486,8 @@ ], "properties": { "pledge_more": { - "type": "object" + "type": "object", + "additionalProperties": false } }, "additionalProperties": false @@ -499,7 +507,54 @@ "owner": { "type": "string" } - } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "decrease_pledge" + ], + "properties": { + "decrease_pledge": { + "type": "object", + "required": [ + "decrease_by" + ], + "properties": { + "decrease_by": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "decrease_pledge_on_behalf" + ], + "properties": { + "decrease_pledge_on_behalf": { + "type": "object", + "required": [ + "decrease_by", + "owner" + ], + "properties": { + "decrease_by": { + "$ref": "#/definitions/Coin" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -511,7 +566,8 @@ ], "properties": { "unbond_mixnode": { - "type": "object" + "type": "object", + "additionalProperties": false } }, "additionalProperties": false @@ -531,7 +587,8 @@ "owner": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -551,7 +608,8 @@ "new_costs": { "$ref": "#/definitions/MixNodeCostParams" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -575,7 +633,8 @@ "owner": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -595,7 +654,8 @@ "new_config": { "$ref": "#/definitions/MixNodeConfigUpdate" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -619,7 +679,8 @@ "owner": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -641,9 +702,10 @@ "$ref": "#/definitions/Gateway" }, "owner_signature": { - "type": "string" + "$ref": "#/definitions/MessageSignature" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -669,9 +731,10 @@ "type": "string" }, "owner_signature": { - "type": "string" + "$ref": "#/definitions/MessageSignature" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -683,7 +746,8 @@ ], "properties": { "unbond_gateway": { - "type": "object" + "type": "object", + "additionalProperties": false } }, "additionalProperties": false @@ -703,7 +767,54 @@ "owner": { "type": "string" } - } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_gateway_config" + ], + "properties": { + "update_gateway_config": { + "type": "object", + "required": [ + "new_config" + ], + "properties": { + "new_config": { + "$ref": "#/definitions/GatewayConfigUpdate" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_gateway_config_on_behalf" + ], + "properties": { + "update_gateway_config_on_behalf": { + "type": "object", + "required": [ + "new_config", + "owner" + ], + "properties": { + "new_config": { + "$ref": "#/definitions/GatewayConfigUpdate" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -725,7 +836,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -751,7 +863,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -773,7 +886,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -799,7 +913,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -825,7 +940,8 @@ "performance": { "$ref": "#/definitions/Percent" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -837,7 +953,8 @@ ], "properties": { "withdraw_operator_reward": { - "type": "object" + "type": "object", + "additionalProperties": false } }, "additionalProperties": false @@ -857,7 +974,8 @@ "owner": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -879,7 +997,8 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -905,7 +1024,8 @@ "owner": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -928,6 +1048,7 @@ } }, "ContractStateParams": { + "description": "Contract parameters that could be adjusted in a transaction by the contract admin.", "type": "object", "required": [ "minimum_gateway_pledge", @@ -961,13 +1082,19 @@ } ] } - } + }, + "additionalProperties": false }, "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", "type": "string" }, + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + }, "Gateway": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", "type": "object", "required": [ "clients_port", @@ -980,17 +1107,58 @@ ], "properties": { "clients_port": { + "description": "Port used by this gateway for listening for client requests.", "type": "integer", "format": "uint16", "minimum": 0.0 }, "host": { + "description": "Network address of this gateway, for example 1.1.1.1 or foo.gateway.com", "type": "string" }, "identity_key": { "description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients", "type": "string" }, + "location": { + "description": "The physical, self-reported, location of this gateway.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this gateway for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "version": { + "description": "The self-reported semver version of this gateway.", + "type": "string" + } + }, + "additionalProperties": false + }, + "GatewayConfigUpdate": { + "type": "object", + "required": [ + "clients_port", + "host", + "location", + "mix_port", + "version" + ], + "properties": { + "clients_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "type": "string" + }, "location": { "type": "string" }, @@ -999,18 +1167,18 @@ "format": "uint16", "minimum": 0.0 }, - "sphinx_key": { - "type": "string" - }, "version": { "type": "string" } - } + }, + "additionalProperties": false }, "IntervalRewardingParamsUpdate": { + "description": "Specification on how the rewarding params should be updated.", "type": "object", "properties": { "active_set_work_factor": { + "description": "Defines the new value of the active set work factor.", "anyOf": [ { "$ref": "#/definitions/Decimal" @@ -1021,6 +1189,7 @@ ] }, "interval_pool_emission": { + "description": "Defines the new value of the interval pool emission rate.", "anyOf": [ { "$ref": "#/definitions/Percent" @@ -1031,6 +1200,7 @@ ] }, "reward_pool": { + "description": "Defines the new value of the reward pool.", "anyOf": [ { "$ref": "#/definitions/Decimal" @@ -1041,6 +1211,7 @@ ] }, "rewarded_set_size": { + "description": "Defines the new size of the rewarded set.", "type": [ "integer", "null" @@ -1049,6 +1220,7 @@ "minimum": 0.0 }, "staking_supply": { + "description": "Defines the new value of the staking supply.", "anyOf": [ { "$ref": "#/definitions/Decimal" @@ -1059,6 +1231,7 @@ ] }, "staking_supply_scale_factor": { + "description": "Defines the new value of the staking supply scale factor.", "anyOf": [ { "$ref": "#/definitions/Percent" @@ -1069,6 +1242,7 @@ ] }, "sybil_resistance_percent": { + "description": "Defines the new value of the sybil resistance percent.", "anyOf": [ { "$ref": "#/definitions/Percent" @@ -1078,7 +1252,8 @@ } ] } - } + }, + "additionalProperties": false }, "Layer": { "type": "string", @@ -1089,6 +1264,7 @@ ] }, "LayerAssignment": { + "description": "Specifies layer assignment for the given mixnode.", "type": "object", "required": [ "layer", @@ -1096,16 +1272,32 @@ ], "properties": { "layer": { - "$ref": "#/definitions/Layer" + "description": "The layer to which it's going to be assigned", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] }, "mix_id": { + "description": "The id of the mixnode.", "type": "integer", "format": "uint32", "minimum": 0.0 } + }, + "additionalProperties": false + }, + "MessageSignature": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 } }, "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", "type": "object", "required": [ "host", @@ -1118,10 +1310,11 @@ ], "properties": { "host": { - "description": "Network address of this mixnode, for example 1.1.1.1:1234 or foo.mixnode.com", + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", "type": "string" }, "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", "type": "integer", "format": "uint16", "minimum": 0.0 @@ -1131,6 +1324,7 @@ "type": "string" }, "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", "type": "integer", "format": "uint16", "minimum": 0.0 @@ -1140,14 +1334,17 @@ "type": "string" }, "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", "type": "integer", "format": "uint16", "minimum": 0.0 }, "version": { + "description": "The self-reported semver version of this mixnode.", "type": "string" } - } + }, + "additionalProperties": false }, "MixNodeConfigUpdate": { "type": "object", @@ -1180,9 +1377,11 @@ "version": { "type": "string" } - } + }, + "additionalProperties": false }, "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", "type": "object", "required": [ "interval_operating_cost", @@ -1198,9 +1397,15 @@ ] }, "profit_margin_percent": { - "$ref": "#/definitions/Percent" + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] } - } + }, + "additionalProperties": false }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", diff --git a/contracts/mixnet/schema/instantiate_msg.json b/contracts/mixnet/schema/raw/instantiate.json similarity index 97% rename from contracts/mixnet/schema/instantiate_msg.json rename to contracts/mixnet/schema/raw/instantiate.json index 6d7c0ea33a..60cf202f08 100644 --- a/contracts/mixnet/schema/instantiate_msg.json +++ b/contracts/mixnet/schema/raw/instantiate.json @@ -32,6 +32,7 @@ "type": "string" } }, + "additionalProperties": false, "definitions": { "Decimal": { "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", @@ -97,7 +98,8 @@ "sybil_resistance": { "$ref": "#/definitions/Percent" } - } + }, + "additionalProperties": false }, "Percent": { "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", diff --git a/contracts/mixnet/schema/migrate_msg.json b/contracts/mixnet/schema/raw/migrate.json similarity index 85% rename from contracts/mixnet/schema/migrate_msg.json rename to contracts/mixnet/schema/raw/migrate.json index d1975efa64..e9962aaf92 100644 --- a/contracts/mixnet/schema/migrate_msg.json +++ b/contracts/mixnet/schema/raw/migrate.json @@ -9,5 +9,6 @@ "null" ] } - } + }, + "additionalProperties": false } diff --git a/contracts/mixnet/schema/query_msg.json b/contracts/mixnet/schema/raw/query.json similarity index 56% rename from contracts/mixnet/schema/query_msg.json rename to contracts/mixnet/schema/raw/query.json index a8dde27e0a..f4738a91fe 100644 --- a/contracts/mixnet/schema/query_msg.json +++ b/contracts/mixnet/schema/raw/query.json @@ -3,6 +3,7 @@ "title": "QueryMsg", "oneOf": [ { + "description": "Gets the list of families registered in this contract.", "type": "object", "required": [ "get_all_families_paged" @@ -12,6 +13,7 @@ "type": "object", "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -20,17 +22,20 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "string", "null" ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the list of all family members registered in this contract.", "type": "object", "required": [ "get_all_members_paged" @@ -40,6 +45,7 @@ "type": "object", "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -48,17 +54,20 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "string", "null" ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Attempts to lookup family information given the family head.", "type": "object", "required": [ "get_family_by_head" @@ -73,12 +82,14 @@ "head": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Attempts to lookup family information given the family label.", "type": "object", "required": [ "get_family_by_label" @@ -93,12 +104,14 @@ "label": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Attempts to retrieve family members given the family head.", "type": "object", "required": [ "get_family_members_by_head" @@ -113,12 +126,14 @@ "head": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Attempts to retrieve family members given the family label.", "type": "object", "required": [ "get_family_members_by_label" @@ -133,84 +148,126 @@ "label": { "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets build information of this contract, such as the commit hash used for the build or rustc version.", "type": "object", "required": [ "get_contract_version" ], "properties": { "get_contract_version": { - "type": "object" + "type": "object", + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.", + "type": "object", + "required": [ + "get_cw2_contract_version" + ], + "properties": { + "get_cw2_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the address of the validator that's allowed to send rewarding transactions and transition the epoch.", "type": "object", "required": [ "get_rewarding_validator_address" ], "properties": { "get_rewarding_validator_address": { - "type": "object" + "type": "object", + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the contract parameters that could be adjusted in a transaction by the contract admin.", "type": "object", "required": [ "get_state_params" ], "properties": { "get_state_params": { - "type": "object" + "type": "object", + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the current state of the contract.", "type": "object", "required": [ "get_state" ], "properties": { "get_state": { - "type": "object" + "type": "object", + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the current parameters used for reward calculation.", "type": "object", "required": [ "get_rewarding_params" ], "properties": { "get_rewarding_params": { - "type": "object" + "type": "object", + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the status of the current rewarding epoch.", + "type": "object", + "required": [ + "get_epoch_status" + ], + "properties": { + "get_epoch_status": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Get the details of the current rewarding interval.", "type": "object", "required": [ "get_current_interval_details" ], "properties": { "get_current_interval_details": { - "type": "object" + "type": "object", + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the current list of mixnodes in the rewarded set.", "type": "object", "required": [ "get_rewarded_set" @@ -220,6 +277,7 @@ "type": "object", "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -228,6 +286,7 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "integer", "null" @@ -235,12 +294,14 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the basic list of all currently bonded mixnodes.", "type": "object", "required": [ "get_mix_node_bonds" @@ -250,6 +311,7 @@ "type": "object", "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -258,6 +320,7 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "integer", "null" @@ -265,12 +328,14 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the detailed list of all currently bonded mixnodes.", "type": "object", "required": [ "get_mix_nodes_detailed" @@ -280,6 +345,7 @@ "type": "object", "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -288,6 +354,7 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "integer", "null" @@ -295,12 +362,14 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the basic list of all unbonded mixnodes.", "type": "object", "required": [ "get_unbonded_mix_nodes" @@ -310,6 +379,7 @@ "type": "object", "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -318,6 +388,7 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "integer", "null" @@ -325,12 +396,14 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the basic list of all unbonded mixnodes that belonged to a particular owner.", "type": "object", "required": [ "get_unbonded_mix_nodes_by_owner" @@ -343,6 +416,7 @@ ], "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -351,9 +425,11 @@ "minimum": 0.0 }, "owner": { + "description": "The address of the owner of the the mixnodes used for the query.", "type": "string" }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "integer", "null" @@ -361,12 +437,14 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the basic list of all unbonded mixnodes that used the particular identity key.", "type": "object", "required": [ "get_unbonded_mix_nodes_by_identity_key" @@ -379,9 +457,11 @@ ], "properties": { "identity_key": { + "description": "The identity key (base58-encoded ed25519 public key) of the mixnode used for the query.", "type": "string" }, "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -390,6 +470,7 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "integer", "null" @@ -397,12 +478,14 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the detailed mixnode information belonging to the particular owner.", "type": "object", "required": [ "get_owned_mixnode" @@ -415,14 +498,17 @@ ], "properties": { "address": { + "description": "Address of the mixnode owner to use for the query.", "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the detailed mixnode information of a node with the provided id.", "type": "object", "required": [ "get_mixnode_details" @@ -435,16 +521,19 @@ ], "properties": { "mix_id": { + "description": "Id of the node to query.", "type": "integer", "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the rewarding information of a mixnode with the provided id.", "type": "object", "required": [ "get_mixnode_rewarding_details" @@ -457,16 +546,19 @@ ], "properties": { "mix_id": { + "description": "Id of the node to query.", "type": "integer", "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the stake saturation of a mixnode with the provided id.", "type": "object", "required": [ "get_stake_saturation" @@ -479,16 +571,19 @@ ], "properties": { "mix_id": { + "description": "Id of the node to query.", "type": "integer", "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the basic information of an unbonded mixnode with the provided id.", "type": "object", "required": [ "get_unbonded_mix_node_information" @@ -501,16 +596,19 @@ ], "properties": { "mix_id": { + "description": "Id of the node to query.", "type": "integer", "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the detailed mixnode information of a node given its current identity key.", "type": "object", "required": [ "get_bonded_mixnode_details_by_identity" @@ -523,26 +621,31 @@ ], "properties": { "mix_identity": { + "description": "The identity key (base58-encoded ed25519 public key) of the mixnode used for the query.", "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the current layer configuration of the mix network.", "type": "object", "required": [ "get_layer_distribution" ], "properties": { "get_layer_distribution": { - "type": "object" + "type": "object", + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the basic list of all currently bonded gateways.", "type": "object", "required": [ "get_gateways" @@ -552,6 +655,7 @@ "type": "object", "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -560,17 +664,20 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "string", "null" ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the gateway details of a node given its identity key.", "type": "object", "required": [ "get_gateway_bond" @@ -583,14 +690,17 @@ ], "properties": { "identity": { + "description": "The identity key (base58-encoded ed25519 public key) of the gateway used for the query.", "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the detailed gateway information belonging to the particular owner.", "type": "object", "required": [ "get_owned_gateway" @@ -603,14 +713,17 @@ ], "properties": { "address": { + "description": "Address of the gateway owner to use for the query.", "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets all delegations associated with particular mixnode", "type": "object", "required": [ "get_mixnode_delegations" @@ -623,6 +736,7 @@ ], "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -631,22 +745,26 @@ "minimum": 0.0 }, "mix_id": { + "description": "Id of the node to query.", "type": "integer", "format": "uint32", "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "string", "null" ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets all delegations associated with particular delegator", "type": "object", "required": [ "get_delegator_delegations" @@ -659,9 +777,11 @@ ], "properties": { "delegator": { + "description": "The address of the owner of the delegations.", "type": "string" }, "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -670,6 +790,7 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "array", "null" @@ -687,12 +808,14 @@ "maxItems": 2, "minItems": 2 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets delegation information associated with particular mixnode - delegator pair", "type": "object", "required": [ "get_delegation_details" @@ -706,25 +829,30 @@ ], "properties": { "delegator": { + "description": "The address of the owner of the delegation.", "type": "string" }, "mix_id": { + "description": "Id of the node to query.", "type": "integer", "format": "uint32", "minimum": 0.0 }, "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", "type": [ "string", "null" ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets all delegations in the system", "type": "object", "required": [ "get_all_delegations" @@ -734,6 +862,7 @@ "type": "object", "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -742,6 +871,7 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "array", "null" @@ -759,12 +889,14 @@ "maxItems": 2, "minItems": 2 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the reward amount accrued by the node operator that has not yet been claimed.", "type": "object", "required": [ "get_pending_operator_reward" @@ -777,14 +909,17 @@ ], "properties": { "address": { + "description": "Address of the operator to use for the query.", "type": "string" } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the reward amount accrued by the particular mixnode that has not yet been claimed.", "type": "object", "required": [ "get_pending_mix_node_operator_reward" @@ -797,16 +932,19 @@ ], "properties": { "mix_id": { + "description": "Id of the node to query.", "type": "integer", "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the reward amount accrued by the particular delegator that has not yet been claimed.", "type": "object", "required": [ "get_pending_delegator_reward" @@ -820,25 +958,30 @@ ], "properties": { "address": { + "description": "Address of the delegator to use for the query.", "type": "string" }, "mix_id": { + "description": "Id of the node to query.", "type": "integer", "format": "uint32", "minimum": 0.0 }, "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", "type": [ "string", "null" ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Given the provided node performance, attempt to estimate the operator reward for the current epoch.", "type": "object", "required": [ "get_estimated_current_epoch_operator_reward" @@ -852,19 +995,27 @@ ], "properties": { "estimated_performance": { - "$ref": "#/definitions/Percent" + "description": "The estimated performance for the current epoch of the given node.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] }, "mix_id": { + "description": "Id of the node to query.", "type": "integer", "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Given the provided node performance, attempt to estimate the delegator reward for the current epoch.", "type": "object", "required": [ "get_estimated_current_epoch_delegator_reward" @@ -879,28 +1030,38 @@ ], "properties": { "address": { + "description": "Address of the delegator to use for the query.", "type": "string" }, "estimated_performance": { - "$ref": "#/definitions/Percent" + "description": "The estimated performance for the current epoch of the given node.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] }, "mix_id": { + "description": "Id of the node to query.", "type": "integer", "format": "uint32", "minimum": 0.0 }, "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", "type": [ "string", "null" ] } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the list of all currently pending epoch events that will be resolved once the current epoch finishes.", "type": "object", "required": [ "get_pending_epoch_events" @@ -910,6 +1071,7 @@ "type": "object", "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -918,6 +1080,7 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "integer", "null" @@ -925,12 +1088,14 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false } }, "additionalProperties": false }, { + "description": "Gets the list of all currently pending interval events that will be resolved once the current interval finishes.", "type": "object", "required": [ "get_pending_interval_events" @@ -940,6 +1105,7 @@ "type": "object", "properties": { "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", "type": [ "integer", "null" @@ -948,6 +1114,7 @@ "minimum": 0.0 }, "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", "type": [ "integer", "null" @@ -955,7 +1122,95 @@ "format": "uint32", "minimum": 0.0 } - } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets detailed information about a pending epoch event given its id.", + "type": "object", + "required": [ + "get_pending_epoch_event" + ], + "properties": { + "get_pending_epoch_event": { + "type": "object", + "required": [ + "event_id" + ], + "properties": { + "event_id": { + "description": "The unique id associated with the event.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets detailed information about a pending interval event given its id.", + "type": "object", + "required": [ + "get_pending_interval_event" + ], + "properties": { + "get_pending_interval_event": { + "type": "object", + "required": [ + "event_id" + ], + "properties": { + "event_id": { + "description": "The unique id associated with the event.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the information about the number of currently pending epoch and interval events.", + "type": "object", + "required": [ + "get_number_of_pending_events" + ], + "properties": { + "get_number_of_pending_events": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the signing nonce associated with the particular cosmos address.", + "type": "object", + "required": [ + "get_signing_nonce" + ], + "properties": { + "get_signing_nonce": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Cosmos address used for the query of the signing nonce.", + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/contracts/mixnet/schema/raw/response_to_get_all_delegations.json b/contracts/mixnet/schema/raw/response_to_get_all_delegations.json new file mode 100644 index 0000000000..a919220c46 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_all_delegations.json @@ -0,0 +1,128 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedAllDelegationsResponse", + "description": "Response containing paged list of all delegations currently active.", + "type": "object", + "required": [ + "delegations" + ], + "properties": { + "delegations": { + "description": "Each individual delegation made.", + "type": "array", + "items": { + "$ref": "#/definitions/Delegation" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "array", + "null" + ], + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "string" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Delegation": { + "description": "Information about tokens being delegated towards given mixnode in order to accrue rewards with their work.", + "type": "object", + "required": [ + "amount", + "cumulative_reward_ratio", + "height", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "Original delegation amount. Note that it is never mutated as delegation accumulates rewards.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "cumulative_reward_ratio": { + "description": "Value of the \"unit delegation\" associated with the mixnode at the time of delegation.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "height": { + "description": "Block height where this delegation occurred.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "Id of the MixNode that this delegation was performed against.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of the owner of this delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Proxy address used to delegate the funds on behalf of another address", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_all_families_paged.json b/contracts/mixnet/schema/raw/response_to_get_all_families_paged.json new file mode 100644 index 0000000000..6744e15be6 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_all_families_paged.json @@ -0,0 +1,62 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedFamiliesResponse", + "description": "Response containing paged list of all families registered in the contract.", + "type": "object", + "required": [ + "families" + ], + "properties": { + "families": { + "description": "The families registered in the contract.", + "type": "array", + "items": { + "$ref": "#/definitions/Family" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "Family": { + "description": "A group of mixnodes associated with particular staking entity. When defined all nodes belonging to the same family will be prioritised to be put onto the same layer.", + "type": "object", + "required": [ + "head", + "label" + ], + "properties": { + "head": { + "description": "Owner of this family.", + "allOf": [ + { + "$ref": "#/definitions/FamilyHead" + } + ] + }, + "label": { + "description": "Human readable label for this family.", + "type": "string" + }, + "proxy": { + "description": "Optional proxy (i.e. vesting contract address) used when creating the family.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_all_members_paged.json b/contracts/mixnet/schema/raw/response_to_get_all_members_paged.json new file mode 100644 index 0000000000..69da663f92 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_all_members_paged.json @@ -0,0 +1,42 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedMembersResponse", + "description": "Response containing paged list of all family members (of ALL families) registered in the contract.", + "type": "object", + "required": [ + "members" + ], + "properties": { + "members": { + "description": "The members alongside their family heads.", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/FamilyHead" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_bonded_mixnode_details_by_identity.json b/contracts/mixnet/schema/raw/response_to_get_bonded_mixnode_details_by_identity.json new file mode 100644 index 0000000000..59fc029331 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_bonded_mixnode_details_by_identity.json @@ -0,0 +1,342 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MixnodeDetailsByIdentityResponse", + "description": "Response containing details of a bonded mixnode with the provided identity key.", + "type": "object", + "required": [ + "identity_key" + ], + "properties": { + "identity_key": { + "description": "The identity key (base58-encoded ed25519 public key) of the mixnode.", + "type": "string" + }, + "mixnode_details": { + "description": "If there exists a bonded mixnode with the provided identity key, this field contains its detailed information.", + "anyOf": [ + { + "$ref": "#/definitions/MixNodeDetails" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Layer": { + "type": "string", + "enum": [ + "One", + "Two", + "Three" + ] + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeBond": { + "description": "Basic mixnode information provided by the node operator.", + "type": "object", + "required": [ + "bonding_height", + "is_unbonding", + "layer", + "mix_id", + "mix_node", + "original_pledge", + "owner" + ], + "properties": { + "bonding_height": { + "description": "Block height at which this mixnode has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "is_unbonding": { + "description": "Flag to indicate whether this node is in the process of unbonding, that will conclude upon the epoch finishing.", + "type": "boolean" + }, + "layer": { + "description": "Layer assigned to this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] + }, + "mix_id": { + "description": "Unique id assigned to the bonded mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mix_node": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/MixNode" + } + ] + }, + "original_pledge": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeDetails": { + "description": "Full details associated with given mixnode.", + "type": "object", + "required": [ + "bond_information", + "rewarding_details" + ], + "properties": { + "bond_information": { + "description": "Basic bond information of this mixnode, such as owner address, original pledge, etc.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeBond" + } + ] + }, + "pending_changes": { + "description": "Adjustments to the mixnode that are ought to happen during future epoch transitions.", + "default": { + "pledge_change": null + }, + "allOf": [ + { + "$ref": "#/definitions/PendingMixNodeChanges" + } + ] + }, + "rewarding_details": { + "description": "Details used for computation of rewarding related data.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeRewarding" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeRewarding": { + "type": "object", + "required": [ + "cost_params", + "delegates", + "last_rewarded_epoch", + "operator", + "total_unit_reward", + "unique_delegations", + "unit_delegation" + ], + "properties": { + "cost_params": { + "description": "Information provided by the operator that influence the cost function.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + }, + "delegates": { + "description": "Total delegation and compounded reward earned by all node delegators.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "last_rewarded_epoch": { + "description": "Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt to reward it multiple times in the same epoch.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "operator": { + "description": "Total pledge and compounded reward earned by the node operator.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "total_unit_reward": { + "description": "Cumulative reward earned by the \"unit delegation\" since the block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "unique_delegations": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unit_delegation": { + "description": "Value of the theoretical \"unit delegation\" that has delegated to this mixnode at block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + }, + "additionalProperties": false + }, + "PendingMixNodeChanges": { + "type": "object", + "properties": { + "pledge_change": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_c_w2_contract_version.json b/contracts/mixnet/schema/raw/response_to_get_c_w2_contract_version.json new file mode 100644 index 0000000000..3ded8c84d9 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_c_w2_contract_version.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractVersion", + "type": "object", + "required": [ + "contract", + "version" + ], + "properties": { + "contract": { + "description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing", + "type": "string" + }, + "version": { + "description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/mixnet/schema/raw/response_to_get_contract_version.json b/contracts/mixnet/schema/raw/response_to_get_contract_version.json new file mode 100644 index 0000000000..60ae65f3d1 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_contract_version.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractBuildInformation", + "type": "object", + "required": [ + "build_timestamp", + "build_version", + "commit_branch", + "commit_sha", + "commit_timestamp", + "rustc_version" + ], + "properties": { + "build_timestamp": { + "description": "Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.", + "type": "string" + }, + "build_version": { + "description": "Provides the build version, for example `0.1.0-9-g46f83e1`.", + "type": "string" + }, + "commit_branch": { + "description": "Provides the name of the git branch that was used for the build, for example `master`.", + "type": "string" + }, + "commit_sha": { + "description": "Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`.", + "type": "string" + }, + "commit_timestamp": { + "description": "Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`.", + "type": "string" + }, + "rustc_version": { + "description": "Provides the rustc version that was used for the build, for example `1.52.0-nightly`.", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/mixnet/schema/raw/response_to_get_current_interval_details.json b/contracts/mixnet/schema/raw/response_to_get_current_interval_details.json new file mode 100644 index 0000000000..1a90ceb1eb --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_current_interval_details.json @@ -0,0 +1,97 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CurrentIntervalResponse", + "description": "Information about the current rewarding interval.", + "type": "object", + "required": [ + "current_blocktime", + "interval", + "is_current_epoch_over", + "is_current_interval_over" + ], + "properties": { + "current_blocktime": { + "description": "The current blocktime", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "interval": { + "description": "Detailed information about the underlying interval.", + "allOf": [ + { + "$ref": "#/definitions/Interval" + } + ] + }, + "is_current_epoch_over": { + "description": "Flag indicating whether the current epoch is over and it should be advanced.", + "type": "boolean" + }, + "is_current_interval_over": { + "description": "Flag indicating whether the current interval is over and it should be advanced.", + "type": "boolean" + } + }, + "additionalProperties": false, + "definitions": { + "Duration": { + "type": "object", + "required": [ + "nanos", + "secs" + ], + "properties": { + "nanos": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "secs": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "Interval": { + "type": "object", + "required": [ + "current_epoch_id", + "current_epoch_start", + "epoch_length", + "epochs_in_interval", + "id", + "total_elapsed_epochs" + ], + "properties": { + "current_epoch_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "current_epoch_start": { + "type": "string" + }, + "epoch_length": { + "$ref": "#/definitions/Duration" + }, + "epochs_in_interval": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "total_elapsed_epochs": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_delegation_details.json b/contracts/mixnet/schema/raw/response_to_get_delegation_details.json new file mode 100644 index 0000000000..8371493b30 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_delegation_details.json @@ -0,0 +1,117 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MixNodeDelegationResponse", + "description": "Response containing delegation details.", + "type": "object", + "required": [ + "mixnode_still_bonded" + ], + "properties": { + "delegation": { + "description": "If the delegation exists, this field contains its detailed information.", + "anyOf": [ + { + "$ref": "#/definitions/Delegation" + }, + { + "type": "null" + } + ] + }, + "mixnode_still_bonded": { + "description": "Flag indicating whether the node towards which the delegation was made is still bonded in the network.", + "type": "boolean" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Delegation": { + "description": "Information about tokens being delegated towards given mixnode in order to accrue rewards with their work.", + "type": "object", + "required": [ + "amount", + "cumulative_reward_ratio", + "height", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "Original delegation amount. Note that it is never mutated as delegation accumulates rewards.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "cumulative_reward_ratio": { + "description": "Value of the \"unit delegation\" associated with the mixnode at the time of delegation.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "height": { + "description": "Block height where this delegation occurred.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "Id of the MixNode that this delegation was performed against.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of the owner of this delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Proxy address used to delegate the funds on behalf of another address", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_delegator_delegations.json b/contracts/mixnet/schema/raw/response_to_get_delegator_delegations.json new file mode 100644 index 0000000000..b6e7e7a6ee --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_delegator_delegations.json @@ -0,0 +1,128 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedDelegatorDelegationsResponse", + "description": "Response containing paged list of all delegations made by the particular address.", + "type": "object", + "required": [ + "delegations" + ], + "properties": { + "delegations": { + "description": "Each individual delegation made.", + "type": "array", + "items": { + "$ref": "#/definitions/Delegation" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "array", + "null" + ], + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "string" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Delegation": { + "description": "Information about tokens being delegated towards given mixnode in order to accrue rewards with their work.", + "type": "object", + "required": [ + "amount", + "cumulative_reward_ratio", + "height", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "Original delegation amount. Note that it is never mutated as delegation accumulates rewards.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "cumulative_reward_ratio": { + "description": "Value of the \"unit delegation\" associated with the mixnode at the time of delegation.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "height": { + "description": "Block height where this delegation occurred.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "Id of the MixNode that this delegation was performed against.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of the owner of this delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Proxy address used to delegate the funds on behalf of another address", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_epoch_status.json b/contracts/mixnet/schema/raw/response_to_get_epoch_status.json new file mode 100644 index 0000000000..1a85e633bf --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_epoch_status.json @@ -0,0 +1,93 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "EpochStatus", + "description": "The status of the current rewarding epoch.", + "type": "object", + "required": [ + "being_advanced_by", + "state" + ], + "properties": { + "being_advanced_by": { + "description": "Specifies either, which validator is currently performing progression into the following epoch (if the epoch is currently being progressed), or which validator was responsible for progressing into the current epoch (if the epoch is currently in progress)", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "state": { + "description": "The concrete state of the epoch.", + "allOf": [ + { + "$ref": "#/definitions/EpochState" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "EpochState": { + "description": "The state of the current rewarding epoch.", + "oneOf": [ + { + "description": "Represents the state of an epoch that's in progress (well, duh.) All actions are allowed to be issued.", + "type": "string", + "enum": [ + "in_progress" + ] + }, + { + "description": "Represents the state of an epoch when the rewarding entity has been decided on, and the mixnodes are in the process of being rewarded for their work in this epoch.", + "type": "object", + "required": [ + "rewarding" + ], + "properties": { + "rewarding": { + "type": "object", + "required": [ + "final_node_id", + "last_rewarded" + ], + "properties": { + "final_node_id": { + "description": "The id of the last node that's going to be rewarded before progressing into the next state.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "last_rewarded": { + "description": "The id of the last node that has already received its rewards.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Represents the state of an epoch when all mixnodes have already been rewarded for their work in this epoch and all issued actions should now get resolved before being allowed to advance into the next epoch.", + "type": "string", + "enum": [ + "reconciling_events" + ] + }, + { + "description": "Represents the state of an epoch when all mixnodes have already been rewarded for their work in this epoch, all issued actions got resolved and the epoch should now be advanced whilst assigning new rewarded set.", + "type": "string", + "enum": [ + "advancing_epoch" + ] + } + ] + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_delegator_reward.json b/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_delegator_reward.json new file mode 100644 index 0000000000..94ca8beb78 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_delegator_reward.json @@ -0,0 +1,89 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "EstimatedCurrentEpochRewardResponse", + "description": "Response containing estimation of node rewards for the current epoch.", + "type": "object", + "properties": { + "current_stake_value": { + "description": "The current stake value given all past rewarding and compounding since the original staking was performed.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "current_stake_value_detailed_amount": { + "description": "The current stake value. Note that it's nearly identical to `current_stake_value`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "detailed_estimation_amount": { + "description": "The full reward estimation. Note that it's nearly identical to `estimation`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "estimation": { + "description": "The reward estimation for the current epoch, i.e. the amount of tokens that could be claimable after the epoch finishes and the state of the network does not change.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "original_stake": { + "description": "The amount of tokens initially staked.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_operator_reward.json b/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_operator_reward.json new file mode 100644 index 0000000000..94ca8beb78 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_estimated_current_epoch_operator_reward.json @@ -0,0 +1,89 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "EstimatedCurrentEpochRewardResponse", + "description": "Response containing estimation of node rewards for the current epoch.", + "type": "object", + "properties": { + "current_stake_value": { + "description": "The current stake value given all past rewarding and compounding since the original staking was performed.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "current_stake_value_detailed_amount": { + "description": "The current stake value. Note that it's nearly identical to `current_stake_value`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "detailed_estimation_amount": { + "description": "The full reward estimation. Note that it's nearly identical to `estimation`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "estimation": { + "description": "The reward estimation for the current epoch, i.e. the amount of tokens that could be claimable after the epoch finishes and the state of the network does not change.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "original_stake": { + "description": "The amount of tokens initially staked.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_family_by_head.json b/contracts/mixnet/schema/raw/response_to_get_family_by_head.json new file mode 100644 index 0000000000..d7bc69b639 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_family_by_head.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FamilyByHeadResponse", + "description": "Response containing family information.", + "type": "object", + "required": [ + "head" + ], + "properties": { + "family": { + "description": "If applicable, the family associated with the provided head.", + "anyOf": [ + { + "$ref": "#/definitions/Family" + }, + { + "type": "null" + } + ] + }, + "head": { + "description": "The family head used for the query.", + "allOf": [ + { + "$ref": "#/definitions/FamilyHead" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Family": { + "description": "A group of mixnodes associated with particular staking entity. When defined all nodes belonging to the same family will be prioritised to be put onto the same layer.", + "type": "object", + "required": [ + "head", + "label" + ], + "properties": { + "head": { + "description": "Owner of this family.", + "allOf": [ + { + "$ref": "#/definitions/FamilyHead" + } + ] + }, + "label": { + "description": "Human readable label for this family.", + "type": "string" + }, + "proxy": { + "description": "Optional proxy (i.e. vesting contract address) used when creating the family.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_family_by_label.json b/contracts/mixnet/schema/raw/response_to_get_family_by_label.json new file mode 100644 index 0000000000..c007bd05dc --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_family_by_label.json @@ -0,0 +1,63 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FamilyByLabelResponse", + "description": "Response containing family information.", + "type": "object", + "required": [ + "label" + ], + "properties": { + "family": { + "description": "If applicable, the family associated with the provided label.", + "anyOf": [ + { + "$ref": "#/definitions/Family" + }, + { + "type": "null" + } + ] + }, + "label": { + "description": "The family label used for the query.", + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "Family": { + "description": "A group of mixnodes associated with particular staking entity. When defined all nodes belonging to the same family will be prioritised to be put onto the same layer.", + "type": "object", + "required": [ + "head", + "label" + ], + "properties": { + "head": { + "description": "Owner of this family.", + "allOf": [ + { + "$ref": "#/definitions/FamilyHead" + } + ] + }, + "label": { + "description": "Human readable label for this family.", + "type": "string" + }, + "proxy": { + "description": "Optional proxy (i.e. vesting contract address) used when creating the family.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_family_members_by_head.json b/contracts/mixnet/schema/raw/response_to_get_family_members_by_head.json new file mode 100644 index 0000000000..7975599604 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_family_members_by_head.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FamilyMembersByHeadResponse", + "description": "Response containing family members information.", + "type": "object", + "required": [ + "head", + "members" + ], + "properties": { + "head": { + "description": "The family head used for the query.", + "allOf": [ + { + "$ref": "#/definitions/FamilyHead" + } + ] + }, + "members": { + "description": "All members belonging to the specified family.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "definitions": { + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_family_members_by_label.json b/contracts/mixnet/schema/raw/response_to_get_family_members_by_label.json new file mode 100644 index 0000000000..a76bb07f7d --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_family_members_by_label.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FamilyMembersByLabelResponse", + "description": "Response containing family members information.", + "type": "object", + "required": [ + "label", + "members" + ], + "properties": { + "label": { + "description": "The family label used for the query.", + "type": "string" + }, + "members": { + "description": "All members belonging to the specified family.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false +} diff --git a/contracts/mixnet/schema/raw/response_to_get_gateway_bond.json b/contracts/mixnet/schema/raw/response_to_get_gateway_bond.json new file mode 100644 index 0000000000..dd7f99b4a4 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_gateway_bond.json @@ -0,0 +1,154 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GatewayBondResponse", + "description": "Response containing details of a gateway with the provided identity key.", + "type": "object", + "required": [ + "identity" + ], + "properties": { + "gateway": { + "description": "If there exists a gateway with the provided identity key, this field contains its details.", + "anyOf": [ + { + "$ref": "#/definitions/GatewayBond" + }, + { + "type": "null" + } + ] + }, + "identity": { + "description": "The identity key (base58-encoded ed25519 public key) of the gateway.", + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Gateway": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "clients_port", + "host", + "identity_key", + "location", + "mix_port", + "sphinx_key", + "version" + ], + "properties": { + "clients_port": { + "description": "Port used by this gateway for listening for client requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "description": "Network address of this gateway, for example 1.1.1.1 or foo.gateway.com", + "type": "string" + }, + "identity_key": { + "description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients", + "type": "string" + }, + "location": { + "description": "The physical, self-reported, location of this gateway.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this gateway for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "version": { + "description": "The self-reported semver version of this gateway.", + "type": "string" + } + }, + "additionalProperties": false + }, + "GatewayBond": { + "description": "Basic gateway information provided by the node operator.", + "type": "object", + "required": [ + "block_height", + "gateway", + "owner", + "pledge_amount" + ], + "properties": { + "block_height": { + "description": "Block height at which this gateway has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "gateway": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/Gateway" + } + ] + }, + "owner": { + "description": "Address of the owner of this gateway.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "pledge_amount": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "proxy": { + "description": "Entity who bonded this gateway on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_gateways.json b/contracts/mixnet/schema/raw/response_to_get_gateways.json new file mode 100644 index 0000000000..0ec50135b6 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_gateways.json @@ -0,0 +1,160 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedGatewayResponse", + "description": "Response containing paged list of all gateway bonds in the contract.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "The gateway bond information present in the contract.", + "type": "array", + "items": { + "$ref": "#/definitions/GatewayBond" + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Gateway": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "clients_port", + "host", + "identity_key", + "location", + "mix_port", + "sphinx_key", + "version" + ], + "properties": { + "clients_port": { + "description": "Port used by this gateway for listening for client requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "description": "Network address of this gateway, for example 1.1.1.1 or foo.gateway.com", + "type": "string" + }, + "identity_key": { + "description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients", + "type": "string" + }, + "location": { + "description": "The physical, self-reported, location of this gateway.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this gateway for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "version": { + "description": "The self-reported semver version of this gateway.", + "type": "string" + } + }, + "additionalProperties": false + }, + "GatewayBond": { + "description": "Basic gateway information provided by the node operator.", + "type": "object", + "required": [ + "block_height", + "gateway", + "owner", + "pledge_amount" + ], + "properties": { + "block_height": { + "description": "Block height at which this gateway has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "gateway": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/Gateway" + } + ] + }, + "owner": { + "description": "Address of the owner of this gateway.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "pledge_amount": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "proxy": { + "description": "Entity who bonded this gateway on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_layer_distribution.json b/contracts/mixnet/schema/raw/response_to_get_layer_distribution.json new file mode 100644 index 0000000000..0ed27f33cb --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_layer_distribution.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LayerDistribution", + "description": "The current layer distribution of the mix network.", + "type": "object", + "required": [ + "layer1", + "layer2", + "layer3" + ], + "properties": { + "layer1": { + "description": "Number of nodes on the first layer.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "layer2": { + "description": "Number of nodes on the second layer.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "layer3": { + "description": "Number of nodes on the third layer.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false +} diff --git a/contracts/mixnet/schema/raw/response_to_get_mix_node_bonds.json b/contracts/mixnet/schema/raw/response_to_get_mix_node_bonds.json new file mode 100644 index 0000000000..61bdf6b3f1 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_mix_node_bonds.json @@ -0,0 +1,193 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedMixnodeBondsResponse", + "description": "Response containing paged list of all mixnode bonds in the contract.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "The mixnode bond information present in the contract.", + "type": "array", + "items": { + "$ref": "#/definitions/MixNodeBond" + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Layer": { + "type": "string", + "enum": [ + "One", + "Two", + "Three" + ] + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeBond": { + "description": "Basic mixnode information provided by the node operator.", + "type": "object", + "required": [ + "bonding_height", + "is_unbonding", + "layer", + "mix_id", + "mix_node", + "original_pledge", + "owner" + ], + "properties": { + "bonding_height": { + "description": "Block height at which this mixnode has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "is_unbonding": { + "description": "Flag to indicate whether this node is in the process of unbonding, that will conclude upon the epoch finishing.", + "type": "boolean" + }, + "layer": { + "description": "Layer assigned to this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] + }, + "mix_id": { + "description": "Unique id assigned to the bonded mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mix_node": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/MixNode" + } + ] + }, + "original_pledge": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_mix_nodes_detailed.json b/contracts/mixnet/schema/raw/response_to_get_mix_nodes_detailed.json new file mode 100644 index 0000000000..bc95bb0839 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_mix_nodes_detailed.json @@ -0,0 +1,350 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedMixnodesDetailsResponse", + "description": "Response containing paged list of all mixnode details in the contract.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "All mixnode details stored in the contract. Apart from the basic bond information it also contains details required for all future reward calculation as well as any pending changes requested by the operator.", + "type": "array", + "items": { + "$ref": "#/definitions/MixNodeDetails" + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Layer": { + "type": "string", + "enum": [ + "One", + "Two", + "Three" + ] + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeBond": { + "description": "Basic mixnode information provided by the node operator.", + "type": "object", + "required": [ + "bonding_height", + "is_unbonding", + "layer", + "mix_id", + "mix_node", + "original_pledge", + "owner" + ], + "properties": { + "bonding_height": { + "description": "Block height at which this mixnode has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "is_unbonding": { + "description": "Flag to indicate whether this node is in the process of unbonding, that will conclude upon the epoch finishing.", + "type": "boolean" + }, + "layer": { + "description": "Layer assigned to this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] + }, + "mix_id": { + "description": "Unique id assigned to the bonded mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mix_node": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/MixNode" + } + ] + }, + "original_pledge": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeDetails": { + "description": "Full details associated with given mixnode.", + "type": "object", + "required": [ + "bond_information", + "rewarding_details" + ], + "properties": { + "bond_information": { + "description": "Basic bond information of this mixnode, such as owner address, original pledge, etc.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeBond" + } + ] + }, + "pending_changes": { + "description": "Adjustments to the mixnode that are ought to happen during future epoch transitions.", + "default": { + "pledge_change": null + }, + "allOf": [ + { + "$ref": "#/definitions/PendingMixNodeChanges" + } + ] + }, + "rewarding_details": { + "description": "Details used for computation of rewarding related data.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeRewarding" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeRewarding": { + "type": "object", + "required": [ + "cost_params", + "delegates", + "last_rewarded_epoch", + "operator", + "total_unit_reward", + "unique_delegations", + "unit_delegation" + ], + "properties": { + "cost_params": { + "description": "Information provided by the operator that influence the cost function.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + }, + "delegates": { + "description": "Total delegation and compounded reward earned by all node delegators.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "last_rewarded_epoch": { + "description": "Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt to reward it multiple times in the same epoch.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "operator": { + "description": "Total pledge and compounded reward earned by the node operator.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "total_unit_reward": { + "description": "Cumulative reward earned by the \"unit delegation\" since the block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "unique_delegations": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unit_delegation": { + "description": "Value of the theoretical \"unit delegation\" that has delegated to this mixnode at block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + }, + "additionalProperties": false + }, + "PendingMixNodeChanges": { + "type": "object", + "properties": { + "pledge_change": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_mixnode_delegations.json b/contracts/mixnet/schema/raw/response_to_get_mixnode_delegations.json new file mode 100644 index 0000000000..82d8c56b90 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_mixnode_delegations.json @@ -0,0 +1,116 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedMixNodeDelegationsResponse", + "description": "Response containing paged list of all delegations made towards particular mixnode.", + "type": "object", + "required": [ + "delegations" + ], + "properties": { + "delegations": { + "description": "Each individual delegation made.", + "type": "array", + "items": { + "$ref": "#/definitions/Delegation" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Delegation": { + "description": "Information about tokens being delegated towards given mixnode in order to accrue rewards with their work.", + "type": "object", + "required": [ + "amount", + "cumulative_reward_ratio", + "height", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "Original delegation amount. Note that it is never mutated as delegation accumulates rewards.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "cumulative_reward_ratio": { + "description": "Value of the \"unit delegation\" associated with the mixnode at the time of delegation.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "height": { + "description": "Block height where this delegation occurred.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "Id of the MixNode that this delegation was performed against.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of the owner of this delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Proxy address used to delegate the funds on behalf of another address", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_mixnode_details.json b/contracts/mixnet/schema/raw/response_to_get_mixnode_details.json new file mode 100644 index 0000000000..4d7bd42da4 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_mixnode_details.json @@ -0,0 +1,344 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MixnodeDetailsResponse", + "description": "Response containing details of a mixnode with the provided id.", + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "Id of the requested mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mixnode_details": { + "description": "If there exists a mixnode with the provided id, this field contains its detailed information.", + "anyOf": [ + { + "$ref": "#/definitions/MixNodeDetails" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Layer": { + "type": "string", + "enum": [ + "One", + "Two", + "Three" + ] + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeBond": { + "description": "Basic mixnode information provided by the node operator.", + "type": "object", + "required": [ + "bonding_height", + "is_unbonding", + "layer", + "mix_id", + "mix_node", + "original_pledge", + "owner" + ], + "properties": { + "bonding_height": { + "description": "Block height at which this mixnode has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "is_unbonding": { + "description": "Flag to indicate whether this node is in the process of unbonding, that will conclude upon the epoch finishing.", + "type": "boolean" + }, + "layer": { + "description": "Layer assigned to this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] + }, + "mix_id": { + "description": "Unique id assigned to the bonded mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mix_node": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/MixNode" + } + ] + }, + "original_pledge": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeDetails": { + "description": "Full details associated with given mixnode.", + "type": "object", + "required": [ + "bond_information", + "rewarding_details" + ], + "properties": { + "bond_information": { + "description": "Basic bond information of this mixnode, such as owner address, original pledge, etc.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeBond" + } + ] + }, + "pending_changes": { + "description": "Adjustments to the mixnode that are ought to happen during future epoch transitions.", + "default": { + "pledge_change": null + }, + "allOf": [ + { + "$ref": "#/definitions/PendingMixNodeChanges" + } + ] + }, + "rewarding_details": { + "description": "Details used for computation of rewarding related data.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeRewarding" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeRewarding": { + "type": "object", + "required": [ + "cost_params", + "delegates", + "last_rewarded_epoch", + "operator", + "total_unit_reward", + "unique_delegations", + "unit_delegation" + ], + "properties": { + "cost_params": { + "description": "Information provided by the operator that influence the cost function.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + }, + "delegates": { + "description": "Total delegation and compounded reward earned by all node delegators.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "last_rewarded_epoch": { + "description": "Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt to reward it multiple times in the same epoch.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "operator": { + "description": "Total pledge and compounded reward earned by the node operator.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "total_unit_reward": { + "description": "Cumulative reward earned by the \"unit delegation\" since the block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "unique_delegations": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unit_delegation": { + "description": "Value of the theoretical \"unit delegation\" that has delegated to this mixnode at block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + }, + "additionalProperties": false + }, + "PendingMixNodeChanges": { + "type": "object", + "properties": { + "pledge_change": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_mixnode_rewarding_details.json b/contracts/mixnet/schema/raw/response_to_get_mixnode_rewarding_details.json new file mode 100644 index 0000000000..9b30dc4a75 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_mixnode_rewarding_details.json @@ -0,0 +1,155 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MixnodeRewardingDetailsResponse", + "description": "Response containing rewarding information of a mixnode with the provided id.", + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "Id of the requested mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "rewarding_details": { + "description": "If there exists a mixnode with the provided id, this field contains its rewarding information.", + "anyOf": [ + { + "$ref": "#/definitions/MixNodeRewarding" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeRewarding": { + "type": "object", + "required": [ + "cost_params", + "delegates", + "last_rewarded_epoch", + "operator", + "total_unit_reward", + "unique_delegations", + "unit_delegation" + ], + "properties": { + "cost_params": { + "description": "Information provided by the operator that influence the cost function.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + }, + "delegates": { + "description": "Total delegation and compounded reward earned by all node delegators.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "last_rewarded_epoch": { + "description": "Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt to reward it multiple times in the same epoch.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "operator": { + "description": "Total pledge and compounded reward earned by the node operator.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "total_unit_reward": { + "description": "Cumulative reward earned by the \"unit delegation\" since the block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "unique_delegations": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unit_delegation": { + "description": "Value of the theoretical \"unit delegation\" that has delegated to this mixnode at block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_number_of_pending_events.json b/contracts/mixnet/schema/raw/response_to_get_number_of_pending_events.json new file mode 100644 index 0000000000..520dbce29c --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_number_of_pending_events.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "NumberOfPendingEventsResponse", + "description": "Response containing number of currently pending epoch and interval events.", + "type": "object", + "required": [ + "epoch_events", + "interval_events" + ], + "properties": { + "epoch_events": { + "description": "The number of the currently pending epoch events.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "interval_events": { + "description": "The number of the currently pending epoch events.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false +} diff --git a/contracts/mixnet/schema/raw/response_to_get_owned_gateway.json b/contracts/mixnet/schema/raw/response_to_get_owned_gateway.json new file mode 100644 index 0000000000..50f2bbd4ad --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_owned_gateway.json @@ -0,0 +1,158 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GatewayOwnershipResponse", + "description": "Response containing details of a gateway belonging to the particular owner.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Validated address of the gateway owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "gateway": { + "description": "If the provided address owns a gateway, this field contains its details.", + "anyOf": [ + { + "$ref": "#/definitions/GatewayBond" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Gateway": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "clients_port", + "host", + "identity_key", + "location", + "mix_port", + "sphinx_key", + "version" + ], + "properties": { + "clients_port": { + "description": "Port used by this gateway for listening for client requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "description": "Network address of this gateway, for example 1.1.1.1 or foo.gateway.com", + "type": "string" + }, + "identity_key": { + "description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients", + "type": "string" + }, + "location": { + "description": "The physical, self-reported, location of this gateway.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this gateway for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "version": { + "description": "The self-reported semver version of this gateway.", + "type": "string" + } + }, + "additionalProperties": false + }, + "GatewayBond": { + "description": "Basic gateway information provided by the node operator.", + "type": "object", + "required": [ + "block_height", + "gateway", + "owner", + "pledge_amount" + ], + "properties": { + "block_height": { + "description": "Block height at which this gateway has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "gateway": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/Gateway" + } + ] + }, + "owner": { + "description": "Address of the owner of this gateway.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "pledge_amount": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "proxy": { + "description": "Entity who bonded this gateway on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_owned_mixnode.json b/contracts/mixnet/schema/raw/response_to_get_owned_mixnode.json new file mode 100644 index 0000000000..da895c4bf4 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_owned_mixnode.json @@ -0,0 +1,346 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MixOwnershipResponse", + "description": "Response containing details of a mixnode belonging to the particular owner.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Validated address of the mixnode owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "mixnode_details": { + "description": "If the provided address owns a mixnode, this field contains its detailed information.", + "anyOf": [ + { + "$ref": "#/definitions/MixNodeDetails" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Layer": { + "type": "string", + "enum": [ + "One", + "Two", + "Three" + ] + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeBond": { + "description": "Basic mixnode information provided by the node operator.", + "type": "object", + "required": [ + "bonding_height", + "is_unbonding", + "layer", + "mix_id", + "mix_node", + "original_pledge", + "owner" + ], + "properties": { + "bonding_height": { + "description": "Block height at which this mixnode has been bonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "is_unbonding": { + "description": "Flag to indicate whether this node is in the process of unbonding, that will conclude upon the epoch finishing.", + "type": "boolean" + }, + "layer": { + "description": "Layer assigned to this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Layer" + } + ] + }, + "mix_id": { + "description": "Unique id assigned to the bonded mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "mix_node": { + "description": "Information provided by the operator for the purposes of bonding.", + "allOf": [ + { + "$ref": "#/definitions/MixNode" + } + ] + }, + "original_pledge": { + "description": "Original amount pledged by the operator of this node.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeDetails": { + "description": "Full details associated with given mixnode.", + "type": "object", + "required": [ + "bond_information", + "rewarding_details" + ], + "properties": { + "bond_information": { + "description": "Basic bond information of this mixnode, such as owner address, original pledge, etc.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeBond" + } + ] + }, + "pending_changes": { + "description": "Adjustments to the mixnode that are ought to happen during future epoch transitions.", + "default": { + "pledge_change": null + }, + "allOf": [ + { + "$ref": "#/definitions/PendingMixNodeChanges" + } + ] + }, + "rewarding_details": { + "description": "Details used for computation of rewarding related data.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeRewarding" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeRewarding": { + "type": "object", + "required": [ + "cost_params", + "delegates", + "last_rewarded_epoch", + "operator", + "total_unit_reward", + "unique_delegations", + "unit_delegation" + ], + "properties": { + "cost_params": { + "description": "Information provided by the operator that influence the cost function.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + }, + "delegates": { + "description": "Total delegation and compounded reward earned by all node delegators.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "last_rewarded_epoch": { + "description": "Marks the epoch when this node was last rewarded so that we wouldn't accidentally attempt to reward it multiple times in the same epoch.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "operator": { + "description": "Total pledge and compounded reward earned by the node operator.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "total_unit_reward": { + "description": "Cumulative reward earned by the \"unit delegation\" since the block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "unique_delegations": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unit_delegation": { + "description": "Value of the theoretical \"unit delegation\" that has delegated to this mixnode at block 0.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + }, + "additionalProperties": false + }, + "PendingMixNodeChanges": { + "type": "object", + "properties": { + "pledge_change": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_delegator_reward.json b/contracts/mixnet/schema/raw/response_to_get_pending_delegator_reward.json new file mode 100644 index 0000000000..e885d5976c --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_pending_delegator_reward.json @@ -0,0 +1,74 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingRewardResponse", + "description": "Response containing information about accrued rewards.", + "type": "object", + "required": [ + "mixnode_still_fully_bonded" + ], + "properties": { + "amount_earned": { + "description": "The amount of tokens that could be claimed.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "amount_earned_detailed": { + "description": "The full pending rewards. Note that it's nearly identical to `amount_earned`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "amount_staked": { + "description": "The amount of tokens initially staked.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "mixnode_still_fully_bonded": { + "description": "The associated mixnode is still fully bonded, meaning it is neither unbonded nor in the process of unbonding that would have finished at the epoch transition.", + "type": "boolean" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_epoch_event.json b/contracts/mixnet/schema/raw/response_to_get_pending_epoch_event.json new file mode 100644 index 0000000000..7588cad3e9 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_pending_epoch_event.json @@ -0,0 +1,298 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingEpochEventResponse", + "type": "object", + "required": [ + "event_id" + ], + "properties": { + "event": { + "anyOf": [ + { + "$ref": "#/definitions/PendingEpochEventData" + }, + { + "type": "null" + } + ] + }, + "event_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "PendingEpochEventData": { + "description": "Details of a particular pending epoch event.", + "type": "object", + "required": [ + "created_at", + "kind" + ], + "properties": { + "created_at": { + "description": "The block height at which the request has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "kind": { + "description": "The underlying event data, containing its concrete type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingEpochEventKind" + } + ] + } + }, + "additionalProperties": false + }, + "PendingEpochEventKind": { + "description": "Enum encompassing all possible epoch events.", + "oneOf": [ + { + "description": "Request to create a delegation towards particular mixnode. Note that if such delegation already exists, it will get updated with the provided token amount.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "The amount of tokens to use for the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode used for the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "The address of the owner of the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to remove delegation from particular mixnode.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "mix_id", + "owner" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode used for the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "The address of the owner of the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to pledge more tokens (by the node operator) towards its node.", + "type": "object", + "required": [ + "pledge_more" + ], + "properties": { + "pledge_more": { + "type": "object", + "required": [ + "amount", + "mix_id" + ], + "properties": { + "amount": { + "description": "The amount of additional tokens to use by the pledge.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode that will have its pledge updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to decrease amount of pledged tokens (by the node operator) from its node.", + "type": "object", + "required": [ + "decrease_pledge" + ], + "properties": { + "decrease_pledge": { + "type": "object", + "required": [ + "decrease_by", + "mix_id" + ], + "properties": { + "decrease_by": { + "description": "The amount of tokens that should be removed from the pledge.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode that will have its pledge updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to unbond a mixnode and completely remove it from the network.", + "type": "object", + "required": [ + "unbond_mixnode" + ], + "properties": { + "unbond_mixnode": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode that will get unbonded.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to update the current size of the active set.", + "type": "object", + "required": [ + "update_active_set_size" + ], + "properties": { + "update_active_set_size": { + "type": "object", + "required": [ + "new_size" + ], + "properties": { + "new_size": { + "description": "The new desired size of the active set.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_epoch_events.json b/contracts/mixnet/schema/raw/response_to_get_pending_epoch_events.json new file mode 100644 index 0000000000..09a4d03787 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_pending_epoch_events.json @@ -0,0 +1,331 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingEpochEventsResponse", + "description": "Response containing all currently pending epoch events that will be resolved once the current epoch finishes.", + "type": "object", + "required": [ + "events", + "seconds_until_executable" + ], + "properties": { + "events": { + "description": "The currently pending events.", + "type": "array", + "items": { + "$ref": "#/definitions/PendingEpochEvent" + } + }, + "seconds_until_executable": { + "description": "Amount of seconds until the events would be eligible to be resolved. It's equivalent to the time until the current epoch finishes.", + "type": "integer", + "format": "int64" + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "PendingEpochEvent": { + "description": "A request made at some point in the current epoch that's going to get resolved once the epoch rolls over.", + "type": "object", + "required": [ + "event", + "id" + ], + "properties": { + "event": { + "description": "The underlying event details, containing its type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingEpochEventData" + } + ] + }, + "id": { + "description": "The unique id associated with the event.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "PendingEpochEventData": { + "description": "Details of a particular pending epoch event.", + "type": "object", + "required": [ + "created_at", + "kind" + ], + "properties": { + "created_at": { + "description": "The block height at which the request has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "kind": { + "description": "The underlying event data, containing its concrete type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingEpochEventKind" + } + ] + } + }, + "additionalProperties": false + }, + "PendingEpochEventKind": { + "description": "Enum encompassing all possible epoch events.", + "oneOf": [ + { + "description": "Request to create a delegation towards particular mixnode. Note that if such delegation already exists, it will get updated with the provided token amount.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "description": "The amount of tokens to use for the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode used for the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "The address of the owner of the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to remove delegation from particular mixnode.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "mix_id", + "owner" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode used for the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "The address of the owner of the delegation.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who made the delegation on behalf of the owner. If present, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to pledge more tokens (by the node operator) towards its node.", + "type": "object", + "required": [ + "pledge_more" + ], + "properties": { + "pledge_more": { + "type": "object", + "required": [ + "amount", + "mix_id" + ], + "properties": { + "amount": { + "description": "The amount of additional tokens to use by the pledge.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode that will have its pledge updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to decrease amount of pledged tokens (by the node operator) from its node.", + "type": "object", + "required": [ + "decrease_pledge" + ], + "properties": { + "decrease_pledge": { + "type": "object", + "required": [ + "decrease_by", + "mix_id" + ], + "properties": { + "decrease_by": { + "description": "The amount of tokens that should be removed from the pledge.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "mix_id": { + "description": "The id of the mixnode that will have its pledge updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to unbond a mixnode and completely remove it from the network.", + "type": "object", + "required": [ + "unbond_mixnode" + ], + "properties": { + "unbond_mixnode": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode that will get unbonded.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to update the current size of the active set.", + "type": "object", + "required": [ + "update_active_set_size" + ], + "properties": { + "update_active_set_size": { + "type": "object", + "required": [ + "new_size" + ], + "properties": { + "new_size": { + "description": "The new desired size of the active set.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_interval_event.json b/contracts/mixnet/schema/raw/response_to_get_pending_interval_event.json new file mode 100644 index 0000000000..a0877027a9 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_pending_interval_event.json @@ -0,0 +1,291 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingIntervalEventResponse", + "type": "object", + "required": [ + "event_id" + ], + "properties": { + "event": { + "anyOf": [ + { + "$ref": "#/definitions/PendingIntervalEventData" + }, + { + "type": "null" + } + ] + }, + "event_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "IntervalRewardingParamsUpdate": { + "description": "Specification on how the rewarding params should be updated.", + "type": "object", + "properties": { + "active_set_work_factor": { + "description": "Defines the new value of the active set work factor.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "interval_pool_emission": { + "description": "Defines the new value of the interval pool emission rate.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + }, + "reward_pool": { + "description": "Defines the new value of the reward pool.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "rewarded_set_size": { + "description": "Defines the new size of the rewarded set.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "staking_supply": { + "description": "Defines the new value of the staking supply.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "staking_supply_scale_factor": { + "description": "Defines the new value of the staking supply scale factor.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + }, + "sybil_resistance_percent": { + "description": "Defines the new value of the sybil resistance percent.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "PendingIntervalEventData": { + "description": "Details of a particular pending interval event.", + "type": "object", + "required": [ + "created_at", + "kind" + ], + "properties": { + "created_at": { + "description": "The block height at which the request has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "kind": { + "description": "The underlying event data, containing its concrete type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingIntervalEventKind" + } + ] + } + }, + "additionalProperties": false + }, + "PendingIntervalEventKind": { + "description": "Enum encompassing all possible interval events.", + "oneOf": [ + { + "description": "Request to update cost parameters of given mixnode.", + "type": "object", + "required": [ + "change_mix_cost_params" + ], + "properties": { + "change_mix_cost_params": { + "type": "object", + "required": [ + "mix_id", + "new_costs" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode that will have its cost parameters updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "new_costs": { + "description": "The new updated cost function of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to update the underlying rewarding parameters used by the system", + "type": "object", + "required": [ + "update_rewarding_params" + ], + "properties": { + "update_rewarding_params": { + "type": "object", + "required": [ + "update" + ], + "properties": { + "update": { + "description": "The detailed specification of the update.", + "allOf": [ + { + "$ref": "#/definitions/IntervalRewardingParamsUpdate" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to change the next interval configuration.", + "type": "object", + "required": [ + "update_interval_config" + ], + "properties": { + "update_interval_config": { + "type": "object", + "required": [ + "epoch_duration_secs", + "epochs_in_interval" + ], + "properties": { + "epoch_duration_secs": { + "description": "The new epoch duration.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "epochs_in_interval": { + "description": "The new number of epochs in intervals.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_interval_events.json b/contracts/mixnet/schema/raw/response_to_get_pending_interval_events.json new file mode 100644 index 0000000000..ccbdc22178 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_pending_interval_events.json @@ -0,0 +1,324 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingIntervalEventsResponse", + "description": "Response containing all currently pending interval events that will be resolved once the current interval finishes.", + "type": "object", + "required": [ + "events", + "seconds_until_executable" + ], + "properties": { + "events": { + "description": "The currently pending events.", + "type": "array", + "items": { + "$ref": "#/definitions/PendingIntervalEvent" + } + }, + "seconds_until_executable": { + "description": "Amount of seconds until the events would be eligible to be resolved. It's equivalent to the time until the current interval finishes.", + "type": "integer", + "format": "int64" + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "IntervalRewardingParamsUpdate": { + "description": "Specification on how the rewarding params should be updated.", + "type": "object", + "properties": { + "active_set_work_factor": { + "description": "Defines the new value of the active set work factor.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "interval_pool_emission": { + "description": "Defines the new value of the interval pool emission rate.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + }, + "reward_pool": { + "description": "Defines the new value of the reward pool.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "rewarded_set_size": { + "description": "Defines the new size of the rewarded set.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "staking_supply": { + "description": "Defines the new value of the staking supply.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "staking_supply_scale_factor": { + "description": "Defines the new value of the staking supply scale factor.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + }, + "sybil_resistance_percent": { + "description": "Defines the new value of the sybil resistance percent.", + "anyOf": [ + { + "$ref": "#/definitions/Percent" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "PendingIntervalEvent": { + "description": "A request made at some point in the current interval that's going to get resolved once the interval rolls over.", + "type": "object", + "required": [ + "event", + "id" + ], + "properties": { + "event": { + "description": "The underlying event details, containing its type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingIntervalEventData" + } + ] + }, + "id": { + "description": "The unique id associated with the event.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "PendingIntervalEventData": { + "description": "Details of a particular pending interval event.", + "type": "object", + "required": [ + "created_at", + "kind" + ], + "properties": { + "created_at": { + "description": "The block height at which the request has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "kind": { + "description": "The underlying event data, containing its concrete type and information on how it should get resolved.", + "allOf": [ + { + "$ref": "#/definitions/PendingIntervalEventKind" + } + ] + } + }, + "additionalProperties": false + }, + "PendingIntervalEventKind": { + "description": "Enum encompassing all possible interval events.", + "oneOf": [ + { + "description": "Request to update cost parameters of given mixnode.", + "type": "object", + "required": [ + "change_mix_cost_params" + ], + "properties": { + "change_mix_cost_params": { + "type": "object", + "required": [ + "mix_id", + "new_costs" + ], + "properties": { + "mix_id": { + "description": "The id of the mixnode that will have its cost parameters updated.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "new_costs": { + "description": "The new updated cost function of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/MixNodeCostParams" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to update the underlying rewarding parameters used by the system", + "type": "object", + "required": [ + "update_rewarding_params" + ], + "properties": { + "update_rewarding_params": { + "type": "object", + "required": [ + "update" + ], + "properties": { + "update": { + "description": "The detailed specification of the update.", + "allOf": [ + { + "$ref": "#/definitions/IntervalRewardingParamsUpdate" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Request to change the next interval configuration.", + "type": "object", + "required": [ + "update_interval_config" + ], + "properties": { + "update_interval_config": { + "type": "object", + "required": [ + "epoch_duration_secs", + "epochs_in_interval" + ], + "properties": { + "epoch_duration_secs": { + "description": "The new epoch duration.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "epochs_in_interval": { + "description": "The new number of epochs in intervals.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_mix_node_operator_reward.json b/contracts/mixnet/schema/raw/response_to_get_pending_mix_node_operator_reward.json new file mode 100644 index 0000000000..e885d5976c --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_pending_mix_node_operator_reward.json @@ -0,0 +1,74 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingRewardResponse", + "description": "Response containing information about accrued rewards.", + "type": "object", + "required": [ + "mixnode_still_fully_bonded" + ], + "properties": { + "amount_earned": { + "description": "The amount of tokens that could be claimed.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "amount_earned_detailed": { + "description": "The full pending rewards. Note that it's nearly identical to `amount_earned`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "amount_staked": { + "description": "The amount of tokens initially staked.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "mixnode_still_fully_bonded": { + "description": "The associated mixnode is still fully bonded, meaning it is neither unbonded nor in the process of unbonding that would have finished at the epoch transition.", + "type": "boolean" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_pending_operator_reward.json b/contracts/mixnet/schema/raw/response_to_get_pending_operator_reward.json new file mode 100644 index 0000000000..e885d5976c --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_pending_operator_reward.json @@ -0,0 +1,74 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PendingRewardResponse", + "description": "Response containing information about accrued rewards.", + "type": "object", + "required": [ + "mixnode_still_fully_bonded" + ], + "properties": { + "amount_earned": { + "description": "The amount of tokens that could be claimed.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "amount_earned_detailed": { + "description": "The full pending rewards. Note that it's nearly identical to `amount_earned`, however, it contains few additional decimal points for more accurate reward calculation.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "amount_staked": { + "description": "The amount of tokens initially staked.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "mixnode_still_fully_bonded": { + "description": "The associated mixnode is still fully bonded, meaning it is neither unbonded nor in the process of unbonding that would have finished at the epoch transition.", + "type": "boolean" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_rewarded_set.json b/contracts/mixnet/schema/raw/response_to_get_rewarded_set.json new file mode 100644 index 0000000000..e623d3c1a8 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_rewarded_set.json @@ -0,0 +1,61 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedRewardedSetResponse", + "description": "Response containing paged list of all mixnodes in the rewarded set.", + "type": "object", + "required": [ + "nodes" + ], + "properties": { + "nodes": { + "description": "Nodes in the current rewarded set.", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "$ref": "#/definitions/RewardedSetNodeStatus" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "RewardedSetNodeStatus": { + "description": "Current state of given node in the rewarded set.", + "oneOf": [ + { + "description": "Node that is currently active, i.e. is expected to be used by clients for mixing packets.", + "type": "string", + "enum": [ + "active" + ] + }, + { + "description": "Node that is currently in standby, i.e. it's present in the rewarded set but is not active.", + "type": "string", + "enum": [ + "standby" + ] + } + ] + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_rewarding_params.json b/contracts/mixnet/schema/raw/response_to_get_rewarding_params.json new file mode 100644 index 0000000000..8840bc31f2 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_rewarding_params.json @@ -0,0 +1,129 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "RewardingParams", + "description": "Parameters used for reward calculation.", + "type": "object", + "required": [ + "active_set_size", + "interval", + "rewarded_set_size" + ], + "properties": { + "active_set_size": { + "description": "The expected number of mixnodes in the active set.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "interval": { + "description": "Parameters that should remain unchanged throughout an interval.", + "allOf": [ + { + "$ref": "#/definitions/IntervalRewardParams" + } + ] + }, + "rewarded_set_size": { + "description": "The expected number of mixnodes in the rewarded set (i.e. active + standby).", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "IntervalRewardParams": { + "description": "Parameters required by the mix-mining reward distribution that do not change during an interval.", + "type": "object", + "required": [ + "active_set_work_factor", + "epoch_reward_budget", + "interval_pool_emission", + "reward_pool", + "stake_saturation_point", + "staking_supply", + "staking_supply_scale_factor", + "sybil_resistance" + ], + "properties": { + "active_set_work_factor": { + "description": "Current active set work factor. It is not really expected to be changing very often. As a matter of fact, unless there's a very specific reason, it should remain constant.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "epoch_reward_budget": { + "description": "Current value of the computed reward budget per epoch, per node. It is expected to be constant throughout the interval.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "interval_pool_emission": { + "description": "Current maximum interval pool emission. Assuming all nodes in the rewarded set are fully saturated and have 100% performance, this % of the reward pool would get distributed in rewards to all operators and its delegators. It is not really expected to be changing very often. As a matter of fact, unless there's a very specific reason, it should remain constant.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + }, + "reward_pool": { + "description": "Current value of the rewarding pool. It is expected to be constant throughout the interval.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "stake_saturation_point": { + "description": "Current value of the stake saturation point. It is expected to be constant throughout the interval.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "staking_supply": { + "description": "Current value of the staking supply. It is expected to be constant throughout the interval.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "staking_supply_scale_factor": { + "description": "Defines the percentage of stake needed to reach saturation for all of the nodes in the rewarded set. Also known as `beta`.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + }, + "sybil_resistance": { + "description": "Current value of the sybil resistance percent (`alpha`). It is not really expected to be changing very often. As a matter of fact, unless there's a very specific reason, it should remain constant.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_rewarding_validator_address.json b/contracts/mixnet/schema/raw/response_to_get_rewarding_validator_address.json new file mode 100644 index 0000000000..f689acebfd --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_rewarding_validator_address.json @@ -0,0 +1,5 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "String", + "type": "string" +} diff --git a/contracts/mixnet/schema/raw/response_to_get_signing_nonce.json b/contracts/mixnet/schema/raw/response_to_get_signing_nonce.json new file mode 100644 index 0000000000..de85c3e56e --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_signing_nonce.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "uint32", + "type": "integer", + "format": "uint32", + "minimum": 0.0 +} diff --git a/contracts/mixnet/schema/raw/response_to_get_stake_saturation.json b/contracts/mixnet/schema/raw/response_to_get_stake_saturation.json new file mode 100644 index 0000000000..4a86c03934 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_stake_saturation.json @@ -0,0 +1,46 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "StakeSaturationResponse", + "description": "Response containing the current state of the stake saturation of a mixnode with the provided id.", + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "current_saturation": { + "description": "The current stake saturation of this node that is indirectly used in reward calculation formulas. Note that it can't be larger than 1.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + }, + "mix_id": { + "description": "Id of the requested mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "uncapped_saturation": { + "description": "The current, absolute, stake saturation of this node. Note that as the name suggests it can be larger than 1. However, anything beyond that value has no effect on the total node reward.", + "anyOf": [ + { + "$ref": "#/definitions/Decimal" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_state.json b/contracts/mixnet/schema/raw/response_to_get_state.json new file mode 100644 index 0000000000..0888e30787 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_state.json @@ -0,0 +1,115 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractState", + "description": "The current state of the mixnet contract.", + "type": "object", + "required": [ + "owner", + "params", + "rewarding_denom", + "rewarding_validator_address", + "vesting_contract_address" + ], + "properties": { + "owner": { + "description": "Address of the contract owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "params": { + "description": "Contract parameters that could be adjusted in a transaction the contract admin.", + "allOf": [ + { + "$ref": "#/definitions/ContractStateParams" + } + ] + }, + "rewarding_denom": { + "description": "The expected denom used for rewarding (and realistically any other operation). Default: `unym`", + "type": "string" + }, + "rewarding_validator_address": { + "description": "Address of \"rewarding validator\" (nym-api) that's allowed to send any rewarding-related transactions.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "vesting_contract_address": { + "description": "Address of the vesting contract to which the mixnet contract would be sending all track-related messages.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "ContractStateParams": { + "description": "Contract parameters that could be adjusted in a transaction by the contract admin.", + "type": "object", + "required": [ + "minimum_gateway_pledge", + "minimum_mixnode_pledge" + ], + "properties": { + "minimum_gateway_pledge": { + "description": "Minimum amount a gateway must pledge to get into the system.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "minimum_mixnode_delegation": { + "description": "Minimum amount a delegator must stake in orders for his delegation to get accepted.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "minimum_mixnode_pledge": { + "description": "Minimum amount a mixnode must pledge to get into the system.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_state_params.json b/contracts/mixnet/schema/raw/response_to_get_state_params.json new file mode 100644 index 0000000000..6e05de3035 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_state_params.json @@ -0,0 +1,61 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractStateParams", + "description": "Contract parameters that could be adjusted in a transaction by the contract admin.", + "type": "object", + "required": [ + "minimum_gateway_pledge", + "minimum_mixnode_pledge" + ], + "properties": { + "minimum_gateway_pledge": { + "description": "Minimum amount a gateway must pledge to get into the system.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "minimum_mixnode_delegation": { + "description": "Minimum amount a delegator must stake in orders for his delegation to get accepted.", + "anyOf": [ + { + "$ref": "#/definitions/Coin" + }, + { + "type": "null" + } + ] + }, + "minimum_mixnode_pledge": { + "description": "Minimum amount a mixnode must pledge to get into the system.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_node_information.json b/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_node_information.json new file mode 100644 index 0000000000..9fa546e39c --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_node_information.json @@ -0,0 +1,76 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "UnbondedMixnodeResponse", + "description": "Response containing basic information of an unbonded mixnode with the provided id.", + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "description": "Id of the requested mixnode.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "unbonded_info": { + "description": "If there existed a mixnode with the provided id, this field contains its basic information.", + "anyOf": [ + { + "$ref": "#/definitions/UnbondedMixnode" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "UnbondedMixnode": { + "description": "Basic information of a node that used to be part of the mix network but has already unbonded.", + "type": "object", + "required": [ + "identity_key", + "owner", + "unbonding_height" + ], + "properties": { + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "unbonding_height": { + "description": "Block height at which this mixnode has unbonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_nodes.json b/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_nodes.json new file mode 100644 index 0000000000..6209bcc677 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_nodes.json @@ -0,0 +1,94 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedUnbondedMixnodesResponse", + "description": "Response containing paged list of all mixnodes that have ever unbonded.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "The past ids of unbonded mixnodes alongside their basic information such as the owner or the identity key.", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "$ref": "#/definitions/UnbondedMixnode" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "UnbondedMixnode": { + "description": "Basic information of a node that used to be part of the mix network but has already unbonded.", + "type": "object", + "required": [ + "identity_key", + "owner", + "unbonding_height" + ], + "properties": { + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "unbonding_height": { + "description": "Block height at which this mixnode has unbonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_nodes_by_identity_key.json b/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_nodes_by_identity_key.json new file mode 100644 index 0000000000..6209bcc677 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_nodes_by_identity_key.json @@ -0,0 +1,94 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedUnbondedMixnodesResponse", + "description": "Response containing paged list of all mixnodes that have ever unbonded.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "The past ids of unbonded mixnodes alongside their basic information such as the owner or the identity key.", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "$ref": "#/definitions/UnbondedMixnode" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "UnbondedMixnode": { + "description": "Basic information of a node that used to be part of the mix network but has already unbonded.", + "type": "object", + "required": [ + "identity_key", + "owner", + "unbonding_height" + ], + "properties": { + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "unbonding_height": { + "description": "Block height at which this mixnode has unbonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_nodes_by_owner.json b/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_nodes_by_owner.json new file mode 100644 index 0000000000..6209bcc677 --- /dev/null +++ b/contracts/mixnet/schema/raw/response_to_get_unbonded_mix_nodes_by_owner.json @@ -0,0 +1,94 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedUnbondedMixnodesResponse", + "description": "Response containing paged list of all mixnodes that have ever unbonded.", + "type": "object", + "required": [ + "nodes", + "per_page" + ], + "properties": { + "nodes": { + "description": "The past ids of unbonded mixnodes alongside their basic information such as the owner or the identity key.", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "$ref": "#/definitions/UnbondedMixnode" + } + ], + "maxItems": 2, + "minItems": 2 + } + }, + "per_page": { + "description": "Maximum number of entries that could be included in a response. `per_page <= nodes.len()`", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "UnbondedMixnode": { + "description": "Basic information of a node that used to be part of the mix network but has already unbonded.", + "type": "object", + "required": [ + "identity_key", + "owner", + "unbonding_height" + ], + "properties": { + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "owner": { + "description": "Address of the owner of this mixnode.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "proxy": { + "description": "Entity who bonded this mixnode on behalf of the owner. If exists, it's most likely the address of the vesting contract.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "unbonding_height": { + "description": "Block height at which this mixnode has unbonded.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/mixnet/src/bin/schema.rs b/contracts/mixnet/src/bin/schema.rs new file mode 100644 index 0000000000..c1e48ba16a --- /dev/null +++ b/contracts/mixnet/src/bin/schema.rs @@ -0,0 +1,14 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::write_api; +use mixnet_contract_common::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + migrate: MigrateMsg, + } +} diff --git a/contracts/mixnet/src/contract.rs b/contracts/mixnet/src/contract.rs index f380b316f2..892f4d2298 100644 --- a/contracts/mixnet/src/contract.rs +++ b/contracts/mixnet/src/contract.rs @@ -404,12 +404,12 @@ pub fn query( to_binary(&crate::mixnet_contract_settings::queries::query_contract_version()) } QueryMsg::GetCW2ContractVersion {} => to_binary(&cw2::get_contract_version(deps.storage)?), - QueryMsg::GetStateParams {} => to_binary( - &crate::mixnet_contract_settings::queries::query_contract_settings_params(deps)?, - ), QueryMsg::GetRewardingValidatorAddress {} => to_binary( &crate::mixnet_contract_settings::queries::query_rewarding_validator_address(deps)?, ), + QueryMsg::GetStateParams {} => to_binary( + &crate::mixnet_contract_settings::queries::query_contract_settings_params(deps)?, + ), QueryMsg::GetState {} => { to_binary(&crate::mixnet_contract_settings::queries::query_contract_state(deps)?) } @@ -594,6 +594,8 @@ pub fn query( QueryMsg::GetNumberOfPendingEvents {} => to_binary( &crate::interval::queries::query_number_of_pending_events(deps)?, ), + + // signing-related QueryMsg::GetSigningNonce { address } => to_binary( &crate::signing::queries::query_current_signing_nonce(deps, address)?, ), diff --git a/contracts/mixnet/src/families/queries.rs b/contracts/mixnet/src/families/queries.rs index dc4799f0b9..96c7f79e23 100644 --- a/contracts/mixnet/src/families/queries.rs +++ b/contracts/mixnet/src/families/queries.rs @@ -1,47 +1,65 @@ // Copyright 2022-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use super::storage::{families, get_family, get_members, MEMBERS}; +use super::storage::{families, get_members, must_get_family, MEMBERS}; use crate::constants::{FAMILIES_DEFAULT_RETRIEVAL_LIMIT, FAMILIES_MAX_RETRIEVAL_LIMIT}; +use crate::families::storage::must_get_family_by_label; use cosmwasm_std::{Order, Storage}; use cw_storage_plus::Bound; -use mixnet_contract_common::families::{Family, FamilyHead}; +use mixnet_contract_common::families::{ + Family, FamilyByHeadResponse, FamilyByLabelResponse, FamilyHead, FamilyMembersByHeadResponse, + PagedFamiliesResponse, PagedMembersResponse, +}; use mixnet_contract_common::{error::MixnetContractError, IdentityKeyRef}; -use mixnet_contract_common::{IdentityKey, PagedFamiliesResponse, PagedMembersResponse}; -use std::collections::HashSet; +use mixnet_contract_common::{FamilyMembersByLabelResponse, IdentityKey}; pub fn get_family_by_label( label: String, storage: &dyn Storage, -) -> Result, MixnetContractError> { - Ok(families().idx.label.item(storage, label)?.map(|o| o.1)) +) -> Result { + let family = families() + .idx + .label + .item(storage, label.clone())? + .map(|o| o.1); + Ok(FamilyByLabelResponse { label, family }) } pub fn get_family_by_head( head: IdentityKeyRef<'_>, storage: &dyn Storage, -) -> Result, MixnetContractError> { - Ok(families().may_load(storage, head.to_string())?) +) -> Result { + let family = families().may_load(storage, head.to_string())?; + Ok(FamilyByHeadResponse { + head: FamilyHead::new(head), + family, + }) } +// TODO: this should be returning a paged response! pub fn get_family_members_by_head( head: IdentityKeyRef<'_>, storage: &dyn Storage, -) -> Result, MixnetContractError> { +) -> Result { let family_head = FamilyHead::new(head); - let family = get_family(&family_head, storage)?; - get_members(&family, storage) + let family = must_get_family(&family_head, storage)?; + let members = get_members(&family, storage)?; + + Ok(FamilyMembersByHeadResponse { + head: family.head().to_owned(), + members, + }) } +// TODO: this should be returning a paged response! pub fn get_family_members_by_label( label: String, storage: &dyn Storage, -) -> Result>, MixnetContractError> { - if let Some(family) = families().idx.label.item(storage, label)?.map(|o| o.1) { - Ok(Some(get_members(&family, storage)?)) - } else { - Ok(None) - } +) -> Result { + let family = must_get_family_by_label(label.clone(), storage)?; + let members = get_members(&family, storage)?; + + Ok(FamilyMembersByLabelResponse { label, members }) } pub fn get_all_families_paged( diff --git a/contracts/mixnet/src/families/storage.rs b/contracts/mixnet/src/families/storage.rs index 1bdd421206..05b9566b7d 100644 --- a/contracts/mixnet/src/families/storage.rs +++ b/contracts/mixnet/src/families/storage.rs @@ -5,12 +5,13 @@ use cosmwasm_std::{Order, Storage}; use cw_storage_plus::{Index, IndexList, IndexedMap, Map, UniqueIndex}; use mixnet_contract_common::families::{Family, FamilyHead}; use mixnet_contract_common::{error::MixnetContractError, IdentityKey, IdentityKeyRef}; -use std::collections::HashSet; use crate::constants::{FAMILIES_INDEX_NAMESPACE, FAMILIES_MAP_NAMESPACE, MEMBERS_MAP_NAMESPACE}; +type FamilyHeadKey = IdentityKey; + pub struct FamilyIndex<'a> { - pub label: UniqueIndex<'a, String, Family>, + pub label: UniqueIndex<'a, FamilyHeadKey, Family>, } impl<'a> IndexList for FamilyIndex<'a> { @@ -21,7 +22,7 @@ impl<'a> IndexList for FamilyIndex<'a> { } // storage access function. -pub fn families<'a>() -> IndexedMap<'a, String, Family, FamilyIndex<'a>> { +pub fn families<'a>() -> IndexedMap<'a, FamilyHeadKey, Family, FamilyIndex<'a>> { let indexes = FamilyIndex { label: UniqueIndex::new(|d| d.label().to_string(), FAMILIES_INDEX_NAMESPACE), }; @@ -30,10 +31,11 @@ pub fn families<'a>() -> IndexedMap<'a, String, Family, FamilyIndex<'a>> { pub const MEMBERS: Map = Map::new(MEMBERS_MAP_NAMESPACE); +// TODO: this introduces an unbounded query. We should redesign it. pub fn get_members( family: &Family, store: &dyn Storage, -) -> Result, MixnetContractError> { +) -> Result, MixnetContractError> { Ok(MEMBERS .range(store, None, None, Order::Ascending) .filter_map(|res| res.ok()) @@ -42,15 +44,29 @@ pub fn get_members( .collect()) } -pub fn get_family(head: &FamilyHead, store: &dyn Storage) -> Result { +pub fn must_get_family( + head: &FamilyHead, + store: &dyn Storage, +) -> Result { let key = head.identity(); - if let Some(family) = families().may_load(store, key.to_string())? { - Ok(family) - } else { - Err(MixnetContractError::FamilyDoesNotExist { + + families() + .may_load(store, key.to_string())? + .ok_or(MixnetContractError::FamilyDoesNotExist { head: head.identity().to_string(), }) - } +} + +pub fn must_get_family_by_label( + label: String, + store: &dyn Storage, +) -> Result { + families() + .idx + .label + .item(store, label.clone())? + .map(|record| record.1) + .ok_or(MixnetContractError::FamilyLabelDoesNotExist { label }) } pub fn save_family(f: &Family, store: &mut dyn Storage) -> Result<(), MixnetContractError> { diff --git a/contracts/mixnet/src/families/transactions.rs b/contracts/mixnet/src/families/transactions.rs index 5a6a9b28e0..912cbc7146 100644 --- a/contracts/mixnet/src/families/transactions.rs +++ b/contracts/mixnet/src/families/transactions.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use super::storage::{ - add_family_member, get_family, is_any_member, is_family_member, remove_family_member, + add_family_member, is_any_member, is_family_member, must_get_family, remove_family_member, save_family, }; use crate::families::queries::get_family_by_label; @@ -48,12 +48,15 @@ fn _try_create_family( let family_head = FamilyHead::new(existing_bond.identity()); // can't overwrite existing family - if get_family(&family_head, deps.storage).is_ok() { + if must_get_family(&family_head, deps.storage).is_ok() { return Err(MixnetContractError::FamilyCanHaveOnlyOne); } // the label must be unique - if get_family_by_label(label.clone(), deps.storage)?.is_some() { + if get_family_by_label(label.clone(), deps.storage)? + .family + .is_some() + { return Err(MixnetContractError::FamilyWithLabelExists(label)); } @@ -118,7 +121,7 @@ fn _try_join_family( join_permit, )?; - let family = get_family(&family_head, deps.storage)?; + let family = must_get_family(&family_head, deps.storage)?; add_family_member(&family, deps.storage, existing_bond.identity())?; @@ -162,7 +165,7 @@ fn _try_leave_family( }); } - let family = get_family(&family_head, deps.storage)?; + let family = must_get_family(&family_head, deps.storage)?; if !is_family_member(deps.storage, &family, existing_bond.identity())? { return Err(MixnetContractError::NotAMember { head: family_head.identity().to_string(), @@ -215,7 +218,7 @@ fn _try_head_kick_member( // get the family details let family_head = FamilyHead::new(head_bond.identity()); - let family = get_family(&family_head, deps.storage)?; + let family = must_get_family(&family_head, deps.storage)?; // make sure the member we're trying to kick is an actual member if !is_family_member(deps.storage, &family, &member)? { @@ -290,7 +293,7 @@ mod test { try_create_family(test.deps_mut(), mock_info(head, &[]), "test".to_string()).unwrap(); let family_head = FamilyHead::new(&head_mixnode.identity_key); - assert!(get_family(&family_head, test.deps().storage).is_ok()); + assert!(must_get_family(&family_head, test.deps().storage).is_ok()); let nope = try_create_family( test.deps_mut(), @@ -306,12 +309,15 @@ mod test { }, } - let family = get_family_by_label("test".to_string(), test.deps().storage).unwrap(); + let family = get_family_by_label("test".to_string(), test.deps().storage) + .unwrap() + .family; assert!(family.is_some()); assert_eq!(family.unwrap().head_identity(), family_head.identity()); let family = get_family_by_head(family_head.identity(), test.deps().storage) .unwrap() + .family .unwrap(); assert_eq!(family.head_identity(), family_head.identity()); @@ -326,7 +332,7 @@ mod test { ) .unwrap(); - let family = get_family(&family_head, test.deps().storage).unwrap(); + let family = must_get_family(&family_head, test.deps().storage).unwrap(); assert!( is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap() @@ -334,7 +340,7 @@ mod test { try_leave_family(test.deps_mut(), mock_info(member, &[]), family_head.clone()).unwrap(); - let family = get_family(&family_head, test.deps().storage).unwrap(); + let family = must_get_family(&family_head, test.deps().storage).unwrap(); assert!( !is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap() ); @@ -350,7 +356,7 @@ mod test { ) .unwrap(); - let family = get_family(&family_head, test.deps().storage).unwrap(); + let family = must_get_family(&family_head, test.deps().storage).unwrap(); assert!( is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap() @@ -363,7 +369,7 @@ mod test { ) .unwrap(); - let family = get_family(&family_head, test.deps().storage).unwrap(); + let family = must_get_family(&family_head, test.deps().storage).unwrap(); assert!( !is_family_member(test.deps().storage, &family, &member_mixnode.identity_key).unwrap() ); @@ -429,7 +435,7 @@ mod test { ); let head_identity = head_keys.public_key().to_base58_string(); - let family_head = FamilyHead::new(&head_identity); + let family_head = FamilyHead::new(head_identity); let res = try_join_family_on_behalf( test.deps_mut(), mock_info(illegal_proxy.as_ref(), &[]), @@ -475,7 +481,7 @@ mod test { ); let head_identity = head_keys.public_key().to_base58_string(); - let family_head = FamilyHead::new(&head_identity); + let family_head = FamilyHead::new(head_identity); try_join_family_on_behalf( test.deps_mut(), mock_info(vesting_contract.as_ref(), &[]), @@ -529,7 +535,7 @@ mod test { ); let head_identity = head_keys.public_key().to_base58_string(); - let family_head = FamilyHead::new(&head_identity); + let family_head = FamilyHead::new(head_identity); try_join_family_on_behalf( test.deps_mut(), diff --git a/contracts/mixnet/src/interval/storage.rs b/contracts/mixnet/src/interval/storage.rs index 0e35f3908d..04f3aa999a 100644 --- a/contracts/mixnet/src/interval/storage.rs +++ b/contracts/mixnet/src/interval/storage.rs @@ -176,7 +176,7 @@ mod tests { use cosmwasm_std::Order; use rand_chacha::rand_core::RngCore; - fn read_entire_set(storage: &mut dyn Storage) -> HashMap { + fn read_entire_set(storage: &dyn Storage) -> HashMap { REWARDED_SET .range(storage, None, None, Order::Ascending) .map(|r| r.unwrap()) diff --git a/contracts/mixnet/src/mixnodes/queries.rs b/contracts/mixnet/src/mixnodes/queries.rs index 5bf845c4a9..5d7b61054d 100644 --- a/contracts/mixnet/src/mixnodes/queries.rs +++ b/contracts/mixnet/src/mixnodes/queries.rs @@ -19,8 +19,8 @@ use mixnet_contract_common::mixnode::{ PagedUnbondedMixnodesResponse, StakeSaturationResponse, UnbondedMixnodeResponse, }; use mixnet_contract_common::{ - IdentityKey, LayerDistribution, MixId, MixOwnershipResponse, MixnodeDetailsResponse, - PagedMixnodeBondsResponse, + IdentityKey, LayerDistribution, MixId, MixOwnershipResponse, MixnodeDetailsByIdentityResponse, + MixnodeDetailsResponse, PagedMixnodeBondsResponse, }; pub fn query_mixnode_bonds_paged( @@ -189,12 +189,16 @@ pub fn query_mixnode_details(deps: Deps<'_>, mix_id: MixId) -> StdResult, - mix_identity: IdentityKey, -) -> StdResult> { - get_mixnode_details_by_identity(deps.storage, mix_identity) + identity_key: IdentityKey, +) -> StdResult { + let mixnode_details = get_mixnode_details_by_identity(deps.storage, identity_key.clone())?; + + Ok(MixnodeDetailsByIdentityResponse { + identity_key, + mixnode_details, + }) } pub fn query_mixnode_rewarding_details( @@ -1110,7 +1114,9 @@ pub(crate) mod tests { let mut test = TestSetup::new(); // no node under this identity - let res = query_mixnode_details_by_identity(test.deps(), "foomp".into()).unwrap(); + let res = query_mixnode_details_by_identity(test.deps(), "foomp".into()) + .unwrap() + .mixnode_details; assert!(res.is_none()); // it exists @@ -1122,7 +1128,9 @@ pub(crate) mod tests { .unwrap(); let mix_identity = expected.bond_information.identity(); - let res = query_mixnode_details_by_identity(test.deps(), mix_identity.into()).unwrap(); + let res = query_mixnode_details_by_identity(test.deps(), mix_identity.into()) + .unwrap() + .mixnode_details; assert_eq!(expected, res.unwrap()); } diff --git a/contracts/mixnet/src/support/tests/mod.rs b/contracts/mixnet/src/support/tests/mod.rs index 6b0e838a1a..851d394bfe 100644 --- a/contracts/mixnet/src/support/tests/mod.rs +++ b/contracts/mixnet/src/support/tests/mod.rs @@ -224,7 +224,7 @@ pub mod test_helpers { let join_permit = self.generate_family_join_permit(head_keys, &member_identity, vesting); - let family_head = FamilyHead::new(&head_identity); + let family_head = FamilyHead::new(head_identity); try_join_family( self.deps_mut(), diff --git a/contracts/multisig/cw3-flex-multisig/.cargo/config b/contracts/multisig/cw3-flex-multisig/.cargo/config index 8d4bc738b1..dbc331ef7a 100644 --- a/contracts/multisig/cw3-flex-multisig/.cargo/config +++ b/contracts/multisig/cw3-flex-multisig/.cargo/config @@ -1,6 +1,6 @@ [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" integration-test = "test --test integration" -schema = "run --example schema" +schema = "run --bin schema --features=schema-gen" \ No newline at end of file diff --git a/contracts/multisig/cw3-flex-multisig/Cargo.toml b/contracts/multisig/cw3-flex-multisig/Cargo.toml index b849828069..bd0e980bde 100644 --- a/contracts/multisig/cw3-flex-multisig/Cargo.toml +++ b/contracts/multisig/cw3-flex-multisig/Cargo.toml @@ -9,6 +9,10 @@ repository = "https://github.com/CosmWasm/cw-plus" homepage = "https://cosmwasm.com" documentation = "https://docs.cosmwasm.com" +[[bin]] +name = "schema" +required-features = ["schema-gen"] + [lib] crate-type = ["cdylib", "rlib"] @@ -16,6 +20,7 @@ crate-type = ["cdylib", "rlib"] backtraces = ["cosmwasm-std/backtraces"] # use library feature to disable all instantiate/execute/query exports library = [] +schema-gen = ["cosmwasm-schema"] [dependencies] cw-utils = { workspace = true } @@ -25,8 +30,8 @@ cw3-fixed-multisig = { workspace = true, features = ["library"] } cw4 = { workspace = true } cw20 = { workspace = true } cw-storage-plus = { workspace = true } +cosmwasm-schema = { workspace = true, optional = true } cosmwasm-std = { workspace = true } -schemars = "0.8.1" nym-group-contract-common = { path = "../../../common/cosmwasm-smart-contracts/group-contract" } nym-multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts/multisig-contract" } diff --git a/contracts/multisig/cw3-flex-multisig/Makefile b/contracts/multisig/cw3-flex-multisig/Makefile new file mode 100644 index 0000000000..f138e2c879 --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/Makefile @@ -0,0 +1,2 @@ +generate-schema: + cargo schema \ No newline at end of file diff --git a/contracts/multisig/cw3-flex-multisig/schema/cw3-flex-multisig.json b/contracts/multisig/cw3-flex-multisig/schema/cw3-flex-multisig.json new file mode 100644 index 0000000000..7f49430331 --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/cw3-flex-multisig.json @@ -0,0 +1,5201 @@ +{ + "contract_name": "cw3-flex-multisig", + "contract_version": "1.0.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "coconut_bandwidth_contract_address", + "coconut_dkg_contract_address", + "group_addr", + "max_voting_period", + "threshold" + ], + "properties": { + "coconut_bandwidth_contract_address": { + "type": "string" + }, + "coconut_dkg_contract_address": { + "type": "string" + }, + "executor": { + "anyOf": [ + { + "$ref": "#/definitions/Executor" + }, + { + "type": "null" + } + ] + }, + "group_addr": { + "type": "string" + }, + "max_voting_period": { + "$ref": "#/definitions/Duration" + }, + "proposal_deposit": { + "description": "The cost of creating a proposal (if any).", + "anyOf": [ + { + "$ref": "#/definitions/UncheckedDepositInfo" + }, + { + "type": "null" + } + ] + }, + "threshold": { + "$ref": "#/definitions/Threshold" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Duration": { + "description": "Duration is a delta of time. You can add it to a BlockInfo or Expiration to move that further in the future. Note that an height-based Duration and a time-based Expiration cannot be combined", + "oneOf": [ + { + "type": "object", + "required": [ + "height" + ], + "properties": { + "height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "Time in seconds", + "type": "object", + "required": [ + "time" + ], + "properties": { + "time": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + }, + "Executor": { + "description": "Defines who is able to execute proposals once passed", + "oneOf": [ + { + "description": "Any member of the voting group, even with 0 points", + "type": "string", + "enum": [ + "member" + ] + }, + { + "description": "Only the given address", + "type": "object", + "required": [ + "only" + ], + "properties": { + "only": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Threshold": { + "description": "This defines the different ways tallies can happen.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation). See also `ThresholdResponse` in the cw3 spec.", + "oneOf": [ + { + "description": "Declares that a fixed weight of Yes votes is needed to pass. See `ThresholdResponse.AbsoluteCount` in the cw3 spec for details.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "weight" + ], + "properties": { + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes in order for a proposal to pass. See `ThresholdResponse.AbsolutePercentage` in the cw3 spec for details.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. See `ThresholdResponse.ThresholdQuorum` in the cw3 spec for details.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "UncheckedDenom": { + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "UncheckedDepositInfo": { + "description": "Information about the deposit required to create a proposal. For use in messages. To validate, transform into `DepositInfo` via `into_checked()`.", + "type": "object", + "required": [ + "amount", + "denom", + "refund_failed_proposals" + ], + "properties": { + "amount": { + "description": "The number tokens required for payment.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "denom": { + "description": "The denom of the deposit payment.", + "allOf": [ + { + "$ref": "#/definitions/UncheckedDenom" + } + ] + }, + "refund_failed_proposals": { + "description": "Should failed proposals have their deposits refunded?", + "type": "boolean" + } + }, + "additionalProperties": false + } + } + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "propose" + ], + "properties": { + "propose": { + "type": "object", + "required": [ + "description", + "msgs", + "title" + ], + "properties": { + "description": { + "type": "string" + }, + "latest": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "msgs": { + "type": "array", + "items": { + "$ref": "#/definitions/CosmosMsg_for_Empty" + } + }, + "title": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "$ref": "#/definitions/Vote" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "proposal_id" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "close" + ], + "properties": { + "close": { + "type": "object", + "required": [ + "proposal_id" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Handles update hook messages from the group contract", + "type": "object", + "required": [ + "member_changed_hook" + ], + "properties": { + "member_changed_hook": { + "$ref": "#/definitions/MemberChangedHookMsg" + } + }, + "additionalProperties": false + } + ], + "definitions": { + "BankMsg": { + "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", + "oneOf": [ + { + "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "send" + ], + "properties": { + "send": { + "type": "object", + "required": [ + "amount", + "to_address" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "to_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CosmosMsg_for_Empty": { + "oneOf": [ + { + "type": "object", + "required": [ + "bank" + ], + "properties": { + "bank": { + "$ref": "#/definitions/BankMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking" + ], + "properties": { + "staking": { + "$ref": "#/definitions/StakingMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution" + ], + "properties": { + "distribution": { + "$ref": "#/definitions/DistributionMsg" + } + }, + "additionalProperties": false + }, + { + "description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)", + "type": "object", + "required": [ + "stargate" + ], + "properties": { + "stargate": { + "type": "object", + "required": [ + "type_url", + "value" + ], + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ibc" + ], + "properties": { + "ibc": { + "$ref": "#/definitions/IbcMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "wasm" + ], + "properties": { + "wasm": { + "$ref": "#/definitions/WasmMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "gov" + ], + "properties": { + "gov": { + "$ref": "#/definitions/GovMsg" + } + }, + "additionalProperties": false + } + ] + }, + "DistributionMsg": { + "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The `withdraw_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "validator" + ], + "properties": { + "validator": { + "description": "The `validator_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "GovMsg": { + "description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```", + "oneOf": [ + { + "description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.", + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See .", + "allOf": [ + { + "$ref": "#/definitions/VoteOption" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcMsg": { + "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", + "oneOf": [ + { + "description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "channel_id", + "timeout", + "to_address" + ], + "properties": { + "amount": { + "description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "channel_id": { + "description": "exisiting channel to send the tokens over", + "type": "string" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + }, + "to_address": { + "description": "address on the remote chain to receive these tokens", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.", + "type": "object", + "required": [ + "send_packet" + ], + "properties": { + "send_packet": { + "type": "object", + "required": [ + "channel_id", + "data", + "timeout" + ], + "properties": { + "channel_id": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Binary" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port", + "type": "object", + "required": [ + "close_channel" + ], + "properties": { + "close_channel": { + "type": "object", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcTimeout": { + "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", + "type": "object", + "properties": { + "block": { + "anyOf": [ + { + "$ref": "#/definitions/IbcTimeoutBlock" + }, + { + "type": "null" + } + ] + }, + "timestamp": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + } + }, + "IbcTimeoutBlock": { + "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", + "type": "object", + "required": [ + "height", + "revision" + ], + "properties": { + "height": { + "description": "block height after which the packet times out. the height within the given revision", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "MemberChangedHookMsg": { + "description": "MemberChangedHookMsg should be de/serialized under `MemberChangedHook()` variant in a ExecuteMsg. This contains a list of all diffs on the given transaction.", + "type": "object", + "required": [ + "diffs" + ], + "properties": { + "diffs": { + "type": "array", + "items": { + "$ref": "#/definitions/MemberDiff" + } + } + }, + "additionalProperties": false + }, + "MemberDiff": { + "description": "MemberDiff shows the old and new states for a given cw4 member They cannot both be None. old = None, new = Some -> Insert old = Some, new = Some -> Update old = Some, new = None -> Delete", + "type": "object", + "required": [ + "key" + ], + "properties": { + "key": { + "type": "string" + }, + "new": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "old": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "StakingMsg": { + "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "redelegate" + ], + "properties": { + "redelegate": { + "type": "object", + "required": [ + "amount", + "dst_validator", + "src_validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "dst_validator": { + "type": "string" + }, + "src_validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "Vote": { + "oneOf": [ + { + "description": "Marks support for the proposal.", + "type": "string", + "enum": [ + "yes" + ] + }, + { + "description": "Marks opposition to the proposal.", + "type": "string", + "enum": [ + "no" + ] + }, + { + "description": "Marks participation but does not count towards the ratio of support / opposed", + "type": "string", + "enum": [ + "abstain" + ] + }, + { + "description": "Veto is generally to be treated as a No vote. Some implementations may allow certain voters to be able to Veto, or them to be counted stronger than No in some way.", + "type": "string", + "enum": [ + "veto" + ] + } + ] + }, + "VoteOption": { + "type": "string", + "enum": [ + "yes", + "no", + "abstain", + "no_with_veto" + ] + }, + "WasmMsg": { + "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", + "oneOf": [ + { + "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "contract_addr", + "funds", + "msg" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "msg": { + "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThe contract address is non-predictable. But it is guaranteed that when emitting the same Instantiate message multiple times, multiple instances on different addresses will be generated. See also Instantiate2.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.29.2/proto/cosmwasm/wasm/v1/tx.proto#L53-L71). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "instantiate" + ], + "properties": { + "instantiate": { + "type": "object", + "required": [ + "code_id", + "funds", + "label", + "msg" + ], + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "label": { + "description": "A human-readbale label for the contract", + "type": "string" + }, + "msg": { + "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "migrate" + ], + "properties": { + "migrate": { + "type": "object", + "required": [ + "contract_addr", + "msg", + "new_code_id" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "msg": { + "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "new_code_id": { + "description": "the code_id of the new logic to place in the given contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "required": [ + "admin", + "contract_addr" + ], + "properties": { + "admin": { + "type": "string" + }, + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "clear_admin" + ], + "properties": { + "clear_admin": { + "type": "object", + "required": [ + "contract_addr" + ], + "properties": { + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "threshold" + ], + "properties": { + "threshold": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "proposal" + ], + "properties": { + "proposal": { + "type": "object", + "required": [ + "proposal_id" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "list_proposals" + ], + "properties": { + "list_proposals": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "reverse_proposals" + ], + "properties": { + "reverse_proposals": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_before": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "voter" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "voter": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "list_votes" + ], + "properties": { + "list_votes": { + "type": "object", + "required": [ + "proposal_id" + ], + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "voter" + ], + "properties": { + "voter": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "list_voters" + ], + "properties": { + "list_voters": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the current configuration.", + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "migrate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "required": [ + "coconut_bandwidth_address", + "coconut_dkg_address" + ], + "properties": { + "coconut_bandwidth_address": { + "type": "string" + }, + "coconut_dkg_address": { + "type": "string" + } + }, + "additionalProperties": false + }, + "sudo": null, + "responses": { + "config": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Config", + "type": "object", + "required": [ + "coconut_bandwidth_addr", + "coconut_dkg_addr", + "group_addr", + "max_voting_period", + "threshold" + ], + "properties": { + "coconut_bandwidth_addr": { + "$ref": "#/definitions/Addr" + }, + "coconut_dkg_addr": { + "$ref": "#/definitions/Addr" + }, + "executor": { + "anyOf": [ + { + "$ref": "#/definitions/Executor" + }, + { + "type": "null" + } + ] + }, + "group_addr": { + "$ref": "#/definitions/Cw4Contract" + }, + "max_voting_period": { + "$ref": "#/definitions/Duration" + }, + "proposal_deposit": { + "description": "The price, if any, of creating a new proposal.", + "anyOf": [ + { + "$ref": "#/definitions/DepositInfo" + }, + { + "type": "null" + } + ] + }, + "threshold": { + "$ref": "#/definitions/Threshold" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Cw4Contract": { + "description": "Cw4Contract is a wrapper around Addr that provides a lot of helpers for working with cw4 contracts\n\nIf you wish to persist this, convert to Cw4CanonicalContract via .canonical()", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Denom": { + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "DepositInfo": { + "description": "Information about the deposit required to create a proposal.", + "type": "object", + "required": [ + "amount", + "denom", + "refund_failed_proposals" + ], + "properties": { + "amount": { + "description": "The number tokens required for payment.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "denom": { + "description": "The denom of the deposit payment.", + "allOf": [ + { + "$ref": "#/definitions/Denom" + } + ] + }, + "refund_failed_proposals": { + "description": "Should failed proposals have their deposits refunded?", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "Duration": { + "description": "Duration is a delta of time. You can add it to a BlockInfo or Expiration to move that further in the future. Note that an height-based Duration and a time-based Expiration cannot be combined", + "oneOf": [ + { + "type": "object", + "required": [ + "height" + ], + "properties": { + "height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "Time in seconds", + "type": "object", + "required": [ + "time" + ], + "properties": { + "time": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + }, + "Executor": { + "description": "Defines who is able to execute proposals once passed", + "oneOf": [ + { + "description": "Any member of the voting group, even with 0 points", + "type": "string", + "enum": [ + "member" + ] + }, + { + "description": "Only the given address", + "type": "object", + "required": [ + "only" + ], + "properties": { + "only": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Threshold": { + "description": "This defines the different ways tallies can happen.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation). See also `ThresholdResponse` in the cw3 spec.", + "oneOf": [ + { + "description": "Declares that a fixed weight of Yes votes is needed to pass. See `ThresholdResponse.AbsoluteCount` in the cw3 spec for details.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "weight" + ], + "properties": { + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes in order for a proposal to pass. See `ThresholdResponse.AbsolutePercentage` in the cw3 spec for details.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. See `ThresholdResponse.ThresholdQuorum` in the cw3 spec for details.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "list_proposals": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ProposalListResponse_for_Empty", + "type": "object", + "required": [ + "proposals" + ], + "properties": { + "proposals": { + "type": "array", + "items": { + "$ref": "#/definitions/ProposalResponse_for_Empty" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "BankMsg": { + "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", + "oneOf": [ + { + "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "send" + ], + "properties": { + "send": { + "type": "object", + "required": [ + "amount", + "to_address" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "to_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CosmosMsg_for_Empty": { + "oneOf": [ + { + "type": "object", + "required": [ + "bank" + ], + "properties": { + "bank": { + "$ref": "#/definitions/BankMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking" + ], + "properties": { + "staking": { + "$ref": "#/definitions/StakingMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution" + ], + "properties": { + "distribution": { + "$ref": "#/definitions/DistributionMsg" + } + }, + "additionalProperties": false + }, + { + "description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)", + "type": "object", + "required": [ + "stargate" + ], + "properties": { + "stargate": { + "type": "object", + "required": [ + "type_url", + "value" + ], + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ibc" + ], + "properties": { + "ibc": { + "$ref": "#/definitions/IbcMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "wasm" + ], + "properties": { + "wasm": { + "$ref": "#/definitions/WasmMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "gov" + ], + "properties": { + "gov": { + "$ref": "#/definitions/GovMsg" + } + }, + "additionalProperties": false + } + ] + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Denom": { + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "DepositInfo": { + "description": "Information about the deposit required to create a proposal.", + "type": "object", + "required": [ + "amount", + "denom", + "refund_failed_proposals" + ], + "properties": { + "amount": { + "description": "The number tokens required for payment.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "denom": { + "description": "The denom of the deposit payment.", + "allOf": [ + { + "$ref": "#/definitions/Denom" + } + ] + }, + "refund_failed_proposals": { + "description": "Should failed proposals have their deposits refunded?", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "DistributionMsg": { + "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The `withdraw_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "validator" + ], + "properties": { + "validator": { + "description": "The `validator_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "GovMsg": { + "description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```", + "oneOf": [ + { + "description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.", + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See .", + "allOf": [ + { + "$ref": "#/definitions/VoteOption" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcMsg": { + "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", + "oneOf": [ + { + "description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "channel_id", + "timeout", + "to_address" + ], + "properties": { + "amount": { + "description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "channel_id": { + "description": "exisiting channel to send the tokens over", + "type": "string" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + }, + "to_address": { + "description": "address on the remote chain to receive these tokens", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.", + "type": "object", + "required": [ + "send_packet" + ], + "properties": { + "send_packet": { + "type": "object", + "required": [ + "channel_id", + "data", + "timeout" + ], + "properties": { + "channel_id": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Binary" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port", + "type": "object", + "required": [ + "close_channel" + ], + "properties": { + "close_channel": { + "type": "object", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcTimeout": { + "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", + "type": "object", + "properties": { + "block": { + "anyOf": [ + { + "$ref": "#/definitions/IbcTimeoutBlock" + }, + { + "type": "null" + } + ] + }, + "timestamp": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + } + }, + "IbcTimeoutBlock": { + "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", + "type": "object", + "required": [ + "height", + "revision" + ], + "properties": { + "height": { + "description": "block height after which the packet times out. the height within the given revision", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "ProposalResponse_for_Empty": { + "description": "Note, if you are storing custom messages in the proposal, the querier needs to know what possible custom message types those are in order to parse the response", + "type": "object", + "required": [ + "description", + "expires", + "id", + "msgs", + "proposer", + "status", + "threshold", + "title" + ], + "properties": { + "deposit": { + "anyOf": [ + { + "$ref": "#/definitions/DepositInfo" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": "string" + }, + "expires": { + "$ref": "#/definitions/Expiration" + }, + "id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "msgs": { + "type": "array", + "items": { + "$ref": "#/definitions/CosmosMsg_for_Empty" + } + }, + "proposer": { + "$ref": "#/definitions/Addr" + }, + "status": { + "$ref": "#/definitions/Status" + }, + "threshold": { + "description": "This is the threshold that is applied to this proposal. Both the rules of the voting contract, as well as the total_weight of the voting group may have changed since this time. That means that the generic `Threshold{}` query does not provide valid information for existing proposals.", + "allOf": [ + { + "$ref": "#/definitions/ThresholdResponse" + } + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": false + }, + "StakingMsg": { + "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "redelegate" + ], + "properties": { + "redelegate": { + "type": "object", + "required": [ + "amount", + "dst_validator", + "src_validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "dst_validator": { + "type": "string" + }, + "src_validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Status": { + "oneOf": [ + { + "description": "proposal was created, but voting has not yet begun for whatever reason", + "type": "string", + "enum": [ + "pending" + ] + }, + { + "description": "you can vote on this", + "type": "string", + "enum": [ + "open" + ] + }, + { + "description": "voting is over and it did not pass", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "voting is over and it did pass, but has not yet executed", + "type": "string", + "enum": [ + "passed" + ] + }, + { + "description": "voting is over it passed, and the proposal was executed", + "type": "string", + "enum": [ + "executed" + ] + } + ] + }, + "ThresholdResponse": { + "description": "This defines the different ways tallies can happen. Every contract should support a subset of these, ideally all.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation).", + "oneOf": [ + { + "description": "Declares that a fixed weight of yes votes is needed to pass. It does not matter how many no votes are cast, or how many do not vote, as long as `weight` yes votes are cast.\n\nThis is the simplest format and usually suitable for small multisigs of trusted parties, like 3 of 5. (weight: 3, total_weight: 5)\n\nA proposal of this type can pass early as soon as the needed weight of yes votes has been cast.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "total_weight", + "weight" + ], + "properties": { + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes, in order for a proposal to pass. The passing weight is computed over the total weight minus the weight of the abstained votes.\n\nThis is useful for similar circumstances as `AbsoluteCount`, where we have a relatively small set of voters, and participation is required. It is understood that if the voting set (group) changes between different proposals that refer to the same group, each proposal will work with a different set of voter weights (the ones snapshotted at proposal creation), and the passing weight for each proposal will be computed based on the absolute percentage, times the total weights of the members at the time of each proposal creation.\n\nExample: we set `percentage` to 51%. Proposal 1 starts when there is a `total_weight` of 5. This will require 3 weight of Yes votes in order to pass. Later, the Proposal 2 starts but the `total_weight` of the group has increased to 9. That proposal will then automatically require 5 Yes of 9 to pass, rather than 3 yes of 9 as would be the case with `AbsoluteCount`.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage", + "total_weight" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "In addition to a `threshold`, declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. Within the votes that were cast, it requires `threshold` votes in favor. That is calculated by ignoring the Abstain votes (they count towards `quorum`, but do not influence `threshold`). That is, we calculate `Yes / (Yes + No + Veto)` and compare it with `threshold` to consider if the proposal was passed.\n\nIt is rather difficult for a proposal of this type to pass early. That can only happen if the required quorum has been already met, and there are already enough Yes votes for the proposal to pass.\n\n30% Yes votes, 10% No votes, and 20% Abstain would pass early if quorum <= 60% (who has cast votes) and if the threshold is <= 37.5% (the remaining 40% voting no => 30% yes + 50% no). Once the voting period has passed with no additional votes, that same proposal would be considered successful if quorum <= 60% and threshold <= 75% (percent in favor if we ignore abstain votes).\n\nThis type is more common in general elections, where participation is often expected to be low, and `AbsolutePercentage` would either be too high to pass anything, or allow low percentages to pass, independently of if there was high participation in the election or not.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold", + "total_weight" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "VoteOption": { + "type": "string", + "enum": [ + "yes", + "no", + "abstain", + "no_with_veto" + ] + }, + "WasmMsg": { + "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", + "oneOf": [ + { + "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "contract_addr", + "funds", + "msg" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "msg": { + "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThe contract address is non-predictable. But it is guaranteed that when emitting the same Instantiate message multiple times, multiple instances on different addresses will be generated. See also Instantiate2.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.29.2/proto/cosmwasm/wasm/v1/tx.proto#L53-L71). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "instantiate" + ], + "properties": { + "instantiate": { + "type": "object", + "required": [ + "code_id", + "funds", + "label", + "msg" + ], + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "label": { + "description": "A human-readbale label for the contract", + "type": "string" + }, + "msg": { + "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "migrate" + ], + "properties": { + "migrate": { + "type": "object", + "required": [ + "contract_addr", + "msg", + "new_code_id" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "msg": { + "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "new_code_id": { + "description": "the code_id of the new logic to place in the given contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "required": [ + "admin", + "contract_addr" + ], + "properties": { + "admin": { + "type": "string" + }, + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "clear_admin" + ], + "properties": { + "clear_admin": { + "type": "object", + "required": [ + "contract_addr" + ], + "properties": { + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + } + } + }, + "list_voters": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VoterListResponse", + "type": "object", + "required": [ + "voters" + ], + "properties": { + "voters": { + "type": "array", + "items": { + "$ref": "#/definitions/VoterDetail" + } + } + }, + "additionalProperties": false, + "definitions": { + "VoterDetail": { + "type": "object", + "required": [ + "addr", + "weight" + ], + "properties": { + "addr": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "list_votes": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VoteListResponse", + "type": "object", + "required": [ + "votes" + ], + "properties": { + "votes": { + "type": "array", + "items": { + "$ref": "#/definitions/VoteInfo" + } + } + }, + "additionalProperties": false, + "definitions": { + "Vote": { + "oneOf": [ + { + "description": "Marks support for the proposal.", + "type": "string", + "enum": [ + "yes" + ] + }, + { + "description": "Marks opposition to the proposal.", + "type": "string", + "enum": [ + "no" + ] + }, + { + "description": "Marks participation but does not count towards the ratio of support / opposed", + "type": "string", + "enum": [ + "abstain" + ] + }, + { + "description": "Veto is generally to be treated as a No vote. Some implementations may allow certain voters to be able to Veto, or them to be counted stronger than No in some way.", + "type": "string", + "enum": [ + "veto" + ] + } + ] + }, + "VoteInfo": { + "description": "Returns the vote (opinion as well as weight counted) as well as the address of the voter who submitted it", + "type": "object", + "required": [ + "proposal_id", + "vote", + "voter", + "weight" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "$ref": "#/definitions/Vote" + }, + "voter": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "proposal": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ProposalResponse_for_Empty", + "description": "Note, if you are storing custom messages in the proposal, the querier needs to know what possible custom message types those are in order to parse the response", + "type": "object", + "required": [ + "description", + "expires", + "id", + "msgs", + "proposer", + "status", + "threshold", + "title" + ], + "properties": { + "deposit": { + "anyOf": [ + { + "$ref": "#/definitions/DepositInfo" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": "string" + }, + "expires": { + "$ref": "#/definitions/Expiration" + }, + "id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "msgs": { + "type": "array", + "items": { + "$ref": "#/definitions/CosmosMsg_for_Empty" + } + }, + "proposer": { + "$ref": "#/definitions/Addr" + }, + "status": { + "$ref": "#/definitions/Status" + }, + "threshold": { + "description": "This is the threshold that is applied to this proposal. Both the rules of the voting contract, as well as the total_weight of the voting group may have changed since this time. That means that the generic `Threshold{}` query does not provide valid information for existing proposals.", + "allOf": [ + { + "$ref": "#/definitions/ThresholdResponse" + } + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "BankMsg": { + "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", + "oneOf": [ + { + "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "send" + ], + "properties": { + "send": { + "type": "object", + "required": [ + "amount", + "to_address" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "to_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CosmosMsg_for_Empty": { + "oneOf": [ + { + "type": "object", + "required": [ + "bank" + ], + "properties": { + "bank": { + "$ref": "#/definitions/BankMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking" + ], + "properties": { + "staking": { + "$ref": "#/definitions/StakingMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution" + ], + "properties": { + "distribution": { + "$ref": "#/definitions/DistributionMsg" + } + }, + "additionalProperties": false + }, + { + "description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)", + "type": "object", + "required": [ + "stargate" + ], + "properties": { + "stargate": { + "type": "object", + "required": [ + "type_url", + "value" + ], + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ibc" + ], + "properties": { + "ibc": { + "$ref": "#/definitions/IbcMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "wasm" + ], + "properties": { + "wasm": { + "$ref": "#/definitions/WasmMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "gov" + ], + "properties": { + "gov": { + "$ref": "#/definitions/GovMsg" + } + }, + "additionalProperties": false + } + ] + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Denom": { + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "DepositInfo": { + "description": "Information about the deposit required to create a proposal.", + "type": "object", + "required": [ + "amount", + "denom", + "refund_failed_proposals" + ], + "properties": { + "amount": { + "description": "The number tokens required for payment.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "denom": { + "description": "The denom of the deposit payment.", + "allOf": [ + { + "$ref": "#/definitions/Denom" + } + ] + }, + "refund_failed_proposals": { + "description": "Should failed proposals have their deposits refunded?", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "DistributionMsg": { + "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The `withdraw_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "validator" + ], + "properties": { + "validator": { + "description": "The `validator_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "GovMsg": { + "description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```", + "oneOf": [ + { + "description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.", + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See .", + "allOf": [ + { + "$ref": "#/definitions/VoteOption" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcMsg": { + "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", + "oneOf": [ + { + "description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "channel_id", + "timeout", + "to_address" + ], + "properties": { + "amount": { + "description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "channel_id": { + "description": "exisiting channel to send the tokens over", + "type": "string" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + }, + "to_address": { + "description": "address on the remote chain to receive these tokens", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.", + "type": "object", + "required": [ + "send_packet" + ], + "properties": { + "send_packet": { + "type": "object", + "required": [ + "channel_id", + "data", + "timeout" + ], + "properties": { + "channel_id": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Binary" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port", + "type": "object", + "required": [ + "close_channel" + ], + "properties": { + "close_channel": { + "type": "object", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcTimeout": { + "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", + "type": "object", + "properties": { + "block": { + "anyOf": [ + { + "$ref": "#/definitions/IbcTimeoutBlock" + }, + { + "type": "null" + } + ] + }, + "timestamp": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + } + }, + "IbcTimeoutBlock": { + "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", + "type": "object", + "required": [ + "height", + "revision" + ], + "properties": { + "height": { + "description": "block height after which the packet times out. the height within the given revision", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "StakingMsg": { + "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "redelegate" + ], + "properties": { + "redelegate": { + "type": "object", + "required": [ + "amount", + "dst_validator", + "src_validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "dst_validator": { + "type": "string" + }, + "src_validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Status": { + "oneOf": [ + { + "description": "proposal was created, but voting has not yet begun for whatever reason", + "type": "string", + "enum": [ + "pending" + ] + }, + { + "description": "you can vote on this", + "type": "string", + "enum": [ + "open" + ] + }, + { + "description": "voting is over and it did not pass", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "voting is over and it did pass, but has not yet executed", + "type": "string", + "enum": [ + "passed" + ] + }, + { + "description": "voting is over it passed, and the proposal was executed", + "type": "string", + "enum": [ + "executed" + ] + } + ] + }, + "ThresholdResponse": { + "description": "This defines the different ways tallies can happen. Every contract should support a subset of these, ideally all.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation).", + "oneOf": [ + { + "description": "Declares that a fixed weight of yes votes is needed to pass. It does not matter how many no votes are cast, or how many do not vote, as long as `weight` yes votes are cast.\n\nThis is the simplest format and usually suitable for small multisigs of trusted parties, like 3 of 5. (weight: 3, total_weight: 5)\n\nA proposal of this type can pass early as soon as the needed weight of yes votes has been cast.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "total_weight", + "weight" + ], + "properties": { + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes, in order for a proposal to pass. The passing weight is computed over the total weight minus the weight of the abstained votes.\n\nThis is useful for similar circumstances as `AbsoluteCount`, where we have a relatively small set of voters, and participation is required. It is understood that if the voting set (group) changes between different proposals that refer to the same group, each proposal will work with a different set of voter weights (the ones snapshotted at proposal creation), and the passing weight for each proposal will be computed based on the absolute percentage, times the total weights of the members at the time of each proposal creation.\n\nExample: we set `percentage` to 51%. Proposal 1 starts when there is a `total_weight` of 5. This will require 3 weight of Yes votes in order to pass. Later, the Proposal 2 starts but the `total_weight` of the group has increased to 9. That proposal will then automatically require 5 Yes of 9 to pass, rather than 3 yes of 9 as would be the case with `AbsoluteCount`.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage", + "total_weight" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "In addition to a `threshold`, declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. Within the votes that were cast, it requires `threshold` votes in favor. That is calculated by ignoring the Abstain votes (they count towards `quorum`, but do not influence `threshold`). That is, we calculate `Yes / (Yes + No + Veto)` and compare it with `threshold` to consider if the proposal was passed.\n\nIt is rather difficult for a proposal of this type to pass early. That can only happen if the required quorum has been already met, and there are already enough Yes votes for the proposal to pass.\n\n30% Yes votes, 10% No votes, and 20% Abstain would pass early if quorum <= 60% (who has cast votes) and if the threshold is <= 37.5% (the remaining 40% voting no => 30% yes + 50% no). Once the voting period has passed with no additional votes, that same proposal would be considered successful if quorum <= 60% and threshold <= 75% (percent in favor if we ignore abstain votes).\n\nThis type is more common in general elections, where participation is often expected to be low, and `AbsolutePercentage` would either be too high to pass anything, or allow low percentages to pass, independently of if there was high participation in the election or not.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold", + "total_weight" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "VoteOption": { + "type": "string", + "enum": [ + "yes", + "no", + "abstain", + "no_with_veto" + ] + }, + "WasmMsg": { + "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", + "oneOf": [ + { + "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "contract_addr", + "funds", + "msg" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "msg": { + "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThe contract address is non-predictable. But it is guaranteed that when emitting the same Instantiate message multiple times, multiple instances on different addresses will be generated. See also Instantiate2.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.29.2/proto/cosmwasm/wasm/v1/tx.proto#L53-L71). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "instantiate" + ], + "properties": { + "instantiate": { + "type": "object", + "required": [ + "code_id", + "funds", + "label", + "msg" + ], + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "label": { + "description": "A human-readbale label for the contract", + "type": "string" + }, + "msg": { + "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "migrate" + ], + "properties": { + "migrate": { + "type": "object", + "required": [ + "contract_addr", + "msg", + "new_code_id" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "msg": { + "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "new_code_id": { + "description": "the code_id of the new logic to place in the given contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "required": [ + "admin", + "contract_addr" + ], + "properties": { + "admin": { + "type": "string" + }, + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "clear_admin" + ], + "properties": { + "clear_admin": { + "type": "object", + "required": [ + "contract_addr" + ], + "properties": { + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + } + } + }, + "reverse_proposals": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ProposalListResponse_for_Empty", + "type": "object", + "required": [ + "proposals" + ], + "properties": { + "proposals": { + "type": "array", + "items": { + "$ref": "#/definitions/ProposalResponse_for_Empty" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "BankMsg": { + "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", + "oneOf": [ + { + "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "send" + ], + "properties": { + "send": { + "type": "object", + "required": [ + "amount", + "to_address" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "to_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CosmosMsg_for_Empty": { + "oneOf": [ + { + "type": "object", + "required": [ + "bank" + ], + "properties": { + "bank": { + "$ref": "#/definitions/BankMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking" + ], + "properties": { + "staking": { + "$ref": "#/definitions/StakingMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution" + ], + "properties": { + "distribution": { + "$ref": "#/definitions/DistributionMsg" + } + }, + "additionalProperties": false + }, + { + "description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)", + "type": "object", + "required": [ + "stargate" + ], + "properties": { + "stargate": { + "type": "object", + "required": [ + "type_url", + "value" + ], + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ibc" + ], + "properties": { + "ibc": { + "$ref": "#/definitions/IbcMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "wasm" + ], + "properties": { + "wasm": { + "$ref": "#/definitions/WasmMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "gov" + ], + "properties": { + "gov": { + "$ref": "#/definitions/GovMsg" + } + }, + "additionalProperties": false + } + ] + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Denom": { + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "DepositInfo": { + "description": "Information about the deposit required to create a proposal.", + "type": "object", + "required": [ + "amount", + "denom", + "refund_failed_proposals" + ], + "properties": { + "amount": { + "description": "The number tokens required for payment.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "denom": { + "description": "The denom of the deposit payment.", + "allOf": [ + { + "$ref": "#/definitions/Denom" + } + ] + }, + "refund_failed_proposals": { + "description": "Should failed proposals have their deposits refunded?", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "DistributionMsg": { + "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The `withdraw_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "validator" + ], + "properties": { + "validator": { + "description": "The `validator_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "GovMsg": { + "description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```", + "oneOf": [ + { + "description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.", + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See .", + "allOf": [ + { + "$ref": "#/definitions/VoteOption" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcMsg": { + "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", + "oneOf": [ + { + "description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "channel_id", + "timeout", + "to_address" + ], + "properties": { + "amount": { + "description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "channel_id": { + "description": "exisiting channel to send the tokens over", + "type": "string" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + }, + "to_address": { + "description": "address on the remote chain to receive these tokens", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.", + "type": "object", + "required": [ + "send_packet" + ], + "properties": { + "send_packet": { + "type": "object", + "required": [ + "channel_id", + "data", + "timeout" + ], + "properties": { + "channel_id": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Binary" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port", + "type": "object", + "required": [ + "close_channel" + ], + "properties": { + "close_channel": { + "type": "object", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcTimeout": { + "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", + "type": "object", + "properties": { + "block": { + "anyOf": [ + { + "$ref": "#/definitions/IbcTimeoutBlock" + }, + { + "type": "null" + } + ] + }, + "timestamp": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + } + }, + "IbcTimeoutBlock": { + "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", + "type": "object", + "required": [ + "height", + "revision" + ], + "properties": { + "height": { + "description": "block height after which the packet times out. the height within the given revision", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "ProposalResponse_for_Empty": { + "description": "Note, if you are storing custom messages in the proposal, the querier needs to know what possible custom message types those are in order to parse the response", + "type": "object", + "required": [ + "description", + "expires", + "id", + "msgs", + "proposer", + "status", + "threshold", + "title" + ], + "properties": { + "deposit": { + "anyOf": [ + { + "$ref": "#/definitions/DepositInfo" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": "string" + }, + "expires": { + "$ref": "#/definitions/Expiration" + }, + "id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "msgs": { + "type": "array", + "items": { + "$ref": "#/definitions/CosmosMsg_for_Empty" + } + }, + "proposer": { + "$ref": "#/definitions/Addr" + }, + "status": { + "$ref": "#/definitions/Status" + }, + "threshold": { + "description": "This is the threshold that is applied to this proposal. Both the rules of the voting contract, as well as the total_weight of the voting group may have changed since this time. That means that the generic `Threshold{}` query does not provide valid information for existing proposals.", + "allOf": [ + { + "$ref": "#/definitions/ThresholdResponse" + } + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": false + }, + "StakingMsg": { + "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "redelegate" + ], + "properties": { + "redelegate": { + "type": "object", + "required": [ + "amount", + "dst_validator", + "src_validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "dst_validator": { + "type": "string" + }, + "src_validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Status": { + "oneOf": [ + { + "description": "proposal was created, but voting has not yet begun for whatever reason", + "type": "string", + "enum": [ + "pending" + ] + }, + { + "description": "you can vote on this", + "type": "string", + "enum": [ + "open" + ] + }, + { + "description": "voting is over and it did not pass", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "voting is over and it did pass, but has not yet executed", + "type": "string", + "enum": [ + "passed" + ] + }, + { + "description": "voting is over it passed, and the proposal was executed", + "type": "string", + "enum": [ + "executed" + ] + } + ] + }, + "ThresholdResponse": { + "description": "This defines the different ways tallies can happen. Every contract should support a subset of these, ideally all.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation).", + "oneOf": [ + { + "description": "Declares that a fixed weight of yes votes is needed to pass. It does not matter how many no votes are cast, or how many do not vote, as long as `weight` yes votes are cast.\n\nThis is the simplest format and usually suitable for small multisigs of trusted parties, like 3 of 5. (weight: 3, total_weight: 5)\n\nA proposal of this type can pass early as soon as the needed weight of yes votes has been cast.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "total_weight", + "weight" + ], + "properties": { + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes, in order for a proposal to pass. The passing weight is computed over the total weight minus the weight of the abstained votes.\n\nThis is useful for similar circumstances as `AbsoluteCount`, where we have a relatively small set of voters, and participation is required. It is understood that if the voting set (group) changes between different proposals that refer to the same group, each proposal will work with a different set of voter weights (the ones snapshotted at proposal creation), and the passing weight for each proposal will be computed based on the absolute percentage, times the total weights of the members at the time of each proposal creation.\n\nExample: we set `percentage` to 51%. Proposal 1 starts when there is a `total_weight` of 5. This will require 3 weight of Yes votes in order to pass. Later, the Proposal 2 starts but the `total_weight` of the group has increased to 9. That proposal will then automatically require 5 Yes of 9 to pass, rather than 3 yes of 9 as would be the case with `AbsoluteCount`.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage", + "total_weight" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "In addition to a `threshold`, declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. Within the votes that were cast, it requires `threshold` votes in favor. That is calculated by ignoring the Abstain votes (they count towards `quorum`, but do not influence `threshold`). That is, we calculate `Yes / (Yes + No + Veto)` and compare it with `threshold` to consider if the proposal was passed.\n\nIt is rather difficult for a proposal of this type to pass early. That can only happen if the required quorum has been already met, and there are already enough Yes votes for the proposal to pass.\n\n30% Yes votes, 10% No votes, and 20% Abstain would pass early if quorum <= 60% (who has cast votes) and if the threshold is <= 37.5% (the remaining 40% voting no => 30% yes + 50% no). Once the voting period has passed with no additional votes, that same proposal would be considered successful if quorum <= 60% and threshold <= 75% (percent in favor if we ignore abstain votes).\n\nThis type is more common in general elections, where participation is often expected to be low, and `AbsolutePercentage` would either be too high to pass anything, or allow low percentages to pass, independently of if there was high participation in the election or not.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold", + "total_weight" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "VoteOption": { + "type": "string", + "enum": [ + "yes", + "no", + "abstain", + "no_with_veto" + ] + }, + "WasmMsg": { + "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", + "oneOf": [ + { + "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "contract_addr", + "funds", + "msg" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "msg": { + "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThe contract address is non-predictable. But it is guaranteed that when emitting the same Instantiate message multiple times, multiple instances on different addresses will be generated. See also Instantiate2.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.29.2/proto/cosmwasm/wasm/v1/tx.proto#L53-L71). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "instantiate" + ], + "properties": { + "instantiate": { + "type": "object", + "required": [ + "code_id", + "funds", + "label", + "msg" + ], + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "label": { + "description": "A human-readbale label for the contract", + "type": "string" + }, + "msg": { + "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "migrate" + ], + "properties": { + "migrate": { + "type": "object", + "required": [ + "contract_addr", + "msg", + "new_code_id" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "msg": { + "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "new_code_id": { + "description": "the code_id of the new logic to place in the given contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "required": [ + "admin", + "contract_addr" + ], + "properties": { + "admin": { + "type": "string" + }, + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "clear_admin" + ], + "properties": { + "clear_admin": { + "type": "object", + "required": [ + "contract_addr" + ], + "properties": { + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + } + } + }, + "threshold": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThresholdResponse", + "description": "This defines the different ways tallies can happen. Every contract should support a subset of these, ideally all.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation).", + "oneOf": [ + { + "description": "Declares that a fixed weight of yes votes is needed to pass. It does not matter how many no votes are cast, or how many do not vote, as long as `weight` yes votes are cast.\n\nThis is the simplest format and usually suitable for small multisigs of trusted parties, like 3 of 5. (weight: 3, total_weight: 5)\n\nA proposal of this type can pass early as soon as the needed weight of yes votes has been cast.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "total_weight", + "weight" + ], + "properties": { + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes, in order for a proposal to pass. The passing weight is computed over the total weight minus the weight of the abstained votes.\n\nThis is useful for similar circumstances as `AbsoluteCount`, where we have a relatively small set of voters, and participation is required. It is understood that if the voting set (group) changes between different proposals that refer to the same group, each proposal will work with a different set of voter weights (the ones snapshotted at proposal creation), and the passing weight for each proposal will be computed based on the absolute percentage, times the total weights of the members at the time of each proposal creation.\n\nExample: we set `percentage` to 51%. Proposal 1 starts when there is a `total_weight` of 5. This will require 3 weight of Yes votes in order to pass. Later, the Proposal 2 starts but the `total_weight` of the group has increased to 9. That proposal will then automatically require 5 Yes of 9 to pass, rather than 3 yes of 9 as would be the case with `AbsoluteCount`.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage", + "total_weight" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "In addition to a `threshold`, declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. Within the votes that were cast, it requires `threshold` votes in favor. That is calculated by ignoring the Abstain votes (they count towards `quorum`, but do not influence `threshold`). That is, we calculate `Yes / (Yes + No + Veto)` and compare it with `threshold` to consider if the proposal was passed.\n\nIt is rather difficult for a proposal of this type to pass early. That can only happen if the required quorum has been already met, and there are already enough Yes votes for the proposal to pass.\n\n30% Yes votes, 10% No votes, and 20% Abstain would pass early if quorum <= 60% (who has cast votes) and if the threshold is <= 37.5% (the remaining 40% voting no => 30% yes + 50% no). Once the voting period has passed with no additional votes, that same proposal would be considered successful if quorum <= 60% and threshold <= 75% (percent in favor if we ignore abstain votes).\n\nThis type is more common in general elections, where participation is often expected to be low, and `AbsolutePercentage` would either be too high to pass anything, or allow low percentages to pass, independently of if there was high participation in the election or not.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold", + "total_weight" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + } + } + }, + "vote": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VoteResponse", + "type": "object", + "properties": { + "vote": { + "anyOf": [ + { + "$ref": "#/definitions/VoteInfo" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Vote": { + "oneOf": [ + { + "description": "Marks support for the proposal.", + "type": "string", + "enum": [ + "yes" + ] + }, + { + "description": "Marks opposition to the proposal.", + "type": "string", + "enum": [ + "no" + ] + }, + { + "description": "Marks participation but does not count towards the ratio of support / opposed", + "type": "string", + "enum": [ + "abstain" + ] + }, + { + "description": "Veto is generally to be treated as a No vote. Some implementations may allow certain voters to be able to Veto, or them to be counted stronger than No in some way.", + "type": "string", + "enum": [ + "veto" + ] + } + ] + }, + "VoteInfo": { + "description": "Returns the vote (opinion as well as weight counted) as well as the address of the voter who submitted it", + "type": "object", + "required": [ + "proposal_id", + "vote", + "voter", + "weight" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "$ref": "#/definitions/Vote" + }, + "voter": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "voter": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VoterResponse", + "type": "object", + "properties": { + "weight": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/execute.json b/contracts/multisig/cw3-flex-multisig/schema/raw/execute.json new file mode 100644 index 0000000000..8c5eb3cdae --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/execute.json @@ -0,0 +1,976 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "propose" + ], + "properties": { + "propose": { + "type": "object", + "required": [ + "description", + "msgs", + "title" + ], + "properties": { + "description": { + "type": "string" + }, + "latest": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "msgs": { + "type": "array", + "items": { + "$ref": "#/definitions/CosmosMsg_for_Empty" + } + }, + "title": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "$ref": "#/definitions/Vote" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "proposal_id" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "close" + ], + "properties": { + "close": { + "type": "object", + "required": [ + "proposal_id" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Handles update hook messages from the group contract", + "type": "object", + "required": [ + "member_changed_hook" + ], + "properties": { + "member_changed_hook": { + "$ref": "#/definitions/MemberChangedHookMsg" + } + }, + "additionalProperties": false + } + ], + "definitions": { + "BankMsg": { + "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", + "oneOf": [ + { + "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "send" + ], + "properties": { + "send": { + "type": "object", + "required": [ + "amount", + "to_address" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "to_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CosmosMsg_for_Empty": { + "oneOf": [ + { + "type": "object", + "required": [ + "bank" + ], + "properties": { + "bank": { + "$ref": "#/definitions/BankMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking" + ], + "properties": { + "staking": { + "$ref": "#/definitions/StakingMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution" + ], + "properties": { + "distribution": { + "$ref": "#/definitions/DistributionMsg" + } + }, + "additionalProperties": false + }, + { + "description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)", + "type": "object", + "required": [ + "stargate" + ], + "properties": { + "stargate": { + "type": "object", + "required": [ + "type_url", + "value" + ], + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ibc" + ], + "properties": { + "ibc": { + "$ref": "#/definitions/IbcMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "wasm" + ], + "properties": { + "wasm": { + "$ref": "#/definitions/WasmMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "gov" + ], + "properties": { + "gov": { + "$ref": "#/definitions/GovMsg" + } + }, + "additionalProperties": false + } + ] + }, + "DistributionMsg": { + "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The `withdraw_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "validator" + ], + "properties": { + "validator": { + "description": "The `validator_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "GovMsg": { + "description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```", + "oneOf": [ + { + "description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.", + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See .", + "allOf": [ + { + "$ref": "#/definitions/VoteOption" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcMsg": { + "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", + "oneOf": [ + { + "description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "channel_id", + "timeout", + "to_address" + ], + "properties": { + "amount": { + "description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "channel_id": { + "description": "exisiting channel to send the tokens over", + "type": "string" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + }, + "to_address": { + "description": "address on the remote chain to receive these tokens", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.", + "type": "object", + "required": [ + "send_packet" + ], + "properties": { + "send_packet": { + "type": "object", + "required": [ + "channel_id", + "data", + "timeout" + ], + "properties": { + "channel_id": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Binary" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port", + "type": "object", + "required": [ + "close_channel" + ], + "properties": { + "close_channel": { + "type": "object", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcTimeout": { + "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", + "type": "object", + "properties": { + "block": { + "anyOf": [ + { + "$ref": "#/definitions/IbcTimeoutBlock" + }, + { + "type": "null" + } + ] + }, + "timestamp": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + } + }, + "IbcTimeoutBlock": { + "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", + "type": "object", + "required": [ + "height", + "revision" + ], + "properties": { + "height": { + "description": "block height after which the packet times out. the height within the given revision", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "MemberChangedHookMsg": { + "description": "MemberChangedHookMsg should be de/serialized under `MemberChangedHook()` variant in a ExecuteMsg. This contains a list of all diffs on the given transaction.", + "type": "object", + "required": [ + "diffs" + ], + "properties": { + "diffs": { + "type": "array", + "items": { + "$ref": "#/definitions/MemberDiff" + } + } + }, + "additionalProperties": false + }, + "MemberDiff": { + "description": "MemberDiff shows the old and new states for a given cw4 member They cannot both be None. old = None, new = Some -> Insert old = Some, new = Some -> Update old = Some, new = None -> Delete", + "type": "object", + "required": [ + "key" + ], + "properties": { + "key": { + "type": "string" + }, + "new": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "old": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "StakingMsg": { + "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "redelegate" + ], + "properties": { + "redelegate": { + "type": "object", + "required": [ + "amount", + "dst_validator", + "src_validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "dst_validator": { + "type": "string" + }, + "src_validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "Vote": { + "oneOf": [ + { + "description": "Marks support for the proposal.", + "type": "string", + "enum": [ + "yes" + ] + }, + { + "description": "Marks opposition to the proposal.", + "type": "string", + "enum": [ + "no" + ] + }, + { + "description": "Marks participation but does not count towards the ratio of support / opposed", + "type": "string", + "enum": [ + "abstain" + ] + }, + { + "description": "Veto is generally to be treated as a No vote. Some implementations may allow certain voters to be able to Veto, or them to be counted stronger than No in some way.", + "type": "string", + "enum": [ + "veto" + ] + } + ] + }, + "VoteOption": { + "type": "string", + "enum": [ + "yes", + "no", + "abstain", + "no_with_veto" + ] + }, + "WasmMsg": { + "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", + "oneOf": [ + { + "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "contract_addr", + "funds", + "msg" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "msg": { + "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThe contract address is non-predictable. But it is guaranteed that when emitting the same Instantiate message multiple times, multiple instances on different addresses will be generated. See also Instantiate2.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.29.2/proto/cosmwasm/wasm/v1/tx.proto#L53-L71). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "instantiate" + ], + "properties": { + "instantiate": { + "type": "object", + "required": [ + "code_id", + "funds", + "label", + "msg" + ], + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "label": { + "description": "A human-readbale label for the contract", + "type": "string" + }, + "msg": { + "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "migrate" + ], + "properties": { + "migrate": { + "type": "object", + "required": [ + "contract_addr", + "msg", + "new_code_id" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "msg": { + "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "new_code_id": { + "description": "the code_id of the new logic to place in the given contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "required": [ + "admin", + "contract_addr" + ], + "properties": { + "admin": { + "type": "string" + }, + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "clear_admin" + ], + "properties": { + "clear_admin": { + "type": "object", + "required": [ + "contract_addr" + ], + "properties": { + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/instantiate.json b/contracts/multisig/cw3-flex-multisig/schema/raw/instantiate.json new file mode 100644 index 0000000000..90fa8736e6 --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/instantiate.json @@ -0,0 +1,261 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "coconut_bandwidth_contract_address", + "coconut_dkg_contract_address", + "group_addr", + "max_voting_period", + "threshold" + ], + "properties": { + "coconut_bandwidth_contract_address": { + "type": "string" + }, + "coconut_dkg_contract_address": { + "type": "string" + }, + "executor": { + "anyOf": [ + { + "$ref": "#/definitions/Executor" + }, + { + "type": "null" + } + ] + }, + "group_addr": { + "type": "string" + }, + "max_voting_period": { + "$ref": "#/definitions/Duration" + }, + "proposal_deposit": { + "description": "The cost of creating a proposal (if any).", + "anyOf": [ + { + "$ref": "#/definitions/UncheckedDepositInfo" + }, + { + "type": "null" + } + ] + }, + "threshold": { + "$ref": "#/definitions/Threshold" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Duration": { + "description": "Duration is a delta of time. You can add it to a BlockInfo or Expiration to move that further in the future. Note that an height-based Duration and a time-based Expiration cannot be combined", + "oneOf": [ + { + "type": "object", + "required": [ + "height" + ], + "properties": { + "height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "Time in seconds", + "type": "object", + "required": [ + "time" + ], + "properties": { + "time": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + }, + "Executor": { + "description": "Defines who is able to execute proposals once passed", + "oneOf": [ + { + "description": "Any member of the voting group, even with 0 points", + "type": "string", + "enum": [ + "member" + ] + }, + { + "description": "Only the given address", + "type": "object", + "required": [ + "only" + ], + "properties": { + "only": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Threshold": { + "description": "This defines the different ways tallies can happen.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation). See also `ThresholdResponse` in the cw3 spec.", + "oneOf": [ + { + "description": "Declares that a fixed weight of Yes votes is needed to pass. See `ThresholdResponse.AbsoluteCount` in the cw3 spec for details.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "weight" + ], + "properties": { + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes in order for a proposal to pass. See `ThresholdResponse.AbsolutePercentage` in the cw3 spec for details.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. See `ThresholdResponse.ThresholdQuorum` in the cw3 spec for details.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "UncheckedDenom": { + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "UncheckedDepositInfo": { + "description": "Information about the deposit required to create a proposal. For use in messages. To validate, transform into `DepositInfo` via `into_checked()`.", + "type": "object", + "required": [ + "amount", + "denom", + "refund_failed_proposals" + ], + "properties": { + "amount": { + "description": "The number tokens required for payment.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "denom": { + "description": "The denom of the deposit payment.", + "allOf": [ + { + "$ref": "#/definitions/UncheckedDenom" + } + ] + }, + "refund_failed_proposals": { + "description": "Should failed proposals have their deposits refunded?", + "type": "boolean" + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/migrate.json b/contracts/multisig/cw3-flex-multisig/schema/raw/migrate.json new file mode 100644 index 0000000000..b983a8e05c --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/migrate.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "required": [ + "coconut_bandwidth_address", + "coconut_dkg_address" + ], + "properties": { + "coconut_bandwidth_address": { + "type": "string" + }, + "coconut_dkg_address": { + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/query.json b/contracts/multisig/cw3-flex-multisig/schema/raw/query.json new file mode 100644 index 0000000000..570a727999 --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/query.json @@ -0,0 +1,232 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "threshold" + ], + "properties": { + "threshold": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "proposal" + ], + "properties": { + "proposal": { + "type": "object", + "required": [ + "proposal_id" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "list_proposals" + ], + "properties": { + "list_proposals": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "reverse_proposals" + ], + "properties": { + "reverse_proposals": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_before": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "voter" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "voter": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "list_votes" + ], + "properties": { + "list_votes": { + "type": "object", + "required": [ + "proposal_id" + ], + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "voter" + ], + "properties": { + "voter": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "list_voters" + ], + "properties": { + "list_voters": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the current configuration.", + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_config.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_config.json new file mode 100644 index 0000000000..b5388ddae1 --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_config.json @@ -0,0 +1,269 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Config", + "type": "object", + "required": [ + "coconut_bandwidth_addr", + "coconut_dkg_addr", + "group_addr", + "max_voting_period", + "threshold" + ], + "properties": { + "coconut_bandwidth_addr": { + "$ref": "#/definitions/Addr" + }, + "coconut_dkg_addr": { + "$ref": "#/definitions/Addr" + }, + "executor": { + "anyOf": [ + { + "$ref": "#/definitions/Executor" + }, + { + "type": "null" + } + ] + }, + "group_addr": { + "$ref": "#/definitions/Cw4Contract" + }, + "max_voting_period": { + "$ref": "#/definitions/Duration" + }, + "proposal_deposit": { + "description": "The price, if any, of creating a new proposal.", + "anyOf": [ + { + "$ref": "#/definitions/DepositInfo" + }, + { + "type": "null" + } + ] + }, + "threshold": { + "$ref": "#/definitions/Threshold" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Cw4Contract": { + "description": "Cw4Contract is a wrapper around Addr that provides a lot of helpers for working with cw4 contracts\n\nIf you wish to persist this, convert to Cw4CanonicalContract via .canonical()", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Denom": { + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "DepositInfo": { + "description": "Information about the deposit required to create a proposal.", + "type": "object", + "required": [ + "amount", + "denom", + "refund_failed_proposals" + ], + "properties": { + "amount": { + "description": "The number tokens required for payment.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "denom": { + "description": "The denom of the deposit payment.", + "allOf": [ + { + "$ref": "#/definitions/Denom" + } + ] + }, + "refund_failed_proposals": { + "description": "Should failed proposals have their deposits refunded?", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "Duration": { + "description": "Duration is a delta of time. You can add it to a BlockInfo or Expiration to move that further in the future. Note that an height-based Duration and a time-based Expiration cannot be combined", + "oneOf": [ + { + "type": "object", + "required": [ + "height" + ], + "properties": { + "height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "Time in seconds", + "type": "object", + "required": [ + "time" + ], + "properties": { + "time": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + ] + }, + "Executor": { + "description": "Defines who is able to execute proposals once passed", + "oneOf": [ + { + "description": "Any member of the voting group, even with 0 points", + "type": "string", + "enum": [ + "member" + ] + }, + { + "description": "Only the given address", + "type": "object", + "required": [ + "only" + ], + "properties": { + "only": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Threshold": { + "description": "This defines the different ways tallies can happen.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation). See also `ThresholdResponse` in the cw3 spec.", + "oneOf": [ + { + "description": "Declares that a fixed weight of Yes votes is needed to pass. See `ThresholdResponse.AbsoluteCount` in the cw3 spec for details.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "weight" + ], + "properties": { + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes in order for a proposal to pass. See `ThresholdResponse.AbsolutePercentage` in the cw3 spec for details.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. See `ThresholdResponse.ThresholdQuorum` in the cw3 spec for details.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_proposals.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_proposals.json new file mode 100644 index 0000000000..0012bec80b --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_proposals.json @@ -0,0 +1,1045 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ProposalListResponse_for_Empty", + "type": "object", + "required": [ + "proposals" + ], + "properties": { + "proposals": { + "type": "array", + "items": { + "$ref": "#/definitions/ProposalResponse_for_Empty" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "BankMsg": { + "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", + "oneOf": [ + { + "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "send" + ], + "properties": { + "send": { + "type": "object", + "required": [ + "amount", + "to_address" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "to_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CosmosMsg_for_Empty": { + "oneOf": [ + { + "type": "object", + "required": [ + "bank" + ], + "properties": { + "bank": { + "$ref": "#/definitions/BankMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking" + ], + "properties": { + "staking": { + "$ref": "#/definitions/StakingMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution" + ], + "properties": { + "distribution": { + "$ref": "#/definitions/DistributionMsg" + } + }, + "additionalProperties": false + }, + { + "description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)", + "type": "object", + "required": [ + "stargate" + ], + "properties": { + "stargate": { + "type": "object", + "required": [ + "type_url", + "value" + ], + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ibc" + ], + "properties": { + "ibc": { + "$ref": "#/definitions/IbcMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "wasm" + ], + "properties": { + "wasm": { + "$ref": "#/definitions/WasmMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "gov" + ], + "properties": { + "gov": { + "$ref": "#/definitions/GovMsg" + } + }, + "additionalProperties": false + } + ] + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Denom": { + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "DepositInfo": { + "description": "Information about the deposit required to create a proposal.", + "type": "object", + "required": [ + "amount", + "denom", + "refund_failed_proposals" + ], + "properties": { + "amount": { + "description": "The number tokens required for payment.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "denom": { + "description": "The denom of the deposit payment.", + "allOf": [ + { + "$ref": "#/definitions/Denom" + } + ] + }, + "refund_failed_proposals": { + "description": "Should failed proposals have their deposits refunded?", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "DistributionMsg": { + "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The `withdraw_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "validator" + ], + "properties": { + "validator": { + "description": "The `validator_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "GovMsg": { + "description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```", + "oneOf": [ + { + "description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.", + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See .", + "allOf": [ + { + "$ref": "#/definitions/VoteOption" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcMsg": { + "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", + "oneOf": [ + { + "description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "channel_id", + "timeout", + "to_address" + ], + "properties": { + "amount": { + "description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "channel_id": { + "description": "exisiting channel to send the tokens over", + "type": "string" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + }, + "to_address": { + "description": "address on the remote chain to receive these tokens", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.", + "type": "object", + "required": [ + "send_packet" + ], + "properties": { + "send_packet": { + "type": "object", + "required": [ + "channel_id", + "data", + "timeout" + ], + "properties": { + "channel_id": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Binary" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port", + "type": "object", + "required": [ + "close_channel" + ], + "properties": { + "close_channel": { + "type": "object", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcTimeout": { + "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", + "type": "object", + "properties": { + "block": { + "anyOf": [ + { + "$ref": "#/definitions/IbcTimeoutBlock" + }, + { + "type": "null" + } + ] + }, + "timestamp": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + } + }, + "IbcTimeoutBlock": { + "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", + "type": "object", + "required": [ + "height", + "revision" + ], + "properties": { + "height": { + "description": "block height after which the packet times out. the height within the given revision", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "ProposalResponse_for_Empty": { + "description": "Note, if you are storing custom messages in the proposal, the querier needs to know what possible custom message types those are in order to parse the response", + "type": "object", + "required": [ + "description", + "expires", + "id", + "msgs", + "proposer", + "status", + "threshold", + "title" + ], + "properties": { + "deposit": { + "anyOf": [ + { + "$ref": "#/definitions/DepositInfo" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": "string" + }, + "expires": { + "$ref": "#/definitions/Expiration" + }, + "id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "msgs": { + "type": "array", + "items": { + "$ref": "#/definitions/CosmosMsg_for_Empty" + } + }, + "proposer": { + "$ref": "#/definitions/Addr" + }, + "status": { + "$ref": "#/definitions/Status" + }, + "threshold": { + "description": "This is the threshold that is applied to this proposal. Both the rules of the voting contract, as well as the total_weight of the voting group may have changed since this time. That means that the generic `Threshold{}` query does not provide valid information for existing proposals.", + "allOf": [ + { + "$ref": "#/definitions/ThresholdResponse" + } + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": false + }, + "StakingMsg": { + "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "redelegate" + ], + "properties": { + "redelegate": { + "type": "object", + "required": [ + "amount", + "dst_validator", + "src_validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "dst_validator": { + "type": "string" + }, + "src_validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Status": { + "oneOf": [ + { + "description": "proposal was created, but voting has not yet begun for whatever reason", + "type": "string", + "enum": [ + "pending" + ] + }, + { + "description": "you can vote on this", + "type": "string", + "enum": [ + "open" + ] + }, + { + "description": "voting is over and it did not pass", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "voting is over and it did pass, but has not yet executed", + "type": "string", + "enum": [ + "passed" + ] + }, + { + "description": "voting is over it passed, and the proposal was executed", + "type": "string", + "enum": [ + "executed" + ] + } + ] + }, + "ThresholdResponse": { + "description": "This defines the different ways tallies can happen. Every contract should support a subset of these, ideally all.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation).", + "oneOf": [ + { + "description": "Declares that a fixed weight of yes votes is needed to pass. It does not matter how many no votes are cast, or how many do not vote, as long as `weight` yes votes are cast.\n\nThis is the simplest format and usually suitable for small multisigs of trusted parties, like 3 of 5. (weight: 3, total_weight: 5)\n\nA proposal of this type can pass early as soon as the needed weight of yes votes has been cast.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "total_weight", + "weight" + ], + "properties": { + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes, in order for a proposal to pass. The passing weight is computed over the total weight minus the weight of the abstained votes.\n\nThis is useful for similar circumstances as `AbsoluteCount`, where we have a relatively small set of voters, and participation is required. It is understood that if the voting set (group) changes between different proposals that refer to the same group, each proposal will work with a different set of voter weights (the ones snapshotted at proposal creation), and the passing weight for each proposal will be computed based on the absolute percentage, times the total weights of the members at the time of each proposal creation.\n\nExample: we set `percentage` to 51%. Proposal 1 starts when there is a `total_weight` of 5. This will require 3 weight of Yes votes in order to pass. Later, the Proposal 2 starts but the `total_weight` of the group has increased to 9. That proposal will then automatically require 5 Yes of 9 to pass, rather than 3 yes of 9 as would be the case with `AbsoluteCount`.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage", + "total_weight" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "In addition to a `threshold`, declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. Within the votes that were cast, it requires `threshold` votes in favor. That is calculated by ignoring the Abstain votes (they count towards `quorum`, but do not influence `threshold`). That is, we calculate `Yes / (Yes + No + Veto)` and compare it with `threshold` to consider if the proposal was passed.\n\nIt is rather difficult for a proposal of this type to pass early. That can only happen if the required quorum has been already met, and there are already enough Yes votes for the proposal to pass.\n\n30% Yes votes, 10% No votes, and 20% Abstain would pass early if quorum <= 60% (who has cast votes) and if the threshold is <= 37.5% (the remaining 40% voting no => 30% yes + 50% no). Once the voting period has passed with no additional votes, that same proposal would be considered successful if quorum <= 60% and threshold <= 75% (percent in favor if we ignore abstain votes).\n\nThis type is more common in general elections, where participation is often expected to be low, and `AbsolutePercentage` would either be too high to pass anything, or allow low percentages to pass, independently of if there was high participation in the election or not.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold", + "total_weight" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "VoteOption": { + "type": "string", + "enum": [ + "yes", + "no", + "abstain", + "no_with_veto" + ] + }, + "WasmMsg": { + "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", + "oneOf": [ + { + "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "contract_addr", + "funds", + "msg" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "msg": { + "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThe contract address is non-predictable. But it is guaranteed that when emitting the same Instantiate message multiple times, multiple instances on different addresses will be generated. See also Instantiate2.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.29.2/proto/cosmwasm/wasm/v1/tx.proto#L53-L71). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "instantiate" + ], + "properties": { + "instantiate": { + "type": "object", + "required": [ + "code_id", + "funds", + "label", + "msg" + ], + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "label": { + "description": "A human-readbale label for the contract", + "type": "string" + }, + "msg": { + "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "migrate" + ], + "properties": { + "migrate": { + "type": "object", + "required": [ + "contract_addr", + "msg", + "new_code_id" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "msg": { + "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "new_code_id": { + "description": "the code_id of the new logic to place in the given contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "required": [ + "admin", + "contract_addr" + ], + "properties": { + "admin": { + "type": "string" + }, + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "clear_admin" + ], + "properties": { + "clear_admin": { + "type": "object", + "required": [ + "contract_addr" + ], + "properties": { + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_voters.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_voters.json new file mode 100644 index 0000000000..9e337d64ea --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_voters.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VoterListResponse", + "type": "object", + "required": [ + "voters" + ], + "properties": { + "voters": { + "type": "array", + "items": { + "$ref": "#/definitions/VoterDetail" + } + } + }, + "additionalProperties": false, + "definitions": { + "VoterDetail": { + "type": "object", + "required": [ + "addr", + "weight" + ], + "properties": { + "addr": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_votes.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_votes.json new file mode 100644 index 0000000000..90a4c459b0 --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_list_votes.json @@ -0,0 +1,80 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VoteListResponse", + "type": "object", + "required": [ + "votes" + ], + "properties": { + "votes": { + "type": "array", + "items": { + "$ref": "#/definitions/VoteInfo" + } + } + }, + "additionalProperties": false, + "definitions": { + "Vote": { + "oneOf": [ + { + "description": "Marks support for the proposal.", + "type": "string", + "enum": [ + "yes" + ] + }, + { + "description": "Marks opposition to the proposal.", + "type": "string", + "enum": [ + "no" + ] + }, + { + "description": "Marks participation but does not count towards the ratio of support / opposed", + "type": "string", + "enum": [ + "abstain" + ] + }, + { + "description": "Veto is generally to be treated as a No vote. Some implementations may allow certain voters to be able to Veto, or them to be counted stronger than No in some way.", + "type": "string", + "enum": [ + "veto" + ] + } + ] + }, + "VoteInfo": { + "description": "Returns the vote (opinion as well as weight counted) as well as the address of the voter who submitted it", + "type": "object", + "required": [ + "proposal_id", + "vote", + "voter", + "weight" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "$ref": "#/definitions/Vote" + }, + "voter": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_proposal.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_proposal.json new file mode 100644 index 0000000000..751ed3c7bc --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_proposal.json @@ -0,0 +1,1030 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ProposalResponse_for_Empty", + "description": "Note, if you are storing custom messages in the proposal, the querier needs to know what possible custom message types those are in order to parse the response", + "type": "object", + "required": [ + "description", + "expires", + "id", + "msgs", + "proposer", + "status", + "threshold", + "title" + ], + "properties": { + "deposit": { + "anyOf": [ + { + "$ref": "#/definitions/DepositInfo" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": "string" + }, + "expires": { + "$ref": "#/definitions/Expiration" + }, + "id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "msgs": { + "type": "array", + "items": { + "$ref": "#/definitions/CosmosMsg_for_Empty" + } + }, + "proposer": { + "$ref": "#/definitions/Addr" + }, + "status": { + "$ref": "#/definitions/Status" + }, + "threshold": { + "description": "This is the threshold that is applied to this proposal. Both the rules of the voting contract, as well as the total_weight of the voting group may have changed since this time. That means that the generic `Threshold{}` query does not provide valid information for existing proposals.", + "allOf": [ + { + "$ref": "#/definitions/ThresholdResponse" + } + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "BankMsg": { + "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", + "oneOf": [ + { + "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "send" + ], + "properties": { + "send": { + "type": "object", + "required": [ + "amount", + "to_address" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "to_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CosmosMsg_for_Empty": { + "oneOf": [ + { + "type": "object", + "required": [ + "bank" + ], + "properties": { + "bank": { + "$ref": "#/definitions/BankMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking" + ], + "properties": { + "staking": { + "$ref": "#/definitions/StakingMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution" + ], + "properties": { + "distribution": { + "$ref": "#/definitions/DistributionMsg" + } + }, + "additionalProperties": false + }, + { + "description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)", + "type": "object", + "required": [ + "stargate" + ], + "properties": { + "stargate": { + "type": "object", + "required": [ + "type_url", + "value" + ], + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ibc" + ], + "properties": { + "ibc": { + "$ref": "#/definitions/IbcMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "wasm" + ], + "properties": { + "wasm": { + "$ref": "#/definitions/WasmMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "gov" + ], + "properties": { + "gov": { + "$ref": "#/definitions/GovMsg" + } + }, + "additionalProperties": false + } + ] + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Denom": { + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "DepositInfo": { + "description": "Information about the deposit required to create a proposal.", + "type": "object", + "required": [ + "amount", + "denom", + "refund_failed_proposals" + ], + "properties": { + "amount": { + "description": "The number tokens required for payment.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "denom": { + "description": "The denom of the deposit payment.", + "allOf": [ + { + "$ref": "#/definitions/Denom" + } + ] + }, + "refund_failed_proposals": { + "description": "Should failed proposals have their deposits refunded?", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "DistributionMsg": { + "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The `withdraw_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "validator" + ], + "properties": { + "validator": { + "description": "The `validator_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "GovMsg": { + "description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```", + "oneOf": [ + { + "description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.", + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See .", + "allOf": [ + { + "$ref": "#/definitions/VoteOption" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcMsg": { + "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", + "oneOf": [ + { + "description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "channel_id", + "timeout", + "to_address" + ], + "properties": { + "amount": { + "description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "channel_id": { + "description": "exisiting channel to send the tokens over", + "type": "string" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + }, + "to_address": { + "description": "address on the remote chain to receive these tokens", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.", + "type": "object", + "required": [ + "send_packet" + ], + "properties": { + "send_packet": { + "type": "object", + "required": [ + "channel_id", + "data", + "timeout" + ], + "properties": { + "channel_id": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Binary" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port", + "type": "object", + "required": [ + "close_channel" + ], + "properties": { + "close_channel": { + "type": "object", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcTimeout": { + "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", + "type": "object", + "properties": { + "block": { + "anyOf": [ + { + "$ref": "#/definitions/IbcTimeoutBlock" + }, + { + "type": "null" + } + ] + }, + "timestamp": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + } + }, + "IbcTimeoutBlock": { + "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", + "type": "object", + "required": [ + "height", + "revision" + ], + "properties": { + "height": { + "description": "block height after which the packet times out. the height within the given revision", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "StakingMsg": { + "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "redelegate" + ], + "properties": { + "redelegate": { + "type": "object", + "required": [ + "amount", + "dst_validator", + "src_validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "dst_validator": { + "type": "string" + }, + "src_validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Status": { + "oneOf": [ + { + "description": "proposal was created, but voting has not yet begun for whatever reason", + "type": "string", + "enum": [ + "pending" + ] + }, + { + "description": "you can vote on this", + "type": "string", + "enum": [ + "open" + ] + }, + { + "description": "voting is over and it did not pass", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "voting is over and it did pass, but has not yet executed", + "type": "string", + "enum": [ + "passed" + ] + }, + { + "description": "voting is over it passed, and the proposal was executed", + "type": "string", + "enum": [ + "executed" + ] + } + ] + }, + "ThresholdResponse": { + "description": "This defines the different ways tallies can happen. Every contract should support a subset of these, ideally all.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation).", + "oneOf": [ + { + "description": "Declares that a fixed weight of yes votes is needed to pass. It does not matter how many no votes are cast, or how many do not vote, as long as `weight` yes votes are cast.\n\nThis is the simplest format and usually suitable for small multisigs of trusted parties, like 3 of 5. (weight: 3, total_weight: 5)\n\nA proposal of this type can pass early as soon as the needed weight of yes votes has been cast.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "total_weight", + "weight" + ], + "properties": { + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes, in order for a proposal to pass. The passing weight is computed over the total weight minus the weight of the abstained votes.\n\nThis is useful for similar circumstances as `AbsoluteCount`, where we have a relatively small set of voters, and participation is required. It is understood that if the voting set (group) changes between different proposals that refer to the same group, each proposal will work with a different set of voter weights (the ones snapshotted at proposal creation), and the passing weight for each proposal will be computed based on the absolute percentage, times the total weights of the members at the time of each proposal creation.\n\nExample: we set `percentage` to 51%. Proposal 1 starts when there is a `total_weight` of 5. This will require 3 weight of Yes votes in order to pass. Later, the Proposal 2 starts but the `total_weight` of the group has increased to 9. That proposal will then automatically require 5 Yes of 9 to pass, rather than 3 yes of 9 as would be the case with `AbsoluteCount`.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage", + "total_weight" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "In addition to a `threshold`, declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. Within the votes that were cast, it requires `threshold` votes in favor. That is calculated by ignoring the Abstain votes (they count towards `quorum`, but do not influence `threshold`). That is, we calculate `Yes / (Yes + No + Veto)` and compare it with `threshold` to consider if the proposal was passed.\n\nIt is rather difficult for a proposal of this type to pass early. That can only happen if the required quorum has been already met, and there are already enough Yes votes for the proposal to pass.\n\n30% Yes votes, 10% No votes, and 20% Abstain would pass early if quorum <= 60% (who has cast votes) and if the threshold is <= 37.5% (the remaining 40% voting no => 30% yes + 50% no). Once the voting period has passed with no additional votes, that same proposal would be considered successful if quorum <= 60% and threshold <= 75% (percent in favor if we ignore abstain votes).\n\nThis type is more common in general elections, where participation is often expected to be low, and `AbsolutePercentage` would either be too high to pass anything, or allow low percentages to pass, independently of if there was high participation in the election or not.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold", + "total_weight" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "VoteOption": { + "type": "string", + "enum": [ + "yes", + "no", + "abstain", + "no_with_veto" + ] + }, + "WasmMsg": { + "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", + "oneOf": [ + { + "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "contract_addr", + "funds", + "msg" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "msg": { + "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThe contract address is non-predictable. But it is guaranteed that when emitting the same Instantiate message multiple times, multiple instances on different addresses will be generated. See also Instantiate2.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.29.2/proto/cosmwasm/wasm/v1/tx.proto#L53-L71). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "instantiate" + ], + "properties": { + "instantiate": { + "type": "object", + "required": [ + "code_id", + "funds", + "label", + "msg" + ], + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "label": { + "description": "A human-readbale label for the contract", + "type": "string" + }, + "msg": { + "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "migrate" + ], + "properties": { + "migrate": { + "type": "object", + "required": [ + "contract_addr", + "msg", + "new_code_id" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "msg": { + "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "new_code_id": { + "description": "the code_id of the new logic to place in the given contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "required": [ + "admin", + "contract_addr" + ], + "properties": { + "admin": { + "type": "string" + }, + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "clear_admin" + ], + "properties": { + "clear_admin": { + "type": "object", + "required": [ + "contract_addr" + ], + "properties": { + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_reverse_proposals.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_reverse_proposals.json new file mode 100644 index 0000000000..0012bec80b --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_reverse_proposals.json @@ -0,0 +1,1045 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ProposalListResponse_for_Empty", + "type": "object", + "required": [ + "proposals" + ], + "properties": { + "proposals": { + "type": "array", + "items": { + "$ref": "#/definitions/ProposalResponse_for_Empty" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "BankMsg": { + "description": "The message types of the bank module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto", + "oneOf": [ + { + "description": "Sends native tokens from the contract to the given address.\n\nThis is translated to a [MsgSend](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28). `from_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "send" + ], + "properties": { + "send": { + "type": "object", + "required": [ + "amount", + "to_address" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "to_address": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will burn the given coins from the contract's account. There is no Cosmos SDK message that performs this, but it can be done by calling the bank keeper. Important if a contract controls significant token supply that must be retired.", + "type": "object", + "required": [ + "burn" + ], + "properties": { + "burn": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec. See also .", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "CosmosMsg_for_Empty": { + "oneOf": [ + { + "type": "object", + "required": [ + "bank" + ], + "properties": { + "bank": { + "$ref": "#/definitions/BankMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "custom" + ], + "properties": { + "custom": { + "$ref": "#/definitions/Empty" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "staking" + ], + "properties": { + "staking": { + "$ref": "#/definitions/StakingMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "distribution" + ], + "properties": { + "distribution": { + "$ref": "#/definitions/DistributionMsg" + } + }, + "additionalProperties": false + }, + { + "description": "A Stargate message encoded the same way as a protobuf [Any](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). This is the same structure as messages in `TxBody` from [ADR-020](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-020-protobuf-transaction-encoding.md)", + "type": "object", + "required": [ + "stargate" + ], + "properties": { + "stargate": { + "type": "object", + "required": [ + "type_url", + "value" + ], + "properties": { + "type_url": { + "type": "string" + }, + "value": { + "$ref": "#/definitions/Binary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "ibc" + ], + "properties": { + "ibc": { + "$ref": "#/definitions/IbcMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "wasm" + ], + "properties": { + "wasm": { + "$ref": "#/definitions/WasmMsg" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "gov" + ], + "properties": { + "gov": { + "$ref": "#/definitions/GovMsg" + } + }, + "additionalProperties": false + } + ] + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Denom": { + "oneOf": [ + { + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "DepositInfo": { + "description": "Information about the deposit required to create a proposal.", + "type": "object", + "required": [ + "amount", + "denom", + "refund_failed_proposals" + ], + "properties": { + "amount": { + "description": "The number tokens required for payment.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "denom": { + "description": "The denom of the deposit payment.", + "allOf": [ + { + "$ref": "#/definitions/Denom" + } + ] + }, + "refund_failed_proposals": { + "description": "Should failed proposals have their deposits refunded?", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "DistributionMsg": { + "description": "The message types of the distribution module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgSetWithdrawAddress](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L29-L37). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "set_withdraw_address" + ], + "properties": { + "set_withdraw_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The `withdraw_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [[MsgWithdrawDelegatorReward](https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/proto/cosmos/distribution/v1beta1/tx.proto#L42-L50). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "withdraw_delegator_reward" + ], + "properties": { + "withdraw_delegator_reward": { + "type": "object", + "required": [ + "validator" + ], + "properties": { + "validator": { + "description": "The `validator_address`", + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Empty": { + "description": "An empty struct that serves as a placeholder in different places, such as contracts that don't set a custom message.\n\nIt is designed to be expressable in correct JSON and JSON Schema but contains no meaningful data. Previously we used enums without cases, but those cannot represented as valid JSON Schema (https://github.com/CosmWasm/cosmwasm/issues/451)", + "type": "object" + }, + "Expiration": { + "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "GovMsg": { + "description": "This message type allows the contract interact with the [x/gov] module in order to cast votes.\n\n[x/gov]: https://github.com/cosmos/cosmos-sdk/tree/v0.45.12/x/gov\n\n## Examples\n\nCast a simple vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); use cosmwasm_std::{GovMsg, VoteOption};\n\n#[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::Vote { proposal_id: 4, vote: VoteOption::Yes, })) } ```\n\nCast a weighted vote:\n\n``` # use cosmwasm_std::{ # HexBinary, # Storage, Api, Querier, DepsMut, Deps, entry_point, Env, StdError, MessageInfo, # Response, QueryResponse, # }; # type ExecuteMsg = (); # #[cfg(feature = \"cosmwasm_1_2\")] use cosmwasm_std::{Decimal, GovMsg, VoteOption, WeightedVoteOption};\n\n# #[cfg(feature = \"cosmwasm_1_2\")] #[entry_point] pub fn execute( deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { // ... Ok(Response::new().add_message(GovMsg::VoteWeighted { proposal_id: 4, options: vec![ WeightedVoteOption { option: VoteOption::Yes, weight: Decimal::percent(65), }, WeightedVoteOption { option: VoteOption::Abstain, weight: Decimal::percent(35), }, ], })) } ```", + "oneOf": [ + { + "description": "This maps directly to [MsgVote](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/proto/cosmos/gov/v1beta1/tx.proto#L46-L56) in the Cosmos SDK with voter set to the contract address.", + "type": "object", + "required": [ + "vote" + ], + "properties": { + "vote": { + "type": "object", + "required": [ + "proposal_id", + "vote" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "description": "The vote option.\n\nThis should be called \"option\" for consistency with Cosmos SDK. Sorry for that. See .", + "allOf": [ + { + "$ref": "#/definitions/VoteOption" + } + ] + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcMsg": { + "description": "These are messages in the IBC lifecycle. Only usable by IBC-enabled contracts (contracts that directly speak the IBC protocol via 6 entry points)", + "oneOf": [ + { + "description": "Sends bank tokens owned by the contract to the given address on another chain. The channel must already be established between the ibctransfer module on this chain and a matching module on the remote chain. We cannot select the port_id, this is whatever the local chain has bound the ibctransfer module to.", + "type": "object", + "required": [ + "transfer" + ], + "properties": { + "transfer": { + "type": "object", + "required": [ + "amount", + "channel_id", + "timeout", + "to_address" + ], + "properties": { + "amount": { + "description": "packet data only supports one coin https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/ibc/applications/transfer/v1/transfer.proto#L11-L20", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "channel_id": { + "description": "exisiting channel to send the tokens over", + "type": "string" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + }, + "to_address": { + "description": "address on the remote chain to receive these tokens", + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sends an IBC packet with given data over the existing channel. Data should be encoded in a format defined by the channel version, and the module on the other side should know how to parse this.", + "type": "object", + "required": [ + "send_packet" + ], + "properties": { + "send_packet": { + "type": "object", + "required": [ + "channel_id", + "data", + "timeout" + ], + "properties": { + "channel_id": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/Binary" + }, + "timeout": { + "description": "when packet times out, measured on remote chain", + "allOf": [ + { + "$ref": "#/definitions/IbcTimeout" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This will close an existing channel that is owned by this contract. Port is auto-assigned to the contract's IBC port", + "type": "object", + "required": [ + "close_channel" + ], + "properties": { + "close_channel": { + "type": "object", + "required": [ + "channel_id" + ], + "properties": { + "channel_id": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "IbcTimeout": { + "description": "In IBC each package must set at least one type of timeout: the timestamp or the block height. Using this rather complex enum instead of two timeout fields we ensure that at least one timeout is set.", + "type": "object", + "properties": { + "block": { + "anyOf": [ + { + "$ref": "#/definitions/IbcTimeoutBlock" + }, + { + "type": "null" + } + ] + }, + "timestamp": { + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + } + }, + "IbcTimeoutBlock": { + "description": "IBCTimeoutHeight Height is a monotonically increasing data type that can be compared against another Height for the purposes of updating and freezing clients. Ordering is (revision_number, timeout_height)", + "type": "object", + "required": [ + "height", + "revision" + ], + "properties": { + "height": { + "description": "block height after which the packet times out. the height within the given revision", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "revision": { + "description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + }, + "ProposalResponse_for_Empty": { + "description": "Note, if you are storing custom messages in the proposal, the querier needs to know what possible custom message types those are in order to parse the response", + "type": "object", + "required": [ + "description", + "expires", + "id", + "msgs", + "proposer", + "status", + "threshold", + "title" + ], + "properties": { + "deposit": { + "anyOf": [ + { + "$ref": "#/definitions/DepositInfo" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": "string" + }, + "expires": { + "$ref": "#/definitions/Expiration" + }, + "id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "msgs": { + "type": "array", + "items": { + "$ref": "#/definitions/CosmosMsg_for_Empty" + } + }, + "proposer": { + "$ref": "#/definitions/Addr" + }, + "status": { + "$ref": "#/definitions/Status" + }, + "threshold": { + "description": "This is the threshold that is applied to this proposal. Both the rules of the voting contract, as well as the total_weight of the voting group may have changed since this time. That means that the generic `Threshold{}` query does not provide valid information for existing proposals.", + "allOf": [ + { + "$ref": "#/definitions/ThresholdResponse" + } + ] + }, + "title": { + "type": "string" + } + }, + "additionalProperties": false + }, + "StakingMsg": { + "description": "The message types of the staking module.\n\nSee https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto", + "oneOf": [ + { + "description": "This is translated to a [MsgDelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "delegate" + ], + "properties": { + "delegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgUndelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "undelegate" + ], + "properties": { + "undelegate": { + "type": "object", + "required": [ + "amount", + "validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "This is translated to a [MsgBeginRedelegate](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105). `delegator_address` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "redelegate" + ], + "properties": { + "redelegate": { + "type": "object", + "required": [ + "amount", + "dst_validator", + "src_validator" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "dst_validator": { + "type": "string" + }, + "src_validator": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + }, + "Status": { + "oneOf": [ + { + "description": "proposal was created, but voting has not yet begun for whatever reason", + "type": "string", + "enum": [ + "pending" + ] + }, + { + "description": "you can vote on this", + "type": "string", + "enum": [ + "open" + ] + }, + { + "description": "voting is over and it did not pass", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "voting is over and it did pass, but has not yet executed", + "type": "string", + "enum": [ + "passed" + ] + }, + { + "description": "voting is over it passed, and the proposal was executed", + "type": "string", + "enum": [ + "executed" + ] + } + ] + }, + "ThresholdResponse": { + "description": "This defines the different ways tallies can happen. Every contract should support a subset of these, ideally all.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation).", + "oneOf": [ + { + "description": "Declares that a fixed weight of yes votes is needed to pass. It does not matter how many no votes are cast, or how many do not vote, as long as `weight` yes votes are cast.\n\nThis is the simplest format and usually suitable for small multisigs of trusted parties, like 3 of 5. (weight: 3, total_weight: 5)\n\nA proposal of this type can pass early as soon as the needed weight of yes votes has been cast.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "total_weight", + "weight" + ], + "properties": { + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes, in order for a proposal to pass. The passing weight is computed over the total weight minus the weight of the abstained votes.\n\nThis is useful for similar circumstances as `AbsoluteCount`, where we have a relatively small set of voters, and participation is required. It is understood that if the voting set (group) changes between different proposals that refer to the same group, each proposal will work with a different set of voter weights (the ones snapshotted at proposal creation), and the passing weight for each proposal will be computed based on the absolute percentage, times the total weights of the members at the time of each proposal creation.\n\nExample: we set `percentage` to 51%. Proposal 1 starts when there is a `total_weight` of 5. This will require 3 weight of Yes votes in order to pass. Later, the Proposal 2 starts but the `total_weight` of the group has increased to 9. That proposal will then automatically require 5 Yes of 9 to pass, rather than 3 yes of 9 as would be the case with `AbsoluteCount`.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage", + "total_weight" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "In addition to a `threshold`, declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. Within the votes that were cast, it requires `threshold` votes in favor. That is calculated by ignoring the Abstain votes (they count towards `quorum`, but do not influence `threshold`). That is, we calculate `Yes / (Yes + No + Veto)` and compare it with `threshold` to consider if the proposal was passed.\n\nIt is rather difficult for a proposal of this type to pass early. That can only happen if the required quorum has been already met, and there are already enough Yes votes for the proposal to pass.\n\n30% Yes votes, 10% No votes, and 20% Abstain would pass early if quorum <= 60% (who has cast votes) and if the threshold is <= 37.5% (the remaining 40% voting no => 30% yes + 50% no). Once the voting period has passed with no additional votes, that same proposal would be considered successful if quorum <= 60% and threshold <= 75% (percent in favor if we ignore abstain votes).\n\nThis type is more common in general elections, where participation is often expected to be low, and `AbsolutePercentage` would either be too high to pass anything, or allow low percentages to pass, independently of if there was high participation in the election or not.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold", + "total_weight" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "VoteOption": { + "type": "string", + "enum": [ + "yes", + "no", + "abstain", + "no_with_veto" + ] + }, + "WasmMsg": { + "description": "The message types of the wasm module.\n\nSee https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto", + "oneOf": [ + { + "description": "Dispatches a call to another contract at a known address (with known ABI).\n\nThis is translated to a [MsgExecuteContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L68-L78). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "execute" + ], + "properties": { + "execute": { + "type": "object", + "required": [ + "contract_addr", + "funds", + "msg" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "msg": { + "description": "msg is the json-encoded ExecuteMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Instantiates a new contracts from previously uploaded Wasm code.\n\nThe contract address is non-predictable. But it is guaranteed that when emitting the same Instantiate message multiple times, multiple instances on different addresses will be generated. See also Instantiate2.\n\nThis is translated to a [MsgInstantiateContract](https://github.com/CosmWasm/wasmd/blob/v0.29.2/proto/cosmwasm/wasm/v1/tx.proto#L53-L71). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "instantiate" + ], + "properties": { + "instantiate": { + "type": "object", + "required": [ + "code_id", + "funds", + "label", + "msg" + ], + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + }, + "code_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "funds": { + "type": "array", + "items": { + "$ref": "#/definitions/Coin" + } + }, + "label": { + "description": "A human-readbale label for the contract", + "type": "string" + }, + "msg": { + "description": "msg is the JSON-encoded InstantiateMsg struct (as raw Binary)", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Migrates a given contracts to use new wasm code. Passes a MigrateMsg to allow us to customize behavior.\n\nOnly the contract admin (as defined in wasmd), if any, is able to make this call.\n\nThis is translated to a [MsgMigrateContract](https://github.com/CosmWasm/wasmd/blob/v0.14.0/x/wasm/internal/types/tx.proto#L86-L96). `sender` is automatically filled with the current contract's address.", + "type": "object", + "required": [ + "migrate" + ], + "properties": { + "migrate": { + "type": "object", + "required": [ + "contract_addr", + "msg", + "new_code_id" + ], + "properties": { + "contract_addr": { + "type": "string" + }, + "msg": { + "description": "msg is the json-encoded MigrateMsg struct that will be passed to the new code", + "allOf": [ + { + "$ref": "#/definitions/Binary" + } + ] + }, + "new_code_id": { + "description": "the code_id of the new logic to place in the given contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Sets a new admin (for migrate) on the given contract. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "required": [ + "admin", + "contract_addr" + ], + "properties": { + "admin": { + "type": "string" + }, + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + }, + { + "description": "Clears the admin on the given contract, so no more migration possible. Fails if this contract is not currently admin of the target contract.", + "type": "object", + "required": [ + "clear_admin" + ], + "properties": { + "clear_admin": { + "type": "object", + "required": [ + "contract_addr" + ], + "properties": { + "contract_addr": { + "type": "string" + } + } + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_threshold.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_threshold.json new file mode 100644 index 0000000000..3c7dae60fe --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_threshold.json @@ -0,0 +1,103 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThresholdResponse", + "description": "This defines the different ways tallies can happen. Every contract should support a subset of these, ideally all.\n\nThe total_weight used for calculating success as well as the weights of each individual voter used in tallying should be snapshotted at the beginning of the block at which the proposal starts (this is likely the responsibility of a correct cw4 implementation).", + "oneOf": [ + { + "description": "Declares that a fixed weight of yes votes is needed to pass. It does not matter how many no votes are cast, or how many do not vote, as long as `weight` yes votes are cast.\n\nThis is the simplest format and usually suitable for small multisigs of trusted parties, like 3 of 5. (weight: 3, total_weight: 5)\n\nA proposal of this type can pass early as soon as the needed weight of yes votes has been cast.", + "type": "object", + "required": [ + "absolute_count" + ], + "properties": { + "absolute_count": { + "type": "object", + "required": [ + "total_weight", + "weight" + ], + "properties": { + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Declares a percentage of the total weight that must cast Yes votes, in order for a proposal to pass. The passing weight is computed over the total weight minus the weight of the abstained votes.\n\nThis is useful for similar circumstances as `AbsoluteCount`, where we have a relatively small set of voters, and participation is required. It is understood that if the voting set (group) changes between different proposals that refer to the same group, each proposal will work with a different set of voter weights (the ones snapshotted at proposal creation), and the passing weight for each proposal will be computed based on the absolute percentage, times the total weights of the members at the time of each proposal creation.\n\nExample: we set `percentage` to 51%. Proposal 1 starts when there is a `total_weight` of 5. This will require 3 weight of Yes votes in order to pass. Later, the Proposal 2 starts but the `total_weight` of the group has increased to 9. That proposal will then automatically require 5 Yes of 9 to pass, rather than 3 yes of 9 as would be the case with `AbsoluteCount`.", + "type": "object", + "required": [ + "absolute_percentage" + ], + "properties": { + "absolute_percentage": { + "type": "object", + "required": [ + "percentage", + "total_weight" + ], + "properties": { + "percentage": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "In addition to a `threshold`, declares a `quorum` of the total votes that must participate in the election in order for the vote to be considered at all. Within the votes that were cast, it requires `threshold` votes in favor. That is calculated by ignoring the Abstain votes (they count towards `quorum`, but do not influence `threshold`). That is, we calculate `Yes / (Yes + No + Veto)` and compare it with `threshold` to consider if the proposal was passed.\n\nIt is rather difficult for a proposal of this type to pass early. That can only happen if the required quorum has been already met, and there are already enough Yes votes for the proposal to pass.\n\n30% Yes votes, 10% No votes, and 20% Abstain would pass early if quorum <= 60% (who has cast votes) and if the threshold is <= 37.5% (the remaining 40% voting no => 30% yes + 50% no). Once the voting period has passed with no additional votes, that same proposal would be considered successful if quorum <= 60% and threshold <= 75% (percent in favor if we ignore abstain votes).\n\nThis type is more common in general elections, where participation is often expected to be low, and `AbsolutePercentage` would either be too high to pass anything, or allow low percentages to pass, independently of if there was high participation in the election or not.", + "type": "object", + "required": [ + "threshold_quorum" + ], + "properties": { + "threshold_quorum": { + "type": "object", + "required": [ + "quorum", + "threshold", + "total_weight" + ], + "properties": { + "quorum": { + "$ref": "#/definitions/Decimal" + }, + "threshold": { + "$ref": "#/definitions/Decimal" + }, + "total_weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + } + } +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_vote.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_vote.json new file mode 100644 index 0000000000..ba779b7a0d --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_vote.json @@ -0,0 +1,81 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VoteResponse", + "type": "object", + "properties": { + "vote": { + "anyOf": [ + { + "$ref": "#/definitions/VoteInfo" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Vote": { + "oneOf": [ + { + "description": "Marks support for the proposal.", + "type": "string", + "enum": [ + "yes" + ] + }, + { + "description": "Marks opposition to the proposal.", + "type": "string", + "enum": [ + "no" + ] + }, + { + "description": "Marks participation but does not count towards the ratio of support / opposed", + "type": "string", + "enum": [ + "abstain" + ] + }, + { + "description": "Veto is generally to be treated as a No vote. Some implementations may allow certain voters to be able to Veto, or them to be counted stronger than No in some way.", + "type": "string", + "enum": [ + "veto" + ] + } + ] + }, + "VoteInfo": { + "description": "Returns the vote (opinion as well as weight counted) as well as the address of the voter who submitted it", + "type": "object", + "required": [ + "proposal_id", + "vote", + "voter", + "weight" + ], + "properties": { + "proposal_id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "vote": { + "$ref": "#/definitions/Vote" + }, + "voter": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_voter.json b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_voter.json new file mode 100644 index 0000000000..125b4e1430 --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/schema/raw/response_to_voter.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VoterResponse", + "type": "object", + "properties": { + "weight": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false +} diff --git a/contracts/multisig/cw3-flex-multisig/src/bin/schema.rs b/contracts/multisig/cw3-flex-multisig/src/bin/schema.rs new file mode 100644 index 0000000000..869f53b3a6 --- /dev/null +++ b/contracts/multisig/cw3-flex-multisig/src/bin/schema.rs @@ -0,0 +1,14 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::write_api; +use nym_multisig_contract_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + migrate: MigrateMsg, + } +} diff --git a/contracts/multisig/cw4-group/Cargo.toml b/contracts/multisig/cw4-group/Cargo.toml index 180ee66b9d..a2897779b9 100644 --- a/contracts/multisig/cw4-group/Cargo.toml +++ b/contracts/multisig/cw4-group/Cargo.toml @@ -16,6 +16,9 @@ exclude = [ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "schema" + [lib] crate-type = ["cdylib", "rlib"] @@ -25,6 +28,7 @@ backtraces = ["cosmwasm-std/backtraces"] # use library feature to disable all instantiate/execute/query exports library = [] + [dependencies] nym-group-contract-common = { path = "../../../common/cosmwasm-smart-contracts/group-contract" } @@ -38,3 +42,4 @@ cosmwasm-std = { workspace = true } schemars = "0.8.1" serde = { version = "1.0.103", default-features = false, features = ["derive"] } thiserror = { version = "1.0.23" } + diff --git a/contracts/multisig/cw4-group/Makefile b/contracts/multisig/cw4-group/Makefile new file mode 100644 index 0000000000..f138e2c879 --- /dev/null +++ b/contracts/multisig/cw4-group/Makefile @@ -0,0 +1,2 @@ +generate-schema: + cargo schema \ No newline at end of file diff --git a/contracts/multisig/cw4-group/schema/cw4-group.json b/contracts/multisig/cw4-group/schema/cw4-group.json new file mode 100644 index 0000000000..b6c6f5df44 --- /dev/null +++ b/contracts/multisig/cw4-group/schema/cw4-group.json @@ -0,0 +1,400 @@ +{ + "contract_name": "cw4-group", + "contract_version": "1.0.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "members" + ], + "properties": { + "admin": { + "description": "The admin is the only account that can update the group state. Omit it to make the group immutable.", + "type": [ + "string", + "null" + ] + }, + "members": { + "type": "array", + "items": { + "$ref": "#/definitions/Member" + } + } + }, + "additionalProperties": false, + "definitions": { + "Member": { + "description": "A group member has a weight associated with them. This may all be equal, or may have meaning in the app that makes use of the group (eg. voting power)", + "type": "object", + "required": [ + "addr", + "weight" + ], + "properties": { + "addr": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "description": "Change the admin", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "apply a diff to the existing members. remove is applied after add, so if an address is in both, it is removed", + "type": "object", + "required": [ + "update_members" + ], + "properties": { + "update_members": { + "type": "object", + "required": [ + "add", + "remove" + ], + "properties": { + "add": { + "type": "array", + "items": { + "$ref": "#/definitions/Member" + } + }, + "remove": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Add a new hook to be informed of all membership changes. Must be called by Admin", + "type": "object", + "required": [ + "add_hook" + ], + "properties": { + "add_hook": { + "type": "object", + "required": [ + "addr" + ], + "properties": { + "addr": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Remove a hook. Must be called by Admin", + "type": "object", + "required": [ + "remove_hook" + ], + "properties": { + "remove_hook": { + "type": "object", + "required": [ + "addr" + ], + "properties": { + "addr": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Member": { + "description": "A group member has a weight associated with them. This may all be equal, or may have meaning in the app that makes use of the group (eg. voting power)", + "type": "object", + "required": [ + "addr", + "weight" + ], + "properties": { + "addr": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "admin" + ], + "properties": { + "admin": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "total_weight" + ], + "properties": { + "total_weight": { + "type": "object", + "properties": { + "at_height": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "list_members" + ], + "properties": { + "list_members": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "member" + ], + "properties": { + "member": { + "type": "object", + "required": [ + "addr" + ], + "properties": { + "addr": { + "type": "string" + }, + "at_height": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Shows all registered hooks.", + "type": "object", + "required": [ + "hooks" + ], + "properties": { + "hooks": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "migrate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false + }, + "sudo": null, + "responses": { + "admin": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AdminResponse", + "description": "Returned from Admin.query_admin()", + "type": "object", + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + }, + "hooks": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "HooksResponse", + "type": "object", + "required": [ + "hooks" + ], + "properties": { + "hooks": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "list_members": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MemberListResponse", + "type": "object", + "required": [ + "members" + ], + "properties": { + "members": { + "type": "array", + "items": { + "$ref": "#/definitions/Member" + } + } + }, + "additionalProperties": false, + "definitions": { + "Member": { + "description": "A group member has a weight associated with them. This may all be equal, or may have meaning in the app that makes use of the group (eg. voting power)", + "type": "object", + "required": [ + "addr", + "weight" + ], + "properties": { + "addr": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "member": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MemberResponse", + "type": "object", + "properties": { + "weight": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "total_weight": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TotalWeightResponse", + "type": "object", + "required": [ + "weight" + ], + "properties": { + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/multisig/cw4-group/schema/raw/execute.json b/contracts/multisig/cw4-group/schema/raw/execute.json new file mode 100644 index 0000000000..526169f1bf --- /dev/null +++ b/contracts/multisig/cw4-group/schema/raw/execute.json @@ -0,0 +1,125 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "description": "Change the admin", + "type": "object", + "required": [ + "update_admin" + ], + "properties": { + "update_admin": { + "type": "object", + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "apply a diff to the existing members. remove is applied after add, so if an address is in both, it is removed", + "type": "object", + "required": [ + "update_members" + ], + "properties": { + "update_members": { + "type": "object", + "required": [ + "add", + "remove" + ], + "properties": { + "add": { + "type": "array", + "items": { + "$ref": "#/definitions/Member" + } + }, + "remove": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Add a new hook to be informed of all membership changes. Must be called by Admin", + "type": "object", + "required": [ + "add_hook" + ], + "properties": { + "add_hook": { + "type": "object", + "required": [ + "addr" + ], + "properties": { + "addr": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Remove a hook. Must be called by Admin", + "type": "object", + "required": [ + "remove_hook" + ], + "properties": { + "remove_hook": { + "type": "object", + "required": [ + "addr" + ], + "properties": { + "addr": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Member": { + "description": "A group member has a weight associated with them. This may all be equal, or may have meaning in the app that makes use of the group (eg. voting power)", + "type": "object", + "required": [ + "addr", + "weight" + ], + "properties": { + "addr": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/multisig/cw4-group/schema/raw/instantiate.json b/contracts/multisig/cw4-group/schema/raw/instantiate.json new file mode 100644 index 0000000000..bba7b445a7 --- /dev/null +++ b/contracts/multisig/cw4-group/schema/raw/instantiate.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "members" + ], + "properties": { + "admin": { + "description": "The admin is the only account that can update the group state. Omit it to make the group immutable.", + "type": [ + "string", + "null" + ] + }, + "members": { + "type": "array", + "items": { + "$ref": "#/definitions/Member" + } + } + }, + "additionalProperties": false, + "definitions": { + "Member": { + "description": "A group member has a weight associated with them. This may all be equal, or may have meaning in the app that makes use of the group (eg. voting power)", + "type": "object", + "required": [ + "addr", + "weight" + ], + "properties": { + "addr": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/multisig/cw4-group/schema/raw/migrate.json b/contracts/multisig/cw4-group/schema/raw/migrate.json new file mode 100644 index 0000000000..7fbe8c5708 --- /dev/null +++ b/contracts/multisig/cw4-group/schema/raw/migrate.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false +} diff --git a/contracts/multisig/cw4-group/schema/raw/query.json b/contracts/multisig/cw4-group/schema/raw/query.json new file mode 100644 index 0000000000..ec6abedb8d --- /dev/null +++ b/contracts/multisig/cw4-group/schema/raw/query.json @@ -0,0 +1,114 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "admin" + ], + "properties": { + "admin": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "total_weight" + ], + "properties": { + "total_weight": { + "type": "object", + "properties": { + "at_height": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "list_members" + ], + "properties": { + "list_members": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "member" + ], + "properties": { + "member": { + "type": "object", + "required": [ + "addr" + ], + "properties": { + "addr": { + "type": "string" + }, + "at_height": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Shows all registered hooks.", + "type": "object", + "required": [ + "hooks" + ], + "properties": { + "hooks": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/multisig/cw4-group/schema/raw/response_to_admin.json b/contracts/multisig/cw4-group/schema/raw/response_to_admin.json new file mode 100644 index 0000000000..c73969ab04 --- /dev/null +++ b/contracts/multisig/cw4-group/schema/raw/response_to_admin.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AdminResponse", + "description": "Returned from Admin.query_admin()", + "type": "object", + "properties": { + "admin": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false +} diff --git a/contracts/multisig/cw4-group/schema/raw/response_to_hooks.json b/contracts/multisig/cw4-group/schema/raw/response_to_hooks.json new file mode 100644 index 0000000000..6d46524400 --- /dev/null +++ b/contracts/multisig/cw4-group/schema/raw/response_to_hooks.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "HooksResponse", + "type": "object", + "required": [ + "hooks" + ], + "properties": { + "hooks": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false +} diff --git a/contracts/multisig/cw4-group/schema/raw/response_to_list_members.json b/contracts/multisig/cw4-group/schema/raw/response_to_list_members.json new file mode 100644 index 0000000000..39617c0538 --- /dev/null +++ b/contracts/multisig/cw4-group/schema/raw/response_to_list_members.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MemberListResponse", + "type": "object", + "required": [ + "members" + ], + "properties": { + "members": { + "type": "array", + "items": { + "$ref": "#/definitions/Member" + } + } + }, + "additionalProperties": false, + "definitions": { + "Member": { + "description": "A group member has a weight associated with them. This may all be equal, or may have meaning in the app that makes use of the group (eg. voting power)", + "type": "object", + "required": [ + "addr", + "weight" + ], + "properties": { + "addr": { + "type": "string" + }, + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/multisig/cw4-group/schema/raw/response_to_member.json b/contracts/multisig/cw4-group/schema/raw/response_to_member.json new file mode 100644 index 0000000000..0de8fdcfde --- /dev/null +++ b/contracts/multisig/cw4-group/schema/raw/response_to_member.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MemberResponse", + "type": "object", + "properties": { + "weight": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false +} diff --git a/contracts/multisig/cw4-group/schema/raw/response_to_total_weight.json b/contracts/multisig/cw4-group/schema/raw/response_to_total_weight.json new file mode 100644 index 0000000000..9b1f597629 --- /dev/null +++ b/contracts/multisig/cw4-group/schema/raw/response_to_total_weight.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TotalWeightResponse", + "type": "object", + "required": [ + "weight" + ], + "properties": { + "weight": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false +} diff --git a/contracts/multisig/cw4-group/src/bin/schema.rs b/contracts/multisig/cw4-group/src/bin/schema.rs new file mode 100644 index 0000000000..7340a28e6a --- /dev/null +++ b/contracts/multisig/cw4-group/src/bin/schema.rs @@ -0,0 +1,14 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::write_api; +use nym_group_contract_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + migrate: MigrateMsg, + } +} diff --git a/contracts/multisig/cw4-group/src/contract.rs b/contracts/multisig/cw4-group/src/contract.rs index a6069f3b23..c5c039658c 100644 --- a/contracts/multisig/cw4-group/src/contract.rs +++ b/contracts/multisig/cw4-group/src/contract.rs @@ -15,7 +15,7 @@ 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}; +use nym_group_contract_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; // version info for migration info const CONTRACT_NAME: &str = "crates.io:cw4-group"; @@ -219,3 +219,8 @@ pub fn query_list_members( Ok(MemberListResponse { members }) } + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { + Ok(Default::default()) +} diff --git a/contracts/name-service/.cargo/config b/contracts/name-service/.cargo/config new file mode 100644 index 0000000000..2fb2c1afdb --- /dev/null +++ b/contracts/name-service/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --bin schema --features=schema-gen" \ No newline at end of file diff --git a/contracts/name-service/Cargo.toml b/contracts/name-service/Cargo.toml index 56fe0145cf..f680b41c35 100644 --- a/contracts/name-service/Cargo.toml +++ b/contracts/name-service/Cargo.toml @@ -3,11 +3,16 @@ name = "nym-name-service" version = "0.1.0" edition = "2021" +[[bin]] +name = "schema" +required-features = ["schema-gen"] + [lib] crate-type = ["cdylib", "rlib"] [dependencies] bs58 = "0.4.0" +cosmwasm-schema = { workspace = true, optional = true } cosmwasm-std = { workspace = true } cw-controllers = { workspace = true } cw-storage-plus = { workspace = true } @@ -26,6 +31,10 @@ vergen = { version = "=7.4.3", default-features = false, features = ["build", "g anyhow = "1.0.40" cw-multi-test = { workspace = true } nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] } +nym-sphinx-addressing = { path = "../../common/nymsphinx/addressing" } rand = "0.8.5" rand_chacha = "0.2" rstest = "0.17.0" + +[features] +schema-gen = ["nym-name-service-common/schema", "cosmwasm-schema"] diff --git a/contracts/name-service/Makefile b/contracts/name-service/Makefile new file mode 100644 index 0000000000..f138e2c879 --- /dev/null +++ b/contracts/name-service/Makefile @@ -0,0 +1,2 @@ +generate-schema: + cargo schema \ No newline at end of file diff --git a/contracts/name-service/schema/nym-name-service.json b/contracts/name-service/schema/nym-name-service.json new file mode 100644 index 0000000000..049c130fbb --- /dev/null +++ b/contracts/name-service/schema/nym-name-service.json @@ -0,0 +1,1382 @@ +{ + "contract_name": "nym-name-service", + "contract_version": "0.1.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "description": "Announcing a name pointing to a nym-address", + "type": "object", + "required": [ + "register" + ], + "properties": { + "register": { + "type": "object", + "required": [ + "name", + "owner_signature" + ], + "properties": { + "name": { + "$ref": "#/definitions/NameDetails" + }, + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Delete a name entry by id", + "type": "object", + "required": [ + "delete_id" + ], + "properties": { + "delete_id": { + "type": "object", + "required": [ + "name_id" + ], + "properties": { + "name_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Delete a name entry by name", + "type": "object", + "required": [ + "delete_name" + ], + "properties": { + "delete_name": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "$ref": "#/definitions/NymName" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Change the deposit required for announcing a name", + "type": "object", + "required": [ + "update_deposit_required" + ], + "properties": { + "update_deposit_required": { + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "MessageSignature": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "description": "Query the name by it's assigned id", + "type": "object", + "required": [ + "name_id" + ], + "properties": { + "name_id": { + "type": "object", + "required": [ + "name_id" + ], + "properties": { + "name_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Query the names by the registrator", + "type": "object", + "required": [ + "by_owner" + ], + "properties": { + "by_owner": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "by_name" + ], + "properties": { + "by_name": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "$ref": "#/definitions/NymName" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "by_address" + ], + "properties": { + "by_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "$ref": "#/definitions/Address" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "signing_nonce" + ], + "properties": { + "signing_nonce": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets build information of this contract, such as the commit hash used for the build or rustc version.", + "type": "object", + "required": [ + "get_contract_version" + ], + "properties": { + "get_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.", + "type": "object", + "required": [ + "get_cw2_contract_version" + ], + "properties": { + "get_cw2_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + } + } + }, + "migrate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false + }, + "sudo": null, + "responses": { + "all": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedNamesListResponse", + "type": "object", + "required": [ + "names", + "per_page" + ], + "properties": { + "names": { + "type": "array", + "items": { + "$ref": "#/definitions/RegisteredName" + } + }, + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "RegisteredName": { + "type": "object", + "required": [ + "block_height", + "deposit", + "id", + "name", + "owner" + ], + "properties": { + "block_height": { + "description": "Block height at which the name was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the name.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "id": { + "description": "Unique id assigned to the registerd name.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "name": { + "description": "The registerd name details.", + "allOf": [ + { + "$ref": "#/definitions/NameDetails" + } + ] + }, + "owner": { + "description": "name owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "by_address": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "NamesListResponse", + "type": "object", + "required": [ + "names" + ], + "properties": { + "names": { + "type": "array", + "items": { + "$ref": "#/definitions/RegisteredName" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "RegisteredName": { + "type": "object", + "required": [ + "block_height", + "deposit", + "id", + "name", + "owner" + ], + "properties": { + "block_height": { + "description": "Block height at which the name was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the name.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "id": { + "description": "Unique id assigned to the registerd name.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "name": { + "description": "The registerd name details.", + "allOf": [ + { + "$ref": "#/definitions/NameDetails" + } + ] + }, + "owner": { + "description": "name owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "by_name": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "RegisteredName", + "type": "object", + "required": [ + "block_height", + "deposit", + "id", + "name", + "owner" + ], + "properties": { + "block_height": { + "description": "Block height at which the name was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the name.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "id": { + "description": "Unique id assigned to the registerd name.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "name": { + "description": "The registerd name details.", + "allOf": [ + { + "$ref": "#/definitions/NameDetails" + } + ] + }, + "owner": { + "description": "name owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "by_owner": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "NamesListResponse", + "type": "object", + "required": [ + "names" + ], + "properties": { + "names": { + "type": "array", + "items": { + "$ref": "#/definitions/RegisteredName" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "RegisteredName": { + "type": "object", + "required": [ + "block_height", + "deposit", + "id", + "name", + "owner" + ], + "properties": { + "block_height": { + "description": "Block height at which the name was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the name.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "id": { + "description": "Unique id assigned to the registerd name.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "name": { + "description": "The registerd name details.", + "allOf": [ + { + "$ref": "#/definitions/NameDetails" + } + ] + }, + "owner": { + "description": "name owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "config": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigResponse", + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_c_w2_contract_version": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractVersion", + "type": "object", + "required": [ + "contract", + "version" + ], + "properties": { + "contract": { + "description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing", + "type": "string" + }, + "version": { + "description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)", + "type": "string" + } + }, + "additionalProperties": false + }, + "get_contract_version": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractBuildInformation", + "type": "object", + "required": [ + "build_timestamp", + "build_version", + "commit_branch", + "commit_sha", + "commit_timestamp", + "rustc_version" + ], + "properties": { + "build_timestamp": { + "description": "Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.", + "type": "string" + }, + "build_version": { + "description": "Provides the build version, for example `0.1.0-9-g46f83e1`.", + "type": "string" + }, + "commit_branch": { + "description": "Provides the name of the git branch that was used for the build, for example `master`.", + "type": "string" + }, + "commit_sha": { + "description": "Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`.", + "type": "string" + }, + "commit_timestamp": { + "description": "Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`.", + "type": "string" + }, + "rustc_version": { + "description": "Provides the rustc version that was used for the build, for example `1.52.0-nightly`.", + "type": "string" + } + }, + "additionalProperties": false + }, + "name_id": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "RegisteredName", + "type": "object", + "required": [ + "block_height", + "deposit", + "id", + "name", + "owner" + ], + "properties": { + "block_height": { + "description": "Block height at which the name was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the name.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "id": { + "description": "Unique id assigned to the registerd name.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "name": { + "description": "The registerd name details.", + "allOf": [ + { + "$ref": "#/definitions/NameDetails" + } + ] + }, + "owner": { + "description": "name owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "signing_nonce": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "uint32", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } +} diff --git a/contracts/name-service/schema/raw/execute.json b/contracts/name-service/schema/raw/execute.json new file mode 100644 index 0000000000..22eadf827b --- /dev/null +++ b/contracts/name-service/schema/raw/execute.json @@ -0,0 +1,201 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "description": "Announcing a name pointing to a nym-address", + "type": "object", + "required": [ + "register" + ], + "properties": { + "register": { + "type": "object", + "required": [ + "name", + "owner_signature" + ], + "properties": { + "name": { + "$ref": "#/definitions/NameDetails" + }, + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Delete a name entry by id", + "type": "object", + "required": [ + "delete_id" + ], + "properties": { + "delete_id": { + "type": "object", + "required": [ + "name_id" + ], + "properties": { + "name_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Delete a name entry by name", + "type": "object", + "required": [ + "delete_name" + ], + "properties": { + "delete_name": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "$ref": "#/definitions/NymName" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Change the deposit required for announcing a name", + "type": "object", + "required": [ + "update_deposit_required" + ], + "properties": { + "update_deposit_required": { + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "MessageSignature": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/name-service/schema/raw/instantiate.json b/contracts/name-service/schema/raw/instantiate.json new file mode 100644 index 0000000000..73867fda63 --- /dev/null +++ b/contracts/name-service/schema/raw/instantiate.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/name-service/schema/raw/migrate.json b/contracts/name-service/schema/raw/migrate.json new file mode 100644 index 0000000000..7fbe8c5708 --- /dev/null +++ b/contracts/name-service/schema/raw/migrate.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false +} diff --git a/contracts/name-service/schema/raw/query.json b/contracts/name-service/schema/raw/query.json new file mode 100644 index 0000000000..38f26f8914 --- /dev/null +++ b/contracts/name-service/schema/raw/query.json @@ -0,0 +1,230 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "description": "Query the name by it's assigned id", + "type": "object", + "required": [ + "name_id" + ], + "properties": { + "name_id": { + "type": "object", + "required": [ + "name_id" + ], + "properties": { + "name_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Query the names by the registrator", + "type": "object", + "required": [ + "by_owner" + ], + "properties": { + "by_owner": { + "type": "object", + "required": [ + "owner" + ], + "properties": { + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "by_name" + ], + "properties": { + "by_name": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "$ref": "#/definitions/NymName" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "by_address" + ], + "properties": { + "by_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "$ref": "#/definitions/Address" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "signing_nonce" + ], + "properties": { + "signing_nonce": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets build information of this contract, such as the commit hash used for the build or rustc version.", + "type": "object", + "required": [ + "get_contract_version" + ], + "properties": { + "get_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.", + "type": "object", + "required": [ + "get_cw2_contract_version" + ], + "properties": { + "get_cw2_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + } + } +} diff --git a/contracts/name-service/schema/raw/response_to_all.json b/contracts/name-service/schema/raw/response_to_all.json new file mode 100644 index 0000000000..24cd05dc11 --- /dev/null +++ b/contracts/name-service/schema/raw/response_to_all.json @@ -0,0 +1,178 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedNamesListResponse", + "type": "object", + "required": [ + "names", + "per_page" + ], + "properties": { + "names": { + "type": "array", + "items": { + "$ref": "#/definitions/RegisteredName" + } + }, + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "RegisteredName": { + "type": "object", + "required": [ + "block_height", + "deposit", + "id", + "name", + "owner" + ], + "properties": { + "block_height": { + "description": "Block height at which the name was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the name.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "id": { + "description": "Unique id assigned to the registerd name.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "name": { + "description": "The registerd name details.", + "allOf": [ + { + "$ref": "#/definitions/NameDetails" + } + ] + }, + "owner": { + "description": "name owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/name-service/schema/raw/response_to_by_address.json b/contracts/name-service/schema/raw/response_to_by_address.json new file mode 100644 index 0000000000..ad54c03055 --- /dev/null +++ b/contracts/name-service/schema/raw/response_to_by_address.json @@ -0,0 +1,163 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "NamesListResponse", + "type": "object", + "required": [ + "names" + ], + "properties": { + "names": { + "type": "array", + "items": { + "$ref": "#/definitions/RegisteredName" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "RegisteredName": { + "type": "object", + "required": [ + "block_height", + "deposit", + "id", + "name", + "owner" + ], + "properties": { + "block_height": { + "description": "Block height at which the name was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the name.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "id": { + "description": "Unique id assigned to the registerd name.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "name": { + "description": "The registerd name details.", + "allOf": [ + { + "$ref": "#/definitions/NameDetails" + } + ] + }, + "owner": { + "description": "name owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/name-service/schema/raw/response_to_by_name.json b/contracts/name-service/schema/raw/response_to_by_name.json new file mode 100644 index 0000000000..fb7d77259e --- /dev/null +++ b/contracts/name-service/schema/raw/response_to_by_name.json @@ -0,0 +1,148 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "RegisteredName", + "type": "object", + "required": [ + "block_height", + "deposit", + "id", + "name", + "owner" + ], + "properties": { + "block_height": { + "description": "Block height at which the name was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the name.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "id": { + "description": "Unique id assigned to the registerd name.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "name": { + "description": "The registerd name details.", + "allOf": [ + { + "$ref": "#/definitions/NameDetails" + } + ] + }, + "owner": { + "description": "name owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/name-service/schema/raw/response_to_by_owner.json b/contracts/name-service/schema/raw/response_to_by_owner.json new file mode 100644 index 0000000000..ad54c03055 --- /dev/null +++ b/contracts/name-service/schema/raw/response_to_by_owner.json @@ -0,0 +1,163 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "NamesListResponse", + "type": "object", + "required": [ + "names" + ], + "properties": { + "names": { + "type": "array", + "items": { + "$ref": "#/definitions/RegisteredName" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "RegisteredName": { + "type": "object", + "required": [ + "block_height", + "deposit", + "id", + "name", + "owner" + ], + "properties": { + "block_height": { + "description": "Block height at which the name was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the name.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "id": { + "description": "Unique id assigned to the registerd name.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "name": { + "description": "The registerd name details.", + "allOf": [ + { + "$ref": "#/definitions/NameDetails" + } + ] + }, + "owner": { + "description": "name owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/name-service/schema/raw/response_to_config.json b/contracts/name-service/schema/raw/response_to_config.json new file mode 100644 index 0000000000..16420c1130 --- /dev/null +++ b/contracts/name-service/schema/raw/response_to_config.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigResponse", + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/name-service/schema/raw/response_to_get_c_w2_contract_version.json b/contracts/name-service/schema/raw/response_to_get_c_w2_contract_version.json new file mode 100644 index 0000000000..3ded8c84d9 --- /dev/null +++ b/contracts/name-service/schema/raw/response_to_get_c_w2_contract_version.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractVersion", + "type": "object", + "required": [ + "contract", + "version" + ], + "properties": { + "contract": { + "description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing", + "type": "string" + }, + "version": { + "description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/name-service/schema/raw/response_to_get_contract_version.json b/contracts/name-service/schema/raw/response_to_get_contract_version.json new file mode 100644 index 0000000000..60ae65f3d1 --- /dev/null +++ b/contracts/name-service/schema/raw/response_to_get_contract_version.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractBuildInformation", + "type": "object", + "required": [ + "build_timestamp", + "build_version", + "commit_branch", + "commit_sha", + "commit_timestamp", + "rustc_version" + ], + "properties": { + "build_timestamp": { + "description": "Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.", + "type": "string" + }, + "build_version": { + "description": "Provides the build version, for example `0.1.0-9-g46f83e1`.", + "type": "string" + }, + "commit_branch": { + "description": "Provides the name of the git branch that was used for the build, for example `master`.", + "type": "string" + }, + "commit_sha": { + "description": "Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`.", + "type": "string" + }, + "commit_timestamp": { + "description": "Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`.", + "type": "string" + }, + "rustc_version": { + "description": "Provides the rustc version that was used for the build, for example `1.52.0-nightly`.", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/name-service/schema/raw/response_to_name_id.json b/contracts/name-service/schema/raw/response_to_name_id.json new file mode 100644 index 0000000000..fb7d77259e --- /dev/null +++ b/contracts/name-service/schema/raw/response_to_name_id.json @@ -0,0 +1,148 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "RegisteredName", + "type": "object", + "required": [ + "block_height", + "deposit", + "id", + "name", + "owner" + ], + "properties": { + "block_height": { + "description": "Block height at which the name was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the name.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "id": { + "description": "Unique id assigned to the registerd name.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "name": { + "description": "The registerd name details.", + "allOf": [ + { + "$ref": "#/definitions/NameDetails" + } + ] + }, + "owner": { + "description": "name owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Address": { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.", + "oneOf": [ + { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddressInner" + } + }, + "additionalProperties": false + } + ] + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NameDetails": { + "type": "object", + "required": [ + "address", + "identity_key", + "name" + ], + "properties": { + "address": { + "description": "The address of the name alias.", + "allOf": [ + { + "$ref": "#/definitions/Address" + } + ] + }, + "identity_key": { + "description": "The identity key of the registered name.", + "type": "string" + }, + "name": { + "description": "The name pointing to the nym address", + "allOf": [ + { + "$ref": "#/definitions/NymName" + } + ] + } + }, + "additionalProperties": false + }, + "NymAddressInner": { + "type": "object", + "required": [ + "client_enc", + "client_id", + "gateway_id" + ], + "properties": { + "client_enc": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "gateway_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "NymName": { + "description": "Name stored and pointing a to a nym-address", + "type": "string" + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/name-service/schema/raw/response_to_signing_nonce.json b/contracts/name-service/schema/raw/response_to_signing_nonce.json new file mode 100644 index 0000000000..de85c3e56e --- /dev/null +++ b/contracts/name-service/schema/raw/response_to_signing_nonce.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "uint32", + "type": "integer", + "format": "uint32", + "minimum": 0.0 +} diff --git a/contracts/name-service/src/bin/schema.rs b/contracts/name-service/src/bin/schema.rs new file mode 100644 index 0000000000..92f8f3f6ab --- /dev/null +++ b/contracts/name-service/src/bin/schema.rs @@ -0,0 +1,14 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::write_api; +use nym_name_service_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + migrate: MigrateMsg, + } +} diff --git a/contracts/name-service/src/contract.rs b/contracts/name-service/src/contract.rs index f3347c4d6a..35294382c8 100644 --- a/contracts/name-service/src/contract.rs +++ b/contracts/name-service/src/contract.rs @@ -155,7 +155,7 @@ mod tests { let deposit = nyms(99); let owner = "steve"; let (name, owner_signature) = - new_name_details_with_sign(deps.as_mut(), &mut rng, "foo", "address", owner, deposit); + new_name_details_with_sign(deps.as_mut(), &mut rng, "foo", owner, deposit); let msg = ExecuteMsg::Register { name, owner_signature, @@ -200,7 +200,7 @@ mod tests { let deposit = nyms(101); let owner = "steve"; let (name, owner_signature) = - new_name_details_with_sign(deps.as_mut(), &mut rng, "foo", "address", owner, deposit); + new_name_details_with_sign(deps.as_mut(), &mut rng, "foo", owner, deposit); let msg = ExecuteMsg::Register { name, owner_signature, @@ -245,14 +245,8 @@ mod tests { // Setup let deposit = nyms(100); let owner = "steve"; - let (name, owner_signature) = new_name_details_with_sign( - deps.as_mut(), - &mut rng, - "my-name", - "my-address", - owner, - deposit, - ); + let (name, owner_signature) = + new_name_details_with_sign(deps.as_mut(), &mut rng, "my-name", owner, deposit); // Register let msg = ExecuteMsg::Register { @@ -290,14 +284,8 @@ mod tests { // Setup let deposit = nyms(100); let owner = "steve"; - let (name, owner_signature) = new_name_details_with_sign( - deps.as_mut(), - &mut rng, - "my-name", - "my-address", - owner, - deposit.clone(), - ); + let (name, owner_signature) = + new_name_details_with_sign(deps.as_mut(), &mut rng, "my-name", owner, deposit.clone()); // Register let msg = ExecuteMsg::Register { @@ -315,10 +303,6 @@ mod tests { get_attribute(&res, "register", "name"), "my-name".to_string() ); - assert_eq!( - get_attribute(&res, "register", "nym_address"), - "my-address".to_string() - ); // Check that the nonce has been incremented, but only for the owner assert_current_nonce(deps.as_ref(), &Addr::unchecked("steve"), 1); @@ -348,14 +332,8 @@ mod tests { // Register let deposit = nyms(100); let steve = "steve"; - let (name, owner_signature) = new_name_details_with_sign( - deps.as_mut(), - &mut rng, - "my-name", - "my-address", - steve, - deposit.clone(), - ); + let (name, owner_signature) = + new_name_details_with_sign(deps.as_mut(), &mut rng, "my-name", steve, deposit.clone()); let msg = ExecuteMsg::Register { name: name.clone(), owner_signature, diff --git a/contracts/name-service/src/contract/execute.rs b/contracts/name-service/src/contract/execute.rs index e41e57d7f4..38084e0f57 100644 --- a/contracts/name-service/src/contract/execute.rs +++ b/contracts/name-service/src/contract/execute.rs @@ -66,13 +66,23 @@ fn ensure_name_exists(deps: Deps, name_id: NameId) -> Result<()> { fn ensure_name_not_exists(deps: Deps, name: &NymName) -> Result<()> { if state::names::has_name(deps.storage, name) { - println!("name already exists"); Err(NameServiceError::NameAlreadyRegistered { name: name.clone() }) } else { Ok(()) } } +fn ensure_identity_key_is_part_of_nym_address(address: &Address, identity_key: &str) -> Result<()> { + if address.client_id() == identity_key { + Ok(()) + } else { + Err(NameServiceError::IdentityKeyMismatch { + address: address.clone(), + identity_key: identity_key.to_string(), + }) + } +} + fn ensure_sender_authorized(info: MessageInfo, names: &RegisteredName) -> Result<()> { if info.sender == names.owner { Ok(()) @@ -135,6 +145,7 @@ pub fn register( name: NameDetails, owner_signature: MessageSignature, ) -> Result { + ensure_identity_key_is_part_of_nym_address(&name.address, &name.identity_key)?; ensure_name_not_exists(deps.as_ref(), &name.name)?; ensure_max_names_per_owner(deps.as_ref(), info.sender.clone())?; ensure_max_names_per_address(deps.as_ref(), name.address.clone())?; diff --git a/contracts/name-service/src/integration_tests/delete.rs b/contracts/name-service/src/integration_tests/delete.rs index feede51739..b26b444ceb 100644 --- a/contracts/name-service/src/integration_tests/delete.rs +++ b/contracts/name-service/src/integration_tests/delete.rs @@ -1,5 +1,5 @@ use cosmwasm_std::Addr; -use nym_name_service_common::{response::PagedNamesListResponse, Address, NymName}; +use nym_name_service_common::{response::PagedNamesListResponse, NymName}; use rstest::rstest; use crate::{ @@ -19,7 +19,6 @@ fn setup() -> TestSetup { fn delete_name(mut setup: TestSetup) { setup.sign_and_register( &NymName::new("my_name").unwrap(), - &Address::new("address"), &Addr::unchecked("owner"), &nyms(100), ); @@ -39,7 +38,6 @@ fn only_owner_can_delete_name(mut setup: TestSetup) { assert_eq!(setup.contract_balance(), nyms(0)); setup.sign_and_register( &NymName::new("name").unwrap(), - &Address::new("nymAddress"), &Addr::unchecked("owner"), &nyms(100), ); @@ -63,7 +61,6 @@ fn only_owner_can_delete_name(mut setup: TestSetup) { fn cant_delete_name_that_does_not_exist(mut setup: TestSetup) { setup.sign_and_register( &NymName::new("foo").unwrap(), - &Address::new("nymAddress"), &Addr::unchecked("owner"), &nyms(100), ); @@ -94,8 +91,6 @@ fn cant_delete_name_that_does_not_exist(mut setup: TestSetup) { fn register_multiple_names_and_deleting_by_name(mut setup: TestSetup) { let owner1 = Addr::unchecked("wealthy_owner_1"); let owner2 = Addr::unchecked("wealthy_owner_2"); - let address1 = Address::new("address1"); - let address2 = Address::new("address2"); let name1 = NymName::new("name1").unwrap(); let name2 = NymName::new("name2").unwrap(); let name3 = NymName::new("name3").unwrap(); @@ -105,11 +100,11 @@ fn register_multiple_names_and_deleting_by_name(mut setup: TestSetup) { // We register the same address three times, but with different owners assert_eq!(setup.contract_balance(), nyms(0)); assert_eq!(setup.balance(&owner1), nyms(1000)); - let s1 = setup.sign_and_register(&name1, &address1, &owner1, &nyms(100)); - let s2 = setup.sign_and_register(&name2, &address1, &owner1, &nyms(100)); - let s3 = setup.sign_and_register(&name3, &address2, &owner1, &nyms(100)); - let s4 = setup.sign_and_register(&name4, &address1, &owner2, &nyms(100)); - let s5 = setup.sign_and_register(&name5, &address2, &owner2, &nyms(100)); + let s1 = setup.sign_and_register(&name1, &owner1, &nyms(100)); + let s2 = setup.sign_and_register(&name2, &owner1, &nyms(100)); + let s3 = setup.sign_and_register(&name3, &owner1, &nyms(100)); + let s4 = setup.sign_and_register(&name4, &owner2, &nyms(100)); + let s5 = setup.sign_and_register(&name5, &owner2, &nyms(100)); assert_eq!(setup.contract_balance(), nyms(500)); assert_eq!(setup.balance(&owner1), nyms(700)); @@ -117,11 +112,11 @@ fn register_multiple_names_and_deleting_by_name(mut setup: TestSetup) { setup.query_all(), PagedNamesListResponse { names: vec![ - new_name(1, &name1, &address1, &owner1, s1.identity_key()), - new_name(2, &name2, &address1, &owner1, s2.identity_key()), - new_name(3, &name3, &address2, &owner1, s3.identity_key()), - new_name(4, &name4, &address1, &owner2, s4.identity_key()), - new_name(5, &name5, &address2, &owner2, s5.identity_key()), + new_name(1, &name1, s1.address(), &owner1, s1.identity_key()), + new_name(2, &name2, s2.address(), &owner1, s2.identity_key()), + new_name(3, &name3, s3.address(), &owner1, s3.identity_key()), + new_name(4, &name4, s4.address(), &owner2, s4.identity_key()), + new_name(5, &name5, s5.address(), &owner2, s5.identity_key()), ], per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize, start_next_after: Some(5), @@ -136,10 +131,10 @@ fn register_multiple_names_and_deleting_by_name(mut setup: TestSetup) { setup.query_all(), PagedNamesListResponse { names: vec![ - new_name(2, &name2, &address1, &owner1, s2.identity_key()), - new_name(3, &name3, &address2, &owner1, s3.identity_key()), - new_name(4, &name4, &address1, &owner2, s4.identity_key()), - new_name(5, &name5, &address2, &owner2, s5.identity_key()), + new_name(2, &name2, s2.address(), &owner1, s2.identity_key()), + new_name(3, &name3, s3.address(), &owner1, s3.identity_key()), + new_name(4, &name4, s4.address(), &owner2, s4.identity_key()), + new_name(5, &name5, s5.address(), &owner2, s5.identity_key()), ], per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize, start_next_after: Some(5), diff --git a/contracts/name-service/src/integration_tests/name_id.rs b/contracts/name-service/src/integration_tests/name_id.rs index 39f5a993cf..9751a4c248 100644 --- a/contracts/name-service/src/integration_tests/name_id.rs +++ b/contracts/name-service/src/integration_tests/name_id.rs @@ -1,5 +1,5 @@ use cosmwasm_std::Addr; -use nym_name_service_common::{Address, NymName}; +use nym_name_service_common::NymName; use crate::test_helpers::{fixture::new_name, helpers::nyms}; @@ -10,19 +10,16 @@ fn name_id_is_not_resused_when_deleting_and_then_adding_a_new_names() { let mut setup = TestSetup::new(); setup.sign_and_register( &NymName::new("myname1").unwrap(), - &Address::new("nymAddress1"), &Addr::unchecked("owner1"), &nyms(100), ); let s2 = setup.sign_and_register( &NymName::new("myname2").unwrap(), - &Address::new("nymAddress2"), &Addr::unchecked("owner2"), &nyms(100), ); setup.sign_and_register( &NymName::new("myname3").unwrap(), - &Address::new("nymAddress3"), &Addr::unchecked("owner3"), &nyms(100), ); @@ -35,7 +32,7 @@ fn name_id_is_not_resused_when_deleting_and_then_adding_a_new_names() { vec![new_name( 2, &NymName::new("myname2").unwrap(), - &Address::new("nymAddress2"), + s2.address(), &Addr::unchecked("owner2"), s2.identity_key(), )] @@ -43,7 +40,6 @@ fn name_id_is_not_resused_when_deleting_and_then_adding_a_new_names() { let s4 = setup.sign_and_register( &NymName::new("myname4").unwrap(), - &Address::new("nymAddress4"), &Addr::unchecked("owner4"), &nyms(100), ); @@ -54,14 +50,14 @@ fn name_id_is_not_resused_when_deleting_and_then_adding_a_new_names() { new_name( 2, &NymName::new("myname2").unwrap(), - &Address::new("nymAddress2"), + s2.address(), &Addr::unchecked("owner2"), s2.identity_key(), ), new_name( 4, &NymName::new("myname4").unwrap(), - &Address::new("nymAddress4"), + s4.address(), &Addr::unchecked("owner4"), s4.identity_key(), ) diff --git a/contracts/name-service/src/integration_tests/query.rs b/contracts/name-service/src/integration_tests/query.rs index 67069a0a1d..2dc47b98ae 100644 --- a/contracts/name-service/src/integration_tests/query.rs +++ b/contracts/name-service/src/integration_tests/query.rs @@ -1,7 +1,7 @@ use cosmwasm_std::Addr; use nym_name_service_common::{ response::{ConfigResponse, PagedNamesListResponse}, - Address, NymName, + NymName, }; use crate::{ @@ -26,8 +26,6 @@ fn check_paging() { let mut setup = TestSetup::new(); let owner1 = Addr::unchecked("wealthy_owner_1"); let owner2 = Addr::unchecked("wealthy_owner_2"); - let address1 = Address::new("nymAddress1"); - let address2 = Address::new("nymAddress2"); let name1 = NymName::new("name1").unwrap(); let name2 = NymName::new("name2").unwrap(); let name3 = NymName::new("name3").unwrap(); @@ -37,23 +35,22 @@ fn check_paging() { // We register the same address three times, but with different owners assert_eq!(setup.contract_balance(), nyms(0)); assert_eq!(setup.balance(&owner1), nyms(1000)); - let s1 = setup.sign_and_register(&name1, &address1, &owner1, &nyms(100)); - let s2 = setup.sign_and_register(&name2, &address1, &owner1, &nyms(100)); - let s3 = setup.sign_and_register(&name3, &address2, &owner1, &nyms(100)); - let s4 = setup.sign_and_register(&name4, &address1, &owner2, &nyms(100)); - let s5 = setup.sign_and_register(&name5, &address2, &owner2, &nyms(100)); - + let s1 = setup.sign_and_register(&name1, &owner1, &nyms(100)); + let s2 = setup.sign_and_register(&name2, &owner1, &nyms(100)); + let s3 = setup.sign_and_register(&name3, &owner1, &nyms(100)); + let s4 = setup.sign_and_register(&name4, &owner2, &nyms(100)); + let s5 = setup.sign_and_register(&name5, &owner2, &nyms(100)); assert_eq!(setup.contract_balance(), nyms(500)); assert_eq!(setup.balance(&owner1), nyms(700)); assert_eq!( setup.query_all(), PagedNamesListResponse { names: vec![ - new_name(1, &name1, &address1, &owner1, s1.identity_key()), - new_name(2, &name2, &address1, &owner1, s2.identity_key()), - new_name(3, &name3, &address2, &owner1, s3.identity_key()), - new_name(4, &name4, &address1, &owner2, s4.identity_key()), - new_name(5, &name5, &address2, &owner2, s5.identity_key()), + new_name(1, &name1, s1.address(), &owner1, s1.identity_key()), + new_name(2, &name2, s2.address(), &owner1, s2.identity_key()), + new_name(3, &name3, s3.address(), &owner1, s3.identity_key()), + new_name(4, &name4, s4.address(), &owner2, s4.identity_key()), + new_name(5, &name5, s5.address(), &owner2, s5.identity_key()), ], per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize, start_next_after: Some(5), @@ -66,8 +63,8 @@ fn check_paging() { setup.query_all_with_limit(Some(2), None), PagedNamesListResponse { names: vec![ - new_name(2, &name2, &address1, &owner1, s2.identity_key()), - new_name(3, &name3, &address2, &owner1, s3.identity_key()), + new_name(2, &name2, s2.address(), &owner1, s2.identity_key()), + new_name(3, &name3, s3.address(), &owner1, s3.identity_key()), ], per_page: 2, start_next_after: Some(3), @@ -78,8 +75,8 @@ fn check_paging() { setup.query_all_with_limit(Some(2), Some(2)), PagedNamesListResponse { names: vec![ - new_name(3, &name3, &address2, &owner1, s3.identity_key()), - new_name(4, &name4, &address1, &owner2, s4.identity_key()), + new_name(3, &name3, s3.address(), &owner1, s3.identity_key()), + new_name(4, &name4, s4.address(), &owner2, s4.identity_key()), ], per_page: 2, start_next_after: Some(4), diff --git a/contracts/name-service/src/integration_tests/register.rs b/contracts/name-service/src/integration_tests/register.rs index 9d6d339dbc..e46499ce32 100644 --- a/contracts/name-service/src/integration_tests/register.rs +++ b/contracts/name-service/src/integration_tests/register.rs @@ -1,7 +1,7 @@ use cosmwasm_std::Addr; +use nym_crypto::asymmetric::identity; use nym_name_service_common::{ - error::NameServiceError, response::PagedNamesListResponse, Address, NameDetails, NymName, - RegisteredName, + error::NameServiceError, response::PagedNamesListResponse, NameDetails, NymName, RegisteredName, }; use rstest::rstest; @@ -31,16 +31,21 @@ fn basic_register(mut setup: TestSetup) { // Register a first name let owner = Addr::unchecked("owner"); let name = NymName::new("steves-server").unwrap(); - let nym_address = Address::new("nym-address"); + let (nym_address, id_keypair) = setup.new_nym_address(); assert_eq!(setup.contract_balance(), nyms(0)); assert_eq!(setup.balance(&owner), nyms(250)); assert_eq!(setup.query_signing_nonce(owner.to_string()), 0); - let reg_name = setup.new_name(&name, &nym_address); + let reg_name = setup.new_name_from_address(&name, &nym_address, id_keypair); let payload = setup.payload_to_sign(&owner, &nyms(100), ®_name.name); let reg_name = reg_name.sign(payload); setup.register(®_name, &owner); + // Confirm that the client id in the name matches the identity key. + let address_client_id = reg_name.name.address.client_id(); + let identity_key = reg_name.keys.public_key().to_base58_string(); + assert_eq!(address_client_id, &identity_key); + // Deposit is deposited to contract and deducted from owners's balance assert_eq!(setup.contract_balance(), nyms(100)); assert_eq!(setup.balance(&owner), nyms(150)); @@ -83,8 +88,8 @@ fn basic_register(mut setup: TestSetup) { // Register a second name let owner2 = Addr::unchecked("owner2"); let name2 = NymName::new("another_server").unwrap(); - let nym_address2 = Address::new("nymAddress2"); - let reg_name2 = setup.new_signed_name(&name2, &nym_address2, &owner2, &nyms(100)); + let reg_name2 = setup.new_signed_name(&name2, &owner2, &nyms(100)); + let nym_address2 = reg_name2.address().clone(); setup.register(®_name2, &owner2); assert_eq!(setup.contract_balance(), nyms(200)); @@ -105,8 +110,7 @@ fn basic_register(mut setup: TestSetup) { fn register_fails_when_owner_mismatch(mut setup: TestSetup) { let owner = Addr::unchecked("owner"); let name = NymName::new("steves-server").unwrap(); - let nym_address = Address::new("nym-address"); - let reg_name = setup.new_signed_name(&name, &nym_address, &owner, &nyms(100)); + let reg_name = setup.new_signed_name(&name, &owner, &nyms(100)); let res = setup .try_register(®_name, &Addr::unchecked("owner2")) .unwrap_err(); @@ -124,32 +128,17 @@ fn signing_nonce_is_increased_when_registering(mut setup: TestSetup) { assert_eq!(setup.query_signing_nonce(owner1.to_string()), 0); assert_eq!(setup.query_signing_nonce(owner2.to_string()), 0); - setup.sign_and_register( - &NymName::new("myname1").unwrap(), - &Address::new("address1"), - &owner1, - &nyms(100), - ); + setup.sign_and_register(&NymName::new("myname1").unwrap(), &owner1, &nyms(100)); assert_eq!(setup.query_signing_nonce(owner1.to_string()), 1); assert_eq!(setup.query_signing_nonce(owner2.to_string()), 0); - setup.sign_and_register( - &NymName::new("myname2").unwrap(), - &Address::new("address2"), - &owner2, - &nyms(100), - ); + setup.sign_and_register(&NymName::new("myname2").unwrap(), &owner2, &nyms(100)); assert_eq!(setup.query_signing_nonce(owner1.to_string()), 1); assert_eq!(setup.query_signing_nonce(owner2.to_string()), 1); - setup.sign_and_register( - &NymName::new("myname3").unwrap(), - &Address::new("address3"), - &owner2, - &nyms(100), - ); + setup.sign_and_register(&NymName::new("myname3").unwrap(), &owner2, &nyms(100)); assert_eq!(setup.query_signing_nonce(owner1.to_string()), 1); assert_eq!(setup.query_signing_nonce(owner2.to_string()), 2); @@ -160,14 +149,12 @@ fn creating_two_names_in_a_row_without_announcing_fails(mut setup: TestSetup) { let owner = Addr::unchecked("wealthy_owner_1"); let name1 = NymName::new("steves-server1").unwrap(); let name2 = NymName::new("steves-server2").unwrap(); - let address1 = Address::new("nymAddress1"); - let address2 = Address::new("nymAddress2"); let deposit = nyms(100); - let s1 = setup.new_signed_name(&name1, &address1, &owner, &deposit); + let s1 = setup.new_signed_name(&name1, &owner, &deposit); // This second name will be signed with the same nonce - let s2 = setup.new_signed_name(&name2, &address2, &owner, &deposit); + let s2 = setup.new_signed_name(&name2, &owner, &deposit); // Announce the first service works, and this increments the nonce setup.register(&s1, &owner); @@ -187,7 +174,6 @@ fn cant_register_a_name_without_funds(mut setup: TestSetup) { assert_eq!(setup.balance("owner"), nyms(250)); let name1 = setup.new_signed_name( &NymName::new("my_name").unwrap(), - &Address::new("nymAddress"), &Addr::unchecked("owner"), &nyms(100), ); @@ -197,7 +183,6 @@ fn cant_register_a_name_without_funds(mut setup: TestSetup) { let name2 = setup.new_signed_name( &NymName::new("my_name2").unwrap(), - &Address::new("nymAddress"), &Addr::unchecked("owner"), &nyms(100), ); @@ -206,7 +191,6 @@ fn cant_register_a_name_without_funds(mut setup: TestSetup) { assert_eq!(setup.balance("owner"), nyms(50)); let name3 = setup.new_signed_name( &NymName::new("my_name3").unwrap(), - &Address::new("nymAddress"), &Addr::unchecked("owner"), &nyms(100), ); @@ -229,7 +213,6 @@ fn cant_register_a_name_without_funds(mut setup: TestSetup) { fn cant_register_the_same_name_multiple_times(mut setup: TestSetup) { let name1 = setup.new_signed_name( &NymName::new("name").unwrap(), - &Address::new("nymAddress"), &Addr::unchecked("owner"), &nyms(100), ); @@ -246,16 +229,40 @@ fn cant_register_the_same_name_multiple_times(mut setup: TestSetup) { ); } +fn clone_keys(keys: &identity::KeyPair) -> identity::KeyPair { + let priv_bytes = keys.private_key().to_bytes(); + let pub_bytes = keys.public_key().to_bytes(); + identity::KeyPair::from_bytes(&priv_bytes, &pub_bytes).unwrap() +} + #[rstest] fn can_register_multiple_names_for_the_same_nym_address(mut setup: TestSetup) { let name1 = NymName::new("name1").unwrap(); let name2 = NymName::new("name2").unwrap(); - let address = Address::new("nymaddress"); + // let address = Address::new("nym.add@ress").unwrap(); + let (address, id_keys) = setup.new_nym_address(); let owner = Addr::unchecked("owner"); - let reg_name1 = setup.new_signed_name(&name1, &address, &owner, &nyms(100)); + // We duplicate the keypair here to ensure that the same keypair is used for both names. + // The private key lacks a clone method, for good reason, so we have to serialize and + // deserialize it. + let id_keys2 = clone_keys(&id_keys); + assert_eq!(id_keys.public_key(), id_keys2.public_key()); + assert_eq!( + id_keys.private_key().to_base58_string(), + id_keys2.private_key().to_base58_string() + ); + + // let reg_name1 = setup.new_signed_name(&name1, &owner, &nyms(100)); + let reg_name1 = setup.new_name_from_address(&name1, &address, id_keys); + let payload = setup.payload_to_sign(&owner, &nyms(100), ®_name1.name); + let reg_name1 = reg_name1.sign(payload); setup.register(®_name1, &owner); - let reg_name2 = setup.new_signed_name(&name2, &address, &owner, &nyms(100)); + + // let reg_name2 = setup.new_signed_name(&name2, &owner, &nyms(100)); + let reg_name2 = setup.new_name_from_address(&name2, &address, id_keys2); + let payload = setup.payload_to_sign(&owner, &nyms(100), ®_name2.name); + let reg_name2 = reg_name2.sign(payload); setup.register(®_name2, &owner); assert_eq!( @@ -266,4 +273,3 @@ fn can_register_multiple_names_for_the_same_nym_address(mut setup: TestSetup) { ], ); } -// diff --git a/contracts/name-service/src/integration_tests/test_name.rs b/contracts/name-service/src/integration_tests/test_name.rs index f33233a0a0..5057bc8075 100644 --- a/contracts/name-service/src/integration_tests/test_name.rs +++ b/contracts/name-service/src/integration_tests/test_name.rs @@ -3,29 +3,28 @@ use nym_crypto::asymmetric::identity; use nym_name_service_common::{ signing_types::SignableNameRegisterMsg, Address, NameDetails, NymName, }; -use rand_chacha::ChaCha20Rng; use crate::test_helpers::signing::ed25519_sign_message; pub struct TestName { pub name: NameDetails, - pub keys: identity::KeyPair, - pub rng: ChaCha20Rng, + pub id_keys: identity::KeyPair, } impl TestName { - pub fn new(rng: &mut ChaCha20Rng, name: NymName, address: Address) -> Self { - let keys = identity::KeyPair::new(rng); + pub fn new(name: NymName, address: Address, id_keys: identity::KeyPair) -> Self { + let identity_key = id_keys.public_key().to_base58_string(); + assert_eq!( + identity_key, + address.client_id().to_string(), + "address and identity key must match" + ); let name = NameDetails { name, address, - identity_key: keys.public_key().to_base58_string(), + identity_key, }; - Self { - name, - keys, - rng: rng.clone(), - } + Self { name, id_keys } } pub fn identity_key(&self) -> &IdentityKey { @@ -37,10 +36,10 @@ impl TestName { } pub fn sign(self, payload: SignableNameRegisterMsg) -> SignedTestName { - let owner_signature = ed25519_sign_message(payload, self.keys.private_key()); + let owner_signature = ed25519_sign_message(payload, self.id_keys.private_key()); SignedTestName { name: self.name, - keys: self.keys, + keys: self.id_keys, owner_signature, } } @@ -66,6 +65,10 @@ impl SignedTestName { pub fn details(&self) -> &NameDetails { &self.name } + + pub fn address(&self) -> &Address { + &self.name.address + } } impl From for NameDetails { diff --git a/contracts/name-service/src/integration_tests/test_setup.rs b/contracts/name-service/src/integration_tests/test_setup.rs index 264d2d236b..fd396aec46 100644 --- a/contracts/name-service/src/integration_tests/test_setup.rs +++ b/contracts/name-service/src/integration_tests/test_setup.rs @@ -1,6 +1,7 @@ use cosmwasm_std::{coins, Addr, Coin, Uint128}; use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor}; use nym_contracts_common::signing::Nonce; +use nym_crypto::asymmetric::identity; use nym_name_service_common::{ msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, response::{ConfigResponse, PagedNamesListResponse}, @@ -103,8 +104,26 @@ impl TestSetup { self.query(&QueryMsg::SigningNonce { address }) } - pub fn new_name(&mut self, name: &NymName, address: &Address) -> TestName { - TestName::new(&mut self.rng, name.clone(), address.clone()) + // Create a new random address and a corresponding keypair. + pub fn new_nym_address(&mut self) -> (Address, identity::KeyPair) { + crate::test_helpers::fixture::new_address(&mut self.rng) + } + + // Create a new name with a given address. It's up to the caller to make sure that the keys + // match. + pub fn new_name_from_address( + &mut self, + name: &NymName, + address: &Address, + id_keys: identity::KeyPair, + ) -> TestName { + TestName::new(name.clone(), address.clone(), id_keys) + } + + // Create a new name with a random address + pub fn new_name(&mut self, name: &NymName) -> TestName { + let (address, id_keys) = self.new_nym_address(); + self.new_name_from_address(name, &address, id_keys) } pub fn payload_to_sign( @@ -120,11 +139,10 @@ impl TestSetup { pub fn new_signed_name( &mut self, name: &NymName, - address: &Address, owner: &Addr, deposit: &Coin, ) -> SignedTestName { - let name = self.new_name(name, address); + let name = self.new_name(name); let payload = self.payload_to_sign(owner, deposit, name.details()); name.sign(payload) } @@ -161,11 +179,10 @@ impl TestSetup { pub fn sign_and_register( &mut self, name: &NymName, - address: &Address, owner: &Addr, deposit: &Coin, ) -> SignedTestName { - let signed_name = self.new_signed_name(name, address, owner, deposit); + let signed_name = self.new_signed_name(name, owner, deposit); self.register(&signed_name, owner); signed_name } diff --git a/contracts/name-service/src/state/name_id_counter.rs b/contracts/name-service/src/state/name_id_counter.rs index c45e5169e6..3c5e2a56f9 100644 --- a/contracts/name-service/src/state/name_id_counter.rs +++ b/contracts/name-service/src/state/name_id_counter.rs @@ -30,18 +30,15 @@ mod tests { let mut deps = instantiate_test_contract(); let mut rng = test_rng(); - let (id1, name1) = register_name(deps.as_mut(), &mut rng, "foo", "addr1", "steve"); - let (id2, name2) = register_name(deps.as_mut(), &mut rng, "bar", "addr2", "steve"); - let (id3, name3) = register_name(deps.as_mut(), &mut rng, "baz", "addr3", "steve"); + let (id1, name1) = register_name(deps.as_mut(), &mut rng, "foo", "steve"); + let (id2, name2) = register_name(deps.as_mut(), &mut rng, "bar", "steve"); + let (id3, name3) = register_name(deps.as_mut(), &mut rng, "baz", "steve"); assert_eq!(id1, 1); assert_eq!(id2, 2); assert_eq!(id3, 3); assert_eq!(name1.name.as_str(), "foo"); - assert_eq!(name1.address.as_str(), "addr1"); assert_eq!(name2.name.as_str(), "bar"); - assert_eq!(name2.address.as_str(), "addr2"); assert_eq!(name3.name.as_str(), "baz"); - assert_eq!(name3.address.as_str(), "addr3"); assert_names( deps.as_ref(), &[ @@ -76,8 +73,8 @@ mod tests { let mut rng = test_rng(); // Register two names - let (_, name1) = register_name(deps.as_mut(), &mut rng, "one", "sdfjkhsdfhr", "steve"); - register_name(deps.as_mut(), &mut rng, "two", "suereljer", "steve"); + let (_, name1) = register_name(deps.as_mut(), &mut rng, "one", "steve"); + register_name(deps.as_mut(), &mut rng, "two", "steve"); // Delete the last entry delete_name_id(deps.as_mut(), 2, "steve"); @@ -94,7 +91,7 @@ mod tests { // Create a third entry. The index should not reuse the previous entry that we just // deleted. - let (id3, _) = register_name(deps.as_mut(), &mut rng, "three", "ufd", "steve"); + let (id3, _) = register_name(deps.as_mut(), &mut rng, "three", "steve"); assert_eq!(id3, 3); } } diff --git a/contracts/name-service/src/state/names.rs b/contracts/name-service/src/state/names.rs index 3b2b02f82d..813a2fe85e 100644 --- a/contracts/name-service/src/state/names.rs +++ b/contracts/name-service/src/state/names.rs @@ -120,6 +120,8 @@ pub fn load_owner(store: &dyn Storage, owner: Addr) -> Result, pub limit: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } @@ -177,27 +179,27 @@ mod tests { #[rstest::fixture] fn uniq_names() -> Vec { vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(2, "two", "address_two", "owner_two"), - name_fixture_full(3, "three", "address_three", "owner_three"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(2, "two", "address.two@b", "owner_two"), + name_fixture_full(3, "three", "address.three@c", "owner_three"), ] } #[rstest::fixture] fn overlapping_addresses() -> Vec { vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(2, "two", "address_two", "owner_two"), - name_fixture_full(3, "three", "address_two", "owner_three"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(2, "two", "address.two@b", "owner_two"), + name_fixture_full(3, "three", "address.two@b", "owner_three"), ] } #[rstest::fixture] fn overlapping_owners() -> Vec { vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(2, "two", "address_two", "owner_two"), - name_fixture_full(3, "three", "address_three", "owner_two"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(2, "two", "address.two@b", "owner_two"), + name_fixture_full(3, "three", "address.three@c", "owner_two"), ] } @@ -393,8 +395,8 @@ mod tests { load_all_paged(deps.as_ref().storage, None, None).unwrap(), PagedLoad { names: vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(3, "three", "address_three", "owner_three"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(3, "three", "address.three@c", "owner_three"), ], limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize, start_next_after: Some(3), @@ -413,8 +415,8 @@ mod tests { load_all_paged(deps.as_ref().storage, None, None).unwrap(), PagedLoad { names: vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(3, "three", "address_three", "owner_three"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(3, "three", "address.three@c", "owner_three"), ], limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize, start_next_after: Some(3), @@ -442,19 +444,23 @@ mod tests { load_all_paged(deps.as_ref().storage, None, None).unwrap(), PagedLoad { names: vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(2, "two", "address_two", "owner_two"), - name_fixture_full(3, "three", "address_two", "owner_three"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(2, "two", "address.two@b", "owner_two"), + name_fixture_full(3, "three", "address.two@b", "owner_three"), ], limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize, start_next_after: Some(3), } ); assert_eq!( - load_address(deps.as_ref().storage, &Address::new("address_two")).unwrap(), + load_address( + deps.as_ref().storage, + &Address::new("address.two@b").unwrap() + ) + .unwrap(), vec![ - name_fixture_full(2, "two", "address_two", "owner_two"), - name_fixture_full(3, "three", "address_two", "owner_three"), + name_fixture_full(2, "two", "address.two@b", "owner_two"), + name_fixture_full(3, "three", "address.two@b", "owner_three"), ] ); } @@ -470,8 +476,8 @@ mod tests { load_all_paged(deps.as_ref().storage, None, None).unwrap(), PagedLoad { names: vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(3, "three", "address_two", "owner_three"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(3, "three", "address.two@b", "owner_three"), ], limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize, start_next_after: Some(3), @@ -494,8 +500,8 @@ mod tests { load_all_paged(deps.as_ref().storage, None, None).unwrap(), PagedLoad { names: vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(3, "three", "address_two", "owner_three"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(3, "three", "address.two@b", "owner_three"), ], limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize, start_next_after: Some(3), @@ -523,9 +529,9 @@ mod tests { load_all_paged(deps.as_ref().storage, None, None).unwrap(), PagedLoad { names: vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(2, "two", "address_two", "owner_two"), - name_fixture_full(3, "three", "address_three", "owner_two"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(2, "two", "address.two@b", "owner_two"), + name_fixture_full(3, "three", "address.three@c", "owner_two"), ], limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize, start_next_after: Some(3), @@ -534,8 +540,8 @@ mod tests { assert_eq!( load_owner(deps.as_ref().storage, Addr::unchecked("owner_two")).unwrap(), vec![ - name_fixture_full(2, "two", "address_two", "owner_two"), - name_fixture_full(3, "three", "address_three", "owner_two"), + name_fixture_full(2, "two", "address.two@b", "owner_two"), + name_fixture_full(3, "three", "address.three@c", "owner_two"), ] ); } @@ -553,8 +559,8 @@ mod tests { load_all_paged(deps.as_ref().storage, None, None).unwrap(), PagedLoad { names: vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(3, "three", "address_three", "owner_two"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(3, "three", "address.three@c", "owner_two"), ], limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize, start_next_after: Some(3), @@ -575,8 +581,8 @@ mod tests { load_all_paged(deps.as_ref().storage, None, None).unwrap(), PagedLoad { names: vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(3, "three", "address_three", "owner_two"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(3, "three", "address.three@c", "owner_two"), ], limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize, start_next_after: Some(3), @@ -591,8 +597,8 @@ mod tests { load_all_paged(&deps.storage, Some(2), None).unwrap(), PagedLoad { names: vec![ - name_fixture_full(1, "one", "address_one", "owner_one"), - name_fixture_full(2, "two", "address_two", "owner_two"), + name_fixture_full(1, "one", "address.one@a", "owner_one"), + name_fixture_full(2, "two", "address.two@b", "owner_two"), ], limit: 2, start_next_after: Some(2), @@ -604,7 +610,7 @@ mod tests { names: vec![name_fixture_full( 3, "three", - "address_three", + "address.three@c", "owner_three" )], limit: 1, @@ -617,7 +623,7 @@ mod tests { names: vec![name_fixture_full( 3, "three", - "address_three", + "address.three@c", "owner_three" )], limit: 2, diff --git a/contracts/name-service/src/test_helpers/fixture.rs b/contracts/name-service/src/test_helpers/fixture.rs index 5c24ece5d8..8a71681486 100644 --- a/contracts/name-service/src/test_helpers/fixture.rs +++ b/contracts/name-service/src/test_helpers/fixture.rs @@ -1,7 +1,8 @@ use cosmwasm_std::{Addr, Coin, DepsMut}; use nym_contracts_common::{signing::MessageSignature, IdentityKeyRef}; -use nym_crypto::asymmetric::identity; +use nym_crypto::asymmetric::{encryption, identity}; use nym_name_service_common::{Address, NameDetails, NameId, NymName, RegisteredName}; +use nym_sphinx_addressing::clients::Recipient; use rand_chacha::rand_core::{CryptoRng, RngCore}; use super::{ @@ -33,18 +34,7 @@ pub fn name_fixture(id: NameId) -> RegisteredName { new_name( id, &NymName::new("my-service").unwrap(), - &Address::new("client_id.client_key@gateway_id"), - &Addr::unchecked("steve"), - "identity", - ) -} - -#[allow(unused)] -pub fn name_fixture_with_name(id: NameId, name: &str, address: &str) -> RegisteredName { - new_name( - id, - &NymName::new(name).unwrap(), - &Address::new(address), + &Address::new("client_id.client_key@gateway_id").unwrap(), &Addr::unchecked("steve"), "identity", ) @@ -54,50 +44,98 @@ pub fn name_fixture_full(id: NameId, name: &str, address: &str, owner: &str) -> new_name( id, &NymName::new(name).unwrap(), - &Address::new(address), + &Address::new(address).unwrap(), &Addr::unchecked(owner), "identity", ) } -// Create a new name, using a correctly generted identity key -pub fn new_name_details( - rng: &mut R, - name: &str, - nym_address: &str, -) -> (NameDetails, identity::KeyPair) +fn new_recipient(rng: &mut R) -> (Recipient, identity::KeyPair) where R: RngCore + CryptoRng, { - let keypair = identity::KeyPair::new(rng); + let client_id_keys = identity::KeyPair::new(rng); + let client_enc_keys = encryption::KeyPair::new(rng); + let gateway_id_keys = identity::KeyPair::new(rng); + ( - NameDetails { - name: NymName::new(name).unwrap(), - address: Address::new(nym_address), - identity_key: keypair.public_key().to_base58_string(), - }, - keypair, + Recipient::new( + *client_id_keys.public_key(), + *client_enc_keys.public_key(), + *gateway_id_keys.public_key(), + ), + client_id_keys, ) } -// Create a new service, with a correctly generated identity key, and sign it +pub fn new_address(rng: &mut R) -> (Address, identity::KeyPair) +where + R: RngCore + CryptoRng, +{ + let (recipient, client_id_keys) = new_recipient(rng); + let address = Address::new(&recipient.to_string()).unwrap(); + (address, client_id_keys) +} + +// Create a new name with a correctly generated nym address and matching identity key +pub fn new_name_details(rng: &mut R, name: &str) -> (NameDetails, identity::KeyPair) +where + R: RngCore + CryptoRng, +{ + let (address, client_id_keys) = new_address(rng); + let identity_key = client_id_keys.public_key().to_base58_string(); + + ( + NameDetails { + name: NymName::new(name).unwrap(), + address, + identity_key, + }, + client_id_keys, + ) +} + +// Create a new name, with a correctly generated nym adress and identity key, and sign it pub fn new_name_details_with_sign( deps: DepsMut<'_>, rng: &mut R, name: &str, - nym_address: &str, owner: &str, deposit: Coin, ) -> (NameDetails, MessageSignature) where R: RngCore + CryptoRng, { - // Service - let (name, keypair) = new_name_details(rng, name, nym_address); + // Name + let (name, client_id_keys) = new_name_details(rng, name); // Sign let sign_msg = name_register_sign_payload(deps.as_ref(), owner, name.clone(), deposit); - let owner_signature = ed25519_sign_message(sign_msg, keypair.private_key()); + let owner_signature = ed25519_sign_message(sign_msg, client_id_keys.private_key()); (name, owner_signature) } + +#[cfg(test)] +mod test { + use crate::test_helpers::helpers; + + use super::*; + + #[test] + fn new_recipient_creates_matching_identity_and_client_id_public_key() { + let mut test_rng = helpers::test_rng(); + let (recipient, client_id_keys) = new_recipient(&mut test_rng); + assert_eq!(recipient.identity(), client_id_keys.public_key()); + + // A test not for our code, but that we actually confirm our understanding of Recipient. + // One might view it as unnecessary, but it's a good sanity check for such a core assumption. + let recipient_str = format!( + "{}.{}@{}", + recipient.identity().to_base58_string(), + recipient.encryption_key().to_base58_string(), + recipient.gateway().to_base58_string() + ); + assert_eq!(recipient.to_string(), recipient_str); + } +} diff --git a/contracts/name-service/src/test_helpers/transactions.rs b/contracts/name-service/src/test_helpers/transactions.rs index 6458ad5b3e..9b90515597 100644 --- a/contracts/name-service/src/test_helpers/transactions.rs +++ b/contracts/name-service/src/test_helpers/transactions.rs @@ -28,7 +28,6 @@ pub fn register_name( mut deps: DepsMut<'_>, rng: &mut R, name: &str, - nym_address: &str, owner: &str, ) -> (NameId, NameDetails) where @@ -39,7 +38,6 @@ where deps.branch(), rng, name, - nym_address, owner, deposit.clone(), ); diff --git a/contracts/service-provider-directory/.cargo/config b/contracts/service-provider-directory/.cargo/config index 4fa7f37d4a..2f698e22b3 100644 --- a/contracts/service-provider-directory/.cargo/config +++ b/contracts/service-provider-directory/.cargo/config @@ -2,4 +2,4 @@ wasm = "build --release --lib --target wasm32-unknown-unknown" wasm-debug = "build --target wasm32-unknown-unknown" unit-test = "test --lib" -schema = "run --bin schema" +schema = "run --bin schema --features=schema-gen" \ No newline at end of file diff --git a/contracts/service-provider-directory/Cargo.toml b/contracts/service-provider-directory/Cargo.toml index cadc17d708..39ffcaf6f7 100644 --- a/contracts/service-provider-directory/Cargo.toml +++ b/contracts/service-provider-directory/Cargo.toml @@ -3,11 +3,16 @@ name = "nym-service-provider-directory" version = "0.1.0" edition = "2021" +[[bin]] +name = "schema" +required-features = ["schema-gen"] + [lib] crate-type = ["cdylib", "rlib"] [dependencies] bs58 = "0.4.0" +cosmwasm-schema = { workspace = true, optional = true } cosmwasm-std = { workspace = true } cw-controllers = { workspace = true } cw-storage-plus = { workspace = true } @@ -28,3 +33,6 @@ cw-multi-test = { workspace = true } nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] } rand_chacha = "0.2" rstest = "0.17.0" + +[features] +schema-gen = ["nym-service-provider-directory-common/schema", "cosmwasm-schema"] diff --git a/contracts/service-provider-directory/Makefile b/contracts/service-provider-directory/Makefile new file mode 100644 index 0000000000..f138e2c879 --- /dev/null +++ b/contracts/service-provider-directory/Makefile @@ -0,0 +1,2 @@ +generate-schema: + cargo schema \ No newline at end of file diff --git a/contracts/service-provider-directory/schema/nym-service-provider-directory.json b/contracts/service-provider-directory/schema/nym-service-provider-directory.json new file mode 100644 index 0000000000..d923ef3f99 --- /dev/null +++ b/contracts/service-provider-directory/schema/nym-service-provider-directory.json @@ -0,0 +1,1104 @@ +{ + "contract_name": "nym-service-provider-directory", + "contract_version": "0.1.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "announce" + ], + "properties": { + "announce": { + "type": "object", + "required": [ + "owner_signature", + "service" + ], + "properties": { + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + }, + "service": { + "$ref": "#/definitions/ServiceDetails" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "delete_id" + ], + "properties": { + "delete_id": { + "type": "object", + "required": [ + "service_id" + ], + "properties": { + "service_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "delete_nym_address" + ], + "properties": { + "delete_nym_address": { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddress" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_deposit_required" + ], + "properties": { + "update_deposit_required": { + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "MessageSignature": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ServiceDetails": { + "type": "object", + "required": [ + "identity_key", + "nym_address", + "service_type" + ], + "properties": { + "identity_key": { + "description": "The identity key of the service.", + "type": "string" + }, + "nym_address": { + "description": "The address of the service.", + "allOf": [ + { + "$ref": "#/definitions/NymAddress" + } + ] + }, + "service_type": { + "description": "The service type.", + "allOf": [ + { + "$ref": "#/definitions/ServiceType" + } + ] + } + }, + "additionalProperties": false + }, + "ServiceType": { + "description": "The type of services provider supported", + "type": "string", + "enum": [ + "network_requester" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "service_id" + ], + "properties": { + "service_id": { + "type": "object", + "required": [ + "service_id" + ], + "properties": { + "service_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "by_announcer" + ], + "properties": { + "by_announcer": { + "type": "object", + "required": [ + "announcer" + ], + "properties": { + "announcer": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "by_nym_address" + ], + "properties": { + "by_nym_address": { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddress" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "signing_nonce" + ], + "properties": { + "signing_nonce": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets build information of this contract, such as the commit hash used for the build or rustc version.", + "type": "object", + "required": [ + "get_contract_version" + ], + "properties": { + "get_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.", + "type": "object", + "required": [ + "get_cw2_contract_version" + ], + "properties": { + "get_cw2_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + }, + "migrate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false + }, + "sudo": null, + "responses": { + "all": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedServicesListResponse", + "type": "object", + "required": [ + "per_page", + "services" + ], + "properties": { + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/Service" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "Service": { + "type": "object", + "required": [ + "announcer", + "block_height", + "deposit", + "service", + "service_id" + ], + "properties": { + "announcer": { + "description": "Address of the service owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "block_height": { + "description": "Block height at which the service was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the service.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "service": { + "description": "The announced service.", + "allOf": [ + { + "$ref": "#/definitions/ServiceDetails" + } + ] + }, + "service_id": { + "description": "Unique id assigned to the anounced service.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "ServiceDetails": { + "type": "object", + "required": [ + "identity_key", + "nym_address", + "service_type" + ], + "properties": { + "identity_key": { + "description": "The identity key of the service.", + "type": "string" + }, + "nym_address": { + "description": "The address of the service.", + "allOf": [ + { + "$ref": "#/definitions/NymAddress" + } + ] + }, + "service_type": { + "description": "The service type.", + "allOf": [ + { + "$ref": "#/definitions/ServiceType" + } + ] + } + }, + "additionalProperties": false + }, + "ServiceType": { + "description": "The type of services provider supported", + "type": "string", + "enum": [ + "network_requester" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "by_announcer": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ServicesListResponse", + "type": "object", + "required": [ + "services" + ], + "properties": { + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/Service" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "Service": { + "type": "object", + "required": [ + "announcer", + "block_height", + "deposit", + "service", + "service_id" + ], + "properties": { + "announcer": { + "description": "Address of the service owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "block_height": { + "description": "Block height at which the service was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the service.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "service": { + "description": "The announced service.", + "allOf": [ + { + "$ref": "#/definitions/ServiceDetails" + } + ] + }, + "service_id": { + "description": "Unique id assigned to the anounced service.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "ServiceDetails": { + "type": "object", + "required": [ + "identity_key", + "nym_address", + "service_type" + ], + "properties": { + "identity_key": { + "description": "The identity key of the service.", + "type": "string" + }, + "nym_address": { + "description": "The address of the service.", + "allOf": [ + { + "$ref": "#/definitions/NymAddress" + } + ] + }, + "service_type": { + "description": "The service type.", + "allOf": [ + { + "$ref": "#/definitions/ServiceType" + } + ] + } + }, + "additionalProperties": false + }, + "ServiceType": { + "description": "The type of services provider supported", + "type": "string", + "enum": [ + "network_requester" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "by_nym_address": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ServicesListResponse", + "type": "object", + "required": [ + "services" + ], + "properties": { + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/Service" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "Service": { + "type": "object", + "required": [ + "announcer", + "block_height", + "deposit", + "service", + "service_id" + ], + "properties": { + "announcer": { + "description": "Address of the service owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "block_height": { + "description": "Block height at which the service was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the service.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "service": { + "description": "The announced service.", + "allOf": [ + { + "$ref": "#/definitions/ServiceDetails" + } + ] + }, + "service_id": { + "description": "Unique id assigned to the anounced service.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "ServiceDetails": { + "type": "object", + "required": [ + "identity_key", + "nym_address", + "service_type" + ], + "properties": { + "identity_key": { + "description": "The identity key of the service.", + "type": "string" + }, + "nym_address": { + "description": "The address of the service.", + "allOf": [ + { + "$ref": "#/definitions/NymAddress" + } + ] + }, + "service_type": { + "description": "The service type.", + "allOf": [ + { + "$ref": "#/definitions/ServiceType" + } + ] + } + }, + "additionalProperties": false + }, + "ServiceType": { + "description": "The type of services provider supported", + "type": "string", + "enum": [ + "network_requester" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "config": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigResponse", + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_c_w2_contract_version": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractVersion", + "type": "object", + "required": [ + "contract", + "version" + ], + "properties": { + "contract": { + "description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing", + "type": "string" + }, + "version": { + "description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)", + "type": "string" + } + }, + "additionalProperties": false + }, + "get_contract_version": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractBuildInformation", + "type": "object", + "required": [ + "build_timestamp", + "build_version", + "commit_branch", + "commit_sha", + "commit_timestamp", + "rustc_version" + ], + "properties": { + "build_timestamp": { + "description": "Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.", + "type": "string" + }, + "build_version": { + "description": "Provides the build version, for example `0.1.0-9-g46f83e1`.", + "type": "string" + }, + "commit_branch": { + "description": "Provides the name of the git branch that was used for the build, for example `master`.", + "type": "string" + }, + "commit_sha": { + "description": "Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`.", + "type": "string" + }, + "commit_timestamp": { + "description": "Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`.", + "type": "string" + }, + "rustc_version": { + "description": "Provides the rustc version that was used for the build, for example `1.52.0-nightly`.", + "type": "string" + } + }, + "additionalProperties": false + }, + "service_id": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Service", + "type": "object", + "required": [ + "announcer", + "block_height", + "deposit", + "service", + "service_id" + ], + "properties": { + "announcer": { + "description": "Address of the service owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "block_height": { + "description": "Block height at which the service was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the service.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "service": { + "description": "The announced service.", + "allOf": [ + { + "$ref": "#/definitions/ServiceDetails" + } + ] + }, + "service_id": { + "description": "Unique id assigned to the anounced service.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ServiceDetails": { + "type": "object", + "required": [ + "identity_key", + "nym_address", + "service_type" + ], + "properties": { + "identity_key": { + "description": "The identity key of the service.", + "type": "string" + }, + "nym_address": { + "description": "The address of the service.", + "allOf": [ + { + "$ref": "#/definitions/NymAddress" + } + ] + }, + "service_type": { + "description": "The service type.", + "allOf": [ + { + "$ref": "#/definitions/ServiceType" + } + ] + } + }, + "additionalProperties": false + }, + "ServiceType": { + "description": "The type of services provider supported", + "type": "string", + "enum": [ + "network_requester" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "signing_nonce": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "uint32", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } +} diff --git a/contracts/service-provider-directory/schema/raw/execute.json b/contracts/service-provider-directory/schema/raw/execute.json new file mode 100644 index 0000000000..995ede2a3a --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/execute.json @@ -0,0 +1,181 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "announce" + ], + "properties": { + "announce": { + "type": "object", + "required": [ + "owner_signature", + "service" + ], + "properties": { + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + }, + "service": { + "$ref": "#/definitions/ServiceDetails" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "delete_id" + ], + "properties": { + "delete_id": { + "type": "object", + "required": [ + "service_id" + ], + "properties": { + "service_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "delete_nym_address" + ], + "properties": { + "delete_nym_address": { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddress" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_deposit_required" + ], + "properties": { + "update_deposit_required": { + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "MessageSignature": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ServiceDetails": { + "type": "object", + "required": [ + "identity_key", + "nym_address", + "service_type" + ], + "properties": { + "identity_key": { + "description": "The identity key of the service.", + "type": "string" + }, + "nym_address": { + "description": "The address of the service.", + "allOf": [ + { + "$ref": "#/definitions/NymAddress" + } + ] + }, + "service_type": { + "description": "The service type.", + "allOf": [ + { + "$ref": "#/definitions/ServiceType" + } + ] + } + }, + "additionalProperties": false + }, + "ServiceType": { + "description": "The type of services provider supported", + "type": "string", + "enum": [ + "network_requester" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/service-provider-directory/schema/raw/instantiate.json b/contracts/service-provider-directory/schema/raw/instantiate.json new file mode 100644 index 0000000000..73867fda63 --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/instantiate.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/service-provider-directory/schema/raw/migrate.json b/contracts/service-provider-directory/schema/raw/migrate.json new file mode 100644 index 0000000000..7fbe8c5708 --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/migrate.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false +} diff --git a/contracts/service-provider-directory/schema/raw/query.json b/contracts/service-provider-directory/schema/raw/query.json new file mode 100644 index 0000000000..f6abe3d45e --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/query.json @@ -0,0 +1,184 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "service_id" + ], + "properties": { + "service_id": { + "type": "object", + "required": [ + "service_id" + ], + "properties": { + "service_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "by_announcer" + ], + "properties": { + "by_announcer": { + "type": "object", + "required": [ + "announcer" + ], + "properties": { + "announcer": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "by_nym_address" + ], + "properties": { + "by_nym_address": { + "type": "object", + "required": [ + "nym_address" + ], + "properties": { + "nym_address": { + "$ref": "#/definitions/NymAddress" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "signing_nonce" + ], + "properties": { + "signing_nonce": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets build information of this contract, such as the commit hash used for the build or rustc version.", + "type": "object", + "required": [ + "get_contract_version" + ], + "properties": { + "get_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.", + "type": "object", + "required": [ + "get_cw2_contract_version" + ], + "properties": { + "get_cw2_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/contracts/service-provider-directory/schema/raw/response_to_all.json b/contracts/service-provider-directory/schema/raw/response_to_all.json new file mode 100644 index 0000000000..e9d7c842dd --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/response_to_all.json @@ -0,0 +1,162 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "PagedServicesListResponse", + "type": "object", + "required": [ + "per_page", + "services" + ], + "properties": { + "per_page": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/Service" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "Service": { + "type": "object", + "required": [ + "announcer", + "block_height", + "deposit", + "service", + "service_id" + ], + "properties": { + "announcer": { + "description": "Address of the service owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "block_height": { + "description": "Block height at which the service was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the service.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "service": { + "description": "The announced service.", + "allOf": [ + { + "$ref": "#/definitions/ServiceDetails" + } + ] + }, + "service_id": { + "description": "Unique id assigned to the anounced service.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "ServiceDetails": { + "type": "object", + "required": [ + "identity_key", + "nym_address", + "service_type" + ], + "properties": { + "identity_key": { + "description": "The identity key of the service.", + "type": "string" + }, + "nym_address": { + "description": "The address of the service.", + "allOf": [ + { + "$ref": "#/definitions/NymAddress" + } + ] + }, + "service_type": { + "description": "The service type.", + "allOf": [ + { + "$ref": "#/definitions/ServiceType" + } + ] + } + }, + "additionalProperties": false + }, + "ServiceType": { + "description": "The type of services provider supported", + "type": "string", + "enum": [ + "network_requester" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/service-provider-directory/schema/raw/response_to_by_announcer.json b/contracts/service-provider-directory/schema/raw/response_to_by_announcer.json new file mode 100644 index 0000000000..a0092cef6b --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/response_to_by_announcer.json @@ -0,0 +1,147 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ServicesListResponse", + "type": "object", + "required": [ + "services" + ], + "properties": { + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/Service" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "Service": { + "type": "object", + "required": [ + "announcer", + "block_height", + "deposit", + "service", + "service_id" + ], + "properties": { + "announcer": { + "description": "Address of the service owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "block_height": { + "description": "Block height at which the service was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the service.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "service": { + "description": "The announced service.", + "allOf": [ + { + "$ref": "#/definitions/ServiceDetails" + } + ] + }, + "service_id": { + "description": "Unique id assigned to the anounced service.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "ServiceDetails": { + "type": "object", + "required": [ + "identity_key", + "nym_address", + "service_type" + ], + "properties": { + "identity_key": { + "description": "The identity key of the service.", + "type": "string" + }, + "nym_address": { + "description": "The address of the service.", + "allOf": [ + { + "$ref": "#/definitions/NymAddress" + } + ] + }, + "service_type": { + "description": "The service type.", + "allOf": [ + { + "$ref": "#/definitions/ServiceType" + } + ] + } + }, + "additionalProperties": false + }, + "ServiceType": { + "description": "The type of services provider supported", + "type": "string", + "enum": [ + "network_requester" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/service-provider-directory/schema/raw/response_to_by_nym_address.json b/contracts/service-provider-directory/schema/raw/response_to_by_nym_address.json new file mode 100644 index 0000000000..a0092cef6b --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/response_to_by_nym_address.json @@ -0,0 +1,147 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ServicesListResponse", + "type": "object", + "required": [ + "services" + ], + "properties": { + "services": { + "type": "array", + "items": { + "$ref": "#/definitions/Service" + } + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "Service": { + "type": "object", + "required": [ + "announcer", + "block_height", + "deposit", + "service", + "service_id" + ], + "properties": { + "announcer": { + "description": "Address of the service owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "block_height": { + "description": "Block height at which the service was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the service.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "service": { + "description": "The announced service.", + "allOf": [ + { + "$ref": "#/definitions/ServiceDetails" + } + ] + }, + "service_id": { + "description": "Unique id assigned to the anounced service.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "ServiceDetails": { + "type": "object", + "required": [ + "identity_key", + "nym_address", + "service_type" + ], + "properties": { + "identity_key": { + "description": "The identity key of the service.", + "type": "string" + }, + "nym_address": { + "description": "The address of the service.", + "allOf": [ + { + "$ref": "#/definitions/NymAddress" + } + ] + }, + "service_type": { + "description": "The service type.", + "allOf": [ + { + "$ref": "#/definitions/ServiceType" + } + ] + } + }, + "additionalProperties": false + }, + "ServiceType": { + "description": "The type of services provider supported", + "type": "string", + "enum": [ + "network_requester" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/service-provider-directory/schema/raw/response_to_config.json b/contracts/service-provider-directory/schema/raw/response_to_config.json new file mode 100644 index 0000000000..16420c1130 --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/response_to_config.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigResponse", + "type": "object", + "required": [ + "deposit_required" + ], + "properties": { + "deposit_required": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/service-provider-directory/schema/raw/response_to_get_c_w2_contract_version.json b/contracts/service-provider-directory/schema/raw/response_to_get_c_w2_contract_version.json new file mode 100644 index 0000000000..3ded8c84d9 --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/response_to_get_c_w2_contract_version.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractVersion", + "type": "object", + "required": [ + "contract", + "version" + ], + "properties": { + "contract": { + "description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing", + "type": "string" + }, + "version": { + "description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/service-provider-directory/schema/raw/response_to_get_contract_version.json b/contracts/service-provider-directory/schema/raw/response_to_get_contract_version.json new file mode 100644 index 0000000000..60ae65f3d1 --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/response_to_get_contract_version.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractBuildInformation", + "type": "object", + "required": [ + "build_timestamp", + "build_version", + "commit_branch", + "commit_sha", + "commit_timestamp", + "rustc_version" + ], + "properties": { + "build_timestamp": { + "description": "Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.", + "type": "string" + }, + "build_version": { + "description": "Provides the build version, for example `0.1.0-9-g46f83e1`.", + "type": "string" + }, + "commit_branch": { + "description": "Provides the name of the git branch that was used for the build, for example `master`.", + "type": "string" + }, + "commit_sha": { + "description": "Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`.", + "type": "string" + }, + "commit_timestamp": { + "description": "Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`.", + "type": "string" + }, + "rustc_version": { + "description": "Provides the rustc version that was used for the build, for example `1.52.0-nightly`.", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/service-provider-directory/schema/raw/response_to_service_id.json b/contracts/service-provider-directory/schema/raw/response_to_service_id.json new file mode 100644 index 0000000000..68f5ff143b --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/response_to_service_id.json @@ -0,0 +1,132 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Service", + "type": "object", + "required": [ + "announcer", + "block_height", + "deposit", + "service", + "service_id" + ], + "properties": { + "announcer": { + "description": "Address of the service owner.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "block_height": { + "description": "Block height at which the service was added.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "deposit": { + "description": "The deposit used to announce the service.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "service": { + "description": "The announced service.", + "allOf": [ + { + "$ref": "#/definitions/ServiceDetails" + } + ] + }, + "service_id": { + "description": "Unique id assigned to the anounced service.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "NymAddress": { + "description": "The types of addresses supported.", + "oneOf": [ + { + "description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.", + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ServiceDetails": { + "type": "object", + "required": [ + "identity_key", + "nym_address", + "service_type" + ], + "properties": { + "identity_key": { + "description": "The identity key of the service.", + "type": "string" + }, + "nym_address": { + "description": "The address of the service.", + "allOf": [ + { + "$ref": "#/definitions/NymAddress" + } + ] + }, + "service_type": { + "description": "The service type.", + "allOf": [ + { + "$ref": "#/definitions/ServiceType" + } + ] + } + }, + "additionalProperties": false + }, + "ServiceType": { + "description": "The type of services provider supported", + "type": "string", + "enum": [ + "network_requester" + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/service-provider-directory/schema/raw/response_to_signing_nonce.json b/contracts/service-provider-directory/schema/raw/response_to_signing_nonce.json new file mode 100644 index 0000000000..de85c3e56e --- /dev/null +++ b/contracts/service-provider-directory/schema/raw/response_to_signing_nonce.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "uint32", + "type": "integer", + "format": "uint32", + "minimum": 0.0 +} diff --git a/contracts/service-provider-directory/src/bin/schema.rs b/contracts/service-provider-directory/src/bin/schema.rs new file mode 100644 index 0000000000..0adf4b8827 --- /dev/null +++ b/contracts/service-provider-directory/src/bin/schema.rs @@ -0,0 +1,16 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::write_api; +use nym_service_provider_directory_common::msg::{ + ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, +}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + migrate: MigrateMsg, + } +} diff --git a/contracts/service-provider-directory/src/integration_tests/test_service.rs b/contracts/service-provider-directory/src/integration_tests/test_service.rs index d0652dee75..6d54648713 100644 --- a/contracts/service-provider-directory/src/integration_tests/test_service.rs +++ b/contracts/service-provider-directory/src/integration_tests/test_service.rs @@ -10,7 +10,6 @@ use crate::test_helpers::signing::ed25519_sign_message; pub struct TestService { pub service: ServiceDetails, pub keys: identity::KeyPair, - pub rng: ChaCha20Rng, } impl TestService { @@ -21,11 +20,7 @@ impl TestService { service_type: ServiceType::NetworkRequester, identity_key: keys.public_key().to_base58_string(), }; - Self { - service, - keys, - rng: rng.clone(), - } + Self { service, keys } } pub fn identity_key(&self) -> &IdentityKey { diff --git a/contracts/service-provider-directory/src/state/services.rs b/contracts/service-provider-directory/src/state/services.rs index e5e68a21ff..d88ebf6722 100644 --- a/contracts/service-provider-directory/src/state/services.rs +++ b/contracts/service-provider-directory/src/state/services.rs @@ -88,6 +88,8 @@ pub fn load_nym_address(store: &dyn Storage, nym_address: NymAddress) -> Result< pub struct PagedLoad { pub services: Vec, pub limit: usize, + + /// Field indicating paging information for the following queries if the caller wishes to get further entries. pub start_next_after: Option, } diff --git a/contracts/vesting/.cargo/config b/contracts/vesting/.cargo/config new file mode 100644 index 0000000000..2c25b8a87e --- /dev/null +++ b/contracts/vesting/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --bin schema --features=schema-gen" \ No newline at end of file diff --git a/contracts/vesting/Cargo.toml b/contracts/vesting/Cargo.toml index 5f988b6431..053aeedd15 100644 --- a/contracts/vesting/Cargo.toml +++ b/contracts/vesting/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-vesting-contract" -version = "1.4.0" +version = "1.4.1" description = "Nym vesting contract" edition = { workspace = true } authors = { workspace = true } @@ -15,6 +15,10 @@ exclude = [ "artifacts", ] +[[bin]] +name = "schema" +required-features = ["schema-gen"] + [lib] name = "vesting_contract" crate-type = ["cdylib", "rlib"] @@ -24,12 +28,12 @@ mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet- contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", package = "nym-contracts-common", version = "0.5.0" } vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract", package = "nym-vesting-contract-common", version = "0.7.0" } +cosmwasm-schema = { workspace = true, optional = true } cosmwasm-std = { workspace = true } cosmwasm-derive = { workspace = true } cw2 = { workspace = true } cw-storage-plus = { workspace = true, features = ["iterator"] } -schemars = "0.8" serde = { version = "1.0", default-features = false, features = ["derive"] } thiserror = { version = "1.0" } semver = { version = "1.0.16", default-features = false } @@ -46,3 +50,6 @@ vergen = { version = "=7.4.3", default-features = false, features = ["build", "g [profile.release] overflow-checks = true + +[features] +schema-gen = ["vesting-contract-common/schema", "cosmwasm-schema"] \ No newline at end of file diff --git a/contracts/vesting/Makefile b/contracts/vesting/Makefile index a00097bc65..8ca651ccec 100644 --- a/contracts/vesting/Makefile +++ b/contracts/vesting/Makefile @@ -1,2 +1,5 @@ wasm: - RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown \ No newline at end of file + RUSTFLAGS='-C link-arg=-s' cargo build --release --target wasm32-unknown-unknown + +generate-schema: + cargo schema \ No newline at end of file diff --git a/contracts/vesting/schema/nym-vesting-contract.json b/contracts/vesting/schema/nym-vesting-contract.json new file mode 100644 index 0000000000..6dd3678fd3 --- /dev/null +++ b/contracts/vesting/schema/nym-vesting-contract.json @@ -0,0 +1,2841 @@ +{ + "contract_name": "nym-vesting-contract", + "contract_version": "1.4.1", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "mix_denom", + "mixnet_contract_address" + ], + "properties": { + "mix_denom": { + "type": "string" + }, + "mixnet_contract_address": { + "type": "string" + } + }, + "additionalProperties": false + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "description": "Only owner of the node can crate the family with node as head", + "type": "object", + "required": [ + "create_family" + ], + "properties": { + "create_family": { + "type": "object", + "required": [ + "label" + ], + "properties": { + "label": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Family head needs to sign the joining node IdentityKey, the Node provides its signature signaling consent to join the family", + "type": "object", + "required": [ + "join_family" + ], + "properties": { + "join_family": { + "type": "object", + "required": [ + "family_head", + "join_permit" + ], + "properties": { + "family_head": { + "$ref": "#/definitions/FamilyHead" + }, + "join_permit": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "leave_family" + ], + "properties": { + "leave_family": { + "type": "object", + "required": [ + "family_head" + ], + "properties": { + "family_head": { + "$ref": "#/definitions/FamilyHead" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "kick_family_member" + ], + "properties": { + "kick_family_member": { + "type": "object", + "required": [ + "member" + ], + "properties": { + "member": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "track_reward" + ], + "properties": { + "track_reward": { + "type": "object", + "required": [ + "address", + "amount" + ], + "properties": { + "address": { + "type": "string" + }, + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "claim_operator_reward" + ], + "properties": { + "claim_operator_reward": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "claim_delegator_reward" + ], + "properties": { + "claim_delegator_reward": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_mixnode_cost_params" + ], + "properties": { + "update_mixnode_cost_params": { + "type": "object", + "required": [ + "new_costs" + ], + "properties": { + "new_costs": { + "$ref": "#/definitions/MixNodeCostParams" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_mixnode_config" + ], + "properties": { + "update_mixnode_config": { + "type": "object", + "required": [ + "new_config" + ], + "properties": { + "new_config": { + "$ref": "#/definitions/MixNodeConfigUpdate" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_mixnet_address" + ], + "properties": { + "update_mixnet_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "delegate_to_mixnode" + ], + "properties": { + "delegate_to_mixnode": { + "type": "object", + "required": [ + "amount", + "mix_id" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "on_behalf_of": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "undelegate_from_mixnode" + ], + "properties": { + "undelegate_from_mixnode": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "on_behalf_of": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "create_account" + ], + "properties": { + "create_account": { + "type": "object", + "required": [ + "owner_address" + ], + "properties": { + "cap": { + "anyOf": [ + { + "$ref": "#/definitions/PledgeCap" + }, + { + "type": "null" + } + ] + }, + "owner_address": { + "type": "string" + }, + "staking_address": { + "type": [ + "string", + "null" + ] + }, + "vesting_spec": { + "anyOf": [ + { + "$ref": "#/definitions/VestingSpecification" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "withdraw_vested_coins" + ], + "properties": { + "withdraw_vested_coins": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "track_undelegation" + ], + "properties": { + "track_undelegation": { + "type": "object", + "required": [ + "amount", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "bond_mixnode" + ], + "properties": { + "bond_mixnode": { + "type": "object", + "required": [ + "amount", + "cost_params", + "mix_node", + "owner_signature" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "cost_params": { + "$ref": "#/definitions/MixNodeCostParams" + }, + "mix_node": { + "$ref": "#/definitions/MixNode" + }, + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "pledge_more" + ], + "properties": { + "pledge_more": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "decrease_pledge" + ], + "properties": { + "decrease_pledge": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "unbond_mixnode" + ], + "properties": { + "unbond_mixnode": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "track_unbond_mixnode" + ], + "properties": { + "track_unbond_mixnode": { + "type": "object", + "required": [ + "amount", + "owner" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "track_decrease_pledge" + ], + "properties": { + "track_decrease_pledge": { + "type": "object", + "required": [ + "amount", + "owner" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "bond_gateway" + ], + "properties": { + "bond_gateway": { + "type": "object", + "required": [ + "amount", + "gateway", + "owner_signature" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "gateway": { + "$ref": "#/definitions/Gateway" + }, + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "unbond_gateway" + ], + "properties": { + "unbond_gateway": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "track_unbond_gateway" + ], + "properties": { + "track_unbond_gateway": { + "type": "object", + "required": [ + "amount", + "owner" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_gateway_config" + ], + "properties": { + "update_gateway_config": { + "type": "object", + "required": [ + "new_config" + ], + "properties": { + "new_config": { + "$ref": "#/definitions/GatewayConfigUpdate" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "transfer_ownership" + ], + "properties": { + "transfer_ownership": { + "type": "object", + "required": [ + "to_address" + ], + "properties": { + "to_address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_staking_address" + ], + "properties": { + "update_staking_address": { + "type": "object", + "properties": { + "to_address": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_locked_pledge_cap" + ], + "properties": { + "update_locked_pledge_cap": { + "type": "object", + "required": [ + "address", + "cap" + ], + "properties": { + "address": { + "type": "string" + }, + "cap": { + "$ref": "#/definitions/PledgeCap" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + }, + "Gateway": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "clients_port", + "host", + "identity_key", + "location", + "mix_port", + "sphinx_key", + "version" + ], + "properties": { + "clients_port": { + "description": "Port used by this gateway for listening for client requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "description": "Network address of this gateway, for example 1.1.1.1 or foo.gateway.com", + "type": "string" + }, + "identity_key": { + "description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients", + "type": "string" + }, + "location": { + "description": "The physical, self-reported, location of this gateway.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this gateway for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "version": { + "description": "The self-reported semver version of this gateway.", + "type": "string" + } + }, + "additionalProperties": false + }, + "GatewayConfigUpdate": { + "type": "object", + "required": [ + "clients_port", + "host", + "location", + "mix_port", + "version" + ], + "properties": { + "clients_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mix_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "type": "string" + } + }, + "additionalProperties": false + }, + "MessageSignature": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeConfigUpdate": { + "type": "object", + "required": [ + "host", + "http_api_port", + "mix_port", + "verloc_port", + "version" + ], + "properties": { + "host": { + "type": "string" + }, + "http_api_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "mix_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "verloc_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "PledgeCap": { + "description": "Defines cap for pleding/staking tokens.", + "oneOf": [ + { + "description": "Specifies a percent-based pledge cap, i.e. only given % of tokens could be pledged/staked.", + "type": "object", + "required": [ + "percent" + ], + "properties": { + "percent": { + "$ref": "#/definitions/Percent" + } + }, + "additionalProperties": false + }, + { + "description": "Specifies an absolute pledge cap, i.e. an explicit value that could be pledged/staked.", + "type": "object", + "required": [ + "absolute" + ], + "properties": { + "absolute": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "VestingSpecification": { + "type": "object", + "properties": { + "num_periods": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "period_seconds": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "start_time": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "description": "Queries exposed by this contract.", + "oneOf": [ + { + "description": "Gets build information of this contract, such as the commit hash used for the build or rustc version.", + "type": "object", + "required": [ + "get_contract_version" + ], + "properties": { + "get_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.", + "type": "object", + "required": [ + "get_cw2_contract_version" + ], + "properties": { + "get_cw2_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the list of vesting accounts held in this contract.", + "type": "object", + "required": [ + "get_accounts_paged" + ], + "properties": { + "get_accounts_paged": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the list of coins that are still vesting for each account held in this contract.", + "type": "object", + "required": [ + "get_accounts_vesting_coins_paged" + ], + "properties": { + "get_accounts_vesting_coins_paged": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of locked coins for the provided vesting account, i.e. coins that are still vesting but have not been staked. `locked_coins = vesting_coins - staked_coins`", + "type": "object", + "required": [ + "locked_coins" + ], + "properties": { + "locked_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "block_time": { + "description": "(deprecated) Optional argument specifying that the query should be performed against non-current block.", + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + }, + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of spendable coins for the provided vesting account, i.e. coins that could be withdrawn. `spendable_coins = account_balance - locked_coins` note: `account_balance` is the amount of coins still physically present in this contract, i.e. not withdrawn or staked.", + "type": "object", + "required": [ + "spendable_coins" + ], + "properties": { + "spendable_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "block_time": { + "description": "(deprecated) Optional argument specifying that the query should be performed against non-current block.", + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + }, + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that have already vested for the provided vesting account address.", + "type": "object", + "required": [ + "get_vested_coins" + ], + "properties": { + "get_vested_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "block_time": { + "description": "(deprecated) Optional argument specifying that the query should be performed against non-current block.", + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + }, + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that are still vesting for the provided vesting account address.", + "type": "object", + "required": [ + "get_vesting_coins" + ], + "properties": { + "get_vesting_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "block_time": { + "description": "(deprecated) Optional argument specifying that the query should be performed against non-current block.", + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + }, + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the starting vesting time for the provided vesting account, i.e. the beginning of the first vesting period.", + "type": "object", + "required": [ + "get_start_time" + ], + "properties": { + "get_start_time": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the ending vesting time for the provided vesting account, i.e. the end of the last vesting period.", + "type": "object", + "required": [ + "get_end_time" + ], + "properties": { + "get_end_time": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the initial vesting specification used for the provided vesting account address.", + "type": "object", + "required": [ + "get_original_vesting" + ], + "properties": { + "get_original_vesting": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the total amount of coins accrued through claimed staking rewards by the provided vesting account.", + "type": "object", + "required": [ + "get_historical_vesting_staking_reward" + ], + "properties": { + "get_historical_vesting_staking_reward": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of spendable vesting coins for the provided vesting account, i.e. coins that could be withdrawn that originated from the vesting specification.", + "type": "object", + "required": [ + "get_spendable_vested_coins" + ], + "properties": { + "get_spendable_vested_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of spendable reward coins for the provided vesting account, i.e. coins that could be withdrawn that originated from the claimed staking rewards.", + "type": "object", + "required": [ + "get_spendable_reward_coins" + ], + "properties": { + "get_spendable_reward_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that are currently delegated for the provided vesting account address.", + "type": "object", + "required": [ + "get_delegated_coins" + ], + "properties": { + "get_delegated_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that are currently pledged for the provided vesting account address.", + "type": "object", + "required": [ + "get_pledged_coins" + ], + "properties": { + "get_pledged_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that are currently staked (i.e. delegations + pledges) for the provided vesting account address.", + "type": "object", + "required": [ + "get_staked_coins" + ], + "properties": { + "get_staked_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that got withdrawn for the provided vesting account address.", + "type": "object", + "required": [ + "get_withdrawn_coins" + ], + "properties": { + "get_withdrawn_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns detailed information associated with the account for the provided vesting account address.", + "type": "object", + "required": [ + "get_account" + ], + "properties": { + "get_account": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns pledge information (if applicable) for bonded mixnode for the provided vesting account address.", + "type": "object", + "required": [ + "get_mixnode" + ], + "properties": { + "get_mixnode": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns pledge information (if applicable) for bonded gateway for the provided vesting account address.", + "type": "object", + "required": [ + "get_gateway" + ], + "properties": { + "get_gateway": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the current vesting period for the provided vesting account address.", + "type": "object", + "required": [ + "get_current_vesting_period" + ], + "properties": { + "get_current_vesting_period": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the information about particular vesting delegation.", + "type": "object", + "required": [ + "get_delegation" + ], + "properties": { + "get_delegation": { + "type": "object", + "required": [ + "address", + "block_timestamp_secs", + "mix_id" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + }, + "block_timestamp_secs": { + "description": "Block timestamp of the delegation.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "Id of the mixnode towards which the delegation has been made.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the total amount of coins delegated towards particular mixnode by the provided vesting account address.", + "type": "object", + "required": [ + "get_total_delegation_amount" + ], + "properties": { + "get_total_delegation_amount": { + "type": "object", + "required": [ + "address", + "mix_id" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + }, + "mix_id": { + "description": "Id of the mixnode towards which the delegations have been made.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns timestamps of delegations made towards particular mixnode by the provided vesting account address.", + "type": "object", + "required": [ + "get_delegation_times" + ], + "properties": { + "get_delegation_times": { + "type": "object", + "required": [ + "address", + "mix_id" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + }, + "mix_id": { + "description": "Id of the mixnode towards which the delegations have been made.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns all active delegations made with vesting tokens stored in this contract.", + "type": "object", + "required": [ + "get_all_delegations" + ], + "properties": { + "get_all_delegations": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "array", + "null" + ], + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + ], + "maxItems": 3, + "minItems": 3 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "migrate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false + }, + "sudo": null, + "responses": { + "get_account": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Account", + "description": "Vesting account information.", + "type": "object", + "required": [ + "coin", + "owner_address", + "periods", + "start_time", + "storage_key" + ], + "properties": { + "coin": { + "description": "The initial amount of coins used creation of this account.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner_address": { + "description": "Address of the owner of the vesting account.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "periods": { + "description": "All vesting periods for this account.", + "type": "array", + "items": { + "$ref": "#/definitions/VestingPeriod" + } + }, + "pledge_cap": { + "description": "Optional custom pledge cap of this vesting account.", + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/PledgeCap" + }, + { + "type": "null" + } + ] + }, + "staking_address": { + "description": "Optional address of an account allowed to perform staking on behalf of the owner.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "start_time": { + "description": "The starting vesting time.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "storage_key": { + "description": "The id/storage_key of this vesting account.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "PledgeCap": { + "description": "Defines cap for pleding/staking tokens.", + "oneOf": [ + { + "description": "Specifies a percent-based pledge cap, i.e. only given % of tokens could be pledged/staked.", + "type": "object", + "required": [ + "percent" + ], + "properties": { + "percent": { + "$ref": "#/definitions/Percent" + } + }, + "additionalProperties": false + }, + { + "description": "Specifies an absolute pledge cap, i.e. an explicit value that could be pledged/staked.", + "type": "object", + "required": [ + "absolute" + ], + "properties": { + "absolute": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "VestingPeriod": { + "description": "Vesting period details.", + "type": "object", + "required": [ + "period_seconds", + "start_time" + ], + "properties": { + "period_seconds": { + "description": "The duration (in seconds) of the vesting period.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "start_time": { + "description": "The start time of this vesting period, as unix timestamp.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "get_accounts_paged": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AccountsResponse", + "description": "Response containing basic vesting account information", + "type": "object", + "required": [ + "accounts" + ], + "properties": { + "accounts": { + "description": "The actual accounts returned by the query.", + "type": "array", + "items": { + "$ref": "#/definitions/BaseVestingAccountInfo" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "BaseVestingAccountInfo": { + "description": "Basic information regarding particular vesting account", + "type": "object", + "required": [ + "account_id", + "owner" + ], + "properties": { + "account_id": { + "description": "Id associated with this account", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of this account's owner", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "get_accounts_vesting_coins_paged": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VestingCoinsResponse", + "description": "Response containing vesting coins held in this contract", + "type": "object", + "required": [ + "accounts" + ], + "properties": { + "accounts": { + "description": "The actual accounts, and their vesting coins, returned by the query.", + "type": "array", + "items": { + "$ref": "#/definitions/AccountVestingCoins" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "AccountVestingCoins": { + "description": "Basic information regarding particular vesting account alongside the amount of vesting coins.", + "type": "object", + "required": [ + "account_id", + "owner", + "still_vesting" + ], + "properties": { + "account_id": { + "description": "Id associated with this account", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of this account's owner", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "still_vesting": { + "description": "Coins that are still vesting belonging to this account.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + } + }, + "additionalProperties": false + }, + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_all_delegations": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllDelegationsResponse", + "description": "Response containing paged list of all vesting delegations made using vesting coins.", + "type": "object", + "required": [ + "delegations" + ], + "properties": { + "delegations": { + "description": "The actual vesting delegations made.", + "type": "array", + "items": { + "$ref": "#/definitions/VestingDelegation" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "array", + "null" + ], + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + ], + "maxItems": 3, + "minItems": 3 + } + }, + "additionalProperties": false, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "VestingDelegation": { + "description": "Details about particular vesting delegation.", + "type": "object", + "required": [ + "account_id", + "amount", + "block_timestamp", + "mix_id" + ], + "properties": { + "account_id": { + "description": "The id of the vesting account that has made the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "amount": { + "description": "The raw amount delegated (interpreted to be in the same denom as the underlying vesting specification)", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "block_timestamp": { + "description": "The block timestamp when the delegation has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "The id of the mixnode towards which the delegation has been made.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } + }, + "get_c_w2_contract_version": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractVersion", + "type": "object", + "required": [ + "contract", + "version" + ], + "properties": { + "contract": { + "description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing", + "type": "string" + }, + "version": { + "description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)", + "type": "string" + } + }, + "additionalProperties": false + }, + "get_contract_version": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractBuildInformation", + "type": "object", + "required": [ + "build_timestamp", + "build_version", + "commit_branch", + "commit_sha", + "commit_timestamp", + "rustc_version" + ], + "properties": { + "build_timestamp": { + "description": "Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.", + "type": "string" + }, + "build_version": { + "description": "Provides the build version, for example `0.1.0-9-g46f83e1`.", + "type": "string" + }, + "commit_branch": { + "description": "Provides the name of the git branch that was used for the build, for example `master`.", + "type": "string" + }, + "commit_sha": { + "description": "Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`.", + "type": "string" + }, + "commit_timestamp": { + "description": "Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`.", + "type": "string" + }, + "rustc_version": { + "description": "Provides the rustc version that was used for the build, for example `1.52.0-nightly`.", + "type": "string" + } + }, + "additionalProperties": false + }, + "get_current_vesting_period": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Period", + "description": "The vesting period.", + "oneOf": [ + { + "description": "Defines a pre-vesting period.", + "type": "string", + "enum": [ + "before" + ] + }, + { + "description": "Defines currently active vesting period.", + "type": "object", + "required": [ + "in" + ], + "properties": { + "in": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "Defines a post-vesting period.", + "type": "string", + "enum": [ + "after" + ] + } + ] + }, + "get_delegated_coins": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_delegation": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VestingDelegation", + "description": "Details about particular vesting delegation.", + "type": "object", + "required": [ + "account_id", + "amount", + "block_timestamp", + "mix_id" + ], + "properties": { + "account_id": { + "description": "The id of the vesting account that has made the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "amount": { + "description": "The raw amount delegated (interpreted to be in the same denom as the underlying vesting specification)", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "block_timestamp": { + "description": "The block timestamp when the delegation has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "The id of the mixnode towards which the delegation has been made.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_delegation_times": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DelegationTimesResponse", + "description": "Response containing timestamps of all delegations made towards particular mixnode by given vesting account.", + "type": "object", + "required": [ + "account_id", + "delegation_timestamps", + "mix_id", + "owner" + ], + "properties": { + "account_id": { + "description": "Id associated with this account", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "delegation_timestamps": { + "description": "All timestamps where a delegation was made", + "type": "array", + "items": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "mix_id": { + "description": "Id of the mixnode towards which the delegation was made", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of this account's owner", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + } + } + }, + "get_end_time": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Timestamp", + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ], + "definitions": { + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "get_gateway": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_PledgeData", + "anyOf": [ + { + "$ref": "#/definitions/PledgeData" + }, + { + "type": "null" + } + ], + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "PledgeData": { + "description": "Information regarding pledge (i.e. mixnode or gateway bonding) made with vesting tokens.", + "type": "object", + "required": [ + "amount", + "block_time" + ], + "properties": { + "amount": { + "description": "The amount pledged.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "block_time": { + "description": "The block timestamp where the pledge occurred.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "get_historical_vesting_staking_reward": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_mixnode": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_PledgeData", + "anyOf": [ + { + "$ref": "#/definitions/PledgeData" + }, + { + "type": "null" + } + ], + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "PledgeData": { + "description": "Information regarding pledge (i.e. mixnode or gateway bonding) made with vesting tokens.", + "type": "object", + "required": [ + "amount", + "block_time" + ], + "properties": { + "amount": { + "description": "The amount pledged.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "block_time": { + "description": "The block timestamp where the pledge occurred.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "get_original_vesting": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "OriginalVestingResponse", + "description": "Details about the original vesting specification used when the account was created.", + "type": "object", + "required": [ + "amount", + "number_of_periods", + "period_duration" + ], + "properties": { + "amount": { + "description": "The original amount that was used for the creation of this vesting account", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "number_of_periods": { + "description": "The number of vesting periods that the account was created with", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "period_duration": { + "description": "Duration of each vesting period in seconds", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_pledged_coins": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_spendable_reward_coins": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_spendable_vested_coins": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_staked_coins": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_start_time": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Timestamp", + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ], + "definitions": { + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "get_total_delegation_amount": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_vested_coins": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_vesting_coins": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "get_withdrawn_coins": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "locked_coins": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + }, + "spendable_coins": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } + } + } +} diff --git a/contracts/vesting/schema/raw/execute.json b/contracts/vesting/schema/raw/execute.json new file mode 100644 index 0000000000..be6d6a73ff --- /dev/null +++ b/contracts/vesting/schema/raw/execute.json @@ -0,0 +1,971 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "description": "Only owner of the node can crate the family with node as head", + "type": "object", + "required": [ + "create_family" + ], + "properties": { + "create_family": { + "type": "object", + "required": [ + "label" + ], + "properties": { + "label": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Family head needs to sign the joining node IdentityKey, the Node provides its signature signaling consent to join the family", + "type": "object", + "required": [ + "join_family" + ], + "properties": { + "join_family": { + "type": "object", + "required": [ + "family_head", + "join_permit" + ], + "properties": { + "family_head": { + "$ref": "#/definitions/FamilyHead" + }, + "join_permit": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "leave_family" + ], + "properties": { + "leave_family": { + "type": "object", + "required": [ + "family_head" + ], + "properties": { + "family_head": { + "$ref": "#/definitions/FamilyHead" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "kick_family_member" + ], + "properties": { + "kick_family_member": { + "type": "object", + "required": [ + "member" + ], + "properties": { + "member": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "track_reward" + ], + "properties": { + "track_reward": { + "type": "object", + "required": [ + "address", + "amount" + ], + "properties": { + "address": { + "type": "string" + }, + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "claim_operator_reward" + ], + "properties": { + "claim_operator_reward": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "claim_delegator_reward" + ], + "properties": { + "claim_delegator_reward": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_mixnode_cost_params" + ], + "properties": { + "update_mixnode_cost_params": { + "type": "object", + "required": [ + "new_costs" + ], + "properties": { + "new_costs": { + "$ref": "#/definitions/MixNodeCostParams" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_mixnode_config" + ], + "properties": { + "update_mixnode_config": { + "type": "object", + "required": [ + "new_config" + ], + "properties": { + "new_config": { + "$ref": "#/definitions/MixNodeConfigUpdate" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_mixnet_address" + ], + "properties": { + "update_mixnet_address": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "delegate_to_mixnode" + ], + "properties": { + "delegate_to_mixnode": { + "type": "object", + "required": [ + "amount", + "mix_id" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "on_behalf_of": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "undelegate_from_mixnode" + ], + "properties": { + "undelegate_from_mixnode": { + "type": "object", + "required": [ + "mix_id" + ], + "properties": { + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "on_behalf_of": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "create_account" + ], + "properties": { + "create_account": { + "type": "object", + "required": [ + "owner_address" + ], + "properties": { + "cap": { + "anyOf": [ + { + "$ref": "#/definitions/PledgeCap" + }, + { + "type": "null" + } + ] + }, + "owner_address": { + "type": "string" + }, + "staking_address": { + "type": [ + "string", + "null" + ] + }, + "vesting_spec": { + "anyOf": [ + { + "$ref": "#/definitions/VestingSpecification" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "withdraw_vested_coins" + ], + "properties": { + "withdraw_vested_coins": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "track_undelegation" + ], + "properties": { + "track_undelegation": { + "type": "object", + "required": [ + "amount", + "mix_id", + "owner" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "mix_id": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "bond_mixnode" + ], + "properties": { + "bond_mixnode": { + "type": "object", + "required": [ + "amount", + "cost_params", + "mix_node", + "owner_signature" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "cost_params": { + "$ref": "#/definitions/MixNodeCostParams" + }, + "mix_node": { + "$ref": "#/definitions/MixNode" + }, + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "pledge_more" + ], + "properties": { + "pledge_more": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "decrease_pledge" + ], + "properties": { + "decrease_pledge": { + "type": "object", + "required": [ + "amount" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "unbond_mixnode" + ], + "properties": { + "unbond_mixnode": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "track_unbond_mixnode" + ], + "properties": { + "track_unbond_mixnode": { + "type": "object", + "required": [ + "amount", + "owner" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "track_decrease_pledge" + ], + "properties": { + "track_decrease_pledge": { + "type": "object", + "required": [ + "amount", + "owner" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "bond_gateway" + ], + "properties": { + "bond_gateway": { + "type": "object", + "required": [ + "amount", + "gateway", + "owner_signature" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "gateway": { + "$ref": "#/definitions/Gateway" + }, + "owner_signature": { + "$ref": "#/definitions/MessageSignature" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "unbond_gateway" + ], + "properties": { + "unbond_gateway": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "track_unbond_gateway" + ], + "properties": { + "track_unbond_gateway": { + "type": "object", + "required": [ + "amount", + "owner" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Coin" + }, + "owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_gateway_config" + ], + "properties": { + "update_gateway_config": { + "type": "object", + "required": [ + "new_config" + ], + "properties": { + "new_config": { + "$ref": "#/definitions/GatewayConfigUpdate" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "transfer_ownership" + ], + "properties": { + "transfer_ownership": { + "type": "object", + "required": [ + "to_address" + ], + "properties": { + "to_address": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_staking_address" + ], + "properties": { + "update_staking_address": { + "type": "object", + "properties": { + "to_address": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_locked_pledge_cap" + ], + "properties": { + "update_locked_pledge_cap": { + "type": "object", + "required": [ + "address", + "cap" + ], + "properties": { + "address": { + "type": "string" + }, + "cap": { + "$ref": "#/definitions/PledgeCap" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "FamilyHead": { + "description": "Head of particular family as identified by its identity key (i.e. public component of its ed25519 keypair stringified into base58).", + "type": "string" + }, + "Gateway": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "clients_port", + "host", + "identity_key", + "location", + "mix_port", + "sphinx_key", + "version" + ], + "properties": { + "clients_port": { + "description": "Port used by this gateway for listening for client requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "description": "Network address of this gateway, for example 1.1.1.1 or foo.gateway.com", + "type": "string" + }, + "identity_key": { + "description": "Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients", + "type": "string" + }, + "location": { + "description": "The physical, self-reported, location of this gateway.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this gateway for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "version": { + "description": "The self-reported semver version of this gateway.", + "type": "string" + } + }, + "additionalProperties": false + }, + "GatewayConfigUpdate": { + "type": "object", + "required": [ + "clients_port", + "host", + "location", + "mix_port", + "version" + ], + "properties": { + "clients_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "host": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mix_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "type": "string" + } + }, + "additionalProperties": false + }, + "MessageSignature": { + "type": "array", + "items": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + } + }, + "MixNode": { + "description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.", + "type": "object", + "required": [ + "host", + "http_api_port", + "identity_key", + "mix_port", + "sphinx_key", + "verloc_port", + "version" + ], + "properties": { + "host": { + "description": "Network address of this mixnode, for example 1.1.1.1 or foo.mixnode.com", + "type": "string" + }, + "http_api_port": { + "description": "Port used by this mixnode for its http(s) API", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "identity_key": { + "description": "Base58-encoded ed25519 EdDSA public key.", + "type": "string" + }, + "mix_port": { + "description": "Port used by this mixnode for listening for mix packets.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "sphinx_key": { + "description": "Base58-encoded x25519 public key used for sphinx key derivation.", + "type": "string" + }, + "verloc_port": { + "description": "Port used by this mixnode for listening for verloc requests.", + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "description": "The self-reported semver version of this mixnode.", + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeConfigUpdate": { + "type": "object", + "required": [ + "host", + "http_api_port", + "mix_port", + "verloc_port", + "version" + ], + "properties": { + "host": { + "type": "string" + }, + "http_api_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "mix_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "verloc_port": { + "type": "integer", + "format": "uint16", + "minimum": 0.0 + }, + "version": { + "type": "string" + } + }, + "additionalProperties": false + }, + "MixNodeCostParams": { + "description": "The cost parameters, or the cost function, defined for the particular mixnode that influences how the rewards should be split between the node operator and its delegators.", + "type": "object", + "required": [ + "interval_operating_cost", + "profit_margin_percent" + ], + "properties": { + "interval_operating_cost": { + "description": "Operating cost of the associated mixnode per the entire interval.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "profit_margin_percent": { + "description": "The profit margin of the associated mixnode, i.e. the desired percent of the reward to be distributed to the operator.", + "allOf": [ + { + "$ref": "#/definitions/Percent" + } + ] + } + }, + "additionalProperties": false + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "PledgeCap": { + "description": "Defines cap for pleding/staking tokens.", + "oneOf": [ + { + "description": "Specifies a percent-based pledge cap, i.e. only given % of tokens could be pledged/staked.", + "type": "object", + "required": [ + "percent" + ], + "properties": { + "percent": { + "$ref": "#/definitions/Percent" + } + }, + "additionalProperties": false + }, + { + "description": "Specifies an absolute pledge cap, i.e. an explicit value that could be pledged/staked.", + "type": "object", + "required": [ + "absolute" + ], + "properties": { + "absolute": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "VestingSpecification": { + "type": "object", + "properties": { + "num_periods": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "period_seconds": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + }, + "start_time": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/vesting/schema/raw/instantiate.json b/contracts/vesting/schema/raw/instantiate.json new file mode 100644 index 0000000000..09b7526278 --- /dev/null +++ b/contracts/vesting/schema/raw/instantiate.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "mix_denom", + "mixnet_contract_address" + ], + "properties": { + "mix_denom": { + "type": "string" + }, + "mixnet_contract_address": { + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/vesting/schema/raw/migrate.json b/contracts/vesting/schema/raw/migrate.json new file mode 100644 index 0000000000..7fbe8c5708 --- /dev/null +++ b/contracts/vesting/schema/raw/migrate.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MigrateMsg", + "type": "object", + "additionalProperties": false +} diff --git a/contracts/vesting/schema/raw/query.json b/contracts/vesting/schema/raw/query.json new file mode 100644 index 0000000000..42b1cd94ea --- /dev/null +++ b/contracts/vesting/schema/raw/query.json @@ -0,0 +1,719 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "description": "Queries exposed by this contract.", + "oneOf": [ + { + "description": "Gets build information of this contract, such as the commit hash used for the build or rustc version.", + "type": "object", + "required": [ + "get_contract_version" + ], + "properties": { + "get_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.", + "type": "object", + "required": [ + "get_cw2_contract_version" + ], + "properties": { + "get_cw2_contract_version": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the list of vesting accounts held in this contract.", + "type": "object", + "required": [ + "get_accounts_paged" + ], + "properties": { + "get_accounts_paged": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Gets the list of coins that are still vesting for each account held in this contract.", + "type": "object", + "required": [ + "get_accounts_vesting_coins_paged" + ], + "properties": { + "get_accounts_vesting_coins_paged": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_next_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of locked coins for the provided vesting account, i.e. coins that are still vesting but have not been staked. `locked_coins = vesting_coins - staked_coins`", + "type": "object", + "required": [ + "locked_coins" + ], + "properties": { + "locked_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "block_time": { + "description": "(deprecated) Optional argument specifying that the query should be performed against non-current block.", + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + }, + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of spendable coins for the provided vesting account, i.e. coins that could be withdrawn. `spendable_coins = account_balance - locked_coins` note: `account_balance` is the amount of coins still physically present in this contract, i.e. not withdrawn or staked.", + "type": "object", + "required": [ + "spendable_coins" + ], + "properties": { + "spendable_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "block_time": { + "description": "(deprecated) Optional argument specifying that the query should be performed against non-current block.", + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + }, + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that have already vested for the provided vesting account address.", + "type": "object", + "required": [ + "get_vested_coins" + ], + "properties": { + "get_vested_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "block_time": { + "description": "(deprecated) Optional argument specifying that the query should be performed against non-current block.", + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + }, + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that are still vesting for the provided vesting account address.", + "type": "object", + "required": [ + "get_vesting_coins" + ], + "properties": { + "get_vesting_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "block_time": { + "description": "(deprecated) Optional argument specifying that the query should be performed against non-current block.", + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + }, + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the starting vesting time for the provided vesting account, i.e. the beginning of the first vesting period.", + "type": "object", + "required": [ + "get_start_time" + ], + "properties": { + "get_start_time": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the ending vesting time for the provided vesting account, i.e. the end of the last vesting period.", + "type": "object", + "required": [ + "get_end_time" + ], + "properties": { + "get_end_time": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the initial vesting specification used for the provided vesting account address.", + "type": "object", + "required": [ + "get_original_vesting" + ], + "properties": { + "get_original_vesting": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the total amount of coins accrued through claimed staking rewards by the provided vesting account.", + "type": "object", + "required": [ + "get_historical_vesting_staking_reward" + ], + "properties": { + "get_historical_vesting_staking_reward": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of spendable vesting coins for the provided vesting account, i.e. coins that could be withdrawn that originated from the vesting specification.", + "type": "object", + "required": [ + "get_spendable_vested_coins" + ], + "properties": { + "get_spendable_vested_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of spendable reward coins for the provided vesting account, i.e. coins that could be withdrawn that originated from the claimed staking rewards.", + "type": "object", + "required": [ + "get_spendable_reward_coins" + ], + "properties": { + "get_spendable_reward_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that are currently delegated for the provided vesting account address.", + "type": "object", + "required": [ + "get_delegated_coins" + ], + "properties": { + "get_delegated_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that are currently pledged for the provided vesting account address.", + "type": "object", + "required": [ + "get_pledged_coins" + ], + "properties": { + "get_pledged_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that are currently staked (i.e. delegations + pledges) for the provided vesting account address.", + "type": "object", + "required": [ + "get_staked_coins" + ], + "properties": { + "get_staked_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the amount of coins that got withdrawn for the provided vesting account address.", + "type": "object", + "required": [ + "get_withdrawn_coins" + ], + "properties": { + "get_withdrawn_coins": { + "type": "object", + "required": [ + "vesting_account_address" + ], + "properties": { + "vesting_account_address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns detailed information associated with the account for the provided vesting account address.", + "type": "object", + "required": [ + "get_account" + ], + "properties": { + "get_account": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns pledge information (if applicable) for bonded mixnode for the provided vesting account address.", + "type": "object", + "required": [ + "get_mixnode" + ], + "properties": { + "get_mixnode": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns pledge information (if applicable) for bonded gateway for the provided vesting account address.", + "type": "object", + "required": [ + "get_gateway" + ], + "properties": { + "get_gateway": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the current vesting period for the provided vesting account address.", + "type": "object", + "required": [ + "get_current_vesting_period" + ], + "properties": { + "get_current_vesting_period": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the information about particular vesting delegation.", + "type": "object", + "required": [ + "get_delegation" + ], + "properties": { + "get_delegation": { + "type": "object", + "required": [ + "address", + "block_timestamp_secs", + "mix_id" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + }, + "block_timestamp_secs": { + "description": "Block timestamp of the delegation.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "Id of the mixnode towards which the delegation has been made.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the total amount of coins delegated towards particular mixnode by the provided vesting account address.", + "type": "object", + "required": [ + "get_total_delegation_amount" + ], + "properties": { + "get_total_delegation_amount": { + "type": "object", + "required": [ + "address", + "mix_id" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + }, + "mix_id": { + "description": "Id of the mixnode towards which the delegations have been made.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns timestamps of delegations made towards particular mixnode by the provided vesting account address.", + "type": "object", + "required": [ + "get_delegation_times" + ], + "properties": { + "get_delegation_times": { + "type": "object", + "required": [ + "address", + "mix_id" + ], + "properties": { + "address": { + "description": "Address of the vesting account in question.", + "type": "string" + }, + "mix_id": { + "description": "Id of the mixnode towards which the delegations have been made.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns all active delegations made with vesting tokens stored in this contract.", + "type": "object", + "required": [ + "get_all_delegations" + ], + "properties": { + "get_all_delegations": { + "type": "object", + "properties": { + "limit": { + "description": "Controls the maximum number of entries returned by the query. Note that too large values will be overwritten by a saner default.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "description": "Pagination control for the values returned by the query. Note that the provided value itself will **not** be used for the response.", + "type": [ + "array", + "null" + ], + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + ], + "maxItems": 3, + "minItems": 3 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_account.json b/contracts/vesting/schema/raw/response_to_get_account.json new file mode 100644 index 0000000000..7d8544539b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_account.json @@ -0,0 +1,179 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Account", + "description": "Vesting account information.", + "type": "object", + "required": [ + "coin", + "owner_address", + "periods", + "start_time", + "storage_key" + ], + "properties": { + "coin": { + "description": "The initial amount of coins used creation of this account.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "owner_address": { + "description": "Address of the owner of the vesting account.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "periods": { + "description": "All vesting periods for this account.", + "type": "array", + "items": { + "$ref": "#/definitions/VestingPeriod" + } + }, + "pledge_cap": { + "description": "Optional custom pledge cap of this vesting account.", + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/PledgeCap" + }, + { + "type": "null" + } + ] + }, + "staking_address": { + "description": "Optional address of an account allowed to perform staking on behalf of the owner.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "start_time": { + "description": "The starting vesting time.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "storage_key": { + "description": "The id/storage_key of this vesting account.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "Percent": { + "description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "PledgeCap": { + "description": "Defines cap for pleding/staking tokens.", + "oneOf": [ + { + "description": "Specifies a percent-based pledge cap, i.e. only given % of tokens could be pledged/staked.", + "type": "object", + "required": [ + "percent" + ], + "properties": { + "percent": { + "$ref": "#/definitions/Percent" + } + }, + "additionalProperties": false + }, + { + "description": "Specifies an absolute pledge cap, i.e. an explicit value that could be pledged/staked.", + "type": "object", + "required": [ + "absolute" + ], + "properties": { + "absolute": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + } + ] + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "VestingPeriod": { + "description": "Vesting period details.", + "type": "object", + "required": [ + "period_seconds", + "start_time" + ], + "properties": { + "period_seconds": { + "description": "The duration (in seconds) of the vesting period.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "start_time": { + "description": "The start time of this vesting period, as unix timestamp.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_accounts_paged.json b/contracts/vesting/schema/raw/response_to_get_accounts_paged.json new file mode 100644 index 0000000000..f84ed729b8 --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_accounts_paged.json @@ -0,0 +1,61 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AccountsResponse", + "description": "Response containing basic vesting account information", + "type": "object", + "required": [ + "accounts" + ], + "properties": { + "accounts": { + "description": "The actual accounts returned by the query.", + "type": "array", + "items": { + "$ref": "#/definitions/BaseVestingAccountInfo" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "BaseVestingAccountInfo": { + "description": "Basic information regarding particular vesting account", + "type": "object", + "required": [ + "account_id", + "owner" + ], + "properties": { + "account_id": { + "description": "Id associated with this account", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of this account's owner", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_accounts_vesting_coins_paged.json b/contracts/vesting/schema/raw/response_to_get_accounts_vesting_coins_paged.json new file mode 100644 index 0000000000..6647e14194 --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_accounts_vesting_coins_paged.json @@ -0,0 +1,89 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VestingCoinsResponse", + "description": "Response containing vesting coins held in this contract", + "type": "object", + "required": [ + "accounts" + ], + "properties": { + "accounts": { + "description": "The actual accounts, and their vesting coins, returned by the query.", + "type": "array", + "items": { + "$ref": "#/definitions/AccountVestingCoins" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "AccountVestingCoins": { + "description": "Basic information regarding particular vesting account alongside the amount of vesting coins.", + "type": "object", + "required": [ + "account_id", + "owner", + "still_vesting" + ], + "properties": { + "account_id": { + "description": "Id associated with this account", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of this account's owner", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "still_vesting": { + "description": "Coins that are still vesting belonging to this account.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + } + }, + "additionalProperties": false + }, + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_all_delegations.json b/contracts/vesting/schema/raw/response_to_get_all_delegations.json new file mode 100644 index 0000000000..89de917311 --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_all_delegations.json @@ -0,0 +1,90 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AllDelegationsResponse", + "description": "Response containing paged list of all vesting delegations made using vesting coins.", + "type": "object", + "required": [ + "delegations" + ], + "properties": { + "delegations": { + "description": "The actual vesting delegations made.", + "type": "array", + "items": { + "$ref": "#/definitions/VestingDelegation" + } + }, + "start_next_after": { + "description": "Field indicating paging information for the following queries if the caller wishes to get further entries.", + "type": [ + "array", + "null" + ], + "items": [ + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + ], + "maxItems": 3, + "minItems": 3 + } + }, + "additionalProperties": false, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "VestingDelegation": { + "description": "Details about particular vesting delegation.", + "type": "object", + "required": [ + "account_id", + "amount", + "block_timestamp", + "mix_id" + ], + "properties": { + "account_id": { + "description": "The id of the vesting account that has made the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "amount": { + "description": "The raw amount delegated (interpreted to be in the same denom as the underlying vesting specification)", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "block_timestamp": { + "description": "The block timestamp when the delegation has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "The id of the mixnode towards which the delegation has been made.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_c_w2_contract_version.json b/contracts/vesting/schema/raw/response_to_get_c_w2_contract_version.json new file mode 100644 index 0000000000..3ded8c84d9 --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_c_w2_contract_version.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractVersion", + "type": "object", + "required": [ + "contract", + "version" + ], + "properties": { + "contract": { + "description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing", + "type": "string" + }, + "version": { + "description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/vesting/schema/raw/response_to_get_contract_version.json b/contracts/vesting/schema/raw/response_to_get_contract_version.json new file mode 100644 index 0000000000..60ae65f3d1 --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_contract_version.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContractBuildInformation", + "type": "object", + "required": [ + "build_timestamp", + "build_version", + "commit_branch", + "commit_sha", + "commit_timestamp", + "rustc_version" + ], + "properties": { + "build_timestamp": { + "description": "Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.", + "type": "string" + }, + "build_version": { + "description": "Provides the build version, for example `0.1.0-9-g46f83e1`.", + "type": "string" + }, + "commit_branch": { + "description": "Provides the name of the git branch that was used for the build, for example `master`.", + "type": "string" + }, + "commit_sha": { + "description": "Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`.", + "type": "string" + }, + "commit_timestamp": { + "description": "Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`.", + "type": "string" + }, + "rustc_version": { + "description": "Provides the rustc version that was used for the build, for example `1.52.0-nightly`.", + "type": "string" + } + }, + "additionalProperties": false +} diff --git a/contracts/vesting/schema/raw/response_to_get_current_vesting_period.json b/contracts/vesting/schema/raw/response_to_get_current_vesting_period.json new file mode 100644 index 0000000000..e001a46a2c --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_current_vesting_period.json @@ -0,0 +1,36 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Period", + "description": "The vesting period.", + "oneOf": [ + { + "description": "Defines a pre-vesting period.", + "type": "string", + "enum": [ + "before" + ] + }, + { + "description": "Defines currently active vesting period.", + "type": "object", + "required": [ + "in" + ], + "properties": { + "in": { + "type": "integer", + "format": "uint", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "Defines a post-vesting period.", + "type": "string", + "enum": [ + "after" + ] + } + ] +} diff --git a/contracts/vesting/schema/raw/response_to_get_delegated_coins.json b/contracts/vesting/schema/raw/response_to_get_delegated_coins.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_delegated_coins.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_delegation.json b/contracts/vesting/schema/raw/response_to_get_delegation.json new file mode 100644 index 0000000000..75c770ef66 --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_delegation.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "VestingDelegation", + "description": "Details about particular vesting delegation.", + "type": "object", + "required": [ + "account_id", + "amount", + "block_timestamp", + "mix_id" + ], + "properties": { + "account_id": { + "description": "The id of the vesting account that has made the delegation.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "amount": { + "description": "The raw amount delegated (interpreted to be in the same denom as the underlying vesting specification)", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "block_timestamp": { + "description": "The block timestamp when the delegation has been made.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "mix_id": { + "description": "The id of the mixnode towards which the delegation has been made.", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_delegation_times.json b/contracts/vesting/schema/raw/response_to_get_delegation_times.json new file mode 100644 index 0000000000..0ac43e6513 --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_delegation_times.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DelegationTimesResponse", + "description": "Response containing timestamps of all delegations made towards particular mixnode by given vesting account.", + "type": "object", + "required": [ + "account_id", + "delegation_timestamps", + "mix_id", + "owner" + ], + "properties": { + "account_id": { + "description": "Id associated with this account", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "delegation_timestamps": { + "description": "All timestamps where a delegation was made", + "type": "array", + "items": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "mix_id": { + "description": "Id of the mixnode towards which the delegation was made", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "owner": { + "description": "Address of this account's owner", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_end_time.json b/contracts/vesting/schema/raw/response_to_get_end_time.json new file mode 100644 index 0000000000..6dd9971a14 --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_end_time.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Timestamp", + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ], + "definitions": { + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_gateway.json b/contracts/vesting/schema/raw/response_to_get_gateway.json new file mode 100644 index 0000000000..1a18cd9f84 --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_gateway.json @@ -0,0 +1,72 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_PledgeData", + "anyOf": [ + { + "$ref": "#/definitions/PledgeData" + }, + { + "type": "null" + } + ], + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "PledgeData": { + "description": "Information regarding pledge (i.e. mixnode or gateway bonding) made with vesting tokens.", + "type": "object", + "required": [ + "amount", + "block_time" + ], + "properties": { + "amount": { + "description": "The amount pledged.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "block_time": { + "description": "The block timestamp where the pledge occurred.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_historical_vesting_staking_reward.json b/contracts/vesting/schema/raw/response_to_get_historical_vesting_staking_reward.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_historical_vesting_staking_reward.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_mixnode.json b/contracts/vesting/schema/raw/response_to_get_mixnode.json new file mode 100644 index 0000000000..1a18cd9f84 --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_mixnode.json @@ -0,0 +1,72 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_PledgeData", + "anyOf": [ + { + "$ref": "#/definitions/PledgeData" + }, + { + "type": "null" + } + ], + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "PledgeData": { + "description": "Information regarding pledge (i.e. mixnode or gateway bonding) made with vesting tokens.", + "type": "object", + "required": [ + "amount", + "block_time" + ], + "properties": { + "amount": { + "description": "The amount pledged.", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "block_time": { + "description": "The block timestamp where the pledge occurred.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Timestamp": { + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_original_vesting.json b/contracts/vesting/schema/raw/response_to_get_original_vesting.json new file mode 100644 index 0000000000..0b0ced2dec --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_original_vesting.json @@ -0,0 +1,55 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "OriginalVestingResponse", + "description": "Details about the original vesting specification used when the account was created.", + "type": "object", + "required": [ + "amount", + "number_of_periods", + "period_duration" + ], + "properties": { + "amount": { + "description": "The original amount that was used for the creation of this vesting account", + "allOf": [ + { + "$ref": "#/definitions/Coin" + } + ] + }, + "number_of_periods": { + "description": "The number of vesting periods that the account was created with", + "type": "integer", + "format": "uint", + "minimum": 0.0 + }, + "period_duration": { + "description": "Duration of each vesting period in seconds", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false, + "definitions": { + "Coin": { + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + } + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_pledged_coins.json b/contracts/vesting/schema/raw/response_to_get_pledged_coins.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_pledged_coins.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_spendable_reward_coins.json b/contracts/vesting/schema/raw/response_to_get_spendable_reward_coins.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_spendable_reward_coins.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_spendable_vested_coins.json b/contracts/vesting/schema/raw/response_to_get_spendable_vested_coins.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_spendable_vested_coins.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_staked_coins.json b/contracts/vesting/schema/raw/response_to_get_staked_coins.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_staked_coins.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_start_time.json b/contracts/vesting/schema/raw/response_to_get_start_time.json new file mode 100644 index 0000000000..6dd9971a14 --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_start_time.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Timestamp", + "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```", + "allOf": [ + { + "$ref": "#/definitions/Uint64" + } + ], + "definitions": { + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_total_delegation_amount.json b/contracts/vesting/schema/raw/response_to_get_total_delegation_amount.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_total_delegation_amount.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_vested_coins.json b/contracts/vesting/schema/raw/response_to_get_vested_coins.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_vested_coins.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_vesting_coins.json b/contracts/vesting/schema/raw/response_to_get_vesting_coins.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_vesting_coins.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_get_withdrawn_coins.json b/contracts/vesting/schema/raw/response_to_get_withdrawn_coins.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_get_withdrawn_coins.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_locked_coins.json b/contracts/vesting/schema/raw/response_to_locked_coins.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_locked_coins.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/schema/raw/response_to_spendable_coins.json b/contracts/vesting/schema/raw/response_to_spendable_coins.json new file mode 100644 index 0000000000..6e18ef9a9b --- /dev/null +++ b/contracts/vesting/schema/raw/response_to_spendable_coins.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Coin", + "type": "object", + "required": [ + "amount", + "denom" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "denom": { + "type": "string" + } + }, + "definitions": { + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/vesting/src/bin/schema.rs b/contracts/vesting/src/bin/schema.rs new file mode 100644 index 0000000000..fc95866f6d --- /dev/null +++ b/contracts/vesting/src/bin/schema.rs @@ -0,0 +1,14 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use cosmwasm_schema::write_api; +use vesting_contract_common::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InitMsg, + query: QueryMsg, + execute: ExecuteMsg, + migrate: MigrateMsg, + } +} diff --git a/contracts/vesting/src/contract.rs b/contracts/vesting/src/contract.rs index 54db2d9464..31d3c21b06 100644 --- a/contracts/vesting/src/contract.rs +++ b/contracts/vesting/src/contract.rs @@ -1,14 +1,13 @@ -use crate::errors::ContractError; pub use crate::queries::*; use crate::storage::{ADMIN, MIXNET_CONTRACT_ADDRESS, MIX_DENOM}; pub use crate::transactions::*; -use crate::vesting::Account; use cosmwasm_std::{ entry_point, to_binary, Addr, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Uint128, }; use semver::Version; use vesting_contract_common::messages::{ExecuteMsg, InitMsg, MigrateMsg, QueryMsg}; +use vesting_contract_common::{Account, VestingContractError}; // version info for migration info const CONTRACT_NAME: &str = "crate:nym-vesting-contract"; @@ -36,7 +35,7 @@ pub fn instantiate( _env: Env, info: MessageInfo, msg: InitMsg, -) -> Result { +) -> Result { // validate the received mixnet contract address let mixnet_contract_address = deps.api.addr_validate(&msg.mixnet_contract_address)?; @@ -51,21 +50,24 @@ pub fn instantiate( } #[entry_point] -pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result { +pub fn migrate( + deps: DepsMut<'_>, + _env: Env, + _msg: MigrateMsg, +) -> Result { // note: don't remove this particular bit of code as we have to ALWAYS check whether we have to update the stored version - let version: Version = - CONTRACT_VERSION - .parse() - .map_err(|error: semver::Error| ContractError::SemVerFailure { - value: CONTRACT_VERSION.to_string(), - error_message: error.to_string(), - })?; + let version: Version = CONTRACT_VERSION.parse().map_err(|error: semver::Error| { + VestingContractError::SemVerFailure { + value: CONTRACT_VERSION.to_string(), + error_message: error.to_string(), + } + })?; let storage_version_raw = cw2::get_contract_version(deps.storage)?.version; let storage_version: Version = storage_version_raw .parse() - .map_err(|error: semver::Error| ContractError::SemVerFailure { + .map_err(|error: semver::Error| VestingContractError::SemVerFailure { value: storage_version_raw, error_message: error.to_string(), })?; @@ -86,7 +88,7 @@ pub fn execute( env: Env, info: MessageInfo, msg: ExecuteMsg, -) -> Result { +) -> Result { match msg { ExecuteMsg::CreateFamily { label } => try_create_family(info, deps, label), ExecuteMsg::JoinFamily { @@ -190,7 +192,11 @@ pub fn execute( } #[entry_point] -pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result { +pub fn query( + deps: Deps<'_>, + env: Env, + msg: QueryMsg, +) -> Result { let query_res = match msg { QueryMsg::GetContractVersion {} => to_binary(&get_contract_version()), QueryMsg::GetCW2ContractVersion {} => to_binary(&cw2::get_contract_version(deps.storage)?), @@ -314,17 +320,23 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result Result { +pub(crate) fn validate_funds( + funds: &[Coin], + mix_denom: String, +) -> Result { if funds.is_empty() || funds[0].amount.is_zero() { - return Err(ContractError::EmptyFunds); + return Err(VestingContractError::EmptyFunds); } if funds.len() > 1 { - return Err(ContractError::MultipleDenoms); + return Err(VestingContractError::MultipleDenoms); } if funds[0].denom != mix_denom { - return Err(ContractError::WrongDenom(funds[0].denom.clone(), mix_denom)); + return Err(VestingContractError::WrongDenom( + funds[0].denom.clone(), + mix_denom, + )); } Ok(funds[0].clone()) @@ -333,13 +345,13 @@ pub(crate) fn validate_funds(funds: &[Coin], mix_denom: String) -> Result Result<(), ContractError> { +) -> Result<(), VestingContractError> { if let Some(staking_address) = account.staking_address() { if staking_address == addr { return Ok(()); } } - Err(ContractError::InvalidStakingAccount { + Err(VestingContractError::InvalidStakingAccount { address: addr.clone(), for_account: account.owner_address(), }) diff --git a/contracts/vesting/src/lib.rs b/contracts/vesting/src/lib.rs index d2238d4d15..527a9ffa53 100644 --- a/contracts/vesting/src/lib.rs +++ b/contracts/vesting/src/lib.rs @@ -5,7 +5,6 @@ #![warn(clippy::unwrap_used)] pub mod contract; -pub mod errors; mod queries; mod queued_migrations; mod storage; diff --git a/contracts/vesting/src/queries.rs b/contracts/vesting/src/queries.rs index a33bb84256..c3c9c0d99d 100644 --- a/contracts/vesting/src/queries.rs +++ b/contracts/vesting/src/queries.rs @@ -1,19 +1,18 @@ // Copyright 2021-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::errors::ContractError; use crate::storage; use crate::storage::{account_from_address, BlockTimestampSecs, ACCOUNTS, DELEGATIONS, MIX_DENOM}; use crate::traits::VestingAccount; -use crate::vesting::Account; +use crate::vesting::StorableVestingAccountExt; use contracts_common::ContractBuildInformation; use cosmwasm_std::{Coin, Deps, Env, Order, StdResult, Timestamp, Uint128}; use cw_storage_plus::Bound; use mixnet_contract_common::MixId; use vesting_contract_common::{ - AccountVestingCoins, AccountsResponse, AllDelegationsResponse, BaseVestingAccountInfo, + Account, AccountVestingCoins, AccountsResponse, AllDelegationsResponse, BaseVestingAccountInfo, DelegationTimesResponse, OriginalVestingResponse, Period, PledgeData, VestingCoinsResponse, - VestingDelegation, + VestingContractError, VestingDelegation, }; /// Get current vesting period for a given [crate::vesting::Account]. @@ -21,24 +20,30 @@ pub fn try_get_current_vesting_period( address: &str, deps: Deps<'_>, env: Env, -) -> Result { +) -> Result { let account = account_from_address(address, deps.storage, deps.api)?; account.get_current_vesting_period(env.block.time) } /// Loads mixnode bond from vesting contract storage. -pub fn try_get_mixnode(address: &str, deps: Deps<'_>) -> Result, ContractError> { +pub fn try_get_mixnode( + address: &str, + deps: Deps<'_>, +) -> Result, VestingContractError> { let account = account_from_address(address, deps.storage, deps.api)?; account.load_mixnode_pledge(deps.storage) } /// Loads gateway bond from vesting contract storage. -pub fn try_get_gateway(address: &str, deps: Deps<'_>) -> Result, ContractError> { +pub fn try_get_gateway( + address: &str, + deps: Deps<'_>, +) -> Result, VestingContractError> { let account = account_from_address(address, deps.storage, deps.api)?; account.load_gateway_pledge(deps.storage) } -pub fn try_get_account(address: &str, deps: Deps<'_>) -> Result { +pub fn try_get_account(address: &str, deps: Deps<'_>) -> Result { account_from_address(address, deps.storage, deps.api) } @@ -65,7 +70,7 @@ pub fn try_get_all_accounts( deps: Deps<'_>, start_after: Option, limit: Option, -) -> Result { +) -> Result { let limit = limit.unwrap_or(150).min(250) as usize; let start = start_after @@ -96,7 +101,7 @@ pub fn try_get_all_accounts_vesting_coins( env: Env, start_after: Option, limit: Option, -) -> Result { +) -> Result { let limit = limit.unwrap_or(150).min(250) as usize; let start = start_after @@ -133,7 +138,7 @@ pub fn try_get_locked_coins( block_time: Option, env: Env, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.locked_coins(block_time, &env, deps.storage) } @@ -144,7 +149,7 @@ pub fn try_get_spendable_coins( block_time: Option, env: Env, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.spendable_coins(block_time, &env, deps.storage) } @@ -155,7 +160,7 @@ pub fn try_get_vested_coins( block_time: Option, env: Env, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.get_vested_coins(block_time, &env, deps.storage) } @@ -166,7 +171,7 @@ pub fn try_get_vesting_coins( block_time: Option, env: Env, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.get_vesting_coins(block_time, &env, deps.storage) } @@ -175,7 +180,7 @@ pub fn try_get_vesting_coins( pub fn try_get_start_time( vesting_account_address: &str, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; Ok(account.get_start_time()) } @@ -184,7 +189,7 @@ pub fn try_get_start_time( pub fn try_get_end_time( vesting_account_address: &str, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; Ok(account.get_end_time()) } @@ -193,7 +198,7 @@ pub fn try_get_end_time( pub fn try_get_original_vesting( vesting_account_address: &str, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.get_original_vesting() } @@ -201,7 +206,7 @@ pub fn try_get_original_vesting( pub fn try_get_historical_vesting_staking_reward( vesting_account_address: &str, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.get_historical_vested_staking_rewards(deps.storage) } @@ -210,7 +215,7 @@ pub fn try_get_spendable_vested_coins( vesting_account_address: &str, deps: Deps<'_>, env: Env, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.spendable_vested_coins(None, &env, deps.storage) } @@ -219,7 +224,7 @@ pub fn try_get_spendable_reward_coins( vesting_account_address: &str, deps: Deps<'_>, env: Env, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; account.spendable_reward_coins(None, &env, deps.storage) } @@ -227,7 +232,7 @@ pub fn try_get_spendable_reward_coins( pub fn try_get_delegated_coins( vesting_account_address: &str, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; let denom = MIX_DENOM.load(deps.storage)?; let amount = account.total_delegations(deps.storage)?; @@ -237,7 +242,7 @@ pub fn try_get_delegated_coins( pub fn try_get_pledged_coins( vesting_account_address: &str, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; let denom = MIX_DENOM.load(deps.storage)?; let amount = account.total_pledged(deps.storage)?; @@ -247,7 +252,7 @@ pub fn try_get_pledged_coins( pub fn try_get_staked_coins( vesting_account_address: &str, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; let denom = MIX_DENOM.load(deps.storage)?; let amount = account.total_staked(deps.storage)?; @@ -257,7 +262,7 @@ pub fn try_get_staked_coins( pub fn try_get_withdrawn_coins( vesting_account_address: &str, deps: Deps<'_>, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; let denom = MIX_DENOM.load(deps.storage)?; let amount = account.load_withdrawn(deps.storage)?; @@ -269,7 +274,7 @@ pub fn try_get_delegation_times( deps: Deps<'_>, vesting_account_address: &str, mix_id: MixId, -) -> Result { +) -> Result { let owner = deps.api.addr_validate(vesting_account_address)?; let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; @@ -288,7 +293,7 @@ pub fn try_get_all_delegations( deps: Deps<'_>, start_after: Option<(u32, MixId, BlockTimestampSecs)>, limit: Option, -) -> Result { +) -> Result { let limit = limit.unwrap_or(100).min(200) as usize; let start = start_after.map(Bound::exclusive); @@ -325,7 +330,7 @@ pub fn try_get_delegation( vesting_account_address: &str, mix_id: MixId, block_timestamp_secs: BlockTimestampSecs, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; let storage_key = (account.storage_key(), mix_id, block_timestamp_secs); @@ -343,7 +348,7 @@ pub fn try_get_delegation_amount( deps: Deps<'_>, vesting_account_address: &str, mix_id: MixId, -) -> Result { +) -> Result { let account = account_from_address(vesting_account_address, deps.storage, deps.api)?; let amount = DELEGATIONS diff --git a/contracts/vesting/src/storage.rs b/contracts/vesting/src/storage.rs index eb49d3c434..d6d363d36f 100644 --- a/contracts/vesting/src/storage.rs +++ b/contracts/vesting/src/storage.rs @@ -1,16 +1,14 @@ -use crate::errors::ContractError; -use crate::vesting::Account; use cosmwasm_std::{Addr, Api, Storage, Uint128}; use cosmwasm_std::{Coin, Order}; use cw_storage_plus::{Item, Map}; use mixnet_contract_common::{IdentityKey, MixId}; -use vesting_contract_common::PledgeData; +use vesting_contract_common::account::VestingAccountStorageKey; +use vesting_contract_common::{Account, PledgeData, VestingContractError}; pub(crate) type BlockTimestampSecs = u64; -pub(crate) type AccountStorageKey = u32; /// Counter for the unique, monotonically increasing storage key id for the vesting account data. -pub const KEY: Item<'_, AccountStorageKey> = Item::new("key"); +pub const KEY: Item<'_, VestingAccountStorageKey> = Item::new("key"); /// Storage map containing vesting account information associated with particular owner address. pub const ACCOUNTS: Map<'_, Addr, Account> = Map::new("acc"); @@ -18,28 +16,31 @@ pub const ACCOUNTS: Map<'_, Addr, Account> = Map::new("acc"); /// Storage map containing information about amount of tokens associated with particular vesting account /// that are currently present in the contract (and have not been withdrawn or staked in the mixnet contract) // note: this assumes I understood the intent behind this correctly -const BALANCES: Map<'_, AccountStorageKey, Uint128> = Map::new("blc"); +const BALANCES: Map<'_, VestingAccountStorageKey, Uint128> = Map::new("blc"); /// Storage map containing information about amount of tokens withdrawn from the contract by a particular vesting account. -const WITHDRAWNS: Map<'_, AccountStorageKey, Uint128> = Map::new("wthd"); +const WITHDRAWNS: Map<'_, VestingAccountStorageKey, Uint128> = Map::new("wthd"); /// Storage map containing information about amount of tokens pledged towards bonding mixnodes /// in the mixnet contract using a particular vesting account. -const BOND_PLEDGES: Map<'_, AccountStorageKey, PledgeData> = Map::new("bnd"); +const BOND_PLEDGES: Map<'_, VestingAccountStorageKey, PledgeData> = Map::new("bnd"); /// Storage map containing information about amount of tokens pledged towards bonding gateways /// in the mixnet contract using a particular vesting account. -const GATEWAY_PLEDGES: Map<'_, AccountStorageKey, PledgeData> = Map::new("gtw"); +const GATEWAY_PLEDGES: Map<'_, VestingAccountStorageKey, PledgeData> = Map::new("gtw"); /// Old, pre-v2 migration, storage map that used to contain information about tokens delegated /// towards particular mixnodes in the mixnet contract with given vesting account. /// It should be completely empty. -pub const _OLD_DELEGATIONS: Map<'_, (AccountStorageKey, IdentityKey, BlockTimestampSecs), Uint128> = - Map::new("dlg"); +pub const _OLD_DELEGATIONS: Map< + '_, + (VestingAccountStorageKey, IdentityKey, BlockTimestampSecs), + Uint128, +> = Map::new("dlg"); /// Storage map containing information about tokens delegated towards particular mixnodes /// in the mixnet contract with given vesting account. -pub const DELEGATIONS: Map<'_, (AccountStorageKey, MixId, BlockTimestampSecs), Uint128> = +pub const DELEGATIONS: Map<'_, (VestingAccountStorageKey, MixId, BlockTimestampSecs), Uint128> = Map::new("dlg_v2"); /// Explicit contract admin that is allowed, among other things, to create new vesting accounts. @@ -52,10 +53,10 @@ pub const MIXNET_CONTRACT_ADDRESS: Item<'_, Addr> = Item::new("mix"); pub const MIX_DENOM: Item<'_, String> = Item::new("den"); pub fn save_delegation( - key: (AccountStorageKey, MixId, BlockTimestampSecs), + key: (VestingAccountStorageKey, MixId, BlockTimestampSecs), amount: Uint128, storage: &mut dyn Storage, -) -> Result<(), ContractError> { +) -> Result<(), VestingContractError> { let existing_delegation_amount = if let Some(delegation) = DELEGATIONS.may_load(storage, key)? { delegation } else { @@ -67,17 +68,17 @@ pub fn save_delegation( } pub fn remove_delegation( - key: (AccountStorageKey, MixId, BlockTimestampSecs), + key: (VestingAccountStorageKey, MixId, BlockTimestampSecs), storage: &mut dyn Storage, -) -> Result<(), ContractError> { +) -> Result<(), VestingContractError> { DELEGATIONS.remove(storage, key); Ok(()) } pub fn load_delegation_timestamps( - prefix: (AccountStorageKey, MixId), + prefix: (VestingAccountStorageKey, MixId), storage: &dyn Storage, -) -> Result, ContractError> { +) -> Result, VestingContractError> { let block_timestamps = DELEGATIONS .prefix(prefix) .keys(storage, None, None, Order::Ascending) @@ -86,7 +87,7 @@ pub fn load_delegation_timestamps( } pub fn count_subdelegations_for_mix( - prefix: (AccountStorageKey, MixId), + prefix: (VestingAccountStorageKey, MixId), storage: &dyn Storage, ) -> u32 { DELEGATIONS @@ -96,9 +97,9 @@ pub fn count_subdelegations_for_mix( } pub fn load_withdrawn( - key: AccountStorageKey, + key: VestingAccountStorageKey, storage: &dyn Storage, -) -> Result { +) -> Result { Ok(WITHDRAWNS .may_load(storage, key) .unwrap_or(None) @@ -106,9 +107,9 @@ pub fn load_withdrawn( } pub fn load_balance( - key: AccountStorageKey, + key: VestingAccountStorageKey, storage: &dyn Storage, -) -> Result { +) -> Result { Ok(BALANCES .may_load(storage, key) .unwrap_or(None) @@ -116,57 +117,57 @@ pub fn load_balance( } pub fn save_balance( - key: AccountStorageKey, + key: VestingAccountStorageKey, value: Uint128, storage: &mut dyn Storage, -) -> Result<(), ContractError> { +) -> Result<(), VestingContractError> { BALANCES.save(storage, key, &value)?; Ok(()) } pub fn save_withdrawn( - key: AccountStorageKey, + key: VestingAccountStorageKey, value: Uint128, storage: &mut dyn Storage, -) -> Result<(), ContractError> { +) -> Result<(), VestingContractError> { WITHDRAWNS.save(storage, key, &value)?; Ok(()) } pub fn load_bond_pledge( - key: AccountStorageKey, + key: VestingAccountStorageKey, storage: &dyn Storage, -) -> Result, ContractError> { +) -> Result, VestingContractError> { Ok(BOND_PLEDGES.may_load(storage, key).unwrap_or(None)) } pub fn remove_bond_pledge( - key: AccountStorageKey, + key: VestingAccountStorageKey, storage: &mut dyn Storage, -) -> Result<(), ContractError> { +) -> Result<(), VestingContractError> { BOND_PLEDGES.remove(storage, key); Ok(()) } pub fn save_bond_pledge( - key: AccountStorageKey, + key: VestingAccountStorageKey, value: &PledgeData, storage: &mut dyn Storage, -) -> Result<(), ContractError> { +) -> Result<(), VestingContractError> { BOND_PLEDGES.save(storage, key, value)?; Ok(()) } pub fn decrease_bond_pledge( - key: AccountStorageKey, + key: VestingAccountStorageKey, amount: Coin, storage: &mut dyn Storage, -) -> Result<(), ContractError> { +) -> Result<(), VestingContractError> { let mut existing = BOND_PLEDGES.load(storage, key)?; if existing.amount.amount <= amount.amount { // this shouldn't be possible! // (but check for it anyway... just in case) - return Err(ContractError::InvalidBondPledgeReduction { + return Err(VestingContractError::InvalidBondPledgeReduction { current: existing.amount, decrease_by: amount, }); @@ -176,30 +177,33 @@ pub fn decrease_bond_pledge( } pub fn load_gateway_pledge( - key: AccountStorageKey, + key: VestingAccountStorageKey, storage: &dyn Storage, -) -> Result, ContractError> { +) -> Result, VestingContractError> { Ok(GATEWAY_PLEDGES.may_load(storage, key).unwrap_or(None)) } pub fn save_gateway_pledge( - key: AccountStorageKey, + key: VestingAccountStorageKey, value: &PledgeData, storage: &mut dyn Storage, -) -> Result<(), ContractError> { +) -> Result<(), VestingContractError> { GATEWAY_PLEDGES.save(storage, key, value)?; Ok(()) } pub fn remove_gateway_pledge( - key: AccountStorageKey, + key: VestingAccountStorageKey, storage: &mut dyn Storage, -) -> Result<(), ContractError> { +) -> Result<(), VestingContractError> { GATEWAY_PLEDGES.remove(storage, key); Ok(()) } -pub fn save_account(account: &Account, storage: &mut dyn Storage) -> Result<(), ContractError> { +pub fn save_account( + account: &Account, + storage: &mut dyn Storage, +) -> Result<(), VestingContractError> { ACCOUNTS.save(storage, account.owner_address(), account)?; Ok(()) } @@ -207,24 +211,27 @@ pub fn save_account(account: &Account, storage: &mut dyn Storage) -> Result<(), pub fn load_account( address: Addr, storage: &dyn Storage, -) -> Result, ContractError> { +) -> Result, VestingContractError> { Ok(ACCOUNTS.may_load(storage, address).unwrap_or(None)) } -pub fn delete_account(address: Addr, storage: &mut dyn Storage) -> Result<(), ContractError> { +pub fn delete_account( + address: Addr, + storage: &mut dyn Storage, +) -> Result<(), VestingContractError> { ACCOUNTS.remove(storage, address); Ok(()) } -fn validate_account(address: Addr, storage: &dyn Storage) -> Result { +fn validate_account(address: Addr, storage: &dyn Storage) -> Result { load_account(address.clone(), storage)? - .ok_or_else(|| ContractError::NoAccountForAddress(address.into_string())) + .ok_or_else(|| VestingContractError::NoAccountForAddress(address.into_string())) } pub fn account_from_address( address: &str, storage: &dyn Storage, api: &dyn Api, -) -> Result { +) -> Result { validate_account(api.addr_validate(address)?, storage) } diff --git a/contracts/vesting/src/support/tests.rs b/contracts/vesting/src/support/tests.rs index 6f48fe6940..98995b1c8e 100644 --- a/contracts/vesting/src/support/tests.rs +++ b/contracts/vesting/src/support/tests.rs @@ -72,7 +72,7 @@ pub mod helpers { use crate::storage::{ACCOUNTS, ADMIN, MIXNET_CONTRACT_ADDRESS, MIX_DENOM}; use crate::support::tests::helpers::state_dump_decoder::RawState; use crate::traits::VestingAccount; - use crate::vesting::{populate_vesting_periods, Account}; + use crate::vesting::{populate_vesting_periods, StorableVestingAccountExt}; use contracts_common::Percent; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier}; use cosmwasm_std::{ @@ -83,8 +83,8 @@ pub mod helpers { use rand_chacha::ChaCha20Rng; use std::path::Path; use std::str::FromStr; - use vesting_contract_common::messages::{InitMsg, VestingSpecification}; - use vesting_contract_common::PledgeCap; + use vesting_contract_common::messages::InitMsg; + use vesting_contract_common::{Account, PledgeCap, VestingSpecification}; // use rng with constant seed for all tests so that they would be deterministic #[allow(unused)] @@ -337,7 +337,7 @@ pub mod helpers { VestingSpecification::new(None, Some(3600), None), ); - Account::new( + Account::save_new( Addr::unchecked("owner"), Some(Addr::unchecked("staking")), Coin { @@ -357,7 +357,7 @@ pub mod helpers { let periods = populate_vesting_periods(start_time.seconds(), VestingSpecification::default()); - Account::new( + Account::save_new( Addr::unchecked("owner"), Some(Addr::unchecked("staking")), Coin { @@ -377,7 +377,7 @@ pub mod helpers { let periods = populate_vesting_periods(start_time.seconds(), VestingSpecification::default()); - Account::new( + Account::save_new( Addr::unchecked("owner"), Some(Addr::unchecked("staking")), Coin { diff --git a/contracts/vesting/src/traits/bonding_account.rs b/contracts/vesting/src/traits/bonding_account.rs index bf5cac3bf5..e5e415e30c 100644 --- a/contracts/vesting/src/traits/bonding_account.rs +++ b/contracts/vesting/src/traits/bonding_account.rs @@ -1,4 +1,3 @@ -use crate::errors::ContractError; use contracts_common::signing::MessageSignature; use cosmwasm_std::{Coin, Env, Response, Storage}; use mixnet_contract_common::{ @@ -6,9 +5,13 @@ use mixnet_contract_common::{ mixnode::{MixNodeConfigUpdate, MixNodeCostParams}, Gateway, MixNode, }; +use vesting_contract_common::VestingContractError; pub trait MixnodeBondingAccount { - fn try_claim_operator_reward(&self, storage: &dyn Storage) -> Result; + fn try_claim_operator_reward( + &self, + storage: &dyn Storage, + ) -> Result; fn try_bond_mixnode( &self, @@ -18,46 +21,46 @@ pub trait MixnodeBondingAccount { pledge: Coin, env: &Env, storage: &mut dyn Storage, - ) -> Result; + ) -> Result; fn try_pledge_additional_tokens( &self, additional_pledge: Coin, env: &Env, storage: &mut dyn Storage, - ) -> Result; + ) -> Result; fn try_decrease_mixnode_pledge( &self, amount: Coin, storage: &mut dyn Storage, - ) -> Result; + ) -> Result; - fn try_unbond_mixnode(&self, storage: &dyn Storage) -> Result; + fn try_unbond_mixnode(&self, storage: &dyn Storage) -> Result; fn try_track_unbond_mixnode( &self, amount: Coin, storage: &mut dyn Storage, - ) -> Result<(), ContractError>; + ) -> Result<(), VestingContractError>; fn try_track_decrease_mixnode_pledge( &self, amount: Coin, storage: &mut dyn Storage, - ) -> Result<(), ContractError>; + ) -> Result<(), VestingContractError>; fn try_update_mixnode_config( &self, new_config: MixNodeConfigUpdate, storage: &mut dyn Storage, - ) -> Result; + ) -> Result; fn try_update_mixnode_cost_params( &self, new_costs: MixNodeCostParams, storage: &mut dyn Storage, - ) -> Result; + ) -> Result; } pub trait GatewayBondingAccount { @@ -68,19 +71,19 @@ pub trait GatewayBondingAccount { pledge: Coin, env: &Env, storage: &mut dyn Storage, - ) -> Result; + ) -> Result; - fn try_unbond_gateway(&self, storage: &dyn Storage) -> Result; + fn try_unbond_gateway(&self, storage: &dyn Storage) -> Result; fn try_track_unbond_gateway( &self, amount: Coin, storage: &mut dyn Storage, - ) -> Result<(), ContractError>; + ) -> Result<(), VestingContractError>; fn try_update_gateway_config( &self, new_config: GatewayConfigUpdate, storage: &mut dyn Storage, - ) -> Result; + ) -> Result; } diff --git a/contracts/vesting/src/traits/delegating_account.rs b/contracts/vesting/src/traits/delegating_account.rs index 4beefabe28..629cfee050 100644 --- a/contracts/vesting/src/traits/delegating_account.rs +++ b/contracts/vesting/src/traits/delegating_account.rs @@ -1,13 +1,13 @@ -use crate::errors::ContractError; use cosmwasm_std::{Coin, Env, Response, Storage, Uint128}; use mixnet_contract_common::MixId; +use vesting_contract_common::VestingContractError; pub trait DelegatingAccount { fn try_claim_delegator_reward( &self, mix_id: MixId, storage: &dyn Storage, - ) -> Result; + ) -> Result; fn try_delegate_to_mixnode( &self, @@ -15,13 +15,13 @@ pub trait DelegatingAccount { amount: Coin, env: &Env, storage: &mut dyn Storage, - ) -> Result; + ) -> Result; fn try_undelegate_from_mixnode( &self, mix_id: MixId, storage: &dyn Storage, - ) -> Result; + ) -> Result; // track_delegation performs internal vesting accounting necessary when // delegating from a vesting account. It accepts the current block height, the @@ -35,7 +35,7 @@ pub trait DelegatingAccount { current_balance: Uint128, delegation: Coin, storage: &mut dyn Storage, - ) -> Result<(), ContractError>; + ) -> Result<(), VestingContractError>; // track_undelegation performs internal vesting accounting necessary when a // vesting account performs an undelegation. fn track_undelegation( @@ -43,5 +43,5 @@ pub trait DelegatingAccount { mix_id: MixId, amount: Coin, storage: &mut dyn Storage, - ) -> Result<(), ContractError>; + ) -> Result<(), VestingContractError>; } diff --git a/contracts/vesting/src/traits/node_families.rs b/contracts/vesting/src/traits/node_families.rs index 163308d30a..f5af460d17 100644 --- a/contracts/vesting/src/traits/node_families.rs +++ b/contracts/vesting/src/traits/node_families.rs @@ -1,32 +1,32 @@ -use crate::errors::ContractError; use contracts_common::signing::MessageSignature; use cosmwasm_std::{Response, Storage}; use mixnet_contract_common::families::FamilyHead; use mixnet_contract_common::IdentityKeyRef; +use vesting_contract_common::VestingContractError; pub trait NodeFamilies { fn try_create_family( &self, storage: &dyn Storage, label: String, - ) -> Result; + ) -> Result; fn try_join_family( &self, storage: &dyn Storage, join_permit: MessageSignature, family_head: FamilyHead, - ) -> Result; + ) -> Result; fn try_leave_family( &self, storage: &dyn Storage, family_head: FamilyHead, - ) -> Result; + ) -> Result; fn try_head_kick_member( &self, storage: &dyn Storage, member: IdentityKeyRef<'_>, - ) -> Result; + ) -> Result; } diff --git a/contracts/vesting/src/traits/vesting_account.rs b/contracts/vesting/src/traits/vesting_account.rs index 258a750492..3699f282ea 100644 --- a/contracts/vesting/src/traits/vesting_account.rs +++ b/contracts/vesting/src/traits/vesting_account.rs @@ -1,9 +1,8 @@ -use crate::errors::ContractError; use cosmwasm_std::{Addr, Coin, Env, Storage, Timestamp, Uint128}; -use vesting_contract_common::OriginalVestingResponse; +use vesting_contract_common::{OriginalVestingResponse, VestingContractError}; pub trait VestingAccount { - fn total_staked(&self, storage: &dyn Storage) -> Result; + fn total_staked(&self, storage: &dyn Storage) -> Result; /// Returns the set of coins that are not spendable (can still be delegated tough) (i.e. locked), /// defined as vesting coins that are not delegated or pledged. @@ -17,7 +16,7 @@ pub trait VestingAccount { block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result; + ) -> Result; /// Calculated as current_balance minus [crate::traits::VestingAccount::locked_coins] /// See [/vesting-contract/struct.Account.html/method.spendable_coins] for impl @@ -26,21 +25,21 @@ pub trait VestingAccount { block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result; + ) -> Result; fn spendable_vested_coins( &self, block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result; + ) -> Result; fn spendable_reward_coins( &self, block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result; + ) -> Result; /// See [/vesting-contract/struct.Account.html/method.get_vested_coins] for impl fn get_vested_coins( @@ -48,7 +47,7 @@ pub trait VestingAccount { block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result; + ) -> Result; /// See [/vesting-contract/struct.Account.html/method.get_vesting_coins] for impl fn get_vesting_coins( @@ -56,7 +55,7 @@ pub trait VestingAccount { block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result; + ) -> Result; /// See [/vesting-contract/struct.Account.html/method.get_start_time] for impl fn get_start_time(&self) -> Timestamp; @@ -65,23 +64,27 @@ pub trait VestingAccount { /// Returns amount of coins set at account creation /// See [/vesting-contract/struct.Account.html/method.get_original_vesting] for impl - fn get_original_vesting(&self) -> Result; + fn get_original_vesting(&self) -> Result; /// See [/vesting-contract/struct.Account.html/method.transfer_ownership] for impl fn transfer_ownership( &mut self, to_address: &Addr, storage: &mut dyn Storage, - ) -> Result<(), ContractError>; + ) -> Result<(), VestingContractError>; /// See [/vesting-contract/struct.Account.html/method.update_staking_address] for impl fn update_staking_address( &mut self, to_address: Option, storage: &mut dyn Storage, - ) -> Result<(), ContractError>; - fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError>; + ) -> Result<(), VestingContractError>; + fn track_reward( + &self, + amount: Coin, + storage: &mut dyn Storage, + ) -> Result<(), VestingContractError>; fn get_historical_vested_staking_rewards( &self, storage: &dyn Storage, - ) -> Result; + ) -> Result; } diff --git a/contracts/vesting/src/transactions.rs b/contracts/vesting/src/transactions.rs index bab50ec369..adbfe39436 100644 --- a/contracts/vesting/src/transactions.rs +++ b/contracts/vesting/src/transactions.rs @@ -2,14 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use crate::contract::{ensure_staking_permission, validate_funds}; -use crate::errors::ContractError; use crate::storage::{ account_from_address, save_account, ADMIN, MIXNET_CONTRACT_ADDRESS, MIX_DENOM, }; use crate::traits::{ DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, NodeFamilies, VestingAccount, }; -use crate::vesting::{populate_vesting_periods, Account}; +use crate::vesting::{populate_vesting_periods, StorableVestingAccountExt}; use contracts_common::signing::MessageSignature; use cosmwasm_std::{coin, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Timestamp}; use mixnet_contract_common::families::FamilyHead; @@ -22,14 +21,13 @@ use vesting_contract_common::events::{ new_track_mixnode_pledge_decrease_event, new_track_mixnode_unbond_event, new_track_reward_event, new_track_undelegation_event, new_vested_coins_withdraw_event, }; -use vesting_contract_common::messages::VestingSpecification; -use vesting_contract_common::PledgeCap; +use vesting_contract_common::{Account, PledgeCap, VestingContractError, VestingSpecification}; pub fn try_create_family( info: MessageInfo, deps: DepsMut, label: String, -) -> Result { +) -> Result { let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?; account.try_create_family(deps.storage, label) } @@ -38,7 +36,7 @@ pub fn try_join_family( deps: DepsMut, join_permit: MessageSignature, family_head: FamilyHead, -) -> Result { +) -> Result { let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?; account.try_join_family(deps.storage, join_permit, family_head) } @@ -46,7 +44,7 @@ pub fn try_leave_family( info: MessageInfo, deps: DepsMut, family_head: FamilyHead, -) -> Result { +) -> Result { let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?; account.try_leave_family(deps.storage, family_head) } @@ -54,7 +52,7 @@ pub fn try_kick_family_member( info: MessageInfo, deps: DepsMut, member: String, -) -> Result { +) -> Result { let account = account_from_address(info.sender.as_ref(), deps.storage, deps.api)?; account.try_head_kick_member(deps.storage, &member) } @@ -67,9 +65,11 @@ pub fn try_update_locked_pledge_cap( cap: PledgeCap, info: MessageInfo, deps: DepsMut, -) -> Result { +) -> Result { if info.sender != ADMIN.load(deps.storage)? { - return Err(ContractError::NotAdmin(info.sender.as_str().to_string())); + return Err(VestingContractError::NotAdmin( + info.sender.as_str().to_string(), + )); } let mut account = account_from_address(&address, deps.storage, deps.api)?; @@ -83,7 +83,7 @@ pub fn try_update_mixnode_config( new_config: MixNodeConfigUpdate, info: MessageInfo, deps: DepsMut, -) -> Result { +) -> Result { let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_update_mixnode_config(new_config, deps.storage) } @@ -92,7 +92,7 @@ pub fn try_update_gateway_config( new_config: GatewayConfigUpdate, info: MessageInfo, deps: DepsMut, -) -> Result { +) -> Result { let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_update_gateway_config(new_config, deps.storage) } @@ -101,7 +101,7 @@ pub fn try_update_mixnode_cost_params( new_costs: MixNodeCostParams, info: MessageInfo, deps: DepsMut, -) -> Result { +) -> Result { let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_update_mixnode_cost_params(new_costs, deps.storage) } @@ -113,9 +113,11 @@ pub fn try_update_mixnet_address( address: String, info: MessageInfo, deps: DepsMut<'_>, -) -> Result { +) -> Result { if info.sender != ADMIN.load(deps.storage)? { - return Err(ContractError::NotAdmin(info.sender.as_str().to_string())); + return Err(VestingContractError::NotAdmin( + info.sender.as_str().to_string(), + )); } let mixnet_contract_address = deps.api.addr_validate(&address)?; @@ -129,16 +131,18 @@ pub fn try_withdraw_vested_coins( env: Env, info: MessageInfo, deps: DepsMut<'_>, -) -> Result { +) -> Result { let mix_denom = MIX_DENOM.load(deps.storage)?; if amount.denom != mix_denom { - return Err(ContractError::WrongDenom(amount.denom, mix_denom)); + return Err(VestingContractError::WrongDenom(amount.denom, mix_denom)); } let address = info.sender; let account = account_from_address(address.as_str(), deps.storage, deps.api)?; if address != account.owner_address() { - return Err(ContractError::NotOwner(account.owner_address().to_string())); + return Err(VestingContractError::NotOwner( + account.owner_address().to_string(), + )); } let spendable_coins = account.spendable_coins(None, &env, deps.storage)?; if amount.amount <= spendable_coins.amount { @@ -157,7 +161,7 @@ pub fn try_withdraw_vested_coins( &coin(new_balance, &amount.denom), ))) } else { - Err(ContractError::InsufficientSpendable( + Err(VestingContractError::InsufficientSpendable( account.owner_address().as_str().to_string(), spendable_coins.amount.u128(), )) @@ -169,7 +173,7 @@ pub fn try_transfer_ownership( to_address: String, info: MessageInfo, deps: DepsMut<'_>, -) -> Result { +) -> Result { let address = info.sender; let to_address = deps.api.addr_validate(&to_address)?; let mut account = account_from_address(address.as_str(), deps.storage, deps.api)?; @@ -177,7 +181,9 @@ pub fn try_transfer_ownership( account.transfer_ownership(&to_address, deps.storage)?; Ok(Response::new().add_event(new_ownership_transfer_event(&address, &to_address))) } else { - Err(ContractError::NotOwner(account.owner_address().to_string())) + Err(VestingContractError::NotOwner( + account.owner_address().to_string(), + )) } } @@ -186,11 +192,13 @@ pub fn try_update_staking_address( to_address: Option, info: MessageInfo, deps: DepsMut<'_>, -) -> Result { +) -> Result { if let Some(ref to_address) = to_address { if account_from_address(to_address, deps.storage, deps.api).is_ok() { // do not allow setting staking address to an existing account's address - return Err(ContractError::StakingAccountExists(to_address.to_string())); + return Err(VestingContractError::StakingAccountExists( + to_address.to_string(), + )); } } @@ -202,7 +210,9 @@ pub fn try_update_staking_address( account.update_staking_address(to_address.clone(), deps.storage)?; Ok(Response::new().add_event(new_staking_address_update_event(&old, &to_address))) } else { - Err(ContractError::NotOwner(account.owner_address().to_string())) + Err(VestingContractError::NotOwner( + account.owner_address().to_string(), + )) } } @@ -214,7 +224,7 @@ pub fn try_bond_gateway( info: MessageInfo, env: Env, deps: DepsMut<'_>, -) -> Result { +) -> Result { let mix_denom = MIX_DENOM.load(deps.storage)?; let pledge = validate_funds(&[amount], mix_denom)?; let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; @@ -222,7 +232,10 @@ pub fn try_bond_gateway( } /// Unbond a gateway, sends [mixnet_contract_common::ExecuteMsg::UnbondGatewayOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS]. -pub fn try_unbond_gateway(info: MessageInfo, deps: DepsMut<'_>) -> Result { +pub fn try_unbond_gateway( + info: MessageInfo, + deps: DepsMut<'_>, +) -> Result { let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_unbond_gateway(deps.storage) } @@ -233,9 +246,9 @@ pub fn try_track_unbond_gateway( amount: Coin, info: MessageInfo, deps: DepsMut<'_>, -) -> Result { +) -> Result { if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? { - return Err(ContractError::NotMixnetContract(info.sender)); + return Err(VestingContractError::NotMixnetContract(info.sender)); } let account = account_from_address(owner, deps.storage, deps.api)?; account.try_track_unbond_gateway(amount, deps.storage)?; @@ -251,7 +264,7 @@ pub fn try_bond_mixnode( info: MessageInfo, env: Env, deps: DepsMut<'_>, -) -> Result { +) -> Result { let mix_denom = MIX_DENOM.load(deps.storage)?; let pledge = validate_funds(&[amount], mix_denom)?; let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; @@ -270,7 +283,7 @@ pub fn try_pledge_more( env: Env, info: MessageInfo, amount: Coin, -) -> Result { +) -> Result { let mix_denom = MIX_DENOM.load(deps.storage)?; let additional_pledge = validate_funds(&[amount], mix_denom)?; @@ -282,7 +295,7 @@ pub fn try_decrease_pledge( deps: DepsMut<'_>, info: MessageInfo, amount: Coin, -) -> Result { +) -> Result { let mix_denom = MIX_DENOM.load(deps.storage)?; // perform basic validation - is it correct demon, is it non-zero, etc. let decrease = validate_funds(&[amount], mix_denom)?; @@ -292,7 +305,10 @@ pub fn try_decrease_pledge( } /// Unbond a mixnode, sends [mixnet_contract_common::ExecuteMsg::UnbondMixnodeOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS]. -pub fn try_unbond_mixnode(info: MessageInfo, deps: DepsMut<'_>) -> Result { +pub fn try_unbond_mixnode( + info: MessageInfo, + deps: DepsMut<'_>, +) -> Result { let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_unbond_mixnode(deps.storage) } @@ -303,9 +319,9 @@ pub fn try_track_unbond_mixnode( amount: Coin, info: MessageInfo, deps: DepsMut<'_>, -) -> Result { +) -> Result { if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? { - return Err(ContractError::NotMixnetContract(info.sender)); + return Err(VestingContractError::NotMixnetContract(info.sender)); } let account = account_from_address(owner, deps.storage, deps.api)?; account.try_track_unbond_mixnode(amount, deps.storage)?; @@ -319,9 +335,9 @@ pub fn try_track_decrease_mixnode_pledge( amount: Coin, info: MessageInfo, deps: DepsMut<'_>, -) -> Result { +) -> Result { if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? { - return Err(ContractError::NotMixnetContract(info.sender)); + return Err(VestingContractError::NotMixnetContract(info.sender)); } let account = account_from_address(owner, deps.storage, deps.api)?; account.try_track_decrease_mixnode_pledge(amount, deps.storage)?; @@ -334,9 +350,9 @@ pub fn try_track_reward( info: MessageInfo, amount: Coin, address: &str, -) -> Result { +) -> Result { if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? { - return Err(ContractError::NotMixnetContract(info.sender)); + return Err(VestingContractError::NotMixnetContract(info.sender)); } let account = account_from_address(address, deps.storage, deps.api)?; account.track_reward(amount, deps.storage)?; @@ -350,9 +366,9 @@ pub fn try_track_undelegation( amount: Coin, info: MessageInfo, deps: DepsMut<'_>, -) -> Result { +) -> Result { if info.sender != MIXNET_CONTRACT_ADDRESS.load(deps.storage)? { - return Err(ContractError::NotMixnetContract(info.sender)); + return Err(VestingContractError::NotMixnetContract(info.sender)); } let account = account_from_address(address, deps.storage, deps.api)?; @@ -368,7 +384,7 @@ pub fn try_delegate_to_mixnode( info: MessageInfo, env: Env, deps: DepsMut<'_>, -) -> Result { +) -> Result { // TODO // as of 01.02.23 // thus restricting it to 25, which is more than double of that, doesn't seem too unreasonable. @@ -397,7 +413,7 @@ pub fn try_delegate_to_mixnode( pub fn try_claim_operator_reward( deps: DepsMut<'_>, info: MessageInfo, -) -> Result { +) -> Result { let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_claim_operator_reward(deps.storage) } @@ -407,7 +423,7 @@ pub fn try_claim_delegator_reward( deps: DepsMut<'_>, info: MessageInfo, mix_id: MixId, -) -> Result { +) -> Result { let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?; account.try_claim_delegator_reward(mix_id, deps.storage) @@ -419,7 +435,7 @@ pub fn try_undelegate_from_mixnode( on_behalf_of: Option, info: MessageInfo, deps: DepsMut<'_>, -) -> Result { +) -> Result { let account = match on_behalf_of { Some(account_owner) => { let account = account_from_address(&account_owner, deps.storage, deps.api)?; @@ -444,16 +460,18 @@ pub fn try_create_periodic_vesting_account( info: MessageInfo, env: Env, deps: DepsMut<'_>, -) -> Result { +) -> Result { if info.sender != ADMIN.load(deps.storage)? { - return Err(ContractError::NotAdmin(info.sender.as_str().to_string())); + return Err(VestingContractError::NotAdmin( + info.sender.as_str().to_string(), + )); } let mix_denom = MIX_DENOM.load(deps.storage)?; let account_exists = account_from_address(owner_address, deps.storage, deps.api).is_ok(); if account_exists { - return Err(ContractError::AccountAlreadyExists( + return Err(VestingContractError::AccountAlreadyExists( owner_address.to_string(), )); } @@ -467,7 +485,9 @@ pub fn try_create_periodic_vesting_account( let staking_account_exists = account_from_address(&staking_address, deps.storage, deps.api).is_ok(); if staking_account_exists { - return Err(ContractError::StakingAccountAlreadyExists(staking_address)); + return Err(VestingContractError::StakingAccountAlreadyExists( + staking_address, + )); } Some(deps.api.addr_validate(&staking_address)?) } else { @@ -483,7 +503,7 @@ pub fn try_create_periodic_vesting_account( let response = Response::new(); - Account::new( + Account::save_new( owner_address.clone(), staking_address.clone(), coin.clone(), diff --git a/contracts/vesting/src/vesting/account/delegating_account.rs b/contracts/vesting/src/vesting/account/delegating_account.rs index beaedf2be2..0371bc7cc3 100644 --- a/contracts/vesting/src/vesting/account/delegating_account.rs +++ b/contracts/vesting/src/vesting/account/delegating_account.rs @@ -1,14 +1,15 @@ use crate::contract::MAX_PER_MIX_DELEGATIONS; -use crate::errors::ContractError; use crate::storage::save_delegation; use crate::storage::MIXNET_CONTRACT_ADDRESS; use crate::traits::DelegatingAccount; +use crate::vesting::account::StorableVestingAccountExt; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; use mixnet_contract_common::ExecuteMsg as MixnetExecuteMsg; use mixnet_contract_common::MixId; use vesting_contract_common::events::{ new_vesting_delegation_event, new_vesting_undelegation_event, }; +use vesting_contract_common::VestingContractError; use super::Account; @@ -17,7 +18,7 @@ impl DelegatingAccount for Account { &self, mix_id: MixId, storage: &dyn Storage, - ) -> Result { + ) -> Result { let msg = MixnetExecuteMsg::WithdrawDelegatorRewardOnBehalf { owner: self.owner_address().to_string(), mix_id, @@ -35,12 +36,12 @@ impl DelegatingAccount for Account { coin: Coin, env: &Env, storage: &mut dyn Storage, - ) -> Result { + ) -> Result { let current_balance = self.ensure_valid_additional_stake(&coin, storage)?; let num_subdelegations = self.num_subdelegations_for_mix(mix_id, storage); if num_subdelegations >= MAX_PER_MIX_DELEGATIONS { - return Err(ContractError::TooManyDelegations { + return Err(VestingContractError::TooManyDelegations { address: self.owner_address.clone(), acc_id: self.storage_key(), mix_id, @@ -75,9 +76,9 @@ impl DelegatingAccount for Account { &self, mix_id: MixId, storage: &dyn Storage, - ) -> Result { + ) -> Result { if !self.any_delegation_for_mix(mix_id, storage) { - return Err(ContractError::NoSuchDelegation( + return Err(VestingContractError::NoSuchDelegation( self.owner_address(), mix_id, )); @@ -102,7 +103,7 @@ impl DelegatingAccount for Account { current_balance: Uint128, delegation: Coin, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { + ) -> Result<(), VestingContractError> { save_delegation( (self.storage_key(), mix_id, block_timestamp_secs), delegation.amount, @@ -118,7 +119,7 @@ impl DelegatingAccount for Account { mix_id: MixId, amount: Coin, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { + ) -> Result<(), VestingContractError> { self.remove_delegations_for_mix(mix_id, storage)?; let new_balance = Uint128::new(self.load_balance(storage)?.u128() + amount.amount.u128()); self.save_balance(new_balance, storage)?; diff --git a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs index 12910acbcc..e63b0d1d09 100644 --- a/contracts/vesting/src/vesting/account/gateway_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/gateway_bonding_account.rs @@ -1,7 +1,7 @@ use super::PledgeData; -use crate::errors::ContractError; use crate::storage::MIXNET_CONTRACT_ADDRESS; use crate::traits::GatewayBondingAccount; +use crate::vesting::account::StorableVestingAccountExt; use contracts_common::signing::MessageSignature; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; use mixnet_contract_common::{ @@ -11,6 +11,7 @@ use vesting_contract_common::events::{ new_vesting_gateway_bonding_event, new_vesting_gateway_unbonding_event, new_vesting_update_gateway_config_event, }; +use vesting_contract_common::VestingContractError; use super::Account; @@ -22,11 +23,11 @@ impl GatewayBondingAccount for Account { pledge: Coin, env: &Env, storage: &mut dyn Storage, - ) -> Result { + ) -> Result { let current_balance = self.ensure_valid_additional_stake(&pledge, storage)?; let pledge_data = if self.load_gateway_pledge(storage)?.is_some() { - return Err(ContractError::AlreadyBonded( + return Err(VestingContractError::AlreadyBonded( self.owner_address().as_str().to_string(), )); } else { @@ -52,7 +53,7 @@ impl GatewayBondingAccount for Account { .add_event(new_vesting_gateway_bonding_event())) } - fn try_unbond_gateway(&self, storage: &dyn Storage) -> Result { + fn try_unbond_gateway(&self, storage: &dyn Storage) -> Result { let msg = MixnetExecuteMsg::UnbondGatewayOnBehalf { owner: self.owner_address().into_string(), }; @@ -64,7 +65,7 @@ impl GatewayBondingAccount for Account { .add_message(unbond_msg) .add_event(new_vesting_gateway_unbonding_event())) } else { - Err(ContractError::NoBondFound( + Err(VestingContractError::NoBondFound( self.owner_address().as_str().to_string(), )) } @@ -74,7 +75,7 @@ impl GatewayBondingAccount for Account { &self, amount: Coin, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { + ) -> Result<(), VestingContractError> { let new_balance = Uint128::new(self.load_balance(storage)?.u128() + amount.amount.u128()); self.save_balance(new_balance, storage)?; @@ -86,7 +87,7 @@ impl GatewayBondingAccount for Account { &self, new_config: GatewayConfigUpdate, storage: &mut dyn Storage, - ) -> Result { + ) -> Result { let msg = MixnetExecuteMsg::UpdateGatewayConfigOnBehalf { new_config, owner: self.owner_address().into_string(), diff --git a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs index 6986ac8793..29ee780ff9 100644 --- a/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs +++ b/contracts/vesting/src/vesting/account/mixnode_bonding_account.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use super::Account; -use crate::errors::ContractError; use crate::storage::MIXNET_CONTRACT_ADDRESS; use crate::traits::MixnodeBondingAccount; +use crate::vesting::account::StorableVestingAccountExt; use contracts_common::signing::MessageSignature; use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128}; use mixnet_contract_common::mixnode::MixNodeConfigUpdate; @@ -15,10 +15,13 @@ use vesting_contract_common::events::{ new_vesting_mixnode_unbonding_event, new_vesting_pledge_more_event, new_vesting_update_mixnode_config_event, new_vesting_update_mixnode_cost_params_event, }; -use vesting_contract_common::PledgeData; +use vesting_contract_common::{PledgeData, VestingContractError}; impl MixnodeBondingAccount for Account { - fn try_claim_operator_reward(&self, storage: &dyn Storage) -> Result { + fn try_claim_operator_reward( + &self, + storage: &dyn Storage, + ) -> Result { let msg = MixnetExecuteMsg::WithdrawOperatorRewardOnBehalf { owner: self.owner_address().into_string(), }; @@ -37,11 +40,11 @@ impl MixnodeBondingAccount for Account { pledge: Coin, env: &Env, storage: &mut dyn Storage, - ) -> Result { + ) -> Result { let current_balance = self.ensure_valid_additional_stake(&pledge, storage)?; let pledge_data = if self.load_mixnode_pledge(storage)?.is_some() { - return Err(ContractError::AlreadyBonded( + return Err(VestingContractError::AlreadyBonded( self.owner_address().as_str().to_string(), )); } else { @@ -73,13 +76,13 @@ impl MixnodeBondingAccount for Account { additional_pledge: Coin, env: &Env, storage: &mut dyn Storage, - ) -> Result { + ) -> Result { let current_balance = self.ensure_valid_additional_stake(&additional_pledge, storage)?; let mut pledge_data = if let Some(pledge_data) = self.load_mixnode_pledge(storage)? { pledge_data } else { - return Err(ContractError::NoBondFound( + return Err(VestingContractError::NoBondFound( self.owner_address().as_str().to_string(), )); }; @@ -113,18 +116,18 @@ impl MixnodeBondingAccount for Account { &self, amount: Coin, storage: &mut dyn Storage, - ) -> Result { + ) -> Result { match self.load_mixnode_pledge(storage)? { Some(pledge) => { if pledge.amount.amount <= amount.amount { - return Err(ContractError::InvalidBondPledgeReduction { + return Err(VestingContractError::InvalidBondPledgeReduction { current: pledge.amount, decrease_by: amount, }); } } None => { - return Err(ContractError::NoBondFound( + return Err(VestingContractError::NoBondFound( self.owner_address().as_str().to_string(), )); } @@ -147,14 +150,14 @@ impl MixnodeBondingAccount for Account { &self, amount: Coin, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { + ) -> Result<(), VestingContractError> { let new_balance = Uint128::new(self.load_balance(storage)?.u128() + amount.amount.u128()); self.save_balance(new_balance, storage)?; self.decrease_mixnode_pledge(amount, storage) } - fn try_unbond_mixnode(&self, storage: &dyn Storage) -> Result { + fn try_unbond_mixnode(&self, storage: &dyn Storage) -> Result { let msg = MixnetExecuteMsg::UnbondMixnodeOnBehalf { owner: self.owner_address().into_string(), }; @@ -166,7 +169,7 @@ impl MixnodeBondingAccount for Account { .add_message(unbond_msg) .add_event(new_vesting_mixnode_unbonding_event())) } else { - Err(ContractError::NoBondFound( + Err(VestingContractError::NoBondFound( self.owner_address().as_str().to_string(), )) } @@ -176,7 +179,7 @@ impl MixnodeBondingAccount for Account { &self, amount: Coin, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { + ) -> Result<(), VestingContractError> { let new_balance = Uint128::new(self.load_balance(storage)?.u128() + amount.amount.u128()); self.save_balance(new_balance, storage)?; @@ -187,7 +190,7 @@ impl MixnodeBondingAccount for Account { &self, new_config: MixNodeConfigUpdate, storage: &mut dyn Storage, - ) -> Result { + ) -> Result { let msg = MixnetExecuteMsg::UpdateMixnodeConfigOnBehalf { new_config, owner: self.owner_address().into_string(), @@ -205,7 +208,7 @@ impl MixnodeBondingAccount for Account { &self, new_costs: MixNodeCostParams, storage: &mut dyn Storage, - ) -> Result { + ) -> Result { let msg = MixnetExecuteMsg::UpdateMixnodeCostParamsOnBehalf { new_costs, owner: self.owner_address().into_string(), diff --git a/contracts/vesting/src/vesting/account/mod.rs b/contracts/vesting/src/vesting/account/mod.rs index e59f27d1a2..7d016fd130 100644 --- a/contracts/vesting/src/vesting/account/mod.rs +++ b/contracts/vesting/src/vesting/account/mod.rs @@ -1,17 +1,18 @@ +// Copyright 2021-2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + use super::VestingPeriod; -use crate::errors::ContractError; use crate::storage::{ count_subdelegations_for_mix, decrease_bond_pledge, load_balance, load_bond_pledge, load_delegation_timestamps, load_gateway_pledge, load_withdrawn, remove_bond_pledge, remove_delegation, remove_gateway_pledge, save_account, save_balance, save_bond_pledge, - save_gateway_pledge, save_withdrawn, AccountStorageKey, BlockTimestampSecs, DELEGATIONS, KEY, + save_gateway_pledge, save_withdrawn, BlockTimestampSecs, DELEGATIONS, KEY, }; use crate::traits::VestingAccount; use cosmwasm_std::{Addr, Coin, Order, Storage, Timestamp, Uint128}; use mixnet_contract_common::MixId; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use vesting_contract_common::{Period, PledgeCap, PledgeData}; +use vesting_contract_common::account::VestingAccountStorageKey; +use vesting_contract_common::{Account, PledgeCap, PledgeData, VestingContractError}; mod delegating_account; mod gateway_bonding_account; @@ -19,26 +20,17 @@ mod mixnode_bonding_account; mod node_families; mod vesting_account; -fn generate_storage_key(storage: &mut dyn Storage) -> Result { +fn generate_storage_key( + storage: &mut dyn Storage, +) -> Result { let key = KEY.may_load(storage)?.unwrap_or(0) + 1; KEY.save(storage, &key)?; Ok(key) } -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -pub struct Account { - pub owner_address: Addr, - pub staking_address: Option, - pub start_time: Timestamp, - pub periods: Vec, - pub coin: Coin, - storage_key: AccountStorageKey, - #[serde(default)] - pub pledge_cap: Option, -} - -impl Account { - pub fn new( +/// Helper trait to extend the `Account` type with methods that require access to the underlying storage +pub(crate) trait StorableVestingAccountExt: VestingAccount { + fn save_new( owner_address: Addr, staking_address: Option, coin: Coin, @@ -46,7 +38,7 @@ impl Account { periods: Vec, pledge_cap: Option, storage: &mut dyn Storage, - ) -> Result { + ) -> Result { let storage_key = generate_storage_key(storage)?; let amount = coin.amount; let account = Account { @@ -63,29 +55,31 @@ impl Account { Ok(account) } + fn owner_address(&self) -> Addr; + /// Checks whether the additional stake would be within the cap associated with the account /// and whether the account has enough tokens for staking. /// Returns the value of the current balance of the account. - pub fn ensure_valid_additional_stake( + fn ensure_valid_additional_stake( &self, additional_stake: &Coin, storage: &dyn Storage, - ) -> Result { + ) -> Result { let current_balance = self.load_balance(storage)?; let current_total_staked = self.total_staked(storage)?; let total_staked_after = current_total_staked + additional_stake.amount; let locked_pledge_cap = self.absolute_pledge_cap()?; if locked_pledge_cap < total_staked_after { - return Err(ContractError::LockedPledgeCapReached { + return Err(VestingContractError::LockedPledgeCapReached { current: total_staked_after, cap: locked_pledge_cap, }); } if current_balance < additional_stake.amount { - return Err(ContractError::InsufficientBalance( - self.owner_address().as_str().to_string(), + return Err(VestingContractError::InsufficientBalance( + self.owner_address().into_string(), current_balance.u128(), )); } @@ -93,107 +87,13 @@ impl Account { Ok(current_balance) } - pub fn pledge_cap(&self) -> PledgeCap { - self.pledge_cap.clone().unwrap_or_default() - } + fn absolute_pledge_cap(&self) -> Result; - pub fn absolute_pledge_cap(&self) -> Result { - match self.pledge_cap() { - PledgeCap::Absolute(cap) => Ok(cap), - PledgeCap::Percent(p) => Ok(p * self.get_original_vesting()?.amount.amount), - } - } - - pub fn coin(&self) -> Coin { - self.coin.clone() - } - - pub fn num_vesting_periods(&self) -> usize { - self.periods.len() - } - - pub fn period_duration(&self) -> Result { - self.periods - .get(0) - .ok_or(ContractError::UnpopulatedVestingPeriods { - owner: self.owner_address.clone(), - }) - .map(|p| p.period_seconds) - } - - pub fn storage_key(&self) -> u32 { - self.storage_key - } - - pub fn owner_address(&self) -> Addr { - self.owner_address.clone() - } - - pub fn staking_address(&self) -> Option<&Addr> { - self.staking_address.as_ref() - } - - #[allow(dead_code)] - pub fn periods(&self) -> Vec { - self.periods.clone() - } - - #[allow(dead_code)] - pub fn start_time(&self) -> Timestamp { - self.start_time - } - - pub fn tokens_per_period(&self) -> Result { - let amount = self.coin.amount.u128(); - if amount < self.num_vesting_periods() as u128 { - Err(ContractError::ImprobableVestingAmount(amount)) - } else { - // Remainder tokens will be lumped into the last period. - Ok(amount / self.num_vesting_periods() as u128) - } - } - - /// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet. - /// In case vesting is over it will always return NUM_VESTING_PERIODS. - pub fn get_current_vesting_period( - &self, - block_time: Timestamp, - ) -> Result { - let first_period = - self.periods - .first() - .ok_or(ContractError::UnpopulatedVestingPeriods { - owner: self.owner_address.clone(), - })?; - - let last_period = self - .periods - .last() - .ok_or(ContractError::UnpopulatedVestingPeriods { - owner: self.owner_address.clone(), - })?; - - if block_time.seconds() < first_period.start_time { - Ok(Period::Before) - } else if last_period.end_time() < block_time { - Ok(Period::After) - } else { - let mut index = 0; - for period in &self.periods { - if block_time < period.end_time() { - break; - } - index += 1; - } - Ok(Period::In(index)) - } - } - - pub fn withdraw( + fn withdraw( &self, amount: &Coin, storage: &mut dyn Storage, - ) -> Result { + ) -> Result { let new_balance = self .load_balance(storage)? .u128() @@ -204,127 +104,73 @@ impl Account { Ok(new_balance) } - pub fn load_withdrawn(&self, storage: &dyn Storage) -> Result { - load_withdrawn(self.storage_key, storage) - } + fn load_withdrawn(&self, storage: &dyn Storage) -> Result; - pub fn save_withdrawn( + fn save_withdrawn( &self, withdrawn: Uint128, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { - save_withdrawn(self.storage_key, withdrawn, storage) - } + ) -> Result<(), VestingContractError>; - pub fn load_balance(&self, storage: &dyn Storage) -> Result { - load_balance(self.storage_key(), storage) - } + fn load_balance(&self, storage: &dyn Storage) -> Result; - pub fn save_balance( + fn save_balance( &self, amount: Uint128, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { - save_balance(self.storage_key(), amount, storage) - } + ) -> Result<(), VestingContractError>; - pub fn load_mixnode_pledge( + fn load_mixnode_pledge( &self, storage: &dyn Storage, - ) -> Result, ContractError> { - load_bond_pledge(self.storage_key(), storage) - } + ) -> Result, VestingContractError>; - pub fn save_mixnode_pledge( + fn save_mixnode_pledge( &self, pledge: PledgeData, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { - save_bond_pledge(self.storage_key(), &pledge, storage) - } + ) -> Result<(), VestingContractError>; - pub fn remove_mixnode_pledge(&self, storage: &mut dyn Storage) -> Result<(), ContractError> { - remove_bond_pledge(self.storage_key(), storage) - } + fn remove_mixnode_pledge(&self, storage: &mut dyn Storage) -> Result<(), VestingContractError>; - pub fn decrease_mixnode_pledge( + fn decrease_mixnode_pledge( &self, amount: Coin, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { - decrease_bond_pledge(self.storage_key, amount, storage) - } + ) -> Result<(), VestingContractError>; - pub fn load_gateway_pledge( + fn load_gateway_pledge( &self, storage: &dyn Storage, - ) -> Result, ContractError> { - load_gateway_pledge(self.storage_key(), storage) - } + ) -> Result, VestingContractError>; - pub fn save_gateway_pledge( + fn save_gateway_pledge( &self, pledge: PledgeData, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { - save_gateway_pledge(self.storage_key(), &pledge, storage) - } + ) -> Result<(), VestingContractError>; - pub fn remove_gateway_pledge(&self, storage: &mut dyn Storage) -> Result<(), ContractError> { - remove_gateway_pledge(self.storage_key(), storage) - } + fn remove_gateway_pledge(&self, storage: &mut dyn Storage) -> Result<(), VestingContractError>; - pub fn any_delegation_for_mix(&self, mix_id: MixId, storage: &dyn Storage) -> bool { - DELEGATIONS - .prefix((self.storage_key(), mix_id)) - .range(storage, None, None, Order::Ascending) - .next() - .is_some() - } + fn any_delegation_for_mix(&self, mix_id: MixId, storage: &dyn Storage) -> bool; - pub fn num_subdelegations_for_mix(&self, mix_id: MixId, storage: &dyn Storage) -> u32 { - count_subdelegations_for_mix((self.storage_key(), mix_id), storage) - } + fn num_subdelegations_for_mix(&self, mix_id: MixId, storage: &dyn Storage) -> u32; - pub fn remove_delegations_for_mix( + fn remove_delegations_for_mix( &self, mix_id: MixId, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { - // note that the limit is implicitly set to `MAX_PER_MIX_DELEGATIONS` - // as it should be impossible to create more delegations than that. - let block_timestamps = load_delegation_timestamps((self.storage_key(), mix_id), storage)?; + ) -> Result<(), VestingContractError>; - for block_timestamp in block_timestamps { - remove_delegation((self.storage_key(), mix_id, block_timestamp), storage)?; - } - Ok(()) - } - - pub fn total_delegations_for_mix( + fn total_delegations_for_mix( &self, mix_id: MixId, storage: &dyn Storage, - ) -> Result { - Ok(DELEGATIONS - .prefix((self.storage_key(), mix_id)) - .range(storage, None, None, Order::Ascending) - .filter_map(|x| x.ok()) - .fold(Uint128::zero(), |acc, (_key, val)| acc + val)) - } + ) -> Result; - // TODO: this should get reworked... somehow... (maybe with a memoized value?) - // as it's an unbounded iteration that could fail if an account has made a lot of delegations - // (I guess in order of thousands) - pub fn total_delegations(&self, storage: &dyn Storage) -> Result { - Ok(DELEGATIONS - .sub_prefix(self.storage_key()) - .range(storage, None, None, Order::Ascending) - .filter_map(|x| x.ok()) - .fold(Uint128::zero(), |acc, (_key, val)| acc + val)) - } + fn total_delegations(&self, storage: &dyn Storage) -> Result; - pub fn total_pledged(&self, storage: &dyn Storage) -> Result { + fn total_pledged(&self, storage: &dyn Storage) -> Result { let amount = if let Some(bond) = self .load_mixnode_pledge(storage)? .or(self.load_gateway_pledge(storage)?) @@ -336,11 +182,147 @@ impl Account { Ok(amount) } - pub fn total_delegations_at_timestamp( + fn total_delegations_at_timestamp( &self, storage: &dyn Storage, start_time: BlockTimestampSecs, - ) -> Result { + ) -> Result; +} + +impl StorableVestingAccountExt for Account { + fn owner_address(&self) -> Addr { + self.owner_address.clone() + } + + fn absolute_pledge_cap(&self) -> Result { + match self.pledge_cap() { + PledgeCap::Absolute(cap) => Ok(cap), + PledgeCap::Percent(p) => Ok(p * self.get_original_vesting()?.amount.amount), + } + } + + fn load_withdrawn(&self, storage: &dyn Storage) -> Result { + load_withdrawn(self.storage_key, storage) + } + + fn save_withdrawn( + &self, + withdrawn: Uint128, + storage: &mut dyn Storage, + ) -> Result<(), VestingContractError> { + save_withdrawn(self.storage_key, withdrawn, storage) + } + + fn load_balance(&self, storage: &dyn Storage) -> Result { + load_balance(self.storage_key(), storage) + } + + fn save_balance( + &self, + amount: Uint128, + storage: &mut dyn Storage, + ) -> Result<(), VestingContractError> { + save_balance(self.storage_key(), amount, storage) + } + + fn load_mixnode_pledge( + &self, + storage: &dyn Storage, + ) -> Result, VestingContractError> { + load_bond_pledge(self.storage_key(), storage) + } + + fn save_mixnode_pledge( + &self, + pledge: PledgeData, + storage: &mut dyn Storage, + ) -> Result<(), VestingContractError> { + save_bond_pledge(self.storage_key(), &pledge, storage) + } + + fn remove_mixnode_pledge(&self, storage: &mut dyn Storage) -> Result<(), VestingContractError> { + remove_bond_pledge(self.storage_key(), storage) + } + + fn decrease_mixnode_pledge( + &self, + amount: Coin, + storage: &mut dyn Storage, + ) -> Result<(), VestingContractError> { + decrease_bond_pledge(self.storage_key(), amount, storage) + } + + fn load_gateway_pledge( + &self, + storage: &dyn Storage, + ) -> Result, VestingContractError> { + load_gateway_pledge(self.storage_key(), storage) + } + + fn save_gateway_pledge( + &self, + pledge: PledgeData, + storage: &mut dyn Storage, + ) -> Result<(), VestingContractError> { + save_gateway_pledge(self.storage_key(), &pledge, storage) + } + + fn remove_gateway_pledge(&self, storage: &mut dyn Storage) -> Result<(), VestingContractError> { + remove_gateway_pledge(self.storage_key(), storage) + } + + fn any_delegation_for_mix(&self, mix_id: MixId, storage: &dyn Storage) -> bool { + DELEGATIONS + .prefix((self.storage_key(), mix_id)) + .range(storage, None, None, Order::Ascending) + .next() + .is_some() + } + + fn num_subdelegations_for_mix(&self, mix_id: MixId, storage: &dyn Storage) -> u32 { + count_subdelegations_for_mix((self.storage_key(), mix_id), storage) + } + + fn remove_delegations_for_mix( + &self, + mix_id: MixId, + storage: &mut dyn Storage, + ) -> Result<(), VestingContractError> { + // note that the limit is implicitly set to `MAX_PER_MIX_DELEGATIONS` + // as it should be impossible to create more delegations than that. + let block_timestamps = load_delegation_timestamps((self.storage_key(), mix_id), storage)?; + + for block_timestamp in block_timestamps { + remove_delegation((self.storage_key(), mix_id, block_timestamp), storage)?; + } + Ok(()) + } + + fn total_delegations_for_mix( + &self, + mix_id: MixId, + storage: &dyn Storage, + ) -> Result { + Ok(DELEGATIONS + .prefix((self.storage_key(), mix_id)) + .range(storage, None, None, Order::Ascending) + .filter_map(|x| x.ok()) + .fold(Uint128::zero(), |acc, (_key, val)| acc + val)) + } + + fn total_delegations(&self, storage: &dyn Storage) -> Result { + Ok(DELEGATIONS + .sub_prefix(self.storage_key()) + .range(storage, None, None, Order::Ascending) + .filter_map(|x| x.ok()) + .fold(Uint128::zero(), |acc, (_key, val)| acc + val)) + } + + fn total_delegations_at_timestamp( + &self, + storage: &dyn Storage, + start_time: BlockTimestampSecs, + ) -> Result { Ok(DELEGATIONS .sub_prefix(self.storage_key()) .range(storage, None, None, Order::Ascending) diff --git a/contracts/vesting/src/vesting/account/node_families.rs b/contracts/vesting/src/vesting/account/node_families.rs index 2d6c4ce436..649531688b 100644 --- a/contracts/vesting/src/vesting/account/node_families.rs +++ b/contracts/vesting/src/vesting/account/node_families.rs @@ -1,16 +1,17 @@ use super::Account; -use crate::{errors::ContractError, storage::MIXNET_CONTRACT_ADDRESS, traits::NodeFamilies}; +use crate::{storage::MIXNET_CONTRACT_ADDRESS, traits::NodeFamilies}; use contracts_common::signing::MessageSignature; use cosmwasm_std::{wasm_execute, Response, Storage}; use mixnet_contract_common::families::FamilyHead; use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, IdentityKeyRef}; +use vesting_contract_common::VestingContractError; impl NodeFamilies for Account { fn try_create_family( &self, storage: &dyn Storage, label: String, - ) -> Result { + ) -> Result { let msg = MixnetExecuteMsg::CreateFamilyOnBehalf { owner_address: self.owner_address().into_string(), label, @@ -26,7 +27,7 @@ impl NodeFamilies for Account { storage: &dyn Storage, join_permit: MessageSignature, family_head: FamilyHead, - ) -> Result { + ) -> Result { let msg = MixnetExecuteMsg::JoinFamilyOnBehalf { member_address: self.owner_address().to_string(), join_permit, @@ -42,7 +43,7 @@ impl NodeFamilies for Account { &self, storage: &dyn Storage, family_head: FamilyHead, - ) -> Result { + ) -> Result { let msg = MixnetExecuteMsg::LeaveFamilyOnBehalf { member_address: self.owner_address().to_string(), family_head, @@ -57,7 +58,7 @@ impl NodeFamilies for Account { &self, storage: &dyn Storage, member: IdentityKeyRef<'_>, - ) -> Result { + ) -> Result { let msg = MixnetExecuteMsg::KickFamilyMemberOnBehalf { head_address: self.owner_address().to_string(), member: member.to_string(), diff --git a/contracts/vesting/src/vesting/account/vesting_account.rs b/contracts/vesting/src/vesting/account/vesting_account.rs index 5b64b9896e..1cb51aae19 100644 --- a/contracts/vesting/src/vesting/account/vesting_account.rs +++ b/contracts/vesting/src/vesting/account/vesting_account.rs @@ -1,14 +1,14 @@ -use crate::errors::ContractError; use crate::storage::{delete_account, save_account, MIX_DENOM}; use crate::traits::VestingAccount; +use crate::vesting::account::StorableVestingAccountExt; use cosmwasm_std::{Addr, Coin, Env, Storage, Timestamp, Uint128}; use std::cmp::min; -use vesting_contract_common::{OriginalVestingResponse, Period}; +use vesting_contract_common::{OriginalVestingResponse, Period, VestingContractError}; use super::Account; impl VestingAccount for Account { - fn total_staked(&self, storage: &dyn Storage) -> Result { + fn total_staked(&self, storage: &dyn Storage) -> Result { Ok(self.total_delegations(storage)? + self.total_pledged(storage)?) } @@ -21,7 +21,7 @@ impl VestingAccount for Account { block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result { + ) -> Result { let still_vesting = self.get_vesting_coins(block_time, env, storage)?.amount; let staked = self.total_staked(storage)?; let locked_amount = still_vesting.saturating_sub(staked); @@ -37,7 +37,7 @@ impl VestingAccount for Account { block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result { + ) -> Result { Ok(Coin { amount: self .load_balance(storage)? @@ -51,7 +51,7 @@ impl VestingAccount for Account { block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result { + ) -> Result { let vested = self.get_vested_coins(block_time, env, storage)?; let withdrawn = self.load_withdrawn(storage)?; @@ -76,7 +76,7 @@ impl VestingAccount for Account { block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result { + ) -> Result { let spendable = self.spendable_coins(block_time, env, storage)?; let spendable_vested = self.spendable_vested_coins(block_time, env, storage)?; @@ -92,7 +92,7 @@ impl VestingAccount for Account { block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result { + ) -> Result { let block_time = block_time.unwrap_or(env.block.time); let period = self.get_current_vesting_period(block_time)?; let denom = MIX_DENOM.load(storage)?; @@ -119,7 +119,7 @@ impl VestingAccount for Account { block_time: Option, env: &Env, storage: &dyn Storage, - ) -> Result { + ) -> Result { Ok(Coin { amount: self.get_original_vesting()?.amount().amount - self.get_vested_coins(block_time, env, storage)?.amount, @@ -135,7 +135,7 @@ impl VestingAccount for Account { self.periods[self.num_vesting_periods() - 1].end_time() } - fn get_original_vesting(&self) -> Result { + fn get_original_vesting(&self) -> Result { Ok(OriginalVestingResponse::new( self.coin.clone(), self.num_vesting_periods(), @@ -147,7 +147,7 @@ impl VestingAccount for Account { &mut self, to_address: &Addr, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { + ) -> Result<(), VestingContractError> { delete_account(self.owner_address(), storage)?; self.owner_address = to_address.to_owned(); save_account(self, storage)?; @@ -158,7 +158,7 @@ impl VestingAccount for Account { &mut self, to_address: Option, storage: &mut dyn Storage, - ) -> Result<(), ContractError> { + ) -> Result<(), VestingContractError> { if let Some(staking_address) = self.staking_address() { delete_account(staking_address.to_owned(), storage)?; } @@ -167,7 +167,11 @@ impl VestingAccount for Account { Ok(()) } - fn track_reward(&self, amount: Coin, storage: &mut dyn Storage) -> Result<(), ContractError> { + fn track_reward( + &self, + amount: Coin, + storage: &mut dyn Storage, + ) -> Result<(), VestingContractError> { let current_balance = self.load_balance(storage)?; let new_balance = current_balance + amount.amount; self.save_balance(new_balance, storage)?; @@ -183,7 +187,7 @@ impl VestingAccount for Account { fn get_historical_vested_staking_rewards( &self, storage: &dyn Storage, - ) -> Result { + ) -> Result { let balance = self.load_balance(storage)?; let withdrawn = self.load_withdrawn(storage)?; let staked = self.total_staked(storage)?; diff --git a/contracts/vesting/src/vesting/mod.rs b/contracts/vesting/src/vesting/mod.rs index 56a02928d7..b16efbdd23 100644 --- a/contracts/vesting/src/vesting/mod.rs +++ b/contracts/vesting/src/vesting/mod.rs @@ -1,43 +1,18 @@ -use cosmwasm_std::Timestamp; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - mod account; -pub use account::*; -use vesting_contract_common::messages::VestingSpecification; - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -pub struct VestingPeriod { - pub start_time: u64, - pub period_seconds: u64, -} - -impl VestingPeriod { - pub fn end_time(&self) -> Timestamp { - Timestamp::from_seconds(self.start_time + self.period_seconds) - } -} +pub(crate) use account::StorableVestingAccountExt; +use vesting_contract_common::{VestingPeriod, VestingSpecification}; pub fn populate_vesting_periods( start_time: u64, vesting_spec: VestingSpecification, ) -> Vec { - let mut periods = Vec::with_capacity(vesting_spec.num_periods() as usize); - for i in 0..vesting_spec.num_periods() { - let period = VestingPeriod { - start_time: start_time + i * vesting_spec.period_seconds(), - period_seconds: vesting_spec.period_seconds(), - }; - periods.push(period); - } - periods + vesting_spec.populate_vesting_periods(start_time) } #[cfg(test)] mod tests { use crate::contract::*; - use crate::errors::ContractError; use crate::storage::*; use crate::support::tests::helpers::vesting_account_percent_fixture; use crate::support::tests::helpers::{ @@ -46,15 +21,16 @@ mod tests { use crate::traits::DelegatingAccount; use crate::traits::VestingAccount; use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount}; - use crate::vesting::{populate_vesting_periods, Account}; + use crate::vesting::account::StorableVestingAccountExt; + use crate::vesting::populate_vesting_periods; use contracts_common::signing::MessageSignature; use cosmwasm_std::testing::{mock_env, mock_info}; use cosmwasm_std::{coin, coins, Addr, Coin, Timestamp, Uint128}; use mixnet_contract_common::mixnode::MixNodeCostParams; use mixnet_contract_common::{Gateway, MixNode, Percent}; - use vesting_contract_common::messages::{ExecuteMsg, VestingSpecification}; - use vesting_contract_common::Period; - use vesting_contract_common::PledgeCap; + use vesting_contract_common::messages::ExecuteMsg; + use vesting_contract_common::{Account, PledgeCap, VestingSpecification}; + use vesting_contract_common::{Period, VestingContractError}; #[test] fn test_account_creation() { @@ -317,7 +293,9 @@ mod tests { staking_address_change, ); assert_eq!( - Err(ContractError::StakingAccountExists("vesting1".to_string())), + Err(VestingContractError::StakingAccountExists( + "vesting1".to_string() + )), res ); @@ -802,7 +780,7 @@ mod tests { VestingSpecification::new(None, Some(vesting_period_length_secs), None), ); - let vesting_account = Account::new( + let vesting_account = Account::save_new( Addr::unchecked("owner"), Some(Addr::unchecked("staking")), Coin { @@ -842,7 +820,7 @@ mod tests { assert_eq!( res, - ContractError::TooManyDelegations { + VestingContractError::TooManyDelegations { address: vesting_account.owner_address(), acc_id: vesting_account.storage_key(), mix_id, diff --git a/documentation/README.md b/documentation/README.md index 3330b3e969..9e490e4d57 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -1,6 +1,13 @@ # Documentation +## Doc projects +Each directory contains a readme with more information about running and contributing to the projects. Each is built with [`mdbook`](https://rust-lang.github.io/mdBook/index.html) - use `mdbook serve` to build and serve them (defaults to `localhost:3000`). * `docs` contains technical documentation hosted at [https://nymtech.net/docs](https://nymtech.net/docs) * `dev-portal` contains developer documentation hosted at [https://nymtech.net/developers](https://nymtech.net/developers) +* `operators` contains node setup and maintenance guides hosted at [https://nymtech.net/operators](https://nymtech.net/operators) + +## Scripts +* `bump_versions.sh` allows you to update the `platform_release_version` and `wallet_release_version` variables in the `book.toml` of each mdbook project at once. You can also optionally update the `minimum_rust_version` as well. Helpful for lazy-updating when cutting a new version of the docs. + * `build_all_to_dist.sh` is used by the `ci-dev.yml` and `cd-dev.yml` scripts for building all mdbook projects and moving the rendered html to `../dist/` to be rsynced with various servers. + -Each directory contains a readme with more information about running and contributing to the projects. Each is built with [`mdbook`](https://rust-lang.github.io/mdBook/index.html) - use `mdbook serve` to build and serve them (defaults to `localhost:3000`). diff --git a/documentation/build_all_to_dist.sh b/documentation/build_all_to_dist.sh new file mode 100755 index 0000000000..b414e92df6 --- /dev/null +++ b/documentation/build_all_to_dist.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# this is a script called by the github CI and CD workflows to build all 3 docs projects +# and move them to /dist/ in the root of the monorepo. They are rsynced to various servers +# from there by subsequent workflow tasks. + +# array of project dirs +declare -a projects=("docs" "dev-portal" "operators") + +# check you're calling from the right place +if [ $(pwd | awk -F/ '{print $NF}') != "documentation" ] +then + echo "failure: please run script from documentation/" +else + for i in "${projects[@]}" + do + # cd to project dir + cd "./$i" && + # little sanity checks + echo $(pwd) && echo $(mdbook --version) && + # clean old book + echo "cleaning old book" + rm -rf ./book/ + # build book + mdbook build + # check for destination, if ! then mkdir & check again else echo thumbs up + if [ ! -d ../../dist/docs/$i ]; then + echo "dest doesn't exist: creating dir" + mkdir -p ../../dist/docs/$i + fi + if [ -d ../../dist/docs/$i ]; then + echo "cp destination exists, all good" + fi + # clean old dist/$i + rm -rf ../../dist/docs/$i + # move newly rendered book/ to dist + rsync -r ./book/html/ ../../dist/docs/$i + # sanity check + ls -laF ../../dist/docs/ + # cd back to ../documentation/ + cd ../ + done + # rename for server paths + rm -rf ../dist/docs/developers + mv ../dist/docs/dev-portal ../dist/docs/developers +fi diff --git a/documentation/bump_versions.sh b/documentation/bump_versions.sh new file mode 100755 index 0000000000..03e698faac --- /dev/null +++ b/documentation/bump_versions.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# this takes two args: platform release version and wallet release version. +# it then uses sed to bump them in the three book.toml files. +# +# e.g if the upcoming platform release was v1.1.29 and the release version 1.2.9 you'd run this as: +# `./bump_versions.sh "1.1.29" "1.2.9"` +# +# you can also set the minumum rust version by passing an optional 3rd argument: +# `./bump_versions.sh "1.1.29" "1.2.9" "1.67"` + +# array of project dirs +declare -a projects=("docs" "dev-portal" "operators") + +# check number of args passed +if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; +then + echo "failure: please pass at least 2 and at most 3 args: " + echo "./bump_version.sh [OPTIONAL]" + exit 0 +fi + +# check you're calling from the right place +if [ $(pwd | awk -F/ '{print $NF}') != "documentation" ] +then + echo "failure: please run script from documentation/" + exit 0 +else + ## now loop through the above array sed-ing the variable values in the book.toml files + for i in "${projects[@]}" + do + # sed the vars in the book.toml file for each project + echo "setting platform and wallet versions in $i/" + sed -i 's/platform_release_version =.*/platform_release_version = "'$1'"/' "$i"/book.toml + sed -i 's/wallet_release_version =.*/wallet_release_version = "'$2'"/' "$i"/book.toml + if [ "$3" ] + then + echo "setting minimum rust version in $i/" + sed -i 's/minimum_rust_version = .*/minimum_rust_version = "'$3'"/' "$i"/book.toml + fi + done +fi diff --git a/documentation/dev-portal/.gitignore b/documentation/dev-portal/.gitignore index bb380f03ab..c29699d0c0 100644 --- a/documentation/dev-portal/.gitignore +++ b/documentation/dev-portal/.gitignore @@ -17,4 +17,6 @@ book theme/ theme -theme/* \ No newline at end of file +theme/* + +.idea \ No newline at end of file diff --git a/documentation/dev-portal/book.toml b/documentation/dev-portal/book.toml index cf434a31f5..315f28698a 100644 --- a/documentation/dev-portal/book.toml +++ b/documentation/dev-portal/book.toml @@ -17,7 +17,7 @@ src = "src" # - run `mdbook build`: this will rebuild the `./theme` directory # - change value of `turn-off` back to `true` [preprocessor.theme] -pagetoc = true +pagetoc = true # some variables related (defined in theme/css/variables.css) # `content-max-width` + `pagetoc-width` = 95% seems the best pagetoc-width = "13%" @@ -37,9 +37,10 @@ nav-chapters-min-width = "auto" chapter-line-height = "2em" section-line-height = "1.5em" -# # if true, never read and touch the files in theme dir +# if true, never read and touch the files in theme dir turn-off = false + [preprocessor.admonish] command = "mdbook-admonish" assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install` @@ -48,9 +49,8 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install` # https://gitlab.com/tglman/mdbook-variables/ [preprocessor.variables.variables] minimum_rust_version = "1.66" -# vars for links: TODO think on how to streamline updating -platform_release_version = "v1.1.25" -wallet_release_version = "v1.2.7" +platform_release_version = "1.1.29" +wallet_release_version = "1.2.8" [preprocessor.last-changed] command = "mdbook-last-changed" @@ -77,14 +77,14 @@ curly-quotes = true # mathjax-support = false # useful if we want to pull equations in ? copy-fonts = true no-section-label = false -additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "last-changed.css"] +additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "custom.css"] additional-js = ["theme/pagetoc.js"] git-repository-url = "https://github.com/nymtech/nym" git-repository-icon = "fa-github" # edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}" site-url = "/developers/" # cname = "nymtech.net" -# input-404 = "not-found.md" # todo make custom 404 page +input-404 = "not-found.md" [output.html.fold] enable = true # whether or not to enable section folding diff --git a/documentation/dev-portal/custom.css b/documentation/dev-portal/custom.css new file mode 100644 index 0000000000..855f5eef5f --- /dev/null +++ b/documentation/dev-portal/custom.css @@ -0,0 +1,37 @@ +:root { + --mono-font: Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace; +} +.coal { + --bg: #121726; + --fg: #f2f2f2; + + --sidebar-bg: #121726; + --sidebar-fg: #f2f2f2; + --sidebar-active: #fb6e4e; + + --icons: #f2f2f2; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +.light { + --bg: #f4f6f8; + --fg: #121726; + + --sidebar-bg: #f4f6f8; + --sidebar-fg: #121726; + --sidebar-active: #fb6e4e; + + --icons: #121726; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +footer { + font-size: 0.8em; + text-align: center; + border-top: 1px solid black; + padding: 5px 0; +} diff --git a/documentation/dev-portal/src/SUMMARY.md b/documentation/dev-portal/src/SUMMARY.md index aeae804662..2095d6bc30 100644 --- a/documentation/dev-portal/src/SUMMARY.md +++ b/documentation/dev-portal/src/SUMMARY.md @@ -11,6 +11,8 @@ # Quickstart - [Overview](quickstart/overview.md) +- [Chat demo (webapp)](quickstart/chat-demo.md) +- [Coconut Credential Playground (webapp)](quickstart/cred-playground.md) - [SOCKS Proxy (CLI)](quickstart/socks-proxy.md) - [NymConnect Beta (GUI)](quickstart/nymconnect-gui.md) @@ -18,6 +20,7 @@ - [NymConnect Monero](tutorials/monero.md) - [NymConnect Matrix](tutorials/matrix.md) +- [NymConnect Telegram](tutorials/telegram.md) # Integrations @@ -28,14 +31,25 @@ # Tutorials -- [Simple Service Provider](tutorials/simple-service-provider.md) - - [Tutorial Overview](tutorials/simple-service-provider/overview.md) - - [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md) - - [Building Your User Client](tutorials/simple-service-provider/user-client.md) - - [Preparing Your Service Provider Environment](tutorials/simple-service-provider/preparating-env2.md) - - [Building Your Service Provider](tutorials/simple-service-provider/service-provider.md) - - [Sending a Message Through the Mixnet](tutorials/simple-service-provider/sending-message.md) -- [IPFS Service Provider (coming soon)](tutorials/ipfs-service-provider.md) +- [Rust SDK](tutorials/rust-sdk.md) + - [Blockchain Service pt1](tutorials/cosmos-service/intro.md) + - [Tutorial Overview](tutorials/cosmos-service/overview.md) + - [Preparing Your Environment](tutorials/cosmos-service/preparing-env.md) + - [Preparing Your Lib](tutorials/cosmos-service/lib.md) + - [Preparing Your Client](tutorials/cosmos-service/client.md) + - [Preparing Your Client pt2](tutorials/cosmos-service/client-src.md) + - [Preparing Your Service](tutorials/cosmos-service/service.md) + - [Preparing Your Service pt2](tutorials/cosmos-service/service-src.md) + - [Querying the Chain](tutorials/cosmos-service/querying.md) +- [Typescript](tutorials/typescript.md) + - [Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md) + - [Tutorial Overview](tutorials/simple-service-provider/overview.md) + - [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md) + - [Building Your User Client](tutorials/simple-service-provider/user-client.md) + - [Preparing Your Service Provider Environment](tutorials/simple-service-provider/preparating-env2.md) + - [Building Your Service Provider](tutorials/simple-service-provider/service-provider.md) + - [Sending a Message Through the Mixnet](tutorials/simple-service-provider/sending-message.md) + # Community Resources @@ -43,10 +57,9 @@ - [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) ---- +--- # Misc. -- [Glossary](glossary.md) - [Code of Conduct](coc.md) - [Licensing](licensing.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 index ba00922658..5186dceae1 100644 --- a/documentation/dev-portal/src/community-resources/community-applications-and-guides.md +++ b/documentation/dev-portal/src/community-resources/community-applications-and-guides.md @@ -87,12 +87,20 @@ We love seeing our developer community create applications using Nym. If you wou ## Minibolt -> Anonymize your p2p inventory messages and mempool for your Bitcoin & Lightning full nodes on consumer PCs! +> Proxies the clearnet connections of a Bitcoin node and its associated tools using the NYM mixnet. * [Github](https://github.com/minibolt-guide/minibolt) * [Documentation](https://v2.minibolt.info/bonus-guides/system/nym-mixnet#proxying-bitcoin-core) +## NymGraph + +> NymGraph is a graphical chat client for Nym running on Ubuntu and Debian. Test it to chat over the Nym network! +* [Github](https://github.com/Tyz3/nymgraph) + + + +

# Community Guides diff --git a/documentation/dev-portal/src/images/profile_picture/minibolt_pp.png b/documentation/dev-portal/src/images/profile_picture/minibolt_pp.png index ed059e6055..455243cc22 100644 Binary files a/documentation/dev-portal/src/images/profile_picture/minibolt_pp.png and b/documentation/dev-portal/src/images/profile_picture/minibolt_pp.png differ diff --git a/documentation/dev-portal/src/images/profile_picture/nymgraph_pp.png b/documentation/dev-portal/src/images/profile_picture/nymgraph_pp.png new file mode 100644 index 0000000000..411dbec6e0 Binary files /dev/null and b/documentation/dev-portal/src/images/profile_picture/nymgraph_pp.png differ diff --git a/documentation/dev-portal/src/info-request.md b/documentation/dev-portal/src/info-request.md index 551da83bf3..e3edbdc3de 100644 --- a/documentation/dev-portal/src/info-request.md +++ b/documentation/dev-portal/src/info-request.md @@ -1,6 +1,6 @@ # Change Service Grantee Information -The Nym Service Grantees User Information Update Form allows Service Grantees to update their information with the Nym team. The form allows grantees to update their current: +If you wish to update any of the following information: - `Email Address` - `NYM address` - `Network Requester` @@ -9,10 +9,6 @@ The Nym Service Grantees User Information Update Form allows Service Grantees to - `Service` - `Payment Address` -Complete and submit your form so the Nym team to keep track of the service grantees and your current information, ensuring that deliver payments and handle other logistics smooth and accurately. - -> ⚠️ Service Grantee User Information will be updated by our team by 5pm UTC between Mon - Fri. - - +Please reach out either in the `#service-grantees` channel on Discord or on [Matrix](https://matrix.to/#/#nym-community:nymtech.chat). diff --git a/documentation/dev-portal/src/infrastructure/node-types.md b/documentation/dev-portal/src/infrastructure/node-types.md index 464dedb80f..589b9ca576 100644 --- a/documentation/dev-portal/src/infrastructure/node-types.md +++ b/documentation/dev-portal/src/infrastructure/node-types.md @@ -36,12 +36,11 @@ For example, a Service Provider could receive a request to check a mail server a Maybe you would like to concentrate on building a application that uses the mixnet: -* Explore the Tutorials section of the Developer Portal. Our in-depth tutorial on [Building a Simple Service Provider](../tutorials/simple-service-provider.md) give a good understanding of building User Clients and Service Providers in TypeScript, and how to configure Nym Websocket Clients for seamless communication with the mixnet. +* Explore the Tutorials section of the Developer Portal. Our in-depth tutorial on [Building a Simple Service Provider](../tutorials/simple-service-provider/simple-service-provider.md) give a good understanding of building User Clients and Service Providers in TypeScript, and how to configure Nym Websocket Clients for seamless communication with the mixnet. * Get started with using the Nym Mixnet quickly and easily by exploring the [Quickstart](../quickstart/overview.md) options, such a NymConnect, proxying traffic through the Nym Socks5 client, or dive into integrating Nym into your existing application with the [Integrations](../integrations/integration-options.md) section. -Or perhaps you a developer that would like to run a infrastructure node such as a Gateway, Mix node or Network Requestor: -* Check out the [Network Overview](https://nymtech.net/docs/architecture/network-overview.html) docs page. +Or perhaps you a developer that would like to run a infrastructure node such as a Gateway, Mix node or Network Requestor: +* Check out the [Network Overview](https://nymtech.net/docs/architecture/network-overview.html) docs page. * Take a look at our [Node Setup Guide](https://nymtech.net/docs/nodes/setup-guides.html) with our Nym Docs, containing setup guides for setting up you own infrastructure node. - diff --git a/documentation/dev-portal/src/integrations/mixnet-integration.md b/documentation/dev-portal/src/integrations/mixnet-integration.md index e4a328bf24..9df20518bd 100644 --- a/documentation/dev-portal/src/integrations/mixnet-integration.md +++ b/documentation/dev-portal/src/integrations/mixnet-integration.md @@ -1,19 +1,19 @@ # Integrating with Nym for network privacy -If you are wanting to integrate Nym by using the Mixnet as a transport layer for application traffic, you will have to run one of the three Nym clients in order to connect to the Mixnet. +If you are wanting to integrate Nym by using the Mixnet as a transport layer for application traffic, you will have to run one of the three Nym clients in order to connect to the Mixnet. ## Connecting applications to the mixnet -### SDK support +### SDK support If your app is written in Typescript or Rust, then you can use the [Typescript](https://nymtech.net/docs/sdk/typescript.html) or [Rust](https://nymtech.net/docs/sdk/rust.html) SDKs. These SDKs abstract away much of the messaging logic from your app, and allow you to run a Nym client as part of your application process, instead of having to run them seperately. ### Choosing a client -In order to connect your application to the mixnet, you need to select one of three clients to use. These clients do the majority of the heavy-lifting with regards to cryptographic operations and routing under the hood, and all do basically the same thing: create a connection to a gateway, encrypt and decrypt packets sent to and received from the mixnet, and send cover traffic to hide the flow of actual app traffic from observers. +In order to connect your application to the mixnet, you need to select one of three clients to use. These clients do the majority of the heavy-lifting with regards to cryptographic operations and routing under the hood, and all do basically the same thing: create a connection to a gateway, encrypt and decrypt packets sent to and received from the mixnet, and send cover traffic to hide the flow of actual app traffic from observers. -As outlined in the [clients overview documentation](https://nymtech.net/docs/clients/overview.html) there are three clients availiable to developers to use when connecting applications to the mixnet: +As outlined in the [clients overview documentation](https://nymtech.net/docs/clients/overview.html) there are three clients availiable to developers to use when connecting applications to the mixnet: #### Websocket client -Your first option is the native websocket client. This is a compiled program that can run on Linux, Mac OS X, and Windows machines. It runs as a persistent process on a desktop or server machine. You can connect to it with any language that supports websockets. +Your first option is the native websocket client. This is a compiled program that can run on Linux, Mac OS X, and Windows machines. It runs as a persistent process on a desktop or server machine. You can connect to it with any language that supports websockets. -You can see an example of how to connect to and manage interactions with this client in the [Simple Service Provider tutorial](../tutorials/simple-service-provider.md). +You can see an example of how to connect to and manage interactions with this client in the [Simple Service Provider tutorial](../tutorials/simple-service-provider/simple-service-provider.md). #### Webassembly client If you’re working in JavaScript or Typescript in the browser, or building an edge computing app, you’ll likely want to choose the webassembly client. @@ -27,16 +27,16 @@ You can find example code in the [examples section](https://github.com/nymtech/n #### SOCKS client This client is useful for allowing existing applications to use the Nym mixnet without any code changes. All that’s necessary is that they can use one of the SOCKS5, SOCKS4a, or SOCKS4 proxy protocols (which many applications can - crypto wallets, browsers, chat applications etc). -It’s less flexible as a way of writing custom applications than the other clients, but able to be used to proxy application traffic through the mixnet without having to make any code changes. +It’s less flexible as a way of writing custom applications than the other clients, but able to be used to proxy application traffic through the mixnet without having to make any code changes. -You can find examples of how to utilise this client in the [Quickstart](../quickstart/socks-proxy.md) section, and the [SOCKS5 documentation](https://nymtech.net/docs/clients/socks5-client.html). +You can find examples of how to utilise this client in the [Quickstart](../quickstart/socks-proxy.md) section, and the [SOCKS5 documentation](https://nymtech.net/docs/clients/socks5-client.html). -## Recommended infrastructure setup -In order to ensure uptime and reliability, it is recommended that you run some pieces of mixnet infrastructure. What infrastructure is necessary to run depends on the architecture of your application, and the endpoints that it needs to hit! +## Recommended infrastructure setup +In order to ensure uptime and reliability, it is recommended that you run some pieces of mixnet infrastructure. What infrastructure is necessary to run depends on the architecture of your application, and the endpoints that it needs to hit! -* 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-and-guides.md) page. +* 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-and-guides.md) page. ## Example application traffic flow ### Initialization @@ -44,36 +44,36 @@ First, we need to initalise an app and connect it to Nym. ``` - +-----------+ - | Gateway | +-----------+ - ^ - | - | - | - | - | - | - +-------------------+ - | +---------------+ | - | | Nym client | | - | +---------------+ | - | ^ | - | | | - | | | - | | | - | v | - | +---------------+ | - | | Your app code | | - | +---------------+ | - +-------------------+ - Your Local Machine + | Gateway | + +-----------+ + ^ + | + | + | + | + | + | + +-------------------+ + | +---------------+ | + | | Nym client | | + | +---------------+ | + | ^ | + | | | + | | | + | | | + | v | + | +---------------+ | + | | Your app code | | + | +---------------+ | + +-------------------+ + Your Local Machine ``` At the bottom we have an app. It consists of two parts: -* your application specific logic -* your Nym client - either running as a standalone process, or as part of the process of your app code if you're using an SDK +* your application specific logic +* your Nym client - either running as a standalone process, or as part of the process of your app code if you're using an SDK Nym apps have a stable, potentially long-lasting relation to a gateway node. A client will register itself with a gateway, and get back an authentication token that it can then use to retrieve messages from the gateway later on. @@ -89,40 +89,40 @@ The Nym client part of the app accepts messages from your code and automatically The app has now connected to the Gateway, but we haven't sent a message to ourselves yet. Let's do that now. ``` - - +----------+ +----------+ +----------+ - | Mix Node |<-----------> | Mix Node |<----------->| Mix Node | - | Layer 1 | | Layer 2 | | Layer 3 | - +----------+ +----------+ +----------+ - ^ ^ - | | + + +----------+ +----------+ +----------+ + | Mix Node |<-----------> | Mix Node |<----------->| Mix Node | + | Layer 1 | | Layer 2 | | Layer 3 | + +----------+ +----------+ +----------+ + ^ ^ + | | |<--------------------------------------------------+ - | - v - +--------------+ - | Your gateway | - +--------------+ - ^ - | - | - v - +-------------------+ - | +---------------+ | - | | Nym client | | - | +---------------+ | - | ^ | - | | | - | | | - | v | - | +---------------+ | - | | Your app code | | - | +---------------+ | - +-------------------+ - Your Local Machine** + | + v + +--------------+ + | Your gateway | + +--------------+ + ^ + | + | + v + +-------------------+ + | +---------------+ | + | | Nym client | | + | +---------------+ | + | ^ | + | | | + | | | + | v | + | +---------------+ | + | | Your app code | | + | +---------------+ | + +-------------------+ + Your Local Machine** ** note that depending on the technical setup, the Nym client running on this machine may -be either a seperate process or embedded in the same process as the app code via one of our SDKs. +be either a seperate process or embedded in the same process as the app code via one of our SDKs. ``` Let's say your code code pokes a message `hello world` into the Nym client. The Nym client automatically wraps that message up into a layer encrypted Sphinx packet, adds some routing information and encryption, and sends it to its own gateway. The gateway strips the first layer of encryption, ending up with the address of the first mixnode it should forward to, and a Sphinx packet. @@ -139,38 +139,37 @@ Messages are end-to-end encrypted. Although the gateway knows our app's IP when The process for sending messages to other apps is exactly the same, you simply specify a different recipient address. Address discovery happens outside the Nym system: in the case of a Service Provider app, the service provider has presumably advertised its own address. If you're sending to a friend of yours, you'll need to get a hold of their address out of band, maybe through a private messaging app such as Signal. ``` - - +----------+ +----------+ +----------+ - | Mix Node |<-----------> | Mix Node |<----------->| Mix Node | - | Layer 1 | | Layer 2 | | Layer 3 | - +----------+ +----------+ +----------+ - ^ ^ - | | - | | - v v - +--------------+ +-----------------+ - | Your gateway | | Service gateway | - +--------------+ +-----------------+ - ^ ^ - | | - | | - v v - +-------------------+ +-------------------+ - | +---------------+ | | +---------------+ | - | | Nym client | | | | Nym Client | | - | +---------------+ | | +---------------+ | - | ^ | | ^ | - | | | | | | - | | | | | | - | v | | v | - | +---------------+ | | +---------------+ | - | | Your app code | | | | Service Code | | - | +---------------+ | | +---------------+ | - +-------------------+ +-------------------+ - Your Local Machine** Service Provider Machine** + + +----------+ +----------+ +----------+ + | Mix Node |<-----------> | Mix Node |<----------->| Mix Node | + | Layer 1 | | Layer 2 | | Layer 3 | + +----------+ +----------+ +----------+ + ^ ^ + | | + | | + v v + +--------------+ +-----------------+ + | Your gateway | | Service gateway | + +--------------+ +-----------------+ + ^ ^ + | | + | | + v v + +-------------------+ +-------------------+ + | +---------------+ | | +---------------+ | + | | Nym client | | | | Nym Client | | + | +---------------+ | | +---------------+ | + | ^ | | ^ | + | | | | | | + | | | | | | + | v | | v | + | +---------------+ | | +---------------+ | + | | Your app code | | | | Service Code | | + | +---------------+ | | +---------------+ | + +-------------------+ +-------------------+ + Your Local Machine** Service Provider Machine** ** note that depending on the technical setup, the Nym client running on these machines may -be either a seperate process or embedded in the same process as the app code via one of our SDKs. +be either a seperate process or embedded in the same process as the app code via one of our SDKs. ``` - diff --git a/documentation/dev-portal/src/introduction.md b/documentation/dev-portal/src/introduction.md index d679a77c53..7335fab32b 100644 --- a/documentation/dev-portal/src/introduction.md +++ b/documentation/dev-portal/src/introduction.md @@ -1,5 +1,5 @@ # Introduction -Welcome to the Nym Developer Portal, containing the resources and tools needed to build privacy enhanced apps (PEApps) with Nym's technology. Whether you're a seasoned developer or just getting started, our portal is designed to help you create decentralized applications and services utilising the Nym mixnet. +Welcome to the Nym Developer Portal, containing quickstart resources, user manuals, integration information, and tutorials outlining to start building privacy enhanced apps. -Join our community of developers and start building the decentralized future today! +For more in-depth information about nodes, network traffic flows, etc check out the [docs](https://nymtech.net/docs). diff --git a/documentation/dev-portal/src/not-found.md b/documentation/dev-portal/src/not-found.md new file mode 100644 index 0000000000..704da5ec19 --- /dev/null +++ b/documentation/dev-portal/src/not-found.md @@ -0,0 +1,11 @@ +# Page Not Found + +You seem to have followed a link to a page that no longer exists. Our documentation is changing and growing over time, so this page has most likely been moved somewhere else. + +For node setup guides, see the [Operator Guides book](https://nymtech.net/operators). + +For information on network architecture traffic flow, clients, the SDKs, or interacting with the Nyx blockchain see the [Technical Documentation](https://nymtech.net/docs). + +If you are looking for information on developer tutorials, app guides, and demo and community apps, check the sitemap on the left hand side of the screen. + +If you still can't find what you're looking for get in touch with us via [Matrix](https://matrix.to/#/#nym-community:nymtech.chat). \ No newline at end of file diff --git a/documentation/dev-portal/src/quickstart/chat-demo.md b/documentation/dev-portal/src/quickstart/chat-demo.md new file mode 100644 index 0000000000..6e2f5c244a --- /dev/null +++ b/documentation/dev-portal/src/quickstart/chat-demo.md @@ -0,0 +1,5 @@ +# Chat demo (webapp) + +You can find a browser-based 'hello world' chat app [here](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. diff --git a/documentation/dev-portal/src/quickstart/cred-playground.md b/documentation/dev-portal/src/quickstart/cred-playground.md new file mode 100644 index 0000000000..aa2220940c --- /dev/null +++ b/documentation/dev-portal/src/quickstart/cred-playground.md @@ -0,0 +1,5 @@ +# Coconut Credential Playground (webapp) + +There is a coconut-scheme based Credential Library playground [here](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. + +For more information on what is happening here check out the [Coconut docs](https://nymtech.net/docs/coconut.html). diff --git a/documentation/dev-portal/src/quickstart/overview.md b/documentation/dev-portal/src/quickstart/overview.md index b2ac4199c9..534ed60fe5 100644 --- a/documentation/dev-portal/src/quickstart/overview.md +++ b/documentation/dev-portal/src/quickstart/overview.md @@ -1,13 +1,7 @@ # Overview -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. +There are multiple options to quickly connect to Nym and play with both the mixnet and credentials on the Sandbox testnet. -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). +At most, these involve running Nym as a second process alongside an existing application in order to send traffic through the mixnet, most are either interact webpages or a standalone app. -If you've already covered the information in this section, or want to jump straight into integrating/ a Nym connection into an existing application, head to the [Integrations](../integrations/integration-options.md) section. +If you've already covered the information in this section, or want to jump straight into integrating/ a Nym connection into an existing application, head to the [Integrations](../integrations/integration-options.md) section. diff --git a/documentation/dev-portal/src/tutorials/cosmos-service/client-src.md b/documentation/dev-portal/src/tutorials/cosmos-service/client-src.md new file mode 100644 index 0000000000..5e5838b60a --- /dev/null +++ b/documentation/dev-portal/src/tutorials/cosmos-service/client-src.md @@ -0,0 +1,59 @@ +# Preparing Your Client pt2 + +Open `src/client.rs`. This is where the logic of the command from the `match` statement in `bin/client.rs` is defined. + +# Dependencies +```rust +use crate::{handle_response, wait_for_non_empty_message, RequestTypes, DEFAULT_VALIDATOR_RPC}; +use cosmrs::AccountId; +use nym_sdk::mixnet::MixnetClient; +use nym_sphinx_addressing::clients::Recipient; +use nym_validator_client::nyxd::Coin; +``` + +As well as importing message-handling functionality, request types, and the default RPC endpoint, this file relies on the `AccountId` type to construct blockchain addresses, the `MixnetClient` for interacting with the mixnet, the `Recipient` type to construct mixnet recipient addresses, and the `Coin` type for properly handling the returned balance of the account that will be queried. + +# Querying via the Mixnet +The following is used to construct a `BalanceRequest`, send this to the supplied `service` address, and then handle the response, matching it to a `ResponseType` (in this case the only expected response, a `BalanceResponse`). + +The actual sending of the request is performed by `client.send_bytes`: sending the serialised `BalanceRequest` to the supplied Nym address (the `Recipient` imported from the `nym_sphinx_addressing` crate). It is sending the default number of SURBs along with the message, defined [here](https://github.com/nymtech/nym/blob/develop/sdk/rust/nym-sdk/src/mixnet/client.rs#L34). + +```rust +pub async fn query_balance( + account: AccountId, + client: &mut MixnetClient, + sp_address: Recipient, +) -> anyhow::Result { + // construct balance request + let message = RequestTypes::Balance(crate::BalanceRequest { + validator: DEFAULT_VALIDATOR_RPC.to_owned(), // rpc endpoint for broadcaster to use + account, + }); + + // send serialised request to service via mixnet + client +<<<<<<< HEAD + .send_message(sp_address, message.serialize(), Default::default()) +======= + .send_bytes(sp_address, message.serialize(), Default::default()) +>>>>>>> e504def9e40b472f224244cfa8d58a94c2e48d20 + .await; + + let received = wait_for_non_empty_message(client).await?; + + // listen for response from service + let sp_response = handle_response(received)?; + + // match JSON -> ResponseType + let res = match sp_response { + crate::ResponseTypes::Balance(response) => { + println!("{:#?}", response); + response.balance + } + }; + + Ok(res) +} +``` + +That is all the client code written: now to move on to the `service` that will be interacting with the blockchain on behalf of the `client`. diff --git a/documentation/dev-portal/src/tutorials/cosmos-service/client.md b/documentation/dev-portal/src/tutorials/cosmos-service/client.md new file mode 100644 index 0000000000..be9f142f09 --- /dev/null +++ b/documentation/dev-portal/src/tutorials/cosmos-service/client.md @@ -0,0 +1,79 @@ +# Preparing Your Client + +Start by creating the startup logic of your `client` in `bin/client.rs` - creating a Nym client and connecting to the mixnet (or just connecting if your client has been started before and config already exists for it), and defining and running commands. + +## Dependencies +Import the following dependencies: +``` +use clap::{Args, Parser, Subcommand}; +use chain_query::{client::query_balance, create_client}; +use nym_sdk::mixnet::Recipient; +use nym_validator_client::nyxd::AccountId; +use nym_bin_common::logging::setup_logging; +``` + +`clap` is used so different commands can be passed to the `client` (even though we're only defining one function in this first part of the tutorial, more will be added in subsequent chapters). `nym_sdk::mixnet::Recipient` is the type used to define the recipient of a mixnet message, `nym_bin_common::logging::setup_logging` is the logging setup for `client`'s Nym client, and `chain_query` imports the `create_client` and `query_balance` functions created on the previous page. + +## CLI Command with Clap +The following simply defines the commands that the client can perform. For the moment, there is only one: the `query_balance` function created in the previous section. + +As with the data structures, this structure is being used for ease of adding future commands in subsequent tutorials. + +```rust +#[derive(Debug, Parser)] +#[clap(name = "rust sdk demo - chain query service")] +#[clap(about = "query the sandbox testnet blockchain via the mixnet... part 2 coming soon")] +struct Cli { + #[clap(subcommand)] + command: Option, +} + +#[derive(Debug, Subcommand)] +enum Commands { + QueryBalance(QueryBalance), +} + +#[derive(Debug, Args)] +struct QueryBalance { + /// the account we want to query + account: AccountId, + /// the address of the broadcaster service - this submits txs and queries the chain on our behalf + sp_address: String, +} +``` + +## `main()` +This is the root logic of the `client`. Using `[tokio](https://tokio.rs/)` for the async runtime, this function performs the following functions: +* If not already existing, create a Nym client with config at `/tmp/client`. Otherwise load the already existing client from this config. +* Matche the command from the CLI - in this instance, the `QueryBalance` function which will be defined in the next section. This creates a `BalanceRequest` and sends this to the `service`, before returning the response back to the main thread and print this to the console. +* Perform a proper shutdown of the Nym client. + +```rust +#[tokio::main] +async fn main() -> anyhow::Result<()> { + setup_logging(); + let cli = Cli::parse(); + let mut client = create_client("/tmp/client2".into()).await; + let our_address = client.nym_address(); + println!("\nclient's nym address: {our_address}"); + + match cli.command { + Some(Commands::QueryBalance(QueryBalance { + account, + sp_address, + })) => { + println!("\nsending bank balance request to service via mixnet"); + let sp_address = Recipient::try_from_base58_string(sp_address).unwrap(); + let returned_balance = query_balance(account, &mut client, sp_address).await?; + println!("\nreturned balance is: {}", returned_balance); + } + None => { + println!("\nno command specified - nothing to do") + } + } + println!("\ndisconnecting client"); + client.disconnect().await; + println!("client disconnected"); + Ok(()) +} +``` diff --git a/documentation/dev-portal/src/tutorials/cosmos-service/intro.md b/documentation/dev-portal/src/tutorials/cosmos-service/intro.md new file mode 100644 index 0000000000..cf9dad5fd0 --- /dev/null +++ b/documentation/dev-portal/src/tutorials/cosmos-service/intro.md @@ -0,0 +1,7 @@ +# Interacting with a Cosmos SDK Blockchain via the Mixnet with the Rust SDK + +This tutorial is for Rust developers wanting to interact with the Rust SDK and take a first step at building a service with which to interact with a Cosmos SDK blockchain. + +The key here is to think of the service as a proxy: it interacts with the blockchain _on the client's behalf_, shielding the client from the Validator it interacts with, whilst also being shielded from the client by the mixnet. + +> This service also nicely highlights the limitations of the mixnet - even though with this code your metadata is shielded from the Validator, and even the service does not know your Nym address, application-level information such as a blockchain address is not made private, in virtue of the fact that using the mixnet provides solely network-level privacy. For information on what application-level privacy Nym offers, check out the [coconut credential SDK example](https://nymtech.net/docs/sdk/rust.html#coconut-credential-generation). diff --git a/documentation/dev-portal/src/tutorials/cosmos-service/lib.md b/documentation/dev-portal/src/tutorials/cosmos-service/lib.md new file mode 100644 index 0000000000..0be72a690d --- /dev/null +++ b/documentation/dev-portal/src/tutorials/cosmos-service/lib.md @@ -0,0 +1,165 @@ +# Preparing Your Lib + +Now move on to preparing shared data structures and functions in `src/lib.rs`. + +These include the request and response types the client and the service will be passing through the mixnet, as well as shared functions such as client creation, and message parsing. + +## Dependencies +The dependencies for the shared `lib` file are the following: +```rust +use anyhow::bail; +use cosmrs::AccountId; +use nym_sdk::mixnet::{ + AnonymousSenderTag, MixnetClient, MixnetClientBuilder, ReconstructedMessage, StoragePaths, +}; +use nym_validator_client::nyxd::Coin; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +pub mod client; +pub mod service; +``` + +Since this is the file where client creation and message parsing are handled, the various `nym_sdk` imports, as well as `serde`'s (de)serialisation functionality, are required. `PathBuf` is for reading filepaths, `cosmrs` types are required for defining Nyx blockchain accounts, and the `Coin` type from the `nyxd_validator_client` is for our Coin balance request and response. `anyhow` is for easy error handing. + +## Constants +Below this are the chain-related `const` variables. These have been hardcoded for this demo. + +```rust +pub const DEFAULT_VALIDATOR_RPC: &str = "https://sandbox-validator1.nymtech.net"; +pub const DEFAULT_DENOM: &str = "unym"; +pub const DEFAULT_PREFIX: &str = "n"; +``` + +These define the RPC endpoint your service will use to interact with the blockchain - in this case the Sandbox testnet - as well as the expected coin denomination, and Bech32-prefix of addresses. + +## Shared Data Structures +Define the following structs for our different request and responses that will be serialised and sent through the mixnet between your client and service binaries: + +```rust +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub struct BalanceRequest { + pub validator: String, + pub account: AccountId, +} + +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub struct BalanceResponse { + pub balance: Coin, +} + +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub enum RequestTypes { + Balance(BalanceRequest), +} + +impl RequestTypes { + pub fn serialize(&self) -> Vec { + serde_json::to_vec(self).expect("serde failure") + } + + pub fn try_deserialize>(raw: M) -> anyhow::Result { + serde_json::from_slice(raw.as_ref()).map_err(Into::into) + } +} + +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub enum ResponseTypes { + Balance(BalanceResponse), +} + +impl ResponseTypes { + pub fn serialize(&self) -> Vec { + serde_json::to_vec(self).expect("serde failure") + } + + pub fn try_deserialize>(raw: M) -> anyhow::Result { + serde_json::from_slice(raw.as_ref()).map_err(Into::into) + } +} +``` + +The above data types are pretty straightforward. Even though there are only one instance of a request type (sent from `client` -> mixnet -> `service`) and one of a response type (`service` -> mixnet -> `client`) so far, a pair of enums has been defined to contain additional response and request types that will be added in part 2 of this tutorial, when adding credential functionality. + +`BalanceRequest` will be used when requesting the service to query the token balance of the supplied address on the client's behalf. You can see the information that will be returned from the chain to the service, and from the service to the client, in `BalanceResponse`. + +Custom serialistion and deserialisation have been implemented for each enum for ease of future modification and testing. + +## Shared Functions +Now to define functions shared by the `client` and `service` binaries. + +### Client Creation +The following function is called on startup by each binary, with the `config_path` being a filepath for storing client config: + +```rust +// create our client with specified path for key storage +pub async fn create_client(config_path: PathBuf) -> MixnetClient { + let config_dir = config_path; + let storage_paths = StoragePaths::new_from_dir(&config_dir).unwrap(); + let client = MixnetClientBuilder::new_with_default_storage(storage_paths) + .await + .unwrap() + .build() + .await + .unwrap(); + + client.connect_to_mixnet().await.unwrap() +} +``` + +If no config files exist at the location designated by `config_path` (in this case `/tmp/service`) then the following files are generated: + +```sh +service +├── ack_key.pem +├── db.sqlite +├── db.sqlite-shm +├── db.sqlite-wal +├── gateway_details.json +├── gateway_shared.pem +├── persistent_reply_store.sqlite +├── private_encryption.pem +├── private_identity.pem +├── public_encryption.pem +└── public_identity.pem + +1 directory, 11 files +``` + +> If keys and config already exist at this location, re-running this function **will not** overwrite them. + +### Listening for & Parsing Incoming messages +Next to define two functions: one for listening _for_ messages from the mixnet (used by `service`), and one for handling a _response_ to a request (used by `client`). + +Both functions attempt to deserialise the vec of `ReconstructedMessages` that are reconstructed by the client from delivered Sphinx packets after decryption. + +`handle_request` performs one additional function - parsing the `sender_tag` from the incoming reconstructed message. This is the randomised alphanumeric string used to identify a bucket of _SURBs_ (Single Use Reply Blocks) that are sent along with any outgoing message by default. More information about them can be found [here](https://nymtech.net/docs/architecture/traffic-flow.html#private-replies-using-surbs) but all that is necessary to know for now is that these are pre-addressed packets that clients send out with their messages. Any reply to their message that is to be sent back to them back be written to the payload of these packets, but without the replying party being able to see the destination that the reply is being sent to. This allows for services to **anonymously reply to clients without being able to doxx them by knowing their Nym address**. + +```rust +pub fn handle_response(message: ReconstructedMessage) -> anyhow::Result { + ResponseTypes::try_deserialize(message.message) +} + +pub fn handle_request( + message: ReconstructedMessage, +) -> anyhow::Result<(RequestTypes, Option)> { + let request = RequestTypes::try_deserialize(message.message)?; + Ok((request, message.sender_tag)) +} +``` + +Before moving on to the `client` and `service` code, one more function is needed. This allows for both binaries to parse empty incoming messages that they might receive. This is necessary as incoming SURBs, as well as requests for more SURBs, contain empty data fields. + +```rust +pub async fn wait_for_non_empty_message( + client: &mut MixnetClient, +) -> anyhow::Result { + while let Some(mut new_message) = client.wait_for_messages().await { + if !new_message.is_empty() { + return Ok(new_message.pop().unwrap()); + } + } + + bail!("did not receive any non-empty message") +} +``` diff --git a/documentation/dev-portal/src/tutorials/cosmos-service/overview.md b/documentation/dev-portal/src/tutorials/cosmos-service/overview.md new file mode 100644 index 0000000000..829ef476ee --- /dev/null +++ b/documentation/dev-portal/src/tutorials/cosmos-service/overview.md @@ -0,0 +1,47 @@ +# Tutorial Overview + +This tutorial involves writing two pieces of code in Rust: + +- A client side binary used to construct a blockchain query and send this query to a service, which will query the Cosmos SDK blockchain and then pass the response back to the client (bear in mind this principle works for all blockchains - we're just utilising the `cosmrs` library to interact with the Sandbox testnet blockchain in this tutorial). This query will be to query the balance of an account, in preparation for spending these tokens on a [bandwidth credential](https://nymtech.net/docs/bandwidth-credentials.html) in a subsequent tutorial. +- A service which will listen out for requests from the mixnet, act on those requests, and anonymously reply to the client sending the requests. + +You will learn how to do the following with the Rust SDK: +- Create clients with manual storage settings. +- Parse incoming traffic from the mixnet and reply anonymously using [SURBs](https://nymtech.net/docs/architecture/traffic-flow.html#private-replies-using-surbs). + +> Services usually run on remote servers to assure reliable uptime and to unlink sender and receiver metadata. For demonstration purposes however, you will run both components on your local machine, looping messages through the mixnet to yourself. + +``` + +TODO + +REDRAW + +ASCII + +DIAGRAM + +FROM + +PREVIOUS + +TUTORIAL + +``` + +You can find the code for these components [here](https://github.com/nymtech/developer-tutorials). You can use it as a reference while building or simply download it and follow along as you progress through the tutorial. + +Notice that this tutorial attempts to use very few external libraries. This tutorial is not showing you how to build production-grade code, but **to understand how to connect and send messages to, as well as receive messages from, the mixnet.** + +```admonish note title="Sidenote: What is a Service / Service Provider?" +'Service' or 'Service Provider' are catchall names used to refer to any type of app that can communicate with the mixnet via a Nym client - in this case, one embedded in its app process via the Rust SDK. + +The first SP to have been released is the [Network Requester](https://nymtech.net/docs/nodes/network-requester-setup.html) - a binary that receives a network request from the mixnet, performs that request (e.g. authenticating with a message server and receiving new messages for a user) and then passes the response back to the user who requested it anonymously, shielding their metadata from the message server. + +The SP you will build in this tutorial is far more simple than this, showing you how to approach building something that can: +* connect to the mixnet, +* listen for messages, and +* perform some action with them - in this case, query a Cosmos SDK blockchain. + +However, once you see how easy it is to integrate with the mixnet for traffic transport, you will be able to build apps with real-world uses easily. +``` diff --git a/documentation/dev-portal/src/tutorials/cosmos-service/preparing-env.md b/documentation/dev-portal/src/tutorials/cosmos-service/preparing-env.md new file mode 100644 index 0000000000..e5016e2ef2 --- /dev/null +++ b/documentation/dev-portal/src/tutorials/cosmos-service/preparing-env.md @@ -0,0 +1,62 @@ +# Preparing Your Environment + +## Prerequisites +* `Rust` & `cargo` + +## Creating your Project Structure + +* Make a new cargo project: +``` +cargo new nym-cosmos-service +``` + +* Create the following directory structure and files: +``` +. +├── Cargo.toml +├── bin +│   ├── client.rs +│   └── service.rs +└── src + ├── client.rs + ├── lib.rs + └── service.rs + +3 directories, 6 files +``` + +* Add the following dependencies to your `Cargo.toml` file: +``` +[dependencies] +clap = { version = "4.0", features = ["derive"] } +cosmrs = "=0.14.0" +tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] } +serde = "1.0.152" +serde_json = "1.0.91" +anyhow = "1.0.72" +``` + +These are non Nym-specific dependencies for the project. `clap` is for setting up the CLI commands, `cosmrs` for cosmos-specific types and functionality, `tokio` for the async/await environment, and `serde` for (de)serialisation. `anyhow` is for catch-all error handling. + +* Next add Nym-specific dependencies. Since these libraries are not yet on [crates io](https://crates.io) then you need to import them from the Nym monorepo: +``` +nym-sdk = { git = "https://github.com/nymtech/nym", rev = "5dacf0c8f8775de6168d4da808fdce56e1ac2706" } +nym-sphinx-addressing = { git = "https://github.com/nymtech/nym", rev = "5dacf0c8f8775de6168d4da808fdce56e1ac2706" } +nym-validator-client = { git = "https://github.com/nymtech/nym", rev = "5dacf0c8f8775de6168d4da808fdce56e1ac2706" } +nym-bin-common = { git = "https://github.com/nymtech/nym", rev = "5dacf0c8f8775de6168d4da808fdce56e1ac2706" } +nym-sphinx-anonymous-replies = { git = "https://github.com/nymtech/nym", rev = "5dacf0c8f8775de6168d4da808fdce56e1ac2706" } +``` + +The `sphinx` dependencies are for packet- and address-related functionality, the `validator-client` for Nyx blockchain specific configs, `common` for client logging, and the `sdk` for SDK functionality: creating and managing client storage and connections, and sending and receiving messages to and from the mixnet. + +* Finally add the following underneath your `[dependencies]`: +``` +[[bin]] +name = "client" +path = "bin/client.rs" + +[[bin]] +name = "service" +path = "bin/service.rs" +``` +This defines multiple binaries to run in a single cargo project, as outlined [here](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#binaries). diff --git a/documentation/dev-portal/src/tutorials/cosmos-service/querying.md b/documentation/dev-portal/src/tutorials/cosmos-service/querying.md new file mode 100644 index 0000000000..c6fb3a431b --- /dev/null +++ b/documentation/dev-portal/src/tutorials/cosmos-service/querying.md @@ -0,0 +1,43 @@ +# Querying the Chain +Now that all the code has been written, time to query the blockchain. + +To test against the account operating one of the mix nodes on the testnet, use `n1lcutqz94k739s39u26rvexql40ehf42zd27fwe` in the following instructions: + +```sh +# build the binaries +cargo build --release + +# open two console windows. In one run the following +./target/release/service + +# copy the printed Nym address from the console - this is used by the client when running the chain query + +# in the other run +./target/release/client query-balance n1lcutqz94k739s39u26rvexql40ehf42zd27fwe +``` + +The following happens: +* `client` and `service` both start their Nym clients and log the address. `service` is listening for incoming messages from the mixnet. +* `client` sends a request to `service` using the supplied `n1...` address as the Nyx account to query the balance of, and the supplied Nym address to communicate with this instance of `service`. +* `service` queries the Sandbox testnet blockchain using the `broadcaster` http client. It then serialises the response and returns it using SURBs to the `client`. + +All in all, quite simple. By using the service as a proxy, the client never interacts with the blockchain, thus is not revealing metadata to the operator of the Validator nor any entities monitoring its incoming and outgoing traffic. Furthermore, the client doesn't even need to share its Nym address with the service, as the service is able to reply via SURBs. + +## Creating Sandbox Account +If you wish to create an account on Sandbox to use instead of the supplied account above, the easiest way is by building the `nym-cli` tool and using that to create one: + +```sh +# start from the root of the nym monorepo +cd tools/nym-cli +# build +cargo build --release +# create account using Sandbox testnet environment +../../target/release/nym-cli --config-env-file ../../envs/sandbox.env account create +``` + +However, since this account is fresh, it won't have any tokens. Querying the balance will still work obviously, it will just return `0`. + +## Get Tokens +We're working on getting the faucet up and running again in preparation for part 2 of this tutorial: using the tokens you have privately checked the balance of to generate a [bandwidth credential](https://nymtech.net/docs/bandwidth-credentials.html). + +If you wish to get testnet tokens already then feel free to ask in the [Dev channel](https://matrix.to/#/#dev:nymtech.chat) on Matrix. \ No newline at end of file diff --git a/documentation/dev-portal/src/tutorials/cosmos-service/service-src.md b/documentation/dev-portal/src/tutorials/cosmos-service/service-src.md new file mode 100644 index 0000000000..0e463362e8 --- /dev/null +++ b/documentation/dev-portal/src/tutorials/cosmos-service/service-src.md @@ -0,0 +1,45 @@ +# Preparing Your Service pt2 + +Now to define the logic of creating the `broadcaster` for interacting with the blockchain, and querying it in `src/service.rs`. + +## Dependencies +The following dependencies are for creating a client to interact with the blockchain, and deal with the returned `Coin` type in `BalanceResponse`. + +```rust +use crate::{BalanceResponse, DEFAULT_DENOM, DEFAULT_VALIDATOR_RPC}; +use cosmrs::rpc::HttpClient; +use cosmrs::AccountId; +use nym_validator_client::nyxd::{Coin, CosmWasmClient}; +``` + +## Creating a `broadcaster` +The `broadcaster` is an `HttpClient` taken from `cosmrs`, created with the `DEFAULT_VALIDATOR_RPC` as its default endpoint. The service will use this to query the Sandbox testnet chain. + +```rust +pub async fn create_broadcaster() -> anyhow::Result { + let broadcaster: HttpClient = HttpClient::new(DEFAULT_VALIDATOR_RPC)?; + Ok(broadcaster) +} +``` + +## Querying the Chain +Now to write the logic for querying the chain, using the `broadcaster` created in the previous step to query for the balance of the `account` that the client sent via the mixnet in the `BalanceRequest`. This function returns a `BalanceResponse` containing a `Coin` type, denoting the `balance` and `denom` returned by `get_balance()`. + +```rust +pub async fn get_balance( + broadcaster: HttpClient, + account: AccountId, +) -> anyhow::Result { + let balance = broadcaster + .get_balance(&account, DEFAULT_DENOM.to_string()) + .await + .unwrap() + .unwrap(); + Ok(BalanceResponse { + balance: Coin { + amount: balance.amount, + denom: balance.denom, + }, + }) +} +``` \ No newline at end of file diff --git a/documentation/dev-portal/src/tutorials/cosmos-service/service.md b/documentation/dev-portal/src/tutorials/cosmos-service/service.md new file mode 100644 index 0000000000..7c0cc762a0 --- /dev/null +++ b/documentation/dev-portal/src/tutorials/cosmos-service/service.md @@ -0,0 +1,71 @@ +# Preparing Your Service +In `bin/src.rs` define the startup and response logic of the `service`. Client connection / config reading happens as it does in `bin/client.rs`. + +## Dependencies +``` +use chain_query::{ + create_client, handle_request, + service::{create_broadcaster, get_balance}, + BalanceResponse, RequestTypes, ResponseTypes, +}; +use nym_sphinx_anonymous_replies::{self, requests::AnonymousSenderTag}; +use nym_bin_common::logging::setup_logging +``` + +The imports from `chain_query` are most of the data types and functions defined in the previous sections of this tutorial. + +The `AnonymousSenderTag` type is used for SURBs. + +## main() +Also using tokio for the async runtime, `main` does the following: +* Create a Nym client with config at `/tmp/service`, or load the existing client from this config directory. +* Create a `broadcaster` - this is used by the service to interact with the blockchain, using the consts defined in `src/lib.rs` as chain config. +* Listen out for incoming messages, and in much the same way as the `client`, handle and match the incoming request. +* Using the `sender_tag`, anonymously reply to the `client` with the response from the blockchain **without having to know the client's Nym address**. + +```rust +#[tokio::main] +async fn main() -> anyhow::Result<()> { + setup_logging(); + let mut client = create_client("/tmp/service".into()).await; + let our_address = client.nym_address(); + println!("\nservice's nym address: {our_address}"); + // the httpclient we will use to broadcast our query to the blockchain + let broadcaster = create_broadcaster().await?; + println!("listening for messages, press CTRL-C to exit"); + + while let Some(received) = client.wait_for_messages().await { + for msg in received { + let request = match handle_request(msg) { + Ok(request) => request, + Err(err) => { + eprintln!("failed to handle received request: {err}"); + continue; + } + }; + + let return_recipient: AnonymousSenderTag = request.1.expect("no sender tag received"); + match request.0 { + RequestTypes::Balance(request) => { + println!("\nincoming balance request for: {}\n", request.account); + + let balance: BalanceResponse = + get_balance(broadcaster.clone(), request.account).await?; + + let response = ResponseTypes::Balance(balance); + + println!("response from chain: {:#?}", response); + + println!("\nreturn recipient surb bucket: {}", &return_recipient); + println!("\nsending response to {}", &return_recipient); + // send response back to anon requesting client via mixnet + client + .send_reply(return_recipient, &serde_json::to_string(&response)?) + .await; + } + } + } + } + + Ok(()) +``` diff --git a/documentation/dev-portal/src/tutorials/ipfs-service-provider.md b/documentation/dev-portal/src/tutorials/ipfs-service-provider.md deleted file mode 100644 index b51f035be4..0000000000 --- a/documentation/dev-portal/src/tutorials/ipfs-service-provider.md +++ /dev/null @@ -1 +0,0 @@ -# Building a Image Upload Service Provider with IPFS (coming soon) diff --git a/documentation/dev-portal/src/tutorials/rust-sdk.md b/documentation/dev-portal/src/tutorials/rust-sdk.md new file mode 100644 index 0000000000..f8413de28b --- /dev/null +++ b/documentation/dev-portal/src/tutorials/rust-sdk.md @@ -0,0 +1,5 @@ +# Rust SDK + +The Rust SDK allows developers building applications in Rust to import and interact with Nym clients as they would any other dependency, instead of running the client as a seperate process on their machine. + +[Read the docs](https://nymtech.net/docs/sdk/rust.html). \ No newline at end of file diff --git a/documentation/dev-portal/src/tutorials/rust.md b/documentation/dev-portal/src/tutorials/rust.md new file mode 100644 index 0000000000..5aef071e46 --- /dev/null +++ b/documentation/dev-portal/src/tutorials/rust.md @@ -0,0 +1 @@ +# Rust SDK diff --git a/documentation/dev-portal/src/tutorials/simple-service-provider.md b/documentation/dev-portal/src/tutorials/simple-service-provider/simple-service-provider.md similarity index 100% rename from documentation/dev-portal/src/tutorials/simple-service-provider.md rename to documentation/dev-portal/src/tutorials/simple-service-provider/simple-service-provider.md diff --git a/documentation/dev-portal/src/tutorials/telegram.md b/documentation/dev-portal/src/tutorials/telegram.md new file mode 100644 index 0000000000..1965fceea4 --- /dev/null +++ b/documentation/dev-portal/src/tutorials/telegram.md @@ -0,0 +1,41 @@ +# Telegram NymConnect Integration + +*This is a shortened version of a [Nym Community post](https://blog.nymtech.net/how-to-use-telegram-in-iraq-with-nymconnect-106a3b8dd050) written by Saliveja.* + +The purpose of the following manual is not to promote Telegram but so people can use it with the Nym mixnet if they wish to, should a situation ask for that. This privacy-enhances Telegram at the network level and allows users to access the application from locations like where the application was banned. + +See also: [Element (Matrix) over the Nym mixnet](./matrix.md): private, decentralised and secure messaging. + +## Setup & Run + +Here’s how to configure Telegram with NymConnect: + +1. Download and install NymConnect ().** + For more releases, check out [Github](https://github.com/nymtech/nym/tags). NymConnect is available for Linux, Windows, and MacOS. + On Linux make sure NymConnect is executable. Opening a terminal in the same directory and run: + ```sh + chmod +x ./.AppImage + ``` +2. **Start NymConnect** + Telegram is added to NymConnect by default. +3. **Click connect - the host and port will now be displayed.** +4. **Click on host or port to copy** the value to the clipboard. +5. **Open the Telegram proxy settings.** + Linux: Telegram -> Settings -> Advanced -> Connection type -> Use custom proxy + MacOS: Telegram -> Settings -> Advanced -> Data & Storage -> Connection Type -> Use custom Proxy + Windows: Telegram -> Settings -> Data and Storage -> Use proxy +6. **Add a proxy** with the Add proxy button. +7. **Select SOCKS5** and make sure the port details are the same as those generated by NymConnect. Alternatively, follow this link: https://t.me/socks?server=127.0.0.1&port=1080 +8. **Save the proxy settings** in Telegram. +9. **Telegram is now running through the Nym Mixnet and is privacy-enhanced!** + This allows you to connect from regions which blocked Telegram. +10. Note if you remain idle on Telegram for a while you might lose connectivity and your messages might not get through via SOCKS5 proxy. If that happens reconnect your NymConnect and reset the proxy again. + + +Follow this [video](https://youtu.be/quj8H2qeOwY?t=97) to see the steps on Telegram setup. + + +**Now your Telegram runs over NymConnect.** + +NymConnect is currently available for several applications and service providers. Support for more apps is on the way. For any bug reports or feedback please reach out to us on Telegram or our [Discord](http://discord.gg/nym) server. + diff --git a/documentation/dev-portal/src/tutorials/typescript.md b/documentation/dev-portal/src/tutorials/typescript.md new file mode 100644 index 0000000000..8c6e758ff4 --- /dev/null +++ b/documentation/dev-portal/src/tutorials/typescript.md @@ -0,0 +1,3 @@ +# Typescript + +Tutorial code in this section is built to interact with a standalone Nym client. You can read about interacting with standalone clients [here](https://nymtech.net/docs/clients/websocket-client.html#connecting-to-the-local-websocket), although it is usually preferable to use the [Typescript SDK](https://nymtech.net/docs/sdk/typescript.html). \ No newline at end of file diff --git a/documentation/dev-portal/theme/css/general.css b/documentation/dev-portal/theme/css/general.css index 84dbfa3371..f8597e5767 100644 --- a/documentation/dev-portal/theme/css/general.css +++ b/documentation/dev-portal/theme/css/general.css @@ -22,7 +22,7 @@ body { } code { - font-family: var(--mono-font) !important; + font-family: var(--mono-font); font-size: 0.9em; } diff --git a/documentation/docs/book.toml b/documentation/docs/book.toml index 996a7ec4e4..846bf5ee2a 100644 --- a/documentation/docs/book.toml +++ b/documentation/docs/book.toml @@ -48,9 +48,8 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install` # https://gitlab.com/tglman/mdbook-variables/ [preprocessor.variables.variables] minimum_rust_version = "1.66" -# vars for links: TODO think on how to streamline updating -platform_release_version = "v1.1.25" -wallet_release_version = "v1.2.7" +platform_release_version = "1.1.29" +wallet_release_version = "1.2.8" [preprocessor.last-changed] command = "mdbook-last-changed" @@ -85,14 +84,14 @@ curly-quotes = true # mathjax-support = false # useful if we want to pull equations in copy-fonts = true no-section-label = false -additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "last-changed.css"] +additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css", "./custom.css"] additional-js = ["theme/pagetoc.js"] git-repository-url = "https://github.com/nymtech/nym" git-repository-icon = "fa-github" # edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}" # site-url = "/docs/" # cname = "nymtech.net" -# input-404 = "not-found.md" # todo make custom 404 page +input-404 = "not-found.md" [output.html.fold] enable = true # whether or not to enable section folding @@ -119,5 +118,5 @@ expand = true # partial words will match longer terms heading-split-level = 3 # link results to heading levels copy-js = true # include Javascript code for search -# [output.linkcheck] -# warning-policy = "warn" +[output.linkcheck] +warning-policy = "warn" diff --git a/documentation/docs/custom.css b/documentation/docs/custom.css new file mode 100644 index 0000000000..855f5eef5f --- /dev/null +++ b/documentation/docs/custom.css @@ -0,0 +1,37 @@ +:root { + --mono-font: Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace; +} +.coal { + --bg: #121726; + --fg: #f2f2f2; + + --sidebar-bg: #121726; + --sidebar-fg: #f2f2f2; + --sidebar-active: #fb6e4e; + + --icons: #f2f2f2; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +.light { + --bg: #f4f6f8; + --fg: #121726; + + --sidebar-bg: #f4f6f8; + --sidebar-fg: #121726; + --sidebar-active: #fb6e4e; + + --icons: #121726; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +footer { + font-size: 0.8em; + text-align: center; + border-top: 1px solid black; + padding: 5px 0; +} diff --git a/documentation/docs/src/SUMMARY.md b/documentation/docs/src/SUMMARY.md index 8d9c174f5a..c9fc9c6d1d 100644 --- a/documentation/docs/src/SUMMARY.md +++ b/documentation/docs/src/SUMMARY.md @@ -14,12 +14,11 @@ # Nodes -- [Node Setup Guides](nodes/setup-guides.md) - - [Mix Node](nodes/mix-node-setup.md) - - [Gateway](nodes/gateway-setup.md) - - [Network Requester](nodes/network-requester-setup.md) - - [Validator](nodes/validator-setup.md) -- [Troubleshooting](nodes/troubleshooting.md) +- [Node Types (Previously Setup Guides)](nodes/overview.md) + - [Mix Node](nodes/mixnode.md) + - [Gateway](nodes/gateway.md) + - [Network Requester](nodes/network-requester.md) + - [Validator](nodes/validator.md) # Clients - [Clients Overview](clients/overview.md) @@ -40,7 +39,7 @@ - [Mixnet Explorer](explorers/mixnet-explorer.md) # Nyx Blockchain - +- [Interacting with Nyx Chain and Smart Contracts](nyx/interacting-with-chain.md) - [Smart Contracts](nyx/smart-contracts.md) - [Mixnet Contract](nyx/mixnet-contract.md) - [Vesting Contract](nyx/vesting-contract.md) diff --git a/documentation/docs/src/architecture/network-overview.md b/documentation/docs/src/architecture/network-overview.md index fab4655e09..3a69f78be7 100644 --- a/documentation/docs/src/architecture/network-overview.md +++ b/documentation/docs/src/architecture/network-overview.md @@ -26,7 +26,7 @@ The mixnet - the different pieces of software that your traffic will pass throug * **Gateways** act as message storage for clients which may go offline and come back online again, and defend against denial of service attacks. The default gateway implementation included in the Nym platform code holds packets for later retrieval. For many applications (such as simple chat), this is usable out of the box, as it provides a place that potentially offline clients can retrieve packets from. The access token allows clients to pull messages from the gateway node. -* **Services** are applications that communicate with nym clients, listening and sending traffic to the mixnet. This is an umbrella term for a variety of different pieces of code, such as the [network requester](../nodes/network-requester-setup.md) binary. +* **Services** are applications that communicate with nym clients, listening and sending traffic to the mixnet. This is an umbrella term for a variety of different pieces of code, such as the [network requester](../nodes/network-requester.md) binary. * **Nyx Blockchain Validators** secure the network with proof-of-stake Sybil defenses, determine which nodes are included within the network, and work together to create Coconut threshold credentials which provide anonymous access to data and resources. They also produce blocks and secure the Nyx Blockchain. Initially, this chain was used only to house the CosmWasm smart contracts keeping track of Nym's network topology, token vesting contracts, and the `NYM` token itself. In recent months, we've decided to expand the role of Nyx and instead expand its role by making it an open smart contract platform for anyone to upload CosmWasm smart contracts to. Validators also provide privacy-enhanced credentials based on the testimony of a set of decentralized, blockchain-based issuing authorities. Nym validators use the [Coconut](https://arxiv.org/abs/1802.07344) [signature scheme](https://en.wikipedia.org/wiki/Digital_signature) to issue credentials. This allows privacy apps to generate anonymous resource claims through decentralised authorities, then use them with Service Providers. diff --git a/documentation/docs/src/bandwidth-credentials.md b/documentation/docs/src/bandwidth-credentials.md index 4bbe6c3a79..f267cb045d 100644 --- a/documentation/docs/src/bandwidth-credentials.md +++ b/documentation/docs/src/bandwidth-credentials.md @@ -7,6 +7,8 @@ Create a `sandbox.env` file with the following details: ``` CONFIGURED=true +NETWORK_NAME=sandbox + RUST_LOG=info RUST_BACKTRACE=1 @@ -20,7 +22,6 @@ 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" diff --git a/documentation/docs/src/binaries/building-nym.md b/documentation/docs/src/binaries/building-nym.md index 06edd54a2f..a79af72c3d 100644 --- a/documentation/docs/src/binaries/building-nym.md +++ b/documentation/docs/src/binaries/building-nym.md @@ -8,7 +8,7 @@ Nym has two main codebases: - the [Nym platform](https://github.com/nymtech/nym), written in Rust. This contains all of our code _except_ for the validators. - the [Nym validators](https://github.com/nymtech/nyxd), written in Go. -> This page details how to build the main Nym platform code. **If you want to build and run a validator, [go here](../nodes/validator-setup.md) instead.** +> This page details how to build the main Nym platform code. **If you want to build and run a validator, [go here](../nodes/validator.md) instead.** ## Prerequisites - Debian/Ubuntu: `pkg-config`, `build-essential`, `libssl-dev`, `curl`, `jq`, `git` @@ -55,11 +55,11 @@ NETWORK=sandbox cargo build --release # build your binaries with **sandbox** con Quite a bit of stuff gets built. The key working parts are: -* [mix node](../nodes/mix-node-setup.md): `nym-mixnode` -* [gateway node](../nodes/gateway-setup.md): `nym-gateway` +* [mix node](../nodes/mixnode.md): `nym-mixnode` +* [gateway node](../nodes/gateway.md): `nym-gateway` * [websocket client](../clients/websocket-client.md): `nym-client` * [socks5 client](../clients/socks5-client.md): `nym-socks5-client` -* [network requester](../nodes/network-requester-setup.md): `nym-network-requester` +* [network requester](../nodes/network-requester.md): `nym-network-requester` * [nym-cli tool](../tools/nym-cli.md): `nym-cli` The repository also contains Typescript applications which aren't built in this process. These can be built by following the instructions on their respective docs pages. diff --git a/documentation/docs/src/clients/overview.md b/documentation/docs/src/clients/overview.md index 4bfc125b5c..c8d2db1503 100644 --- a/documentation/docs/src/clients/overview.md +++ b/documentation/docs/src/clients/overview.md @@ -23,19 +23,23 @@ At present, there are three Nym clients: You need to choose which one you want incorporate into your app. Which one you use will depend largely on your preferred programming style and the purpose of your app. ### The websocket client -Your first option is the native websocket client (`nym-client`). This is a compiled program that can run on Linux, Mac OS X, and Windows machines. It runs as a persistent process on a desktop or server machine. You can connect to it with **any language that supports websockets**. +Your first option is the native websocket client (`nym-client`). This is a compiled program that can run on Linux, Mac OS X, and Windows machines. It can be run as a persistent process on a desktop or server machine. You can connect to it with **any language that supports websockets**. + +_Rust developers can import websocket client functionality into their code via the [Rust SDK](../sdk/rust.md)_. ### The webassembly client If you're working in JavaScript or Typescript in the browser, or building an [edge computing](https://en.wikipedia.org/wiki/Edge_computing) app, you'll likely want to choose the webassembly client. It's packaged and [available on the npm registry](https://www.npmjs.com/package/@nymproject/nym-client-wasm), so you can `npm install` it into your JavaScript or TypeScript application. -The webassembly client is most easily used via the [sdk](../sdk/typescript.md). +_The webassembly client is most easily used via the [Typescript SDK](../sdk/typescript.md)_. ### The SOCKS5 client -This client (`nym-socks5-client`) is useful for allowing existing applications to use the Nym mixnet without any code changes. All that's necessary is that they can use one of the SOCKS5, SOCKS4a, or SOCKS4 proxy protocols (which many applications can - crypto wallets, browsers, chat applications etc). +The `nym-socks5-client` is useful for allowing existing applications to use the Nym mixnet without any code changes. All that's necessary is that they can use one of the SOCKS5, SOCKS4a, or SOCKS4 proxy protocols (which many applications can - crypto wallets, browsers, chat applications etc). -It's less flexible as a way of writing custom applications than the other clients, but able to be used to proxy application traffic through the mixnet without having to make any code changes. +When used as a standalone client, it's less flexible as a way of writing custom applications than the other clients, but able to be used to proxy application traffic through the mixnet without having to make any code changes. + +_Rust developers can import socks client functionality into their code via the [Rust SDK](../sdk/rust.md)_. ## Commonalities between clients All Nym client packages present basically the same capabilities to the privacy application developer. They need to run as a persistent process in order to stay connected and ready to receive any incoming messages from their gateway nodes. They register and authenticate to gateways, and encrypt Sphinx packets. diff --git a/documentation/docs/src/clients/socks5-client.md b/documentation/docs/src/clients/socks5-client.md index e7163347cb..052a52f027 100644 --- a/documentation/docs/src/clients/socks5-client.md +++ b/documentation/docs/src/clients/socks5-client.md @@ -60,7 +60,7 @@ There are 2 pieces of software that work together to send SOCKS traffic through The `nym-socks5-client` allows you to do the following from your local machine: * Take a TCP data stream from a application that can send traffic via SOCKS5. * Chop up the TCP stream into multiple Sphinx packets, assigning sequence numbers to them, while leaving the TCP connection open for more data -* Send the Sphinx packets through the mixnet to a [network requester](../nodes/network-requester-setup.md). Packets are shuffled and mixed as they transit the mixnet. +* Send the Sphinx packets through the mixnet to a [network requester](../nodes/network-requester.md). Packets are shuffled and mixed as they transit the mixnet. The `nym-network-requester` then reassembles the original TCP stream using the packets' sequence numbers, and make the intended request. It will then chop up the response into Sphinx packets and send them back through the mixnet to your `nym-socks5-client`. The application will then receive its data, without even noticing that it wasn't talking to a "normal" SOCKS5 proxy! @@ -82,7 +82,7 @@ You can check that your binaries are properly compiled with: You can check the necessary parameters for the available commands by running: ``` -./nym-client --help +./nym-client --help ``` ### Initialising a new client instance @@ -100,9 +100,9 @@ Before you can use the client, you need to initalise a new instance of it, which The `--id` in the example above is a local identifier so that you can name your clients and keep track of them on your local system; it is **never** transmitted over the network. -The `--provider` field needs to be filled with the Nym address of a Network Requester that can make network requests on your behalf. If you don't want to [run your own](../nodes/network-requester-setup.md) you can select one from the [mixnet explorer](https://explorer.nymtech.net/network-components/service-providers) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [this list](https://harbourmaster.nymtech.net/). +The `--provider` field needs to be filled with the Nym address of a Network Requester that can make network requests on your behalf. If you don't want to [run your own](../nodes/network-requester.md) you can select one from the [mixnet explorer](https://explorer.nymtech.net/network-components/service-providers) by copying its `Client ID` and using this as the value of the `--provider` flag. Alternatively, you could use [this list](https://harbourmaster.nymtech.net/). -Since the nodes on this list are the infrastructure for [Nymconnect](https://nymtech.net/developers/quickstart/nymconnect-gui.html) they will support all apps on the [default whitelist](../nodes/network-requester-setup.md#network-requester-whitelist): Keybase, Telegram, Electrum, Blockstream Green, and Helios. +Since the nodes on this list are the infrastructure for [Nymconnect](https://nymtech.net/developers/quickstart/nymconnect-gui.html) they will support all apps on the [default whitelist](../nodes/network-requester.md#network-requester-whitelist): Keybase, Telegram, Electrum, Blockstream Green, and Helios. #### Choosing a Gateway By default - as in the example above - your client will choose a random gateway to connect to. @@ -146,8 +146,6 @@ You can set this via the `--host` flag during either the `init` or `run` command Alternatively, a custom host can be set in the `config.toml` file under the `socket` section. If you do this, remember to restart your client process. - - ### Running the socks5 client You can run the initalised client by doing this: @@ -156,6 +154,37 @@ You can run the initalised client by doing this: ./nym-socks5-client run --id docs-example ``` +## Automating your socks5 client with systemd + +Create a service file for the socks5 client at `/etc/systemd/system/nym-socks5-client.service`: + +```ini +[Unit] +Description=Nym Socks5 Client ({{platform_release_version}}) +StartLimitInterval=350 +StartLimitBurst=10 + +[Service] +User=nym # replace this with whatever user you wish +LimitNOFILE=65536 +ExecStart=/home/nym/nym-socks5-client run --id +KillSignal=SIGINT +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target +``` + +Now enable and start your socks5 client: + +``` +systemctl enable nym-socks5-client.service +systemctl start nym-socks5-client.service +# you can always check your socks5 client has succesfully started with: +systemctl status nym-socks5-client.service +``` + ## Using your Socks5 Client After completing the steps above, your local Socks5 Client will be listening on `localhost:1080` ready to proxy traffic to the Network Requester set as the `--provider` when initialising. @@ -169,3 +198,13 @@ Here is an example of setting the proxy connecting in Blockstream Green: Most wallets and other applications will work basically the same way: find the network proxy settings, enter the proxy url (host: **localhost**, port: **1080**). In some other applications, this might be written as **localhost:1080** if there's only one proxy entry field. + +## Useful Commands + +**no-banner** + +Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. + +**build-info** + +A `build-info` command prints the build information like commit hash, rust version, binary version just like what command `--version` does. However, you can also specify an `--output=json` flag that will format the whole output as a json, making it an order of magnitude easier to parse. diff --git a/documentation/docs/src/images/nym-token-flow.png b/documentation/docs/src/images/nym-token-flow.png new file mode 100644 index 0000000000..eb026e2325 Binary files /dev/null and b/documentation/docs/src/images/nym-token-flow.png differ diff --git a/documentation/docs/src/introduction.md b/documentation/docs/src/introduction.md index a6cf38aae7..9964ec5b75 100644 --- a/documentation/docs/src/introduction.md +++ b/documentation/docs/src/introduction.md @@ -2,27 +2,29 @@ This is Nym's technical documentation, containing information and setup guides about the various pieces of Nym software such as different mixnet infrastructure nodes, application clients, and existing applications like the desktop wallet and mixnet explorer. -If you are new to Nym and want to learn about the mixnet, how to integrate with the network, developer tutorials and quickstart guides, check out the [Developer Portal](https://nymtech.net/developers/). - -If you are looking for information on grants and beta-release Nym apps, check out the [Shipyard](https://shipyard.nymtech.net) site. +If you are looking for information and setup guides for the various pieces of Nym mixnet infrastructure (mix nodes, gateways and network requesters) and Nyx blockchain validators see the **new [Operators Guides](https://nymtech.net/operators)** book. +If you are new to Nym and want to learn about the mixnet, explore kickstart options and demos, learn how to integrate with the network, and follow developer tutorials check out the [Developer Portal](https://nymtech.net/developers/). ## Popular pages **Network Architecture:** * [Network Overview](./architecture/network-overview.md) * [Mixnet Traffic Flow](./architecture/traffic-flow.md) -**Node setup and usage guides:** -* [Mix nodes](./nodes/mix-node-setup.md) -* [Gateways](./nodes/gateway-setup.md) -* [Network requesters](./nodes/network-requester-setup.md) -* [Validators](./nodes/validator-setup.md) +**SDK examples:** +* [Typescript SDK](./sdk/typescript.md) +* [Rust SDK](./sdk/rust.md) + +**Nyx** +* [Interacting with the Nyx chain](./nyx/interacting-with-chain.md) +* [Ledger Live setup](./nyx/ledger-live.md) +* [RPC Node](./nyx/rpc-node.md) **Client setup and usage guides:** * [Websocket client](./clients/websocket-client.md) * [Socks5 client](./clients/socks5-client.md) * [Webassembly client](./clients/webassembly-client.md) -**SDK guides:** -* [Typescript SDK](./sdk/typescript.md) -* [Rust SDK](./sdk/rust.md) +**Wallets** +* [Desktop](./wallet/desktop-wallet.md) +* [CLI](./wallet/cli-wallet.md) \ No newline at end of file diff --git a/documentation/docs/src/nodes/gateway-setup.md b/documentation/docs/src/nodes/gateway-setup.md deleted file mode 100644 index a933dc5b04..0000000000 --- a/documentation/docs/src/nodes/gateway-setup.md +++ /dev/null @@ -1,260 +0,0 @@ -# Gateways - -> The Nym gateway was built in the [building nym](../binaries/building-nym.md) section. If you haven't yet built Nym and want to run the code, go there first. - -## Current version -``` - -``` - - -## Preliminary steps -There are a couple of steps that need completing before starting to set up your gateway: - -- preparing your wallet -- requisitioning a VPS (Virtual Private Server) - -### Wallet preparation -#### Mainnet -Before you initialise and run your gateway, head to our [website](https://nymtech.net/download/) and download the Nym wallet for your operating system. If pre-compiled binaries for your operating system aren't availiable, you can build the wallet yourself with instructions [here](../wallet/desktop-wallet.md). - -If you don't already have one, please create a Nym address using the wallet, and fund it with tokens. The minimum amount required to bond a gateway is 100 `NYM`, but make sure you have a bit more to account for gas costs. - -`NYM` can be purchased via Bity from the wallet itself with BTC or fiat, and is currently present on several exchanges. - -> Remember that you can **only** use native Cosmos `NYM` tokens to bond your gateway. You **cannot** use ERC20 representations of `NYM` to run a node. - - -#### Sandbox testnet -Make sure to download a wallet and create an account as outlined above. Then head to our [token faucet](https://faucet.nymtech.net/) and get some tokens to use to bond it. - -### VPS Hardware Specs -You will need to rent a VPS to run your mix node on. One key reason for this is that your node **must be able to send TCP data using both IPv4 and IPv6** (as other nodes you talk to may use either protocol. - -We currently have these _rough_ specs for VPS hardware: - -- Processors: 2 cores are fine. Get the fastest CPUs you can afford. -- RAM: Memory requirements depend on the amount of users your Gateway will be serving at any one time. If you're just going to be using it yourself, then minimal RAM is fine. **If you're running your Gateway as part of a Service Grant, get something with at least 4GB RAM.** -- Disks: much like the amount of RAM your Gateway could use, the amount of disk space required will vary with the amount of users your Gateway is serving. **If you're running your Gateway as part of a Service Grant, get something with at least 40GB storage.** - -## Gateway setup -Now that you have built the codebase, set up your wallet, and have a VPS with the `nym-gateway` binary, you can set up your gateway with the instructions below. - -### Viewing command help -You can check that your binaries are properly compiled with: - -``` -./nym-gateway --help -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -You can also check the various arguments required for individual commands with: - -``` -./nym-gateway --help -``` - -### Initialising your gateway -To check available configuration options use: - -``` - ./nym-gateway init --help -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -The following command returns a gateway on your current IP with the `id` of `supergateway`: - -``` -./nym-gateway init --id supergateway --host $(curl ifconfig.me) --wallet-address n1eufxdlgt0puwrwptgjfqne8pj4nhy2u5ft62uq -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -The `$(curl ifconfig.me)` command above returns your IP automatically using an external service. Alternatively, you can enter your IP manually wish. If you do this, remember to enter your IP **without** any port information. - -### Bonding your gateway -#### Via the Desktop wallet -You can bond your gateway via the Desktop wallet. - -* Open your wallet, and head to the `Bond` page, then select the node type and input your node details. Press `continue` - -* You will be asked to run a the `sign` command with your `gateway` - copy and paste the long signature as the value of `--contract-msg` and run it. It will look something like this: - -~~~admonish example collapsible=true title="Console output" -``` -./nym-gateway sign --id upgrade_test --contract-msg 2Mf8xYytgEeyJke9LA7TjhHoGQWNBEfgHZtTyy2krFJfGHSiqy7FLgTnauSkQepCZTqKN5Yfi34JQCuog9k6FGA2EjsdpNGAWHZiuUGDipyJ6UksNKRxnFKhYW7ri4MRduyZwbR98y5fQMLAwHne1Tjm9cXYCn8McfigNt77WAYwBk5bRRKmC34BJMmWcAxphcLES2v9RdSR68tkHSpy2C8STfdmAQs3tZg8bJS5Qa8pQdqx14TnfQAPLk3QYCynfUJvgcQTrg29aqCasceGRpKdQ3Tbn81MLXAGAs7JLBbiMEAhCezAr2kEN8kET1q54zXtKz6znTPgeTZoSbP8rzf4k2JKHZYWrHYF9JriXepuZTnyxAKAxvGFPBk8Z6KAQi33NRQkwd7MPyttatHna6kG9x7knffV6ebGzgRBf7NV27LurH8x4L1uUXwm1v1UYCA1WSBQ9Pp2JW69k5v5v7G9gBy8RUcZnMbeL26Qqb8WkuGcmuHhaFfoqSfV7PRHPpPT4M8uRqUyR4bjUtSJJM1yh6QSeZk9BEazzoJqPeYeGoiFDZ3LMj2jesbJweQR4caaYuRczK92UGSSqu9zBKmE45a - - - _ __ _ _ _ __ ___ - | '_ \| | | | '_ \ _ \ - | | | | |_| | | | | | | - |_| |_|\__, |_| |_| |_| - |___/ - - (nym-gateway - version {{platform_release_version}}) - - ->>> attempting to sign 2Mf8xYytgEeyJke9LA7TjhHoGQWNBEfgHZtTyy2krFJfGHSiqy7FLgTnauSkQepCZTqKN5Yfi34JQCuog9k6FGA2EjsdpNGAWHZiuUGDipyJ6UksNKRxnFKhYW7ri4MRduyZwbR98y5fQMLAwHne1Tjm9cXYCn8McfigNt77WAYwBk5bRRKmC34BJMmWcAxphcLES2v9RdSR68tkHSpy2C8STfdmAQs3tZg8bJS5Qa8pQdqx14TnfQAPLk3QYCynfUJvgcQTrg29aqCasceGRpKdQ3Tbn81MLXAGAs7JLBbiMEAhCezAr2kEN8kET1q54zXtKz6znTPgeTZoSbP8rzf4k2JKHZYWrHYF9JriXepuZTnyxAKAxvGFPBk8Z6KAQi33NRQkwd7MPyttatHna6kG9x7knffV6ebGzgRBf7NV27LurH8x4L1uUXwm1v1UYCA1WSBQ9Pp2JW69k5v5v7G9gBy8RUcZnMbeL26Qqb8WkuGcmuHhaFfoqSfV7PRHPpPT4M8uRqUyR4bjUtSJJM1yh6QSeZk9BEazzoJqPeYeGoiFDZ3LMj2jesbJweQR4caaYuRczK92UGSSqu9zBKmE45a ->>> decoding the message... ->>> message to sign: {"nonce":0,"algorithm":"ed25519","message_type":"gateway-bonding","content":{"sender":"n1ewmme88q22l8syvgshqma02jv0vqrug9zq9dy8","proxy":null,"funds":[{"denom":"unym","amount":"100000000"}],"data":{"gateway":{"host":"62.240.134.189","mix_port":1789,"clients_port":9000,"location":"62.240.134.189","sphinx_key":"FKbuN7mPdoCG9jA3CkAfXxC5X4rHhqeMVtmfRtJ3cFZd","identity_key":"3RoAhR8gEdfBETMjm2vbMFzKddxXDdE9ygBAnJHWqSzD","version":"1.1.13"}}}} -``` -~~~ - -* Copy the resulting signature: - -``` ->>> The base58-encoded signature is: -2SPDjLjX4b6XEtkgG7yD8Znsb1xycL1edFvRK4JcVnPsM9k6HXEUUeVS6rswRiYxoj1bMgiRKyPDwiksiuyxu8Xi -``` - -* And paste it into the wallet nodal, then confirm the transaction. - -![Paste Signature](../images/wallet-sign.png) - -* Your gateway is now bonded. - -> You are asked to `sign` a transaction on bonding so that the mixnet smart contract is able to map your nym address to your node. This allows us to create a nonce for each account and defend against replay attacks. - -#### Via the CLI (power users) -If you want to bond your mix node via the CLI, then check out the [relevant section in the Nym CLI](../tools/nym-cli.md#bond-a-gateway) docs. - -### Running your gateway -The `run` command starts the gateway: - -``` -./nym-gateway run --id supergateway -``` - -### Upgrading your gateway -Upgrading your node is a two-step process: -* Updating the binary and `config.toml` on your VPS -* Updating the node information in the [mixnet smart contract](../nyx/mixnet-contract.md). **This is the information that is present on the [mixnet explorer](https://explorer.nymtech.net)**. - ->These instructions are specifically regarding upgrading your gateway binary from one version to another. If you want to change node information such as the listening port, you can do this by clicking the `node settings` tab in the `bond` page of the wallet. - -#### Step 1: upgrading your binary -Follow these steps to upgrade your binary and update its config file: -* pause your gateway process. -* replace the existing binary with the newest binary (which you can either compile yourself or grab from our [releases page](https://github.com/nymtech/nym/releases)). -* 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. - -#### 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). - -You can either do this graphically via the Desktop Wallet, or the CLI. - -#### Updating node information via the Desktop Wallet -* Navigate to the `Bonding` page and click the `Node Settings` link in the top right corner: -![Bonding page](../images/wallet-screenshots/bonding.png) - -* Update the fields in the `Node Settings` page and click `Submit changes to the blockchain`. -![Node Settings Page](../images/wallet-screenshots/node_settings.png) - -#### Updating node information via the CLI -If you want to bond your mix node via the CLI, then check out the [relevant section in the Nym CLI](../tools/nym-cli.md#upgrade-a-mix-node) docs. - -## VPS Setup and Automation -### Configure your firewall -Although your gateway is now ready to receive traffic, your server may not be - the following commands will allow you to set up a properly configured firewall using `ufw`: - -``` -# check if you have ufw installed -ufw version -# if it is not installed, install with -sudo apt install ufw -y -# enable ufw -sudo ufw enable -# check the status of the firewall -sudo ufw status -``` - -Finally open your gateway's p2p port, as well as ports for ssh and incoming traffic connections: - -``` -sudo ufw allow 1789,22,9000/tcp -# check the status of the firewall -sudo ufw status -``` - -For more information about your gateway's port configuration, check the [gateway port reference table](#gateway-port-reference) below. - -### Automating your gateway with systemd -Although it's not totally necessary, it's useful to have the gateway automatically start at system boot time. Here's a systemd service file to do that: - -```ini -[Unit] -Description=Nym Gateway ({{platform_release_version}}) -StartLimitInterval=350 -StartLimitBurst=10 - -[Service] -User=nym -LimitNOFILE=65536 -ExecStart=/home/nym/nym-gateway run --id supergateway -KillSignal=SIGINT -Restart=on-failure -RestartSec=30 - -[Install] -WantedBy=multi-user.target -``` - -Put the above file onto your system at `/etc/systemd/system/nym-gateway.service`. - -Change the path in `ExecStart` to point at your gateway binary (`nym-gateway`), and the `User` so it is the user you are running as. - -If you have built nym on your server, and your username is `jetpanther`, then the start command might look like this: - -`ExecStart=/home/jetpanther/nym/target/release/nym-gateway run --id your-id`. Basically, you want the full `/path/to/nym-gateway run --id whatever-your-node-id-is` - - -Then run: - -``` -systemctl enable nym-gateway.service -``` - -Start your node: - -``` -service nym-gateway start -``` - -This will cause your node to start at system boot time. If you restart your machine, the node will come back up automatically. - -You can also do `service nym-gateway stop` or `service nym-gateway restart`. - -Note: if you make any changes to your systemd script after you've enabled it, you will need to run: - -``` -systemctl daemon-reload -``` - -This lets your operating system know it's ok to reload the service configuration. - -## Gateway related Validator API endpoints -Numerous gateway related API endpoints are documented on the Validator API's [Swagger Documentation](https://validator.nymtech.net/api/swagger/index.html). There you can also try out various requests from your broswer, and download the response from the API. Swagger will also show you what commands it is running, so that you can run these from an app or from your CLI if you prefer. - -## Ports -All gateway specific port configuration can be found in `$HOME/.nym/gateways//config/config.toml`. If you do edit any port configs, remember to restart your gateway. - -### Gateway port reference -| Default port | Use | -|--------------|---------------------------| -| 1789 | Listen for Mixnet traffic | -| 9000 | Listen for Client traffic | diff --git a/documentation/docs/src/nodes/gateway.md b/documentation/docs/src/nodes/gateway.md new file mode 100644 index 0000000000..3634c36652 --- /dev/null +++ b/documentation/docs/src/nodes/gateway.md @@ -0,0 +1,11 @@ +# Gateways + +> The gateway setup and maintenance guide has migrated to the [Operator Guides book](https://nymtech.net/operators/nodes/gateway-setup.html). + +Gateways are key to both the usability of the mixnet, and the operation of the mixnet's tokenomics. They serve two main functions: +* In the future (when the mixnet is no longer running in 'free to use' mode), to check for zkNym credentials (previously referred to as [Coconut Credentials](../bandwidth-credentials.md)) with which users can anonymously prove they have paid to send traffic through the mixnet. A % of the worth of these credentials will be distributed to the operator of the gateway periodically as payment for providing their service. The more credentials user clients 'spend' with them, the higher the rewards for the gateway operator and their delegators will be. The rest of this value will be sent to the Mixmining Pool, a pool of tokens from which `NYM` rewards are distributed to mix node operators. +* Act as a mailbox for connected clients. Clients create a lasting relationship with a gateway on initialisation, binding themselves to always use a particular gateway as their ingress point for mixnet traffic (this also means that this gateway is the egress point for any traffic sent _to_ this client - see the [mixnet traffic flow page](../architecture/traffic-flow.md) and the [addressing scheme](../clients/addressing-system.md) for further details). If a client is offline and the Gateway can't deliver packets addressed to it, they will hold these packets until the client comes back online. + +## Further Reading +* [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf) section 4.2 +* [Nym Blog: Gateways to Privacy](https://blog.nymtech.net/gateways-to-privacy-51196005bf5) diff --git a/documentation/docs/src/nodes/mix-node-setup.md b/documentation/docs/src/nodes/mix-node-setup.md deleted file mode 100644 index 2f93797e1e..0000000000 --- a/documentation/docs/src/nodes/mix-node-setup.md +++ /dev/null @@ -1,564 +0,0 @@ -# Mix Nodes - -> The Nym mix node binary was built in the [building nym](../binaries/building-nym.md) section. If you haven't yet built Nym and want to run the code, go there first. - -## Current version -``` - -``` - -The `nym-mixnode` binary is currently one point version ahead of the rest of the platform binaries due to a patch applied between releases. - -## Preliminary steps - -There are a couple of steps that need completing before starting to set up your mix node: - -- preparing your wallet -- requisitioning a VPS (Virtual Private Server) - -### Wallet preparation - -#### Mainnet - -Before you initialise and run your mixnode, head to our [website](https://nymtech.net/download/) and download the Nym wallet for your operating system. If pre-compiled binaries for your operating system aren't availiable, you can build the wallet yourself with instructions [here](../wallet/desktop-wallet.md). - -If you don't already have one, please create a Nym address using the wallet, and fund it with tokens. The minimum amount required to bond a mixnode is 100 `NYM`, but make sure you have a bit more to account for gas costs. - -`NYM` can be purchased via Bity from the wallet itself with BTC or fiat, and is currently present on several exchanges. - -> Remember that you can **only** use Cosmos `NYM` tokens to bond your mixnode. You **cannot** use ERC20 representations of `NYM` to run a node. - -#### Sandbox testnet - -Make sure to download a wallet and create an account as outlined above. Then head to our [token faucet](https://faucet.nymtech.net/) and get some tokens to use to bond it. - -### VPS Hardware Specs - -You will need to rent a VPS to run your mix node on. One key reason for this is that your node **must be able to send TCP data using both IPv4 and IPv6** (as other nodes you talk to may use either protocol). - -For the moment, we haven't put a great amount of effort into optimizing concurrency to increase throughput, so don't bother provisioning a beastly server with multiple cores. This will change when we get a chance to start doing performance optimizations in a more serious way. Sphinx packet decryption is CPU-bound, so once we optimise, more fast cores will be better. - -For now, see the below rough specs: - -- Processors: 2 cores are fine. Get the fastest CPUs you can afford. -- RAM: Memory requirements are very low - typically a mix node may use only a few hundred MB of RAM. -- Disks: The mixnodes require no disk space beyond a few bytes for the configuration files. - -## Mix node setup - -Now that you have built the codebase, set up your wallet, and have a VPS with the `nym-mixnode` binary, you can set up your mix node with the instructions below. - -### Viewing command help - -You can check that your binaries are properly compiled with: - -``` -./nym-mixnode --help -``` - -Which should return a list of all avaliable commands. - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -You can also check the various arguments required for individual commands with: - -``` -./nym-mixnode --help -``` - -### Initialising your mix node - -To check available configuration options for initializing your node use: - -``` -./nym-mixnode init --help -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -Initalise your mixnode with the following command, replacing the value of `--id` with the moniker you wish to give your mixnode, and the `--wallet-address` with the Nym address you created earlier. Your `--host` must be publicly routable on the internet in order to mix packets, and can be either an Ipv4 or IPv6 address. The `$(curl ifconfig.me)` command returns your IP automatically using an external service. If you enter your IP address manually, enter it **without** any port information. - -``` -./nym-mixnode init --id winston-smithnode --host $(curl ifconfig.me) --wallet-address n1eufxdlgt0puwrwptgjfqne8pj4nhy2u5ft62uq -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -> The `init` command will refuse to destroy existing mix node keys. - -During the `init` process you will have the option to change the `http_api`, `verloc` and `mixnode` ports from their default settings. If you wish to change these in the future you can edit their values in the `config.toml` file created by the initialization process, which is located at `~/.nym/mixnodes//`. - -### Bonding your mix node - -```admonish caution -From `v1.1.3`, if you unbond your mixnode that means you are leaving the mixnet and you will lose all your delegations (permanently). You can join again with the same identity key, however, you will start with **no delegations**. -``` - -#### Bond via the Desktop wallet (recommended) - -You can bond your mix node via the Desktop wallet. - -* Open your wallet, and head to the `Bond` page, then select the node type and input your node details. Press `continue` - -* You will be asked to run a the `sign` command with your `gateway` - copy and paste the long signature as the value of `--contract-msg` and run it. It will look something like this: - -~~~admonish example collapsible=true title="Console output" -``` -./nym-mixnode sign --id upgrade_test --contract-msg 5XrvVEMzRJk2AcT2h1o6ErZNb8z1ZzD3h7teipBW3NUtrtYq7vu4DRMgzZRTPVPnyr2YWCxpmKCMFaEXvksnJ -4jt7np3NMLxsLMrFjEBhh67Crtjy4868vCzAivUqzdc365RiqxQQKtv4r9eTk9mTbE9JY8U3TxzKJCSGcBqbrb9JX3HrZVWm6tqbUYbsnku9pqnfeyeUiaYKY44Lm72TYrkZfRrMAZLMATiXT1ntmiKqT37HzRxNZjiH8qHeQEoRHkgDsmXDXRbfppGTpPrN7R4sjynJzehzUBZ8Ug7ovT9FoAHb8kuVQhUiMs1js6tdwtthzQMbPi9vwxUtVvjYknN2fnJgMnckEhzJJpJDCNdH7YhpPaWQnGVVS334mskiuqkbRVrFPJN2nnwArHr3L2cLxSMk9toKfw7ViKJ2p5E5JxiSmKY1cFGZ7uRLsuQ833PJN9JE8crPtkBNefqkbFNz68S5jPmzUShSvAc4TqXKeovDASFmmhKaPqLUrfsSWm7nzuKnzJSMADF6xSuwr9cknMoirqkRkLe7ybJ2ERwSdf5cUxMjF7yjS8tW9hZudnTUb1uPNDuSmPPVrCR12XZyFzBvVgxH51ZNJTym46nqnfA881LQcmFMnCwJf39rVJ4ASLnzEzmuwXj75QoB9ce9kiLmoBNLYe4QKSB6gDd858VnBtBNQELVuCCZbrTYuSCeNdUFhvMwD4kryc1pBYUa8Ro81F3QVfiKN - - _ __ _ _ _ __ ___ - | '_ \| | | | '_ \ _ \ - | | | | |_| | | | | | | - |_| |_|\__, |_| |_| |_| - |___/ - - (nym-mixnode - version {{mix_node_release_version}}) - - ->>> attempting to sign 5XrvVEMzRJk2AcT2h1o6ErZNb8z1ZzD3h7teipBW3NUtrtYq7vu4DRMgzZRTPVPnyr2YWCxpmKCMFaEXvksnJ4jt7np3NMLxsLMrFjEBhh67Crtjy4868vCzAivUqzdc365RiqxQQKtv4r9eTk9mTbE9JY8U3TxzKJCSGcBqbrb9JX3HrZVWm6tqbUYbsnku9pqnfeyeUiaYKY44Lm72TYrkZfRrMAZLMATiXT1ntmiKqT37HzRxNZjiH8qHeQEoRHkgDsmXDXRbfppGTpPrN7R4sjynJzehzUBZ8Ug7ovT9FoAHb8kuVQhUiMs1js6tdwtthzQMbPi9vwxUtVvjYknN2fnJgMnckEhzJJpJDCNdH7YhpPaWQnGVVS334mskiuqkbRVrFPJN2nnwArHr3L2cLxSMk9toKfw7ViKJ2p5E5JxiSmKY1cFGZ7uRLsuQ833PJN9JE8crPtkBNefqkbFNz68S5jPmzUShSvAc4TqXKeovDASFmmhKaPqLUrfsSWm7nzuKnzJSMADF6xSuwr9cknMoirqkRkLe7ybJ2ERwSdf5cUxMjF7yjS8tW9hZudnTUb1uPNDuSmPPVrCR12XZyFzBvVgxH51ZNJTym46nqnfA881LQcmFMnCwJf39rVJ4ASLnzEzmuwXj75QoB9ce9kiLmoBNLYe4QKSB6gDd858VnBtBNQELVuCCZbrTYuSCeNdUFhvMwD4kryc1pBYUa8Ro81F3QVfiKN ->>> decoding the message... ->>> message to sign: {"nonce":0,"algorithm":"ed25519","message_type":"mixnode-bonding","content":{"sender":"n1eufxdlgt0puwrwptgjfqne8pj4nhy2u5ft62uq","proxy":null,"funds":[{"denom":"unym","amount":"100000000"}],"data":{"mix_node":{"host":"62.240.134.189","mix_port":1789,"verloc_port":1790,"http_api_port":8000,"sphinx_key":"CfZSy1jRfrfiVi9JYexjFWPqWkKoY72t7NdpWaq37K8Z","identity_key":"DhmUYedPZvhP9MMwXdNpPaqCxxTQgjAg78s2nqtTTiNF","version":"1.1.14"},"cost_params":{"profit_margin_percent":"0.1","interval_operating_cost":{"denom":"unym","amount":"40000000"}}}}} -``` -~~~ - -* Copy the resulting signature: - -``` ->>> The base58-encoded signature is: -2GbKcZVKFdpi3sR9xoJWzwPuGdj3bvd7yDtDYVoKfbTWdpjqAeU8KS5bSftD5giVLJC3gZiCg2kmEjNG5jkdjKUt -``` - -* And paste it into the wallet nodal, then confirm the transaction. - -![Paste Signature](../images/wallet-sign.png) - -* Your node will now be bonded and ready to mix at the beginning of the next epoch (at most 1 hour). - -> You are asked to `sign` a transaction on bonding so that the mixnet smart contract is able to map your nym address to your node. This allows us to create a nonce for each account and defend against replay attacks. - -#### Bond via the CLI (power users) -If you want to bond your mix node via the CLI, then check out the [relevant section in the Nym CLI](../tools/nym-cli.md#bond-a-mix-node) docs. - -### Running your mix node - -Now you've bonded your mix node, run it with: - -``` -./nym-mixnode run --id winston-smithnode -``` - -If everything worked, you'll see your node running on the either the [Sandbox testnet network explorer](https://sandbox-explorer.nymtech.net) or the [mainnet network explorer](https://explorer.nymtech.net), depending on which network you're running. - -Note that your node's public identity key is displayed during startup, you can use it to identify your node in the list. - -Keep reading to find out more about configuration options or troubleshooting if you're having issues. There are also some tips for running on AWS and other cloud providers, some of which require minor additional setup. - -Also have a look at the saved configuration files in `$HOME/.nym/mixnodes/` to see more configuration options. - -### Describe your mix node (optional) - -In order to easily identify your node via human-readable information later on in the development of the testnet when delegated staking is implemented, you can `describe` your mixnode with the following command: - -``` -./nym-mixnode describe --id winston-smithnode -``` - -> Remember to restart your mix node process in order for the new description to be propogated. - -### Upgrading your mix node - -Upgrading your node is a two-step process: -* Updating the binary and `config.toml` on your VPS -* Updating the node information in the [mixnet smart contract](../nyx/mixnet-contract.md). **This is the information that is present on the [mixnet explorer](https://explorer.nymtech.net)**. - -#### Step 1: upgrading your binary -Follow these steps to upgrade your mix node binary and update its config file: -* pause your mix node process. -* replace the existing binary with the newest binary (which you can either compile yourself or grab from our [releases page](https://github.com/nymtech/nym/releases)). -* 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. - -#### 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). - -You can either do this graphically via the Desktop Wallet, or the CLI. - -#### Updating node information via the Desktop Wallet -* Navigate to the `Bonding` page and click the `Node Settings` link in the top right corner: -![Bonding page](../images/wallet-screenshots/bonding.png) - -* Update the fields in the `Node Settings` page and click `Submit changes to the blockchain`. -![Node Settings Page](../images/wallet-screenshots/node_settings.png) - -#### Updating node information via the CLI -If you want to bond your mix node via the CLI, then check out the [relevant section in the Nym CLI](../tools/nym-cli.md#upgrade-a-mix-node) docs. - -### Displaying mix node information - -You can always check the details of your mix node with the `node-details` command: - -``` -./nym-mixnode node-details --id winston-smithnode -``` - -## VPS Setup and Automation -### Configure your firewall -The following commands will allow you to set up a firewall using `ufw`. - -``` -# check if you have ufw installed -ufw version -# if it is not installed, install with -sudo apt install ufw -y -# enable ufw -sudo ufw enable -# check the status of the firewall -sudo ufw status -``` - -Finally open your mix node's p2p port, as well as ports for ssh and ports `8000` and `1790` for verloc and measurement pings: - -``` -sudo ufw allow 1789,1790,8000,22/tcp -# check the status of the firewall -sudo ufw status -``` - -For more information about your mix node's port configuration, check the [mix node port reference table](./mix-node-setup.md#mixnode-port-reference) below. - -### Automating your mix node with systemd - -It's useful to have the mix node automatically start at system boot time. Here's a systemd service file to do that: - -```ini -[Unit] -Description=Nym Mixnode ({{mix_node_release_version}}) -StartLimitInterval=350 -StartLimitBurst=10 - -[Service] -User=nym -LimitNOFILE=65536 -ExecStart=/home/nym/nym-mixnode run --id mix0100 -KillSignal=SIGINT -Restart=on-failure -RestartSec=30 - -[Install] -WantedBy=multi-user.target -``` - -Put the above file onto your system at `/etc/systemd/system/nym-mixnode.service`. - -Change the path in `ExecStart` to point at your mix node binary (`nym-mixnode`), and the `User` so it is the user you are running as. - -If you have built nym on your server, and your username is `jetpanther`, then the start command might look like this: - -`ExecStart=/home/jetpanther/nym/target/release/nym-mixnode run --id your-id`. Basically, you want the full `/path/to/nym-mixnode run --id whatever-your-node-id-is` - -Then run: - -``` -systemctl enable nym-mixnode.service -``` - -Start your node: - -``` -service nym-mixnode start -``` - -This will cause your node to start at system boot time. If you restart your machine, the node will come back up automatically. - -You can also do `service nym-mixnode stop` or `service nym-mixnode restart`. - -Note: if you make any changes to your systemd script after you've enabled it, you will need to run: - -``` -systemctl daemon-reload -``` - -This lets your operating system know it's ok to reload the service configuration. - -#### Setting the ulimit - -Linux machines limit how many open files a user is allowed to have. This is called a `ulimit`. - -`ulimit` is 1024 by default on most systems. It needs to be set higher, because mix nodes make and receive a lot of connections to other nodes. - -If you see errors such as: - -``` -Failed to accept incoming connection - Os { code: 24, kind: Other, message: "Too many open files" } -``` - -This means that the operating system is preventing network connections from being made. - -##### Set the ulimit via `systemd` service file - -Query the `ulimit` of your mix node with: - -``` -grep -i "open files" /proc/$(ps -A -o pid,cmd|grep nym-mixnode | grep -v grep |head -n 1 | awk '{print $1}')/limits -``` - -You'll get back the hard and soft limits, which looks something like this: - -``` -Max open files 65536 65536 files -``` - -If your output is **the same as above**, your node will not encounter any `ulimit` related issues. - -However if either value is `1024`, you must raise the limit via the systemd service file. Add the line: - -``` -LimitNOFILE=65536 -``` - -Reload the daemon: - -``` -systemctl daemon-reload -``` - -or execute this as root for system-wide setting of `ulimit`: - -``` -echo "DefaultLimitNOFILE=65535" >> /etc/systemd/system.conf -``` - -Reboot your machine and restart your node. When it comes back, use `cat /proc/$(pidof nym-mixnode)/limits | grep "Max open files"` to make sure the limit has changed to 65535. - -##### Set the ulimit on `non-systemd` based distributions - -Edit `etc/security/conf` and add the following lines: - -``` -# Example hard limit for max opened files -username hard nofile 4096 -# Example soft limit for max opened files -username soft nofile 4096 -``` - -Then reboot your server and restart your mixnode. - -## Node Description -Node description is a short text that describes your node. It is displayed in the `nym-mixnode list` command and in the `nym-mixnode node-details` command. it also shows up in the node explorer to let people know what your node is about and link to your website. - -To set your node description, create a file called `description.toml` and put it in the same directory as your `config.toml` file. The file should look like this: - -```toml -name = "Winston Smith" -description = "I am the Sphinx" -link = "https://nymtech.net" -location = "Giza, Egypt" -``` - -you will need to restart your node for the changes to take effect. - -## Node Families - -Node family involves setting up a group of mix nodes that work together to provide greater privacy and security for network communications. This is achieved by having the nodes in the family share information and routes, creating a decentralized network that makes it difficult for third parties to monitor or track communication traffic. - -### Create a Node Family - -To create a Node family, you will need to install and configure multiple mix nodes, and then use the CLI to link them together into a family. Once your Node family is up and running, you can use it to route your network traffic through a series of nodes, obscuring the original source and destination of the communication. - -You can use either `nym-cli` which can be downloaded from the [release page](https://github.com/nymtech/nym/releases) or compiling `nyxd`. - - -`/path/to/the/release` and run the following on the family head to obtain the signature for the member: - -``` -./nym-mixnode sign --id winston-smithnode --text -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -Using `nym-cli`: - -> `--mnemonic` is the mnemonic of the member wanting to be the head of family. - -``` -/nym-cli cosmwasm execute '{"create_family": {"signature": "","family_head": "","owner_signature":"","label": ""}}' --mnemonic -``` - -Using `nyxd`: - -> `--from` is mnemonic of the member wanting to join the family. - -``` -./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block -``` - -To get the node owner signature, use: - -`./nym-mixnode node-details --id ` - -### Joining a Node Family - -`/path/to/the/release` and run the following on the family head to obtain the signature for the member: - -``` -./nym-mixnode sign --id mixnode --text -``` - -~~~admonish example collapsible=true title="Console output" -``` - -``` -~~~ - -Using `nym-cli`: - -``` -./nym-cli cosmwasm execute '{"join_family": {"signature": "","family_head": "","owner_signautre": "", "label":""}}' --mnemonic -``` - -Using `nyxd`: - -``` -./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block -``` - - -To get the node owner signature, use: - -`./nym-mixnode node-details --id ` - - -### Leaving a family -If wanting to leave, run the same initial command as above, followed by: - -Using `nym-cli`: - -``` -./nym-cli cosmwasm execute n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr '{"leave_family": {"signature": "","family_head": "","owner_signautre": ""}}' --mnemonic -``` - -Using `nyxd`: - -``` -./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block -``` - -## Checking that your node is mixing correctly -### Network explorers -Once you've started your mix node and it connects to the validator, your node will automatically show up in the 'Mix nodes' section of either the Nym Network Explorers: - -- [Mainnet](https://explorer.nymtech.net/overview) -- [Sandbox testnet](https://sandbox-explorer.nymtech.net/) - -Enter your **identity key** to find your node. There are numerous statistics about your node on that page that are useful for checking your uptime history, packets mixed, and any delegations your node may have. - -There are also 2 community explorers which have been created by [Nodes Guru](https://nodes.guru): - -- [Mainnet](https://mixnet.explorers.guru/) -- [Sandbox testnet](https://sandbox.mixnet.explorers.guru/) - -For more details see [Troubleshooting FAQ](../nodes/troubleshooting.md) - -### Virtual IPs and hosting via Google & AWS -On some services (AWS, Google, etc), the machine's available bind address is not the same as the public IP address. In this case, bind `--host` to the local machine address returned by `ifconfig`, but also specify `--announce-host` with the public IP. Please make sure that you pass the correct, routable `--announce-host`. - -For example, on a Google machine, you may see the following output from the `ifconfig` command: - -``` -ens4: flags=4163 mtu 1460 - inet 10.126.5.7 netmask 255.255.255.255 broadcast 0.0.0.0 - ... -``` - -The `ens4` interface has the IP `10.126.5.7`. But this isn't the public IP of the machine, it's the IP of the machine on Google's internal network. Google uses virtual routing, so the public IP of this machine is something else, maybe `36.68.243.18`. - -`nym-mixnode init --host 10.126.5.7`, initalises the mix node, but no packets will be routed because `10.126.5.7` is not on the public internet. - -Trying `nym-mixnode init --host 36.68.243.18`, you'll get back a startup error saying `AddrNotAvailable`. This is because the mix node doesn't know how to bind to a host that's not in the output of `ifconfig`. - -The right thing to do in this situation is `nym-mixnode init --host 10.126.5.7 --announce-host 36.68.243.18`. - -This will bind the mix node to the available host `10.126.5.7`, but announce the mix node's public IP to the directory server as `36.68.243.18`. It's up to you as a node operator to ensure that your public and private IPs match up properly. - -## Metrics / API endpoints -The mix node binary exposes several API endpoints that can be pinged in order to gather information about the node, and the Nym API (previously 'Validator API') exposes numerous mix node related endpoints which provide network-wide information about mix nodes, the network topology (the list of avaliable mix nodes for packet routing), and information regarding uptime monitoring and rewarding history. - -### Mix node API endpoints -Since the mix node binary exposes several API endpoints itself, you can ping these easily via curl: - -| Endpoint | Description | Command | -| -------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -| `/description` | Returns the description of the node set with the `describe` command | `curl :8000/description` | -| `/hardware` | Returns the hardware information of the node | `curl :8000/hardware` | -| `/verloc` | Returns the verloc information of the node, updated every 12 hours | `curl :8000/verloc` | - -The code for exposed API endpoints can be found [here](https://github.com/nymtech/nym/tree/release/{{platform_release_version}}/mixnode/src/node/http). - -> You can get more detailed info by appending `?debug` to the URL, like so: `curl http://:8000/stats?debug` - -### Mix node related Nym API (previously 'Validator API') endpoints -Numerous endpoints are documented on the Nym API (previously 'Validator API')'s [Swagger Documentation](https://validator.nymtech.net/api/swagger/index.html). There you can also try out various requests from your broswer, and download the response from the API. Swagger will also show you what commands it is running, so that you can run these from an app or from your CLI if you prefer. - -#### Mix node Reward Estimation API endpoint - -The Reward Estimation API endpoint allows mix node operators to estimate the rewards they could earn for running a Nym mixnode with a specific `mix_id`. - -> The `{mix_id}` can be found in the "Mix ID" column of the [Network Explorer](https://explorer.nymtech.net/network-components/mixnodes/active). - -The endpoint is a particularly common for mix node operators as it can provide an estimate of potential earnings based on factors such as the amount of traffic routed through the mixnode, the quality of the mix node's performance, and the overall demand for mix nodes in the network. This information can be useful for mix node operators in deciding whether or not to run a mix node and in optimizing its operations for maximum profitability. - -Using this API endpoint returns information about the Reward Estimation: - -``` -/status/mixnode/{mix_id}/reward-estimation -``` - -Query Response: - -``` - "estimation": { - "total_node_reward": "942035.916721770541325331", - "operator": "161666.263307386408152071", - "delegates": "780369.65341438413317326", - "operating_cost": "54444.444444444444444443" - }, -``` - -> The unit of value is measured in `uNYM`. - -- `estimated_total_node_reward` - An estimate of the total amount of rewards that a particular mix node can expect to receive during the current epoch. This value is calculated by the Nym Validator based on a number of factors, including the current state of the network, the number of mix nodes currently active in the network, and the amount of network traffic being processed by the mix node. - -- `estimated_operator_reward` - An estimate of the amount of rewards that a particular mix node operator can expect to receive. This value is calculated by the Nym Validator based on a number of factors, including the amount of traffic being processed by the mix node, the quality of service provided by the mix node, and the operator's stake in the network. - -- `estimated_delegators_reward` - An estimate of the amount of rewards that mix node delegators can expect to receive individually. This value is calculated by the Nym Validator based on a number of factors, including the amount of traffic being processed by the mix node, the quality of service provided by the mix node, and the delegator's stake in the network. - -- `estimated_node_profit` - An estimate of the profit that a particular mix node operator can expect to earn. This value is calculated by subtracting the mix node operator's `operating_costs` from their `estimated_operator_reward` for the current epoch. - -- `estimated_operator_cost` - An estimate of the total cost that a particular mix node operator can expect to incur for their participation. This value is calculated by the Nym Validator based on a number of factors, including the cost of running a mix node, such as server hosting fees, and other expenses associated with operating the mix node. - -## Ports -All mix node-specific port configuration can be found in `$HOME/.nym/mixnodes//config/config.toml`. If you do edit any port configs, remember to restart your mix node. - -### Mix node port reference -| Default port | Use | -| ------------ | ------------------------- | -| `1789` | Listen for mixnet traffic | -| `1790` | Listen for VerLoc traffic | -| `8000` | Metrics http API endpoint | diff --git a/documentation/docs/src/nodes/mixnode.md b/documentation/docs/src/nodes/mixnode.md new file mode 100644 index 0000000000..b093c6f0b4 --- /dev/null +++ b/documentation/docs/src/nodes/mixnode.md @@ -0,0 +1,17 @@ +# Mix Nodes + +> The mix node setup and maintenance guide has migrated to the [Operator Guides book](https://nymtech.net/developers/nodes/mix-node-setup.html). + +Mix nodes are the backbone of the mixnet. These are the nodes that perform 'mix mining', otherwise known simply as 'mixing'. + +Mix nodes, after receiving a packet, decrypt its outer 'layer' and hold it for a variable amount of time before passing it to its next destination - either another mix node, or a gateway. In doing so, they 'mix' packets by sending them to their next destination in a different order than they were received. + +Mix nodes are rewarded according to their quality of service, and the probability of their inclusion in the active set (i.e. the nodes that mix traffic for the next epoch) is also affected by this (as well as their delegation-based reputation - see the [Mix node deepdive](#further-reading) below for more on this). + +## (Coming soon) Mixing: a Step-by-Step Breakdown + +## Further reading +* [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf) section 4 +* [Nym Blog: Mix node deepdive](https://blog.nymtech.net/nym-mixnodes-deep-dive-d2b91917f097) +* [Mixnet Traffic Flow overview](../architecture/traffic-flow.md) +* [Reward Sharing for Mixnets](https://nymtech.net/nym-cryptoecon-paper.pdf) \ No newline at end of file diff --git a/documentation/docs/src/nodes/network-requester.md b/documentation/docs/src/nodes/network-requester.md new file mode 100644 index 0000000000..ac083a3143 --- /dev/null +++ b/documentation/docs/src/nodes/network-requester.md @@ -0,0 +1,22 @@ +# Network Requester + +> The network requester setup and maintenance guide has moved to the [Operator Guides book](https://nymtech.net/developers/nodes/network-requester-setup.html). + +Network requesters are the first instance of the catch-all term 'service', or 'service providers'. In essence, think of services as being the part of the mixnet infrastructure that let you _do_ something, such as access emails, messaging service backends, or blockchains via the mixnet. + +## Domain filtering +Network requesters, in essence, act as a form of proxy (somewhat analagous to a Tor exit node). 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. + +By default the network requester is **not** an open proxy (although it can be used as one). It uses a whitelist for outbound requests. + +Any request to a URL which is not on this local list (modified by the node operator) or [Nym's default list](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) will be blocked. + +This default whitelist is useful for knowing that the majority of Network requesters are able to support certain apps 'out of the box', and the local whitelist allows operators to include their own whitelisted domains. + +> Substantial changes are on the horizon concerning how Network Requesters manage incoming requests - if you are an operator and have experience running software such as Tor exit nodes or p2p nodes get in touch via our [Matrix server](https://matrix.to/#/#dev:nymtech.chat). + +## (Coming soon) Consuming credentials for anonymous service payment + +## Further reading +* [Nym Blog: Network Requester deepdive](https://blog.nymtech.net/tech-deepdive-network-requesters-e5359a6cc31c) +* [Nym Blog: Choose Your Character](https://blog.nymtech.net/choose-your-character-an-overview-of-nym-network-actors-19e6a9808540) \ No newline at end of file diff --git a/documentation/docs/src/nodes/overview.md b/documentation/docs/src/nodes/overview.md new file mode 100644 index 0000000000..497a347365 --- /dev/null +++ b/documentation/docs/src/nodes/overview.md @@ -0,0 +1,9 @@ +# Node Types + +> This section was previously the node setup guides. These have been migrated to their own [**Operator Guides**](https://nymtech.net/operators) book. +> +> For setup and maintenance guides, go to the Operators book linked above. +> +> This section is a little spartan for the moment, but we will be adding detailed information about how exactly each node functions, as well as references to any literature and technical specs in the near future. + +This section contains information on the different node types of the mixnet and blockchain. \ No newline at end of file diff --git a/documentation/docs/src/nodes/setup-guides.md b/documentation/docs/src/nodes/setup-guides.md deleted file mode 100644 index 02a23ebff6..0000000000 --- a/documentation/docs/src/nodes/setup-guides.md +++ /dev/null @@ -1,8 +0,0 @@ -# Node Setup Guides - -This section contains setup guides for the following node types: -* [Mix node](./mix-node-setup.md) -* [Gateway](./gateway-setup.md) -* [Network Requester](./network-requester-setup.md) -* [Validator](./validator-setup.md) - diff --git a/documentation/docs/src/nodes/validator.md b/documentation/docs/src/nodes/validator.md new file mode 100644 index 0000000000..855cf21700 --- /dev/null +++ b/documentation/docs/src/nodes/validator.md @@ -0,0 +1,12 @@ +# Validators + +> The validator setup and maintenance guide has moved to the [Operator Guides book](https://nymtech.net/operators/nodes/validator-setup.html). + +Validators secure the Nyx blockchain via Proof of Stake consensus. The Nyx blockchain records the ledger of `NYM` transactions and executes the smart contracts for distributing `NYM` rewards. The Nyx validators are run via the `nyxd` binary ([codebase](https://github.com/nymtech/nyxd)), maintaining a CosmWasm- and IBC-enabled blockchain. + +The blockchain plays a supporting but fundamental role in the mixnet: the `NYM` token used to incentivise node operators is one of two native tokens of the chain, and the chain is where the [Mixnet](../nyx/mixnet-contract.md) and [Vesting](../nyx/vesting-contract.md) smart contracts are deployed. + +## Further Reading +* Detailed info on Nyx Validators and token flow can be found in [Nym Reward Sharing for Mixnets document](https://nymtech.net/nym-cryptoecon-paper.pdf) in section 2.3 and 2.4. +* Our [quarterly update](https://blog.nymtech.net/quarterly-token-economic-parameter-update-b2862948710f) on token economics from July 2023. +* [Nym Whitepaper](https://nymtech.net/nym-whitepaper.pdf) section 3.1 \ No newline at end of file diff --git a/documentation/docs/src/not-found.md b/documentation/docs/src/not-found.md new file mode 100644 index 0000000000..af87d6dd59 --- /dev/null +++ b/documentation/docs/src/not-found.md @@ -0,0 +1,11 @@ +# Page Not Found + +You seem to have followed a link to a page that no longer exists. Our documentation is changing and growing over time, so this page has most likely been moved somewhere else. + +For node setup guides, see the [Operator Guides book](https://nymtech.net/operators). + +For developer tutorials, app guides, and demo and community apps, see the [Developer Portal](https://nymtech.net/developers). + +If you are looking for information on network architecture traffic flow, clients, the SDKs, or interacting with the Nyx blockchain, check the sitemap on the left hand side of the screen. + +If you still can't find what you're looking for get in touch with us via [Matrix](https://matrix.to/#/#nym-community:nymtech.chat). \ No newline at end of file diff --git a/documentation/docs/src/nyx/ledger-live.md b/documentation/docs/src/nyx/ledger-live.md index f7559b6fd2..031834fa24 100644 --- a/documentation/docs/src/nyx/ledger-live.md +++ b/documentation/docs/src/nyx/ledger-live.md @@ -4,7 +4,7 @@ Use the following instructions to interact with the Nyx blockchain - either with ## Prerequisites * Download and install [Ledger Live](https://www.ledger.com/ledger-live). -* Compile the `nyxd` binary as per the instructions [here](../nodes/validator-setup.md#building-the-nym-validator). Stop after you can successfully run `nyxd` and get the helptext in your console output. +* Compile the `nyxd` binary as per the instructions [here](https://nymtech.net/operators/nodes/validator-setup.md#building-the-nym-validator). Stop after you can successfully run `nyxd` and get the helptext in your console output. ## Prepare your Ledger App * Plug in your Ledger device diff --git a/documentation/docs/src/nyx/rpc-node.md b/documentation/docs/src/nyx/rpc-node.md index af76f1f927..dc48d68409 100644 --- a/documentation/docs/src/nyx/rpc-node.md +++ b/documentation/docs/src/nyx/rpc-node.md @@ -4,6 +4,6 @@ RPC Nodes (which might otherwise be referred to as 'Lite Nodes' or just 'Full No You may want to set up an RPC Node for querying the blockchain, or in order to have an endpoint that your app can use to send transactions. -In order to set up an RPC Node, simply follow the instructions to set up a [Validator](../nodes/validator-setup.md), but **exclude the `nyxd tx staking create-validator` command**. +In order to set up an RPC Node, simply follow the instructions to set up a [Validator](https://nymtech.net/operators/nodes/validator-setup.md), but **exclude the `nyxd tx staking create-validator` command**. If you want to fast-sync your node, check out the Polkachu snapshot and their other [resources](https://polkachu.com/seeds/nym). \ No newline at end of file diff --git a/documentation/docs/src/sdk/rust.md b/documentation/docs/src/sdk/rust.md index 68004b7576..c53fde7b01 100644 --- a/documentation/docs/src/sdk/rust.md +++ b/documentation/docs/src/sdk/rust.md @@ -22,13 +22,19 @@ In the future the SDK will be made up of several components, each of which will | Coconut | Create & verify Coconut credentials | 🛠️ | | Validator | Sign & broadcast Nyx blockchain transactions, query the blockchain | ❌ | -The `mixnet` component currently exposes the logic of two clients: the websocket client, and the socks client. +The `mixnet` component currently exposes the logic of two clients: the [websocket client](../clients/websocket-client.md), and the [socks](../clients/socks5-client.md) client. The `coconut` component is currently being worked on. Right now it exposes logic allowing for the creation of coconut credentials on the Sandbox testnet. ## Websocket client examples > All the codeblocks below can be found in the `nym-sdk` [examples directory](https://github.com/nymtech/nym/tree/release/{{platform_release_version}}/sdk/rust/nym-sdk/examples) in the monorepo. Just navigate to `nym/sdk/rust/nym-sdk/examples/` and run the files from there. If you wish to run these outside of the workspace - such as if you want to use one as the basis for your own project - then make sure to import the `sdk`, `tokio`, and `nym_bin_common` crates. +### Different message types +There are two methods for sending messages through the mixnet using your client: +* `send_plain_message()` is the most simple: pass the recipient address and the message you wish to send as a string (this was previously `send_str()`). This is a nicer-to-use wrapper around `send_message()`. +* `send_message()` allows you to also define the amount of SURBs to send along with your message (which is sent as bytes). + +### Simple example Lets look at a very simple example of how you can import and use the websocket client in a piece of Rust code (`examples/simple.rs`): ```rust,noplayground @@ -46,6 +52,26 @@ The example above involves ephemeral keys - if we want to create and then mainta As seen in the example above, the `mixnet::MixnetClientBuilder::new()` function handles checking for keys in a storage location, loading them if present, or creating them and storing them if not, making client key management very simple. +Assuming our client config is stored in `/tmp/mixnet-client`, the following files are generated: +``` +$ tree /tmp/mixnet-client + +mixnet-client +├── ack_key.pem +├── db.sqlite +├── db.sqlite-shm +├── db.sqlite-wal +├── gateway_details.json +├── gateway_shared.pem +├── persistent_reply_store.sqlite +├── private_encryption.pem +├── private_identity.pem +├── public_encryption.pem +└── public_identity.pem + +1 directory, 11 files +``` + ### Manually handling storage If you're integrating mixnet functionality into an existing app and want to integrate saving client configs and keys into your existing storage logic, you can manually perform the actions taken automatically above (`examples/manually_handle_keys_and_config.rs`) @@ -54,7 +80,7 @@ If you're integrating mixnet functionality into an existing app and want to inte ``` ### Anonymous replies with SURBs -Both functions used to send messages through the mixnet (`send_str` and `send_bytes`) send a pre-determined number of SURBs along with their messages by default. +Both functions used to send messages through the mixnet (`send_message` and `send_plain_message`) send a pre-determined number of SURBs along with their messages by default. The number of SURBs is set [here](https://github.com/nymtech/nym/blob/release/{{platform_release_version}}/sdk/rust/nym-sdk/src/mixnet/client.rs#L34): @@ -64,19 +90,19 @@ The number of SURBs is set [here](https://github.com/nymtech/nym/blob/release/{{ You can read more about how SURBs function under the hood [here](../architecture/traffic-flow.md#private-replies-using-surbs). -In order to reply to an incoming message using SURBs, you can construct a `recipient` from the `sender_tag` sent along with the message you wish to reply to: +In order to reply to an incoming message using SURBs, you can construct a `recipient` from the `sender_tag` sent along with the message you wish to reply to: ```rust,noplayground {{#include ../../../../sdk/rust/nym-sdk/examples/surb-reply.rs}} ``` ### Importing and using a custom network topology -If you want to send traffic through a sub-set of nodes (for instance, ones you control, or a small test setup) when developing, debugging, or peforming research, you will need to import these nodes as a custom network topology, instead of grabbing it from the [`Mainnet Nym-API`](https://validator.nymtech.net/api/swagger/index.html) (`examples/custom_topology_provider.rs`). +If you want to send traffic through a sub-set of nodes (for instance, ones you control, or a small test setup) when developing, debugging, or performing research, you will need to import these nodes as a custom network topology, instead of grabbing it from the [`Mainnet Nym-API`](https://validator.nymtech.net/api/swagger/index.html) (`examples/custom_topology_provider.rs`). There are two ways to do this: #### Import a custom Nym API endpoint -If you are also running a Validator and Nym API for your network, you can specify that endpoint as such and interact with it as clients ususally do (under the hood): +If you are also running a Validator and Nym API for your network, you can specify that endpoint as such and interact with it as clients usually do (under the hood): ```rust,noplayground {{#include ../../../../sdk/rust/nym-sdk/examples/custom_topology_provider.rs}} @@ -89,6 +115,13 @@ If you aren't running a Validator and Nym API, and just want to import a specifi {{#include ../../../../sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs}} ``` +### Send and receive in different tasks +If you need to split the different actions of your client across different tasks, you can do so like this: + +```rust, noplayground +{{#include ../../../../sdk/rust/nym-sdk/examples/parallel_sending_and_receiving.rs}} +``` + ## Socks client example There is also the option to embed the [`socks5-client`](../clients/socks5-client.md) into your app code (`examples/socks5.rs`): diff --git a/documentation/docs/src/sdk/typescript.md b/documentation/docs/src/sdk/typescript.md index eed1167692..5ce939e858 100644 --- a/documentation/docs/src/sdk/typescript.md +++ b/documentation/docs/src/sdk/typescript.md @@ -47,7 +47,7 @@ There are multiple example projects in [`nym/sdk/typescript/examples/`](https:// The best place to start if you just want to quickly get a basic frontend up and running with which to experiment is `examples/plain-html`: ```typescript -{{#include ../../../../sdk/typescript/examples/plain-html/src/index.ts}} +{{#include ../../../../sdk/typescript/examples/chat-app/plain-html/src/index.ts}} ``` As you can see, all that is required to create an ephemeral keypair and connect to the mixnet is creating a client and then subscribing to the mixnet events coming down the websocket, and adding logic to deal with them. diff --git a/documentation/docs/src/tools/nym-cli.md b/documentation/docs/src/tools/nym-cli.md index f516190002..b6ed36bfa7 100644 --- a/documentation/docs/src/tools/nym-cli.md +++ b/documentation/docs/src/tools/nym-cli.md @@ -4,14 +4,14 @@ This is a CLI tool for interacting with: * the Nyx blockchain (account management, querying the chain state, etc) -* the smart contracts deployed on Nyx (bonding and unbonding mixnodes, collecting rewards, etc) +* the smart contracts deployed on Nyx (bonding and un-bonding mixnodes, collecting rewards, etc) It provides a convenient wrapper around the `nymd` client, and has similar functionality to the `nyxd` binary for querying the chain or executing smart contract methods. ## Building The `nym-cli` binary can be built by running `cargo build --release` in the `nym/tools/nym-cli` directory. -### Useage +### Usage You can see all available commands with: ``` @@ -225,9 +225,9 @@ Bonding a mix node is a process that takes a few steps due to the need to sign a ./nym-cli --mnemonic mixnet operators gateway bond --amount 100000000 --mix-port 1789 --version "1.1.13" --host "85.163.111.99" --identity-key "B6pWscxYb8sPAdKTci8zPy5AgMzn5Zx8KpWwQNCyUSU7" --location "nym-town" --sphinx-key "o6MmKHzRewpNzVwaV37ZX9G3BfK4AmfYvsQfyoyAFRk" --signature "2TujBZfer8r5QM639Yb8coD9xH6f5eXzjAT5dD7wMom9fH8D1u36d7UpPdVaaZrWsCynmYpobwMWqiMKr5kM6CprD" ``` -### Unbond a node +### Un-bond a node -Unbond a mix node or gateway. +Un-bond a mix node or gateway. ``` ./nym-cli mixnet operators gateway unbound --mnemonic ``` @@ -270,7 +270,7 @@ Delegate to a mix node. ./nym-cli mixnet delegators delegate --amount –mix-id --mnemonic ``` -### Undelegate Stake +### Un-delegate Stake Remove stake from a mix node. ``` @@ -321,10 +321,10 @@ Query for vesting schedule in the [vesting smart contract](../nyx/vesting-contra ## Staking on someone's behalf (for custodians) -There is a limitation the the staking address can only perform the following actions (and are visible via the Nym Wallet: +There is a limitation the staking address can only perform the following actions (and are visible via the Nym Wallet: -- Bond on the gaƒteway's or mix node's behalf. -- Delegate or Undelegate (to a mix node in order to begin receiving rewards) +- Bond on the gateway's or mix node's behalf. +- Delegate or Un-delegate (to a mix node in order to begin receiving rewards) - Claiming the rewards on the account ```admonish note title="" @@ -333,7 +333,7 @@ The staking address has no ability to withdraw any coins from the parent's accou The staking address must maintain the same level of security as the parent mnemonic; while the parent mnemonic's delegations and bonding events will be visible to the parent owner, the staking address will be the only account capable of undoing the bonding and delegating from the mix nodes or gateway. -Query for staking on behlaf of someone else +Query for staking on behalf of someone else ``` ./nym-cli --mnemonic mixnet delegators delegate --mix-id --identity-key --amount ``` diff --git a/documentation/docs/src/wallet/cli-wallet.md b/documentation/docs/src/wallet/cli-wallet.md index e4d2468068..36670190a3 100644 --- a/documentation/docs/src/wallet/cli-wallet.md +++ b/documentation/docs/src/wallet/cli-wallet.md @@ -1,8 +1,8 @@ # CLI Wallet -If you have already read our validator setup and maintenance [documentation](../nodes/validator-setup.md) you will have seen that we compile and use the `nyxd` binary primarily for our validators. This binary can however be used for many other tasks, such as creating and using keypairs for wallets, or automated setups that require the signing and broadcasting of transactions. +If you have already read our validator setup and maintenance [documentation](../nodes/validator.md) you will have seen that we compile and use the `nyxd` binary primarily for our validators. This binary can however be used for many other tasks, such as creating and using keypairs for wallets, or automated setups that require the signing and broadcasting of transactions. ### Using `nyxd` binary as a CLI wallet -You can use the `nyxd` as a minimal CLI wallet if you want to set up an account (or multiple accounts). Just compile the binary as per the documentation, **stopping after** the [building your validator](../nodes/validator-setup.md#building-your-validator) step is complete. You can then run `nyxd keys --help` to see how you can set up and store different keypairs with which to interact with the Nyx blockchain. +You can use the `nyxd` as a minimal CLI wallet if you want to set up an account (or multiple accounts). Just compile the binary as per the documentation, **stopping after** the [building your validator](../nodes/validator.md) step is complete. You can then run `nyxd keys --help` to see how you can set up and store different keypairs with which to interact with the Nyx blockchain. - +For more on interacting with the chain, see the [Interacting with Nyx Chain and Smart Contracts](../nyx/interacting-with-chain.md) page. diff --git a/documentation/operators/.gitignore b/documentation/operators/.gitignore new file mode 100644 index 0000000000..7585238efe --- /dev/null +++ b/documentation/operators/.gitignore @@ -0,0 +1 @@ +book diff --git a/documentation/operators/book.toml b/documentation/operators/book.toml new file mode 100644 index 0000000000..02f3878356 --- /dev/null +++ b/documentation/operators/book.toml @@ -0,0 +1,122 @@ +[book] +title = "Nym Operators Guide" +authors = ["Max Hampshire", "serinko"] +description = "Guides to setup Nym infrastructure" +language = "en" +multilingual = false # for the moment - ideally work on chinese, brazillian, spanish next +src = "src" + +################# +# PREPROCESSORS # +################# + +# Note if changing these fields: +# - make sure you aren't running `mdbook serve` +# - change value of `turn-off = true` to `false`. This is set to `true` usually to avoid a known constant-reload bug with `mdbook serve` and this preprocessor +# - delete `./theme/` dir +# - change the preprocessor values that you want to edit +# - run `mdbook build`: this will rebuild the `./theme` directory +# - change value of `turn-off` back to `true` +[preprocessor.theme] +pagetoc = true +# some variables related (defined in theme/css/variables.css) +pagetoc-width = "13%" +content-max-width = "70%" # 82 +pagetoc-fontsize = "14.5px" +sidebar-width = "300px" +menu-bar-height = "40px" # memu-bar = the bar on the top +page-padding = "20px" +mobile-content-max-width = "98%" + +# layout +content-padding = "0 10px" +content-main-margin-left = "10%" +content-main-margin-right = "10%" +nav-chapters-max-width = "auto" +nav-chapters-min-width = "auto" +chapter-line-height = "2em" +section-line-height = "1.5em" + +# if true, never read and touch the files in theme dir: this is used to stop the looping reload issue referred to in the readme +turn-off = false + +[preprocessor.admonish] +command = "mdbook-admonish" +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] +minimum_rust_version = "1.66" +platform_release_version = "1.1.29" +wallet_release_version = "1.2.8" + +[preprocessor.last-changed] +command = "mdbook-last-changed" +renderer = ["html"] + +# used for grabbing output of binary commands for automation +# https://github.com/FauconFan/mdbook-cmdrun +[preprocessor.cmdrun] + +# more pre-processor plugins to look into from https://github.com/rust-lang/mdBook/wiki/Third-party-plugins & https://lib.rs/keywords/mdbook-preprocessor +# mdbook-i18n + +######### +# BUILD # +######### + +[build] +build-dir = "book" # the directory where the output is placed +create-missing = true # whether or not to create missing pages +use-default-preprocessors = true # use the default preprocessors +extra-watch-dirs = [] # directories to watch for triggering builds + +########## +# OUTPUT # +########## + +[output.html] +theme = "theme" # this is the default if not explicitly set +default-theme = "coal" +preferred-dark-theme = "coal" +curly-quotes = true +# mathjax-support = false # useful if we want to pull equations in +copy-fonts = true +no-section-label = false +additional-css = ["theme/pagetoc.css", "././mdbook-admonish.css","./custom.css"] +additional-js = ["theme/pagetoc.js"] +git-repository-url = "https://github.com/nymtech/nym" +git-repository-icon = "fa-github" +# edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}" +site-url = "/operators/" +# cname = "nymtech.net" +input-404 = "not-found.md" + +[output.html.fold] +enable = true # whether or not to enable section folding +level = 0 # the depth to start folding + +# controlling rust sample code blocks +[output.html.playground] +editable = false # allows editing the source code +copyable = true # include the copy button for copying code snippets +copy-js = true # includes the JavaScript for the code editor +line-numbers = false # displays line numbers for editable code +runnable = true # displays a run button for rust code + +# options for the built in text search +[output.html.search] +enable = true # enables the search feature +limit-results = 30 # maximum number of search results +teaser-word-count = 30 # number of words used for a search result teaser +use-boolean-and = true # multiple search terms must all match +boost-title = 2 # ranking boost factor for matches in headers +boost-hierarchy = 1 # ranking boost factor for matches in page names +boost-paragraph = 1 # ranking boost factor for matches in text +expand = true # partial words will match longer terms +heading-split-level = 3 # link results to heading levels +copy-js = true # include Javascript code for search + +[output.linkcheck] +warning-policy = "warn" diff --git a/documentation/operators/custom.css b/documentation/operators/custom.css new file mode 100644 index 0000000000..855f5eef5f --- /dev/null +++ b/documentation/operators/custom.css @@ -0,0 +1,37 @@ +:root { + --mono-font: Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace; +} +.coal { + --bg: #121726; + --fg: #f2f2f2; + + --sidebar-bg: #121726; + --sidebar-fg: #f2f2f2; + --sidebar-active: #fb6e4e; + + --icons: #f2f2f2; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +.light { + --bg: #f4f6f8; + --fg: #121726; + + --sidebar-bg: #f4f6f8; + --sidebar-fg: #121726; + --sidebar-active: #fb6e4e; + + --icons: #121726; + --icons-hover: #fb6e4e; + + --links: #fb6e4e; +} + +footer { + font-size: 0.8em; + text-align: center; + border-top: 1px solid black; + padding: 5px 0; +} diff --git a/documentation/dev-portal/last-changed.css b/documentation/operators/last-changed.css similarity index 100% rename from documentation/dev-portal/last-changed.css rename to documentation/operators/last-changed.css diff --git a/documentation/operators/mdbook-admonish.css b/documentation/operators/mdbook-admonish.css new file mode 100644 index 0000000000..5e360387df --- /dev/null +++ b/documentation/operators/mdbook-admonish.css @@ -0,0 +1,352 @@ +@charset "UTF-8"; +:root { + --md-admonition-icon--note: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--abstract: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--info: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--tip: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--success: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--question: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--warning: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--failure: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--danger: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--bug: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--example: + url("data:image/svg+xml;charset=utf-8,"); + --md-admonition-icon--quote: + url("data:image/svg+xml;charset=utf-8,"); + --md-details-icon: + url("data:image/svg+xml;charset=utf-8,"); +} + +:is(.admonition) { + display: flow-root; + margin: 1.5625em 0; + padding: 0 1.2rem; + color: var(--fg); + page-break-inside: avoid; + background-color: var(--bg); + border: 0 solid black; + border-inline-start-width: 0.4rem; + border-radius: 0.2rem; + box-shadow: 0 0.2rem 1rem rgba(0, 0, 0, 0.05), 0 0 0.1rem rgba(0, 0, 0, 0.1); +} +@media print { + :is(.admonition) { + box-shadow: none; + } +} +:is(.admonition) > * { + box-sizing: border-box; +} +:is(.admonition) :is(.admonition) { + margin-top: 1em; + margin-bottom: 1em; +} +:is(.admonition) > .tabbed-set:only-child { + margin-top: 0; +} +html :is(.admonition) > :last-child { + margin-bottom: 1.2rem; +} + +a.admonition-anchor-link { + display: none; + position: absolute; + left: -1.2rem; + padding-right: 1rem; +} +a.admonition-anchor-link:link, a.admonition-anchor-link:visited { + color: var(--fg); +} +a.admonition-anchor-link:link:hover, a.admonition-anchor-link:visited:hover { + text-decoration: none; +} +a.admonition-anchor-link::before { + content: "§"; +} + +:is(.admonition-title, summary) { + position: relative; + margin-block: 0; + margin-inline: -1.6rem -1.2rem; + padding-block: 0.8rem; + padding-inline: 4.4rem 1.2rem; + font-weight: 700; + background-color: rgba(68, 138, 255, 0.1); + display: flex; +} +:is(.admonition-title, summary) p { + margin: 0; +} +html :is(.admonition-title, summary):last-child { + margin-bottom: 0; +} +:is(.admonition-title, summary)::before { + position: absolute; + top: 0.625em; + inset-inline-start: 1.6rem; + width: 2rem; + height: 2rem; + background-color: #448aff; + mask-image: url('data:image/svg+xml;charset=utf-8,'); + -webkit-mask-image: url('data:image/svg+xml;charset=utf-8,'); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-size: contain; + content: ""; +} +:is(.admonition-title, summary):hover a.admonition-anchor-link { + display: initial; +} + +details.admonition > summary.admonition-title::after { + position: absolute; + top: 0.625em; + inset-inline-end: 1.6rem; + height: 2rem; + width: 2rem; + background-color: currentcolor; + mask-image: var(--md-details-icon); + -webkit-mask-image: var(--md-details-icon); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-size: contain; + content: ""; + transform: rotate(0deg); + transition: transform 0.25s; +} +details[open].admonition > summary.admonition-title::after { + transform: rotate(90deg); +} + +:is(.admonition):is(.note) { + border-color: #448aff; +} + +:is(.note) > :is(.admonition-title, summary) { + background-color: rgba(68, 138, 255, 0.1); +} +:is(.note) > :is(.admonition-title, summary)::before { + background-color: #448aff; + mask-image: var(--md-admonition-icon--note); + -webkit-mask-image: var(--md-admonition-icon--note); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.abstract, .summary, .tldr) { + border-color: #00b0ff; +} + +:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary) { + background-color: rgba(0, 176, 255, 0.1); +} +:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary)::before { + background-color: #00b0ff; + mask-image: var(--md-admonition-icon--abstract); + -webkit-mask-image: var(--md-admonition-icon--abstract); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.info, .todo) { + border-color: #00b8d4; +} + +:is(.info, .todo) > :is(.admonition-title, summary) { + background-color: rgba(0, 184, 212, 0.1); +} +:is(.info, .todo) > :is(.admonition-title, summary)::before { + background-color: #00b8d4; + mask-image: var(--md-admonition-icon--info); + -webkit-mask-image: var(--md-admonition-icon--info); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.tip, .hint, .important) { + border-color: #00bfa5; +} + +:is(.tip, .hint, .important) > :is(.admonition-title, summary) { + background-color: rgba(0, 191, 165, 0.1); +} +:is(.tip, .hint, .important) > :is(.admonition-title, summary)::before { + background-color: #00bfa5; + mask-image: var(--md-admonition-icon--tip); + -webkit-mask-image: var(--md-admonition-icon--tip); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.success, .check, .done) { + border-color: #00c853; +} + +:is(.success, .check, .done) > :is(.admonition-title, summary) { + background-color: rgba(0, 200, 83, 0.1); +} +:is(.success, .check, .done) > :is(.admonition-title, summary)::before { + background-color: #00c853; + mask-image: var(--md-admonition-icon--success); + -webkit-mask-image: var(--md-admonition-icon--success); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.question, .help, .faq) { + border-color: #64dd17; +} + +:is(.question, .help, .faq) > :is(.admonition-title, summary) { + background-color: rgba(100, 221, 23, 0.1); +} +:is(.question, .help, .faq) > :is(.admonition-title, summary)::before { + background-color: #64dd17; + mask-image: var(--md-admonition-icon--question); + -webkit-mask-image: var(--md-admonition-icon--question); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.warning, .caution, .attention) { + border-color: #ff9100; +} + +:is(.warning, .caution, .attention) > :is(.admonition-title, summary) { + background-color: rgba(255, 145, 0, 0.1); +} +:is(.warning, .caution, .attention) > :is(.admonition-title, summary)::before { + background-color: #ff9100; + mask-image: var(--md-admonition-icon--warning); + -webkit-mask-image: var(--md-admonition-icon--warning); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.failure, .fail, .missing) { + border-color: #ff5252; +} + +:is(.failure, .fail, .missing) > :is(.admonition-title, summary) { + background-color: rgba(255, 82, 82, 0.1); +} +:is(.failure, .fail, .missing) > :is(.admonition-title, summary)::before { + background-color: #ff5252; + mask-image: var(--md-admonition-icon--failure); + -webkit-mask-image: var(--md-admonition-icon--failure); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.danger, .error) { + border-color: #ff1744; +} + +:is(.danger, .error) > :is(.admonition-title, summary) { + background-color: rgba(255, 23, 68, 0.1); +} +:is(.danger, .error) > :is(.admonition-title, summary)::before { + background-color: #ff1744; + mask-image: var(--md-admonition-icon--danger); + -webkit-mask-image: var(--md-admonition-icon--danger); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.bug) { + border-color: #f50057; +} + +:is(.bug) > :is(.admonition-title, summary) { + background-color: rgba(245, 0, 87, 0.1); +} +:is(.bug) > :is(.admonition-title, summary)::before { + background-color: #f50057; + mask-image: var(--md-admonition-icon--bug); + -webkit-mask-image: var(--md-admonition-icon--bug); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.example) { + border-color: #7c4dff; +} + +:is(.example) > :is(.admonition-title, summary) { + background-color: rgba(124, 77, 255, 0.1); +} +:is(.example) > :is(.admonition-title, summary)::before { + background-color: #7c4dff; + mask-image: var(--md-admonition-icon--example); + -webkit-mask-image: var(--md-admonition-icon--example); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +:is(.admonition):is(.quote, .cite) { + border-color: #9e9e9e; +} + +:is(.quote, .cite) > :is(.admonition-title, summary) { + background-color: rgba(158, 158, 158, 0.1); +} +:is(.quote, .cite) > :is(.admonition-title, summary)::before { + background-color: #9e9e9e; + mask-image: var(--md-admonition-icon--quote); + -webkit-mask-image: var(--md-admonition-icon--quote); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-repeat: no-repeat; +} + +.navy :is(.admonition) { + background-color: var(--sidebar-bg); +} + +.ayu :is(.admonition), .coal :is(.admonition) { + background-color: var(--theme-hover); +} + +.rust :is(.admonition) { + background-color: var(--sidebar-bg); + color: var(--sidebar-fg); +} +.rust .admonition-anchor-link:link, .rust .admonition-anchor-link:visited { + color: var(--sidebar-fg); +} diff --git a/documentation/operators/src/SUMMARY.md b/documentation/operators/src/SUMMARY.md new file mode 100644 index 0000000000..82345c6f49 --- /dev/null +++ b/documentation/operators/src/SUMMARY.md @@ -0,0 +1,27 @@ +# Summary + +- [Introduction](introduction.md) + +# Binaries +- [Pre-built Binaries](./binaries/pre-built-binaries.md) +- [Building from Source](./binaries/building-nym.md) +- [Binary Initialisation and Configuration](./binaries/init-and-config.md) + + +# Operators Guides + +- [Mixnet Nodes Setup](./nodes/setup-guides.md) + - [Preliminary Steps](./preliminary-steps.md) + - [Mix Node](./nodes/mix-node-setup.md) + - [Gateway](./nodes/gateway-setup.md) + - [Network Requester](./nodes/network-requester-setup.md) +- [Nyx Validator Setup](./nodes/validator-setup.md) +- [Maintenance](./nodes/maintenance.md) +- [Troubleshooting](./nodes/troubleshooting.md) +- [FAQ](./faq.md) + +--- +# Misc. +- [Code of Conduct](coc.md) +- [Licensing](licensing.md) +--- diff --git a/documentation/operators/src/binaries/building-nym.md b/documentation/operators/src/binaries/building-nym.md new file mode 100644 index 0000000000..8610fcb63a --- /dev/null +++ b/documentation/operators/src/binaries/building-nym.md @@ -0,0 +1,71 @@ +# Building from Source + +> Nym runs on Mac OS X, Linux, and Windows. All nodes **except the Desktop Wallet and NymConnect** on Windows should be considered experimental - it works fine if you're an app developer but isn't recommended for running nodes. + +## Building Nym +Nym has two main codebases: + +- the [Nym platform](https://github.com/nymtech/nym), written in Rust. This contains all of our code _except_ for the validators. +- the [Nym validators](https://github.com/nymtech/nyxd), written in Go. + +> This page details how to build the main Nym platform code. **If you want to build and run a validator, [go here](../nodes/validator-setup.md) instead.** + +## Prerequisites +- Debian/Ubuntu: `pkg-config`, `build-essential`, `libssl-dev`, `curl`, `jq`, `git` + +``` +apt install pkg-config build-essential libssl-dev curl jq git +``` + +- Arch/Manjaro: `base-devel` + +``` +pacman -S base-devel +``` + +- Mac OS X: `pkg-config` , `brew`, `openss1`, `protobuf`, `curl`, `git` +Running the following the script installs Homebrew and the above dependencies: + +``` +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` + +- `Rust & cargo >= {{minimum_rust_version}}` + +We recommend using the [Rust shell script installer](https://www.rust-lang.org/tools/install). Installing cargo from your package manager (e.g. `apt`) is not recommended as the packaged versions are usually too old. + +If you really don't want to use the shell script installer, the [Rust installation docs](https://forge.rust-lang.org/infra/other-installation-methods.html) contain instructions for many platforms. + +## Download and build Nym binaries +The following commands will compile binaries into the `nym/target/release` directory: + +``` +rustup update +git clone https://github.com/nymtech/nym.git +cd nym + +git reset --hard # in case you made any changes on your branch +git pull # in case you've checked it out before + +git checkout release/{{platform_release_version}} # checkout to the latest release branch: `develop` will most likely be incompatible with deployed public networks + +cargo build --release # build your binaries with **mainnet** configuration +NETWORK=sandbox cargo build --release # build your binaries with **sandbox** configuration +``` + +Quite a bit of stuff gets built. The key working parts are: + +* [mix node](../nodes/mix-node-setup.md): `nym-mixnode` +* [gateway node](../nodes/gateway-setup.md): `nym-gateway` +* [websocket client](https://nymtech.net/docs/clients/websocket-client.html): `nym-client` +* [socks5 client](https://nymtech.net/docs/clients/socks5-client.html): `nym-socks5-client` +* [webassembly client](https://nymtech.net/docs/clients/webassembly-client.html): `webassembly-client` +* [network requester](../nodes/network-requester-setup.md): `nym-network-requester` +* [nym-cli tool](https://nymtech.net/docs/tools/nym-cli.html): `nym-cli` + +The repository also contains Typescript applications which aren't built in this process. These can be built by following the instructions on their respective docs pages. +* [Nym Wallet](https://nymtech.net/docs/wallet/desktop-wallet.html) +* [Nym Connect](https://nymtech.net/developers/quickstart/nymconnect-gui.html) +* [Network Explorer UI](https://nymtech.net/docs/explorers/mixnet-explorer.html) + +> You cannot build from GitHub's .zip or .tar.gz archive files on the releases page - the Nym build scripts automatically include the current git commit hash in the built binary during compilation, so the build will fail if you use the archive code (which isn't a Git repository). Check the code out from github using `git clone` instead. diff --git a/documentation/operators/src/binaries/init-and-config.md b/documentation/operators/src/binaries/init-and-config.md new file mode 100644 index 0000000000..e749662b8e --- /dev/null +++ b/documentation/operators/src/binaries/init-and-config.md @@ -0,0 +1,13 @@ +# Binary Initialisation and Configuration + +All Nym binaries must first be initialised with `init` before being `run`. + +The `init` command is usually where you pass flags specifying configuration arguments such as the gateway you wish to communicate with, the ports you wish your binary to listen on, etc. + +The `init` command will also create the necessary keypairs and configuration files at `~/.nym///` if these files do not already exist. **It will not overwrite existing keypairs if they are present.** + +You can reconfigure your binaries at any time by editing the config file located at `~/.nym///config/config.toml` and restarting the binary process. + +Once you have run `init`, you can start your binary with the `run` command, usually only accompanied by the `id` of the binary that you specified. + +This `id` is **never** transmitted over the network, and is used to select which local config and key files to use for startup. diff --git a/documentation/operators/src/binaries/pre-built-binaries.md b/documentation/operators/src/binaries/pre-built-binaries.md new file mode 100644 index 0000000000..269c26276c --- /dev/null +++ b/documentation/operators/src/binaries/pre-built-binaries.md @@ -0,0 +1,6 @@ +# Pre-built Binaries + +The [Github releases page](https://github.com/nymtech/nym/releases) has pre-built binaries which should work on Ubuntu 20.04 and other Debian-based systems, but at this stage cannot be guaranteed to work everywhere. + +If the pre-built binaries don't work or are unavailable for your system, you will need to build the platform yourself. + diff --git a/documentation/operators/src/binaries/version-compatiblity.md b/documentation/operators/src/binaries/version-compatiblity.md new file mode 100644 index 0000000000..67e8a71ce6 --- /dev/null +++ b/documentation/operators/src/binaries/version-compatiblity.md @@ -0,0 +1,38 @@ +# Version Compatibility Table + +There are numerous components to Nym which are released independently of one another aside from when breaking changes occur in the [core platform code](https://github.com/nymtech/nym/) - mix nodes, gateways, network requesters, and clients. + +Whilst in general it recommended to be running the most recent version of any software, if you cannot do that for whatever reason this table will tell you which versions of different components are mutually compatible with which platform code releases. + + +| Core Platform | SDK | Wallet | NymConnect | Network Explorer | Mixnet contract | Vesting contract | +| ---------------- | ------------- | --------------- | --------------- | ---------------- | --------------- | ---------------- | +| 1.1.13 - 1.1.14* | 1.1.7 | 1.1.12 - 1.1.13 | 1.1.12 - 1.1.13 | 1.1.2 | 1.2.0 - 1.3.0 | 1.2.0 - 1.3.0 | +| 1.1.1 - 1.1.12 | 1.1.4 - 1.1.7 | 1.1.0 - 1.1.12 | 1.1.1 - 1.1.12 | 1.1.0 - 1.1.2 | 1.1.0 - 1.1.3 | 1.1.0 - 1.1.3 | +| 1.1.0 - 1.1.1 | 1.1.4 | 1.1.0 | 1.1.0 - 1.1.1 | 1.1.0 | 1.1.0 | 1.1.0 | +| 1.1.0 | x | 1.1.0 | 1.1.0 | 1.1.0 | 1.1.0 | 1.1.0 | + +`*` the `nym-mixnode` binary is currently one point ahead of the main platform release version + +> There are seperate changelogs for [`NymConnect`](https://github.com/nymtech/nym/blob/release/{{platform_release_version}}/nym-connect/CHANGELOG.md) and the [`Desktop Wallet`](https://github.com/nymtech/nym/blob/release/{{platform_release_version}}/nym-wallet/CHANGELOG.md). The changelog referenced below is for the core platform code. + +| Platform release changelog | +| ---------------------------------------------------------------------------------------- | +| 1.1.14 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.14/CHANGELOG.md)) | +| 1.1.13 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.13/CHANGELOG.md)) | +| 1.1.12 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.12/CHANGELOG.md)) | +| 1.1.11 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.11/CHANGELOG.md)) | +| 1.1.10 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.10/CHANGELOG.md)) | +| 1.1.9 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.9/CHANGELOG.md)) | +| 1.1.8 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.8/CHANGELOG.md)) | +| 1.1.7 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.7/CHANGELOG.md)) | +| 1.1.6 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.6/CHANGELOG.md)) | +| 1.1.5 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.5/CHANGELOG.md)) | +| 1.1.4 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.4/CHANGELOG.md)) | +| 1.1.3 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.3/CHANGELOG.md)) | +| 1.1.2 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.2/CHANGELOG.md)) | +| 1.1.1 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.1/CHANGELOG.md)) | +| 1.1.0 ([CHANGELOG](https://github.com/nymtech/nym/blob/release/v1.1.0/CHANGELOG.md)) | +| 1.0.2 ([CHANGELOG](https://github.com/nymtech/nym/blob/nym-binaries-1.0.2/CHANGELOG.md)) | +| 1.0.1 ([CHANGELOG](https://github.com/nymtech/nym/blob/nym-binaries-1.0.1/CHANGELOG.md)) | +| 1.0.0 ([CHANGELOG](https://github.com/nymtech/nym/blob/nym-binaries-1.0.0/CHANGELOG.md)) | diff --git a/documentation/operators/src/coc.md b/documentation/operators/src/coc.md new file mode 100644 index 0000000000..f8771a73bf --- /dev/null +++ b/documentation/operators/src/coc.md @@ -0,0 +1,17 @@ +# Code of Conduct + +We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. + +Please avoid using overtly sexual aliases or other nicknames that might detract from a friendly, safe and welcoming environment for all. + +Please be kind and courteous. There’s no need to be mean or rude. + +Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. + +Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. + +We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behaviour. We interpret the term “harassment” as including the definition in the Citizen Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don’t tolerate behaviour that excludes people in socially marginalized groups. + +Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the Rust moderation team immediately. Whether you’re a regular contributor or a newcomer, we care about making this community a safe place for you and we’ve got your back. + +Likewise any spamming, trolling, flaming, baiting or other attention-stealing behaviour is not welcome. diff --git a/documentation/operators/src/faq.md b/documentation/operators/src/faq.md new file mode 100644 index 0000000000..9bb2c6dec9 --- /dev/null +++ b/documentation/operators/src/faq.md @@ -0,0 +1,57 @@ +# Frequently Asked Questions + +## Mixnet nodes + +### What determines the rewards when running a mix node? + +The stake required for a mix node to achieve maximum rewards is called mix node saturation point. This is calculated from the staking supply (all circulating supply + part of unlocked tokens). The target level of staking is to have 50% of the staking supply locked in mix nodes. + +The node stake saturation point, which we denote by Nsat, is given by the stake supply, target level of staking divided by the number of rewarded (active) nodes. + +This design ensures the nodes aim to have a same size of stake (reputation) which can be done by delegation staking, as well as it ensures that there is a decentralization of staking as any higher level of staked tokens per node results in worse rewards. On the contrary, the more mix nodes are active, the lower is Nsat. The equilibrium is reached when the staked tokens are delegated equally across the active mix-nodes and that's our basis for this incentive system. + +For more detailed calculation, read our blog post [Nym Token Economics update](https://blog.nymtech.net/nym-token-economics-update-fedff0ed5267). More info on staking can be found [here](https://blog.nymtech.net/staking-in-nym-introducing-mainnet-mixmining-f9bb1cbc7c36). And [here](https://blog.nymtech.net/want-to-stake-in-nym-here-is-how-to-choose-a-mix-node-to-delegate-nym-to-c3b862add165) is more info on how to choose a mix node for delegation. And finally an [update](https://blog.nymtech.net/quarterly-token-economic-parameter-update-b2862948710f) on token economics from July 2023. + +### Which VPS providers would you recommend? + +Consider in which jurisdiction you reside and where do you want to run a mix node. Do you want to pay by crypto or not and what are the other important particularities for your case? We always recommend operators to try to chose smaller and decentralized VPS providers over the most known ones controlling a majority of the internet. We receive some good feedback on these: Hertzner, DigitalOcean, Linode, Ghandi and Exoscale. Do your own research and share with the community. + + + +### What's the Sphinx packet size? + +The sizes are shown in the configs [here](https://github.com/nymtech/nym/blob/1ba6444e722e7757f1175a296bed6e31e25b8db8/common/nymsphinx/params/src/packet_sizes.rs#L12) (default is the one clients use, the others are for research purposes, not to be used in production as this would fragment the anonymity set). More info can be found [here](https://github.com/nymtech/nym/blob/4844ac953a12b29fa27688609ec193f1d560c996/common/nymsphinx/anonymous-replies/src/reply_surb.rs#L80). + +### Why a mix node and a gateway cannot be bond to the same wallet? + +Becauase of the way the smart contract works we keep it one-node one-address at the moment. + +### Which nodes are the most needed to be setup to strengthen Nym infrastructure and which ones bring rewards? + +Right now only mix nodes are rewarded. We're working on gateway and service payments. Gateways are the weak link right now due mostly to lack of incentivisation. Services like Network Requesters are obviously the most necessary for people to start using the platform, and we're working on smart contracts to allow for people to start advertising them the same way they do mix nodes. + +### Are mixnodes whitelisted? + +Nope, anyone can run a mix node. Purely reliant on the node's reputation (self stake + delegations) & routing score. + +## Validators and tokens + +### What's the difference between NYM and uNYM? + +1 NYM = 1 000 000 uNYM + + + +### Can I run a validator? + +We are currently working towards building up a closed set of reputable validators. You can ask us for coins to get in, but please don't be offended if we say no - validators are part of our system's core security and we are starting out with people we already know or who have a solid reputation. + +### Why is validator set entry whitelisted? + +We understand that the early days of the Nyx blockchain will face possible vulnerabilities in terms of size - easy to disrupt or halt the chain if a malicious party entered with a large portion of stake. Besides that, there are some legal issues we need to address before we can distribute the validator set in a fully permissions fashion. + +### Why does Nym do many airdrops? + +It is part of ensuring decentralisation - we need to avoid a handful of people having too much control over the token and market. Of course ideally people will stake the tokens and contribute to the project at this stage. We run surveys to better understand what people are doing with their tokens and what usability issues there are for staking. Any feedback is appreciated as it helps us improve all aspects of using the token and participating in the ecosystem. diff --git a/documentation/operators/src/images/wallet-screenshots/bonding.png b/documentation/operators/src/images/wallet-screenshots/bonding.png new file mode 100644 index 0000000000..b5cef3bb89 Binary files /dev/null and b/documentation/operators/src/images/wallet-screenshots/bonding.png differ diff --git a/documentation/operators/src/images/wallet-screenshots/node_settings.png b/documentation/operators/src/images/wallet-screenshots/node_settings.png new file mode 100644 index 0000000000..69568ce6a5 Binary files /dev/null and b/documentation/operators/src/images/wallet-screenshots/node_settings.png differ diff --git a/documentation/operators/src/images/wallet-screenshots/wallet-gateway-sign.png b/documentation/operators/src/images/wallet-screenshots/wallet-gateway-sign.png new file mode 100644 index 0000000000..63a76aaf4d Binary files /dev/null and b/documentation/operators/src/images/wallet-screenshots/wallet-gateway-sign.png differ diff --git a/documentation/operators/src/images/wallet-screenshots/wallet-sign.png b/documentation/operators/src/images/wallet-screenshots/wallet-sign.png new file mode 100644 index 0000000000..a31dddd391 Binary files /dev/null and b/documentation/operators/src/images/wallet-screenshots/wallet-sign.png differ diff --git a/documentation/operators/src/introduction.md b/documentation/operators/src/introduction.md new file mode 100644 index 0000000000..4050fe141c --- /dev/null +++ b/documentation/operators/src/introduction.md @@ -0,0 +1,26 @@ +# Introduction + +This is Nym's Operators guide, containing information and setup guides for the various pieces of Nym mixnet infrastructure (mix node, gateway and network requester) and Nyx blockchain validators. + +If you are new to Nym and want to learn about the mixnet, explore kickstart options and demos, learn how to integrate with the network, and follow developer tutorials check out the [Developer Portal](https://nymtech.net/developers/). + +If you want to dive deeper into Nym's architecture, clients, nodes, and SDK examples visit the [technical docs](https://nymtech.net/docs/). + + +## Popular pages +**Binary Information** +* [Building Nym](./binaries/building-nym.md) +* [Pre-built Binaries](./binaries/pre-built-binaries.md) +* [Init & Configuration](./binaries/init-and-config.md) + +**Node setup and usage guides:** +* [Mix nodes](./nodes/mix-node-setup.md) +* [Gateways](./nodes/gateway-setup.md) +* [Network requesters](./nodes/network-requester-setup.md) +* [Validators](./nodes/validator-setup.md) + +**Maintenance, troubleshooting and FAQ** +* [Maintenance](./nodes/maintenance.md) +* [Troubleshooting](./nodes/troubleshooting.md) +* [FAQ](./faq.md) + diff --git a/documentation/operators/src/licensing.md b/documentation/operators/src/licensing.md new file mode 100644 index 0000000000..2e8612d4d4 --- /dev/null +++ b/documentation/operators/src/licensing.md @@ -0,0 +1,5 @@ +# Licensing + +Nym is Free Software released under the Apache License V2. + +All of the contributions of the Nym core developers are © Nym Technologies SA. If you are interested in talking to us about other licenses, please get in touch. diff --git a/documentation/operators/src/nodes/gateway-setup.md b/documentation/operators/src/nodes/gateway-setup.md new file mode 100644 index 0000000000..f1cb589756 --- /dev/null +++ b/documentation/operators/src/nodes/gateway-setup.md @@ -0,0 +1,136 @@ +# Gateways + +> The Nym gateway was built in the [building nym](../binaries/building-nym.md) section. If you haven't yet built Nym and want to run the code, go there first. + +> Any syntax in `<>` brackets is a user's unique variable. Exchange with a corresponding name without the `<>` brackets. + +## Current version +``` + +``` + +## Preliminary steps + +Make sure you do the preparation listed in the [preliminary steps page](../preliminary-steps.md) before setting up your gateway. + + +## Gateway setup +Now that you have built the codebase, set up your wallet, and have a VPS with the `nym-gateway` binary, you can set up your gateway with the instructions below. + +To begin, move to `/target/release` directory from which you run the node commands: + +``` +cd target/release +``` + +### Viewing command help +You can check that your binaries are properly compiled with: + +``` +./nym-gateway --help +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +You can also check the various arguments required for individual commands with: + +``` +./nym-gateway --help +``` +> Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. + +### Initialising your gateway +To check available configuration options use: + +``` + ./nym-gateway init --help +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +The following command returns a gateway on your current IP with the `id` of `supergateway`: + +``` +./nym-gateway init --id supergateway --host $(curl ifconfig.me) --wallet-address n1eufxdlgt0puwrwptgjfqne8pj4nhy2u5ft62uq +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +The `$(curl ifconfig.me)` command above returns your IP automatically using an external service. Alternatively, you can enter your IP manually if you wish. If you do this, remember to enter your IP **without** any port information. + +### Bonding your gateway +#### Via the Desktop wallet +You can bond your gateway via the Desktop wallet. + +1. Open your wallet, and head to the `Bonding` page, then select the node type `Gateway` and input your node details (Location format is , ). Press `Next` + +2. Enter the `Amount`, `Operating cost` and press `Next`. + +3. You will be asked to run a the `sign` command with your `gateway` - copy and paste the long signature as the value of `--contract-msg` and run it. + +``` +./nym-mixnode sign --id --contract-msg +``` + +It will look something like this: + +~~~admonish example collapsible=true title="Console output" +``` +./nym-gateway sign --id upgrade_test --contract-msg 2Mf8xYytgEeyJke9LA7TjhHoGQWNBEfgHZtTyy2krFJfGHSiqy7FLgTnauSkQepCZTqKN5Yfi34JQCuog9k6FGA2EjsdpNGAWHZiuUGDipyJ6UksNKRxnFKhYW7ri4MRduyZwbR98y5fQMLAwHne1Tjm9cXYCn8McfigNt77WAYwBk5bRRKmC34BJMmWcAxphcLES2v9RdSR68tkHSpy2C8STfdmAQs3tZg8bJS5Qa8pQdqx14TnfQAPLk3QYCynfUJvgcQTrg29aqCasceGRpKdQ3Tbn81MLXAGAs7JLBbiMEAhCezAr2kEN8kET1q54zXtKz6znTPgeTZoSbP8rzf4k2JKHZYWrHYF9JriXepuZTnyxAKAxvGFPBk8Z6KAQi33NRQkwd7MPyttatHna6kG9x7knffV6ebGzgRBf7NV27LurH8x4L1uUXwm1v1UYCA1WSBQ9Pp2JW69k5v5v7G9gBy8RUcZnMbeL26Qqb8WkuGcmuHhaFfoqSfV7PRHPpPT4M8uRqUyR4bjUtSJJM1yh6QSeZk9BEazzoJqPeYeGoiFDZ3LMj2jesbJweQR4caaYuRczK92UGSSqu9zBKmE45a + + + _ __ _ _ _ __ ___ + | '_ \| | | | '_ \ _ \ + | | | | |_| | | | | | | + |_| |_|\__, |_| |_| |_| + |___/ + + (nym-gateway - version {{platform_release_version}}) + + +>>> attempting to sign 2Mf8xYytgEeyJke9LA7TjhHoGQWNBEfgHZtTyy2krFJfGHSiqy7FLgTnauSkQepCZTqKN5Yfi34JQCuog9k6FGA2EjsdpNGAWHZiuUGDipyJ6UksNKRxnFKhYW7ri4MRduyZwbR98y5fQMLAwHne1Tjm9cXYCn8McfigNt77WAYwBk5bRRKmC34BJMmWcAxphcLES2v9RdSR68tkHSpy2C8STfdmAQs3tZg8bJS5Qa8pQdqx14TnfQAPLk3QYCynfUJvgcQTrg29aqCasceGRpKdQ3Tbn81MLXAGAs7JLBbiMEAhCezAr2kEN8kET1q54zXtKz6znTPgeTZoSbP8rzf4k2JKHZYWrHYF9JriXepuZTnyxAKAxvGFPBk8Z6KAQi33NRQkwd7MPyttatHna6kG9x7knffV6ebGzgRBf7NV27LurH8x4L1uUXwm1v1UYCA1WSBQ9Pp2JW69k5v5v7G9gBy8RUcZnMbeL26Qqb8WkuGcmuHhaFfoqSfV7PRHPpPT4M8uRqUyR4bjUtSJJM1yh6QSeZk9BEazzoJqPeYeGoiFDZ3LMj2jesbJweQR4caaYuRczK92UGSSqu9zBKmE45a +>>> decoding the message... +>>> message to sign: {"nonce":0,"algorithm":"ed25519","message_type":"gateway-bonding","content":{"sender":"n1ewmme88q22l8syvgshqma02jv0vqrug9zq9dy8","proxy":null,"funds":[{"denom":"unym","amount":"100000000"}],"data":{"gateway":{"host":"62.240.134.189","mix_port":1789,"clients_port":9000,"location":"62.240.134.189","sphinx_key":"FKbuN7mPdoCG9jA3CkAfXxC5X4rHhqeMVtmfRtJ3cFZd","identity_key":"3RoAhR8gEdfBETMjm2vbMFzKddxXDdE9ygBAnJHWqSzD","version":"1.1.13"}}}} +``` +~~~ + +* Copy the resulting signature: + +``` +>>> The base58-encoded signature is: +2SPDjLjX4b6XEtkgG7yD8Znsb1xycL1edFvRK4JcVnPsM9k6HXEUUeVS6rswRiYxoj1bMgiRKyPDwiksiuyxu8Xi +``` + +* And paste it into the wallet nodal, press `Next` and confirm the transaction. + +![Paste Signature](../images/wallet-screenshots/wallet-gateway-sign.png) + +* Your gateway is now bonded. + +> You are asked to `sign` a transaction on bonding so that the mixnet smart contract is able to map your nym address to your node. This allows us to create a nonce for each account and defend against replay attacks. + +#### Via the CLI (power users) +If you want to bond your mix node via the CLI, then check out the [relevant section in the Nym CLI](https://nymtech.net/docs/tools/nym-cli.html#bond-a-mix-node) docs. + +### Running your gateway +The `run` command starts the gateway: + +``` +./nym-gateway run --id +``` +## Maintenance + +For gateway upgrade, firewall setup, port configuration, API endpoints, VPS suggestions, automation and more, see the [maintenance page](./maintenance.md) + diff --git a/documentation/operators/src/nodes/maintenance.md b/documentation/operators/src/nodes/maintenance.md new file mode 100644 index 0000000000..c955975118 --- /dev/null +++ b/documentation/operators/src/nodes/maintenance.md @@ -0,0 +1,690 @@ +# Maintenance + +## Useful commands + +> Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. + +**build-info** + +A `build-info` command prints the build information like commit hash, rust version, binary version just like what command `--version` does. However, you can also specify an `--output=json` flag that will format the whole output as a json, making it an order of magnitude easier to parse. + +For example `./target/debug/nym-network-requester --no-banner build-info --output json` will return: + +``` +{"binary_name":"nym-network-requester","build_timestamp":"2023-07-24T15:38:37.00657Z","build_version":"1.1.23","commit_sha":"c70149400206dce24cf20babb1e64f22202672dd","commit_timestamp":"2023-07-24T14:45:45Z","commit_branch":"feature/simplify-cli-parsing","rustc_version":"1.71.0","rustc_channel":"stable","cargo_profile":"debug"} +``` + +## Upgrading your node + +> The process is the similar for mix node, gateway and network requester. In the following steps we use a placeholder `` in the commands, please change it for the type of node you want to upgrade. Any particularities for the given type of node are included. + +Upgrading your node is a two-step process: +* Updating the binary and `~/.nym///config.toml` on your VPS +* Updating the node information in the [mixnet smart contract](https://nymtech.net/docs/nyx/mixnet-contract.html). **This is the information that is present on the [mixnet explorer](https://explorer.nymtech.net)**. + +### Step 1: Upgrading your binary +Follow these steps to upgrade your mix node binary and update its config file: +* pause your mix node process. +* replace the existing binary with the newest binary (which you can either [compile yourself](https://nymtech.net/docs/binaries/building-nym.html) or grab from our [releases page](https://github.com/nymtech/nym/releases)). +* 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. + +> In case of a network requester this is all all, the following step is only for mix nodes and gateways. + +### Step 2: Updating your node information in the smart contract +Follow these steps to update the information about your `` which is publicly available from the [Nym API](https://validator.nymtech.net/api/swagger/index.html) and information displayed on the [mixnet explorer](https://explorer.nymtech.net). + +You can either do this graphically via the Desktop Wallet, or the CLI. + +### Updating node information via the Desktop Wallet +* Navigate to the `Bonding` page and click the `Node Settings` link in the top right corner: + +![Bonding page](../images/wallet-screenshots/bonding.png) + +* Update the fields in the `Node Settings` page and click `Submit changes to the blockchain`. + +![Node Settings Page](../images/wallet-screenshots/node_settings.png) + +### Updating node information via the CLI +If you want to bond your `` via the CLI, then check out the [relevant section in the Nym CLI](https://nymtech.net/docs/tools/nym-cli.md#upgrade-a-mix-node) docs. + + +### Upgrading Network Requester to >= v1.1.10 from +``` + +Copy the old keys from your client to the network-requester configuration that was created above: + +``` +cp -vr ~/.nym/clients/myoldclient/data/* ~/.nym/service-providers/network-requester//data +``` + +Edit the configuration to match what you used on your client. Specifically, edit the configuration file at: + +``` +~/.nym/service-providers/network-requester//config/config.toml +``` + +Ensure that the fields `gateway_id`, `gateway_owner`, `gateway_listener` in the new config match those in the old client config at: + +``` +~/.nym/clients/myoldclient/config/config.toml +``` + +### Upgrading your validator + +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.32.0 +- the new wasmvm (it depends on your platform, but most common filename is `libwasmvm.x86_64.so`) + +Wait for the upgrade height to be reached and the chain to halt awaiting upgrade, then: + +* 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/9). + +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 + +The most common reason for your validator being jailed is that your validator is out of memory because of bloated syslogs. + +Running the command `df -H` will return the size of the various partitions of your VPS. + +If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog archives and restart your validator process. + +## Moving a node + +In case of a need to move a node from one machine to another and avoiding to lose the delegation, here are few steps how to do it. + +The following examples transfers a mix node (in case of other nodes, change the `mixnodes` in the command for the `` of your desire. + +* Pause your node process. + +Assuming both machines are remote VPS. + +* Make sure your `~/.ssh/.pub` is in both of the machines `~/.ssh/authorized_keys` file +* Create a `mixnodes` folder in the target VPS. Ssh in from your terminal and run: +``` +# in case none of the nym configs was created previously +mkdir ~/.nym + +#in case no nym mix node was initialized previously +mkdir ~/.nym/mixnodes +``` +* Move the node data (keys) and config file to the new machine by opening a local terminal (as that one's ssh key is authorized in both of the machines) and running: +``` +scp -r -3 @:~/.nym/mixnodes/ @:~/.nym/mixnodes/ +``` +* Re-run init (remember that init doesn't overwrite existing keys) to generate a config with the new listening address etc. +* Change the node smart contract info via the wallet interface. Otherwise the keys will point to the old IP address in the smart contract, and the node will not be able to be connected, and it will fail up-time checks. +* Re-run the node from the new location. + +## VPS Setup and Automation +### Configure your firewall +Although your `` is now ready to receive traffic, your server may not be. The following commands will allow you to set up a firewall using `ufw`. + +``` +# check if you have ufw installed +ufw version + +# if it is not installed, install with +sudo apt install ufw -y + +# enable ufw +sudo ufw enable + +# check the status of the firewall +sudo ufw status +``` + +Finally open your `` p2p port, as well as ports for ssh and ports for verloc and measurement pings: + +``` +# for mix node +sudo ufw allow 1789,1790,8000,22/tcp + +# for gateway +sudo ufw allow 1789,22,9000/tcp + +# for network requester +sudo ufw allow 22,9000/tcp + +# for validator +sudo ufw allow 1317,26656,26660,22,80,443/tcp +``` + +Check the status of the firewall: +``` +sudo ufw status +``` + +For more information about your node's port configuration, check the [port reference table](https://nymtech.net/docs/nodes/gateway-setup.html#ports) below. + +### Automating your node with nohup, tmux and systemd + +Although it’s not totally necessary, it's useful to have the mix node automatically start at system boot time. + +#### nohup + +`nohup` is a command with which your terminal is told to ignore the `HUP` or 'hangup' signal. This will stop the node process ending if you kill your session. + +``` +nohup ./ run --id # where `` is the id you set during the `init` command and depends on which node you starting +``` + +#### tmux + +One way is to use `tmux` shell on top of your current VPS terminal. Tmux is a terminal multiplexer, it allows you to create several terminal windows and panes from a single terminal. Processes started in `tmux` keep running after closing the terminal as long as the given `tmux` window was not terminated. + +Use the following command to get `tmux`. + +Platform|Install Command +---|--- +Arch Linux|`pacman -S tmux` +Debian or Ubuntu|`apt install tmux` +Fedora|`dnf install tmux` +RHEL or CentOS|`yum install tmux` +macOS (using Homebrew|`brew install tmux` +macOS (using MacPorts)|`port install tmux` +openSUSE|`zypper install tmux` + +In case it didn't work for your distribution, see how to build `tmux` from [version control](https://github.com/tmux/tmux#from-version-control). + +**Running tmux** + +No when you installed tmux on your VPS, let's run a mix node on tmux, which allows you to detach your terminal and let your `` run on its own on the VPS. + +* Pause your `` +* Start tmux with the command +``` +tmux +``` +* The tmux terminal should open in the same working directory, just the layout changed into tmux default layout. +* Start the `` again with a command: +``` +./ run --id +``` +* Now, without closing the tmux window, you can close the whole terminal and the `` (and any other process running in tmux) will stay active. +* Next time just start your teminal, ssh into the VPS and run the following command to attach back to your previous session: +``` +tmux attach-session +``` +* To see keybinding options of tmux press `ctrl`+`b` and after 1 second `?` + +#### systemd + +Here's a systemd service file to do that: + +##### For mix node + +```ini +[Unit] +Description=Nym Mixnode ({{platform_release_version}}) +StartLimitInterval=350 +StartLimitBurst=10 + +[Service] +User= +LimitNOFILE=65536 +ExecStart=/home///nym-mixnode run --id +KillSignal=SIGINT +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target +``` + +* Put the above file onto your system at `/etc/systemd/system/nym-mixnode.service`. + +##### For Gateway + +```ini +[Unit] +Description=Nym Gateway ({{platform_release_version}}) +StartLimitInterval=350 +StartLimitBurst=10 + +[Service] +User=nym +LimitNOFILE=65536 +ExecStart=/home///nym-gateway run --id +KillSignal=SIGINT +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target +``` + +* Put the above file onto your system at `/etc/systemd/system/nym-gateway.service`. + +##### For Network requester + +```ini +[Unit] +Description=Nym Network Requester ({{platform_release_version}}) +StartLimitInterval=350 +StartLimitBurst=10 + +[Service] +User=nym # replace this with whatever user you wish +LimitNOFILE=65536 +# remember to add the `--enable-statistics` flag if running as part of a service grant and check the path to your nym-network-requester binary +ExecStart=/home/nym/nym-network-requester run --id +KillSignal=SIGINT +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target +``` + +Now enable and start your requester: + +``` +systemctl enable nym-network-requester.service +systemctl start nym-network-requester.service + +# you can always check your requester has succesfully started with: +systemctl status nym-network-requester.service +``` +* Put the above file onto your system at `/etc/systemd/system/nym-network-requester.service`. + +##### For Validator + +Below is a systemd unit file to place at `/etc/systemd/system/nymd.service`: + +```ini +[Unit] +Description=Nyxd +StartLimitInterval=350 +StartLimitBurst=10 + +[Service] +User=nyx # change to your user +Type=simple +Environment="LD_LIBRARY_PATH=/home/youruser/path/to/nyx/binaries" # change to correct path +ExecStart=/home/youruser/path/to/nyx/binaries/nymd start # change to correct path +Restart=on-failure +RestartSec=30 +LimitNOFILE=infinity + +[Install] +WantedBy=multi-user.target +``` + +Proceed to start it with: + +``` +systemctl daemon-reload # to pickup the new unit file +systemctl enable nymd # to enable the service +systemctl start nymd # to actually start the service +journalctl -f # to monitor system logs showing the service start +``` + +##### Following steps for Nym Mixnet nodes + +Change the `` in `ExecStart` to point at your `` binary (`nym-mixnode`, `nym-gateway` or `nym-network-requester`), and the `` so it is the user you are running as. + +If you have built nym in the `$HOME` directory on your server, and your username is `jetpanther`, then the start command for nym mixnode might look like this: + +`ExecStart=/home/jetpanther/nym/target/release/nym-mixnode run --id `. Basically, you want the full `/path/to/nym-mixnode run --id whatever-your-node-id-is` + +Then run: + +``` +# for mix node +systemctl enable nym-mix node.service + +# for gateway +systemctl enable nym-gateway.service +``` + +Start your node: + +``` +# for mix node +service nym-mixnode start + +# for gateway +service nym-gateway start + +``` + +This will cause your node to start at system boot time. If you restart your machine, the node will come back up automatically. + +You can also do `service stop` or `service restart`. + +Note: if you make any changes to your systemd script after you've enabled it, you will need to run: + +``` +systemctl daemon-reload +``` + +This lets your operating system know it's ok to reload the service configuration. + +### Setting the ulimit + +Linux machines limit how many open files a user is allowed to have. This is called a `ulimit`. + +`ulimit` is 1024 by default on most systems. It needs to be set higher, because mix nodes make and receive a lot of connections to other nodes. + +If you see errors such as: + +``` +Failed to accept incoming connection - Os { code: 24, kind: Other, message: "Too many open files" } +``` + +This means that the operating system is preventing network connections from being made. + +#### Set the ulimit via `systemd` service file + +> Replace `` variable with `nym-mixnode`, `nym-gateway` or `nym-network-requester` according the node you running on your machine. + +The ulimit setup is relevant for maintenance of nym mix node only. + +Query the `ulimit` of your `` with: + +``` +# for nym-, nym-gateway and nym-network requester: +grep -i "open files" /proc/$(ps -A -o pid,cmd|grep | grep -v grep |head -n 1 | awk '{print $1}')/limits + +# for nyx validator: +grep -i "open files" /proc/$(ps -A -o pid,cmd|grep nymd | grep -v grep |head -n 1 | awk '{print $1}')/limits +``` + + + +You'll get back the hard and soft limits, which looks something like this: + +``` +Max open files 65536 65536 files +``` + +If your output is **the same as above**, your node will not encounter any `ulimit` related issues. + +However if either value is `1024`, you must raise the limit via the systemd service file. Add the line: + +``` +LimitNOFILE=65536 +``` + +Reload the daemon: + +``` +systemctl daemon-reload +``` + +or execute this as root for system-wide setting of `ulimit`: + +``` +echo "DefaultLimitNOFILE=65535" >> /etc/systemd/system.conf +``` + +Reboot your machine and restart your node. When it comes back, use: +``` +# for nym-mixnode, nym-gateway and nym-network requester: +cat /proc/$(pidof )/limits | grep "Max open files"` to make sure the limit has changed to 65535. + +# for validator +cat /proc/$(pidof nym-validator)/limits | grep "Max open files" +``` +Make sure the limit has changed to 65535. + +#### Set the ulimit on `non-systemd` based distributions + +In case you chose tmux option for mix node automatization, see your `ulimit` list by running: + +``` +ulimit -a + +# watch for the output line -n +-n: file descriptors 1024 +``` + +You can change it either by running a command: + +``` +ulimit -u -n 4096 +``` + +or editing `etc/security/conf` and add the following lines: + +``` +# Example hard limit for max opened files +username hard nofile 4096 + +# Example soft limit for max opened files +username soft nofile 4096 +``` + +Then reboot your server and restart your mix node. + +## Virtual IPs and hosting via Google & AWS +For true internet decentralization we encourage operators to use diverse VPS providers instead of the largest companies offering such services. If for some reasons you have already running AWS or Google and want to setup a `` there, please read the following. + +On some services (AWS, Google, etc) the machine's available bind address is not the same as the public IP address. In this case, bind `--host` to the local machine address returned by `$(curl ifconfig.me)`, but also specify `--announce-host` with the public IP. Please make sure that you pass the correct, routable `--announce-host`. + +For example, on a Google machine, you may see the following output from the `ifconfig` command: + +``` +ens4: flags=4163 mtu 1460 + inet 10.126.5.7 netmask 255.255.255.255 broadcast 0.0.0.0 + ... +``` + +The `ens4` interface has the IP `10.126.5.7`. But this isn't the public IP of the machine, it's the IP of the machine on Google's internal network. Google uses virtual routing, so the public IP of this machine is something else, maybe `36.68.243.18`. + +`./nym-mixnode init --host 10.126.5.7`, initalises the mix node, but no packets will be routed because `10.126.5.7` is not on the public internet. + +Trying `nym-mixnode init --host 36.68.243.18`, you'll get back a startup error saying `AddrNotAvailable`. This is because the mix node doesn't know how to bind to a host that's not in the output of `ifconfig`. + +The right thing to do in this situation is to init with a command: +``` +./nym-mixnode init --host 10.126.5.7 --announce-host 36.68.243.18 +``` + +This will bind the mix node to the available host `10.126.5.7`, but announce the mix node's public IP to the directory server as `36.68.243.18`. It's up to you as a node operator to ensure that your public and private IPs match up properly. + +To find the right IP configuration, contact your VPS provider for support. + +## Nym API (previously 'Validator API') endpoints +Numerous API endpoints are documented on the Nym API (previously 'Validator API')'s [Swagger Documentation](https://validator.nymtech.net/api/swagger/index.html). There you can also try out various requests from your browser, and download the response from the API. Swagger will also show you what commands it is running, so that you can run these from an app or from your CLI if you prefer. + +### Mix node Reward Estimation API endpoint + +The Reward Estimation API endpoint allows mix node operators to estimate the rewards they could earn for running a Nym mix node with a specific `MIX_ID`. + +> The `` can be found in the "Mix ID" column of the [Network Explorer](https://explorer.nymtech.net/network-components/mixnodes/active). + +The endpoint is a particularly common for mix node operators as it can provide an estimate of potential earnings based on factors such as the amount of traffic routed through the mix node, the quality of the mix node's performance, and the overall demand for mixnodes in the network. This information can be useful for mix node operators in deciding whether or not to run a mix node and in optimizing its operations for maximum profitability. + +Using this API endpoint returns information about the Reward Estimation: + +``` +/status/mixnode//reward-estimation +``` + +Query Response: + +``` + "estimation": { + "total_node_reward": "942035.916721770541325331", + "operator": "161666.263307386408152071", + "delegates": "780369.65341438413317326", + "operating_cost": "54444.444444444444444443" + }, +``` + +> The unit of value is measured in `uNYM`. + +- `estimated_total_node_reward` - An estimate of the total amount of rewards that a particular mix node can expect to receive during the current epoch. This value is calculated by the Nym Validator based on a number of factors, including the current state of the network, the number of mix nodes currently active in the network, and the amount of network traffic being processed by the mix node. + +- `estimated_operator_reward` - An estimate of the amount of rewards that a particular mix node operator can expect to receive. This value is calculated by the Nym Validator based on a number of factors, including the amount of traffic being processed by the mix node, the quality of service provided by the mix node, and the operator's stake in the network. + +- `estimated_delegators_reward` - An estimate of the amount of rewards that mix node delegators can expect to receive individually. This value is calculated by the Nym Validator based on a number of factors, including the amount of traffic being processed by the mix node, the quality of service provided by the mix node, and the delegator's stake in the network. + +- `estimated_node_profit` - An estimate of the profit that a particular mix node operator can expect to earn. This value is calculated by subtracting the mix node operator's `operating_costs` from their `estimated_operator_reward` for the current epoch. + +- `estimated_operator_cost` - An estimate of the total cost that a particular mix node operator can expect to incur for their participation. This value is calculated by the Nym Validator based on a number of factors, including the cost of running a mix node, such as server hosting fees, and other expenses associated with operating the mix node. + +### Validator: Installing and configuring nginx for HTTPS +#### Setup +[Nginx](https://www.nginx.com/resources/glossary/nginx/#:~:text=NGINX%20is%20open%20source%20software,%2C%20media%20streaming%2C%20and%20more.&text=In%20addition%20to%20its%20HTTP,%2C%20TCP%2C%20and%20UDP%20servers.) is an open source software used for operating high-performance web servers. It allows us to set up reverse proxying on our validator server to improve performance and security. + +Install `nginx` and allow the 'Nginx Full' rule in your firewall: + +``` +sudo ufw allow 'Nginx Full' +``` + +Check nginx is running via systemctl: + +``` +systemctl status nginx +``` + +Which should return: + +``` +● nginx.service - A high performance web server and a reverse proxy server + Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) + Active: active (running) since Fri 2018-04-20 16:08:19 UTC; 3 days ago + Docs: man:nginx(8) + Main PID: 2369 (nginx) + Tasks: 2 (limit: 1153) + CGroup: /system.slice/nginx.service + ├─2369 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; + └─2380 nginx: worker process +``` + +#### Configuration + +Proxying your validator's port `26657` to nginx port `80` can then be done by creating a file with the following at `/etc/nginx/conf.d/validator.conf`: + +``` +server { + listen 80; + listen [::]:80; + server_name "domain_name"; + + location / { + proxy_pass http://127.0.0.1:26657; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` + +Followed by: + +``` +sudo apt install certbot nginx python3 +certbot --nginx -d nym-validator.yourdomain.com -m you@yourdomain.com --agree-tos --noninteractive --redirect +``` + +```admonish caution title="" +If using a VPS running Ubuntu 20: replace `certbot nginx python3` with `python3-certbot-nginx` +``` + +These commands will get you an https encrypted nginx proxy in front of the API. + +### Configuring Prometheus metrics (optional) + +Configure Prometheus with the following commands (adapted from NodesGuru's [Agoric setup guide](https://nodes.guru/agoric/setup-guide/en)): + +``` +echo 'export OTEL_EXPORTER_PROMETHEUS_PORT=9464' >> $HOME/.bashrc +source ~/.bashrc +sed -i '/\[telemetry\]/{:a;n;/enabled/s/false/true/;Ta}' $HOME/.nymd/config/app.toml +sed -i "s/prometheus-retention-time = 0/prometheus-retention-time = 60/g" $HOME/.nymd/config/app.toml +sudo ufw allow 9464 +echo 'Metrics URL: http://'$(curl -s ifconfig.me)':26660/metrics' +``` + +Your validator's metrics will be available to you at the returned 'Metrics URL'. + +~~~admonish example collapsible=true title="Console output" +``` +# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. +# TYPE go_gc_duration_seconds summary +go_gc_duration_seconds{quantile="0"} 6.7969e-05 +go_gc_duration_seconds{quantile="0.25"} 7.864e-05 +go_gc_duration_seconds{quantile="0.5"} 8.4591e-05 +go_gc_duration_seconds{quantile="0.75"} 0.000115919 +go_gc_duration_seconds{quantile="1"} 0.001137591 +go_gc_duration_seconds_sum 0.356555301 +go_gc_duration_seconds_count 2448 +# HELP go_goroutines Number of goroutines that currently exist. +# TYPE go_goroutines gauge +go_goroutines 668 +# HELP go_info Information about the Go environment. +# TYPE go_info gauge +go_info{version="go1.15.7"} 1 +# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. +# TYPE go_memstats_alloc_bytes gauge +go_memstats_alloc_bytes 1.62622216e+08 +# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed. +# TYPE go_memstats_alloc_bytes_total counter +go_memstats_alloc_bytes_total 2.09341707264e+11 +# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. +# TYPE go_memstats_buck_hash_sys_bytes gauge +go_memstats_buck_hash_sys_bytes 5.612319e+06 +# HELP go_memstats_frees_total Total number of frees. +# TYPE go_memstats_frees_total counter +go_memstats_frees_total 2.828263344e+09 +# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started. +# TYPE go_memstats_gc_cpu_fraction gauge +go_memstats_gc_cpu_fraction 0.03357798610671518 +# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. +# TYPE go_memstats_gc_sys_bytes gauge +go_memstats_gc_sys_bytes 1.3884192e+07 +``` +~~~ + +## Ports +All ``-specific port configuration can be found in `$HOME/.nym///config/config.toml`. If you do edit any port configs, remember to restart your client and node processes. + +### Mix node port reference +| Default port | Use | +| ------------ | ------------------------- | +| `1789` | Listen for Mixnet traffic | +| `1790` | Listen for VerLoc traffic | +| `8000` | Metrics http API endpoint | + + +### Gateway port reference +| Default port | Use | +|--------------|---------------------------| +| `1789` | Listen for Mixnet traffic | +| `9000` | Listen for Client traffic | + +### Network requester port reference + +| Default port | Use | +|--------------|---------------------------| +| `9000` | Listen for Client traffic | + +### Validator port reference +All validator-specific port configuration can be found in `$HOME/.nymd/config/config.toml`. If you do edit any port configs, remember to restart your validator. + +| Default port | Use | +|--------------|--------------------------------------| +| 1317 | REST API server endpoint | +| 26656 | Listen for incoming peer connections | +| 26660 | Listen for Prometheus connections | + +/ diff --git a/documentation/operators/src/nodes/mix-node-setup.md b/documentation/operators/src/nodes/mix-node-setup.md new file mode 100644 index 0000000000..b168329487 --- /dev/null +++ b/documentation/operators/src/nodes/mix-node-setup.md @@ -0,0 +1,309 @@ +# Mix Nodes + +> The Nym mix node binary was built in the [building nym](../binaries/building-nym.md) section. If you haven't yet built Nym and want to run the code, go there first. + +> Any syntax in `<>` brackets is a user's unique variable. Exchange with a corresponding name without the `<>` brackets. + +## Current version +``` + +``` + +The `nym-mix node` binary is currently one point version ahead of the rest of the platform binaries due to a patch applied between releases. + +## Preliminary steps + +Make sure you do the preparation listed in the [preliminary steps page](../preliminary-steps.md) before setting up your mix node. + +## Mix node setup + +Now that you have built the [codebase](../binaries/building-nym.md), set up your [wallet](https://nymtech.net/docs/wallet/desktop-wallet.html), and have a VPS with the `nym-mix node` binary, you can set up your mix node with the instructions below. + +To begin, move to `/target/release` directory from which you run the node commands: + +``` +cd target/release +``` + +### Viewing command help + +You can check that your binaries are properly compiled with: + +``` +./nym-mixnode --help +``` + +Which should return a list of all available commands. + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +You can also check the various arguments required for individual commands with: + +``` +./nym-mixnode --help +``` + +> Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. + +### Initialising your mix node + +To check available configuration options for initializing your node use: + +``` +./nym-mixnode init --help +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +Initalise your mix node with the following command, replacing the value of `--id` with the moniker you wish to give your mix node. Your `--host` must be publicly routable on the internet in order to mix packets, and can be either an Ipv4 or IPv6 address. The `$(curl -4 https://ifconfig.me)` command returns your IP automatically using an external service. If you enter your IP address manually, enter it **without** any port information. + +``` +./nym-mixnode init --id --host $(curl -4 https://ifconfig.me) +``` + + +~~~admonish example collapsible=true title="Console output" +``` +.nym-mixnode init --id --host $(curl -4 https://ifconfig.me) --wallet-address + + +Initialising mixnode ... +Saved mixnet identity and sphinx keypairs + 2023-06-04T08:20:32.862Z INFO nym_config > Configuration file will be saved to "/home//.nym/mixnodes//config/config.toml" +Saved configuration file to "/home//.nym/mixnodes//config/config.toml" +Mixnode configuration completed. + + _ __ _ _ _ __ ___ + | '_ \| | | | '_ \ _ \ + | | | | |_| | | | | | | + |_| |_|\__, |_| |_| |_| + |___/ + + (nym-mixnode - version {{platform_release_version}}) + + +Identity Key: DhmUYedPZvhP9MMwXdNpPaqCxxTQgjAg78s2nqtTTiNF","version":"{{platform_release_version}}"},"cost_params +Sphinx Key: CfZSy1jRfrfiVi9JYexjFWPqWkKoY72t7NdpWaq37K8Z +Host: 62.240.134.189 (bind address: 62.240.134.189) +Version: {{platform_release_version}} +Mix Port: 1789, Verloc port: 1790, Http Port: 8000 +``` +~~~ + +> The `init` command will refuse to destroy existing mix node keys. + +During the `init` process you will have the option to change the `http_api`, `verloc` and `mixnode` ports from their default settings. If you wish to change these in the future you can edit their values in the `config.toml` file created by the initialization process, which is located at `~/.nym/mixnodes//`. + +### Bonding your mix node + +```admonish caution +From `v1.1.3`, if you unbond your mix node that means you are leaving the mixnet and you will lose all your delegations (permanently). You can join again with the same identity key, however, you will start with **no delegations**. +``` + +#### Bond via the Desktop wallet (recommended) + +You can bond your mix node via the Desktop wallet. + +* Open your wallet, and head to the `Bond` page, then select the node type `Mixnode` and input your node details. Press `Next`. + +* Enter the `Amount`, `Operating cost` and `Profit margin` and press `Next`. + +* You will be asked to run a the `sign` command with your `gateway` - copy and paste the long signature as the value of `--contract-msg` and run it. + +``` +./nym-mixnode sign --id --contract-msg +``` + +It will look something like this: + +~~~admonish example collapsible=true title="Console output" +``` +./nym-mixnode sign --id upgrade_test --contract-msg 22Z9wt4PyiBCbMiErxj5bBa4VCCFsjNawZ1KnLyMeV9pMUQGyksRVANbXHjWndMUaXNRnAuEVJW6UCxpRJwZe788hDt4sicsrv7iAXRajEq19cWPVybbUqgeo76wbXbCbRdg1FvVKgYZGZZp8D72p5zWhKSBRD44qgCrqzfV1SkiFEhsvcLUvZATdLRocAUL75KmWivyRiQjCE1XYEWyRH9yvRYn4TymWwrKVDtEB63zhHjATN4QEi2E5qSrSbBcmmqatXsKakbgSbQoLsYygcHx7tkwbQ2HDYzeiKP1t16Rhcjn6Ftc2FuXUNnTcibk2LQ1hiqu3FAq31bHUbzn2wiaPfm4RgqTwGM4eqnjBofwR3251wQSxbYwKUYwGsrkweRcoPuEaovApR9R19oJ7GVG5BrKmFwZWX3XFVuECe8vt1x9MY7DbQ3xhAapsHhThUmzN6JPPU4qbQ3PdMt3YVWy6oRhap97ma2dPMBaidebfgLJizpRU3Yu7mtb6E8vgi5Xnehrgtd35gitoJqJUY5sB1p6TDPd6vk3MVU1zqusrke7Lvrud4xKfCLqp672Bj9eGb2wPwow643CpHuMkhigfSWsv9jDq13d75EGTEiprC2UmWTzCJWHrDH7ka68DZJ5XXAW67DBewu7KUm1jrJkNs55vS83SWwm5RjzQLVhscdtCH1Bamec6uZoFBNVzjs21o7ax2WHDghJpGMxFi6dmdMCZpqn618t4 + + _ __ _ _ _ __ ___ + | '_ \| | | | '_ \ _ \ + | | | | |_| | | | | | | + |_| |_|\__, |_| |_| |_| + |___/ + + (nym-mixnode - version {{platform_release_version}}) + + +>>> attempting to sign 22Z9wt4PyiBCbMiErxj5bBa4VCCFsjNawZ1KnLyMeV9pMUQGyksRVANbXHjWndMUaXNRnAuEVJW6UCxpRJwZe788hDt4sicsrv7iAXRajEq19cWPVybbUqgeo76wbXbCbRdg1FvVKgYZGZZp8D72p5zWhKSBRD44qgCrqzfV1SkiFEhsvcLUvZATdLRocAUL75KmWivyRiQjCE1XYEWyRH9yvRYn4TymWwrKVDtEB63zhHjATN4QEi2E5qSrSbBcmmqatXsKakbgSbQoLsYygcHx7tkwbQ2HDYzeiKP1t16Rhcjn6Ftc2FuXUNnTcibk2LQ1hiqu3FAq31bHUbzn2wiaPfm4RgqTwGM4eqnjBofwR3251wQSxbYwKUYwGsrkweRcoPuEaovApR9R19oJ7GVG5BrKmFwZWX3XFVuECe8vt1x9MY7DbQ3xhAapsHhThUmzN6JPPU4qbQ3PdMt3YVWy6oRhap97ma2dPMBaidebfgLJizpRU3Yu7mtb6E8vgi5Xnehrgtd35gitoJqJUY5sB1p6TDPd6vk3MVU1zqusrke7Lvrud4xKfCLqp672Bj9eGb2wPwow643CpHuMkhigfSWsv9jDq13d75EGTEiprC2UmWTzCJWHrDH7ka68DZJ5XXAW67DBewu7KUm1jrJkNs55vS83SWwm5RjzQLVhscdtCH1Bamec6uZoFBNVzjs21o7ax2WHDghJpGMxFi6dmdMCZpqn618t4 +>>> decoding the message... +>>> message to sign: {"nonce":0,"algorithm":"ed25519","message_type":"mixnode-bonding","content":{"sender":"n1eufxdlgt0puwrwptgjfqne8pj4nhy2u5ft62uq","proxy":null,"funds":[{"denom":"unym","amount":"100000000"}],"data":{"mix_node":{"host":"62.240.134.189","mix_port":1789,"verloc_port":1790,"http_api_port":8000,"sphinx_key":"CfZSy1jRfrfiVi9JYexjFWPqWkKoY72t7NdpWaq37K8Z","identity_key":"DhmUYedPZvhP9MMwXdNpPaqCxxTQgjAg78s2nqtTTiNF","version":"1.1.14"},"cost_params":{"profit_margin_percent":"0.1","interval_operating_cost":{"denom":"unym","amount":"40000000"}}}}} +``` +~~~ + +* Copy the resulting signature: + +``` +>>> The base58-encoded signature is: +2GbKcZVKFdpi3sR9xoJWzwPuGdj3bvd7yDtDYVoKfbTWdpjqAeU8KS5bSftD5giVLJC3gZiCg2kmEjNG5jkdjKUt +``` + +* And paste it into the wallet nodal, press `Next` and confirm the transaction. + +![Paste Signature](../images/wallet-screenshots/wallet-sign.png) + +* Your node will now be bonded and ready to mix at the beginning of the next epoch (at most 1 hour). + +> You are asked to `sign` a transaction on bonding so that the mixnet smart contract is able to map your nym address to your node. This allows us to create a nonce for each account and defend against replay attacks. + +#### Bond via the CLI (power users) +If you want to bond your mix node via the CLI, then check out the [relevant section in the Nym CLI](https://nymtech.net/docs/tools/nym-cli.html#bond-a-mix-node) docs. + +### Running your mix node + +Now you've bonded your mix node, run it with: + +``` +./nym-mixnode run --id +``` + +If everything worked, you'll see your node running on the either the [Sandbox testnet network explorer](https://sandbox-explorer.nymtech.net) or the [mainnet network explorer](https://explorer.nymtech.net), depending on which network you're running. + +Note that your node's public identity key is displayed during startup, you can use it to identify your node in the list. + +Have a look at the saved configuration files in `$HOME/.nym/mixnodes/` to see more configuration options. + +## Node Description (optional) + +In order to easily identify your node via human-readable information later on in the development of the testnet when delegated staking is implemented, you can `describe` your mix node with the following command: + +``` +./nym-mixnode describe --id +``` +Node description is a short text that describes your node. It is displayed in the `./nym-mixnode list` command and in the `./nym-mixnode node-details --id ` command. It also shows up in the node explorer to let people know what your node is about and link to your website. + +You can set your node description, by creating a file called `description.toml` and put it in the same directory as your `config.toml` file (`~/.nym/mixnodes//description.toml`). The file should look like this example: + +```toml +name = "Winston Smith" +description = "I am the Sphinx" +link = "https://nymtech.net" +location = "Giza, Egypt" +``` + +> Remember to restart your mix node process in order for the new description to be propagated. + +## Node Families + +Node family involves setting up a group of mix nodes that work together to provide greater privacy and security for network communications. This is achieved by having the nodes in the family share information and routes, creating a decentralized network that makes it difficult for third parties to monitor or track communication traffic. + +### Create a Node Family + +To create a Node family, you will need to install and configure multiple mix nodes, and then use the CLI to link them together into a family. Once your Node family is up and running, you can use it to route your network traffic through a series of nodes, obscuring the original source and destination of the communication. + +You can use either `nym-cli` which can be downloaded from the [release page](https://github.com/nymtech/nym/releases) or compiling `nyxd`. + + +Change directory by `cd ///` and run the following on the family head to obtain the signature for the member: + +``` +./nym-mixnode sign --id --text +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +Using `nym-cli`: + +> `--mnemonic` is the mnemonic of the member wanting to be the head of family. + +``` +/nym-cli cosmwasm execute '{"create_family": {"signature": "","family_head": "","owner_signature":"","label": ""}}' --mnemonic +``` + +Using `nyxd`: + +> `--from` is mnemonic of the member wanting to join the family. + +``` +./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block +``` + +To get the node owner signature, use: + +`./nym-mixnode node-details --id ` + +### Joining a Node Family + +Change directory by `cd ///` and run the following on the family head to obtain the signature for the member: + +``` +./nym-mixnode sign --id --text +``` + +~~~admonish example collapsible=true title="Console output" +``` + +``` +~~~ + +Using `nym-cli`: + +``` +./nym-cli cosmwasm execute '{"join_family": {"signature": "","family_head": "","owner_signautre": "", "label":""}}' --mnemonic +``` + +Using `nyxd`: + +``` +./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block +``` + + +To get the node owner signature, use: + +`./nym-mixnode node-details --id ` + + +### Leaving a family +If wanting to leave, run the same initial command as above, followed by: + +Using `nym-cli`: + +``` +./nym-cli cosmwasm execute '{"leave_family": {"signature": "","family_head": "","owner_signautre": ""}}' --mnemonic +``` + +Using `nyxd`: + +``` +./nyxd tx wasm execute ${MIXNET-CONTRACT} '{"join_family": {"signature": "","family_head": ""}}' --node ${VALIDATOR-ENDPOINT} --from mix1 --chain-id nyx --gas-prices 0.025unym --gas auto --gas-adjustment 1.3 -y -b block +``` + +## Checking that your node is mixing correctly +### Network explorers +Once you've started your mix node and it connects to the validator, your node will automatically show up in the 'Mix nodes' section of either the Nym Network Explorers: + +- [Mainnet](https://explorer.nymtech.net/overview) +- [Sandbox testnet](https://sandbox-explorer.nymtech.net/) + +Enter your **identity key** to find your node. There are numerous statistics about your node on that page that are useful for checking your up-time history, packets mixed, and any delegations your node may have. + +There are also 2 community explorers which have been created by [Nodes Guru](https://nodes.guru): + +- [Mainnet](https://mixnet.explorers.guru/) +- [Sandbox testnet](https://sandbox.mixnet.explorers.guru/) + +For more details see [Troubleshooting FAQ](../nodes/troubleshooting.md) + + + +## Maintenance + +For mix node upgrade, firewall setup, port configuration, API endpoints, VPS suggestions, automation and more, see the [maintenance page](./maintenance.md) + diff --git a/documentation/docs/src/nodes/network-requester-setup.md b/documentation/operators/src/nodes/network-requester-setup.md similarity index 79% rename from documentation/docs/src/nodes/network-requester-setup.md rename to documentation/operators/src/nodes/network-requester-setup.md index 6fe3ef7971..2082c2035f 100644 --- a/documentation/docs/src/nodes/network-requester-setup.md +++ b/documentation/operators/src/nodes/network-requester-setup.md @@ -1,23 +1,28 @@ # Network Requesters -> The Nym network requester was built in the [building nym](../binaries/building-nym.md) section. If you haven't yet built Nym and want to run the code on this page, go there first. +> The Nym gateway was built in the [building nym](../binaries/building-nym.md) section. If you haven't yet built Nym and want to run the code, go there first. +> Any syntax in `<>` brackets is a user's unique variable. Exchange with a corresponding name without the `<>` brackets. ## Current version ``` ``` +## Preliminary steps + +Make sure you do the preparation listed in the [preliminary steps page](../preliminary-steps.md) before setting up your network requester. + ## 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. -By default the network requester is **not** an open proxy (although it can be used as one). It uses a file called `allowed.list` (located in `~/.nym/service-providers/network-requester//`) as a whitelist for outbound requests. +By default the network requester is **not** an open proxy (although it can be used as one). It uses a file called `allowed.list` (located in `~/.nym/service-providers/network-requester//`) as a whitelist for outbound requests. Any request to a URL which is not on this list will be blocked. -On startup, if this file is not present, the requester will grab the default whitelist from [here](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) automatically. +On startup, if this file is not present, the requester will grab the default whitelist from [Nym's default list](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt) automatically. -This default whitelist is useful for knowing that the majority of network requesters are able to support certain apps 'out of the box'. +This default whitelist is useful for knowing that the majority of Network requesters are able to support certain apps 'out of the box'. **Operators of a network requester are of course free to edit this file and add the URLs of services they wish to support to it!** You can find instructions below on adding your own URLs or IPs to this list. @@ -95,12 +100,18 @@ alephium.org ``` ## Network Requester Directory -You can find a list of Network Requesters running the default whitelist [here](https://explorer.nymtech.net/network-components/service-providers). This list comprises of the NRs running as infrastructure for NymConnect. +You can find a list of Network requesters running the default whitelist in the [explorer](https://explorer.nymtech.net/network-components/service-providers). This list comprises of the NRs running as infrastructure for NymConnect. > We are currently working on a smart-contract based solution more in line with how Mix nodes and Gateways announce themselves to the network. ## Viewing command help +To begin, move to `/target/release` directory from which you run the node commands: + +``` +cd target/release +``` + The `./nym-network-requester --help ` command can be used to show a list of available parameters. ~~~admonish example collapsible=true title="Console output" @@ -112,17 +123,21 @@ The `./nym-network-requester --help ` command can be used to show a list of avai You can check the required parameters for available commands by running: ``` -./nym-network-requester --help +./nym-network-requester --help ``` +> Adding `--no-banner` startup flag will prevent Nym banner being printed even if run in tty environment. + ## Initializing and running your network requester -The network-requester needs to be initialized before it can be run. This is required for the embedded nym-client to connect successfully to the mixnet. We want to specify an `id` using the `--id` command and give it a value of your choosing. The following command will achieve that: +The network-requester needs to be initialized before it can be run. This is required for the embedded nym-client to connect successfully to the mixnet. We want to specify an `` using the `--id` command and give it a value of your choice. The following command will achieve that: ``` - ./nym-network-requester init --id example + ./nym-network-requester init --id ``` +In the following we used `example`. + ~~~admonish example collapsible=true title="Console output" ``` @@ -133,15 +148,12 @@ The network-requester needs to be initialized before it can be run. This is requ Now that we have initialized our network-requester, we can start it with the following command: ``` - ./nym-network-requester run --id example + ./nym-network-requester run --id ``` -## Upgrading your network requester -You can upgrade your network requester by following these steps: +## Maintenance -* stop your network requester service -* replace the old binary with the new binary -* restart your service using the commands in the previous section of the document +For network requester upgrade (including an upgrade from `= v1.1.10`), firewall setup, port configuration, API endpoints, VPS suggestions, automation and more, see the [maintenance page](./maintenance.md). ### Upgrading to >= v1.1.10 from >>>>>> release/v1.1.27:documentation/docs/src/nodes/network-requester-setup.md +>>>>>>> 85ab634d9c1f1f54073c97a133c83e645a0a3f41 ## Using your network requester @@ -254,7 +268,7 @@ ls $HOME/.nym/service-providers/network-requester/ # returns: allowed.list unknown.list ``` -We already know that `allowed.list` is what lets requests go through. All unknown requests are logged to `unknown.list`. If you want to try using a new client type, just start the new application, point it at your local [socks client](../clients/socks5-client.md) (configured to use your remote `nym-network-requester`), and keep copying URLs from `unknown.list` into `allowed.list` (it may take multiple tries until you get all of them, depending on the complexity of the application). Make sure to restart your network requester! +We already know that `allowed.list` is what lets requests go through. All unknown requests are logged to `unknown.list`. If you want to try using a new client type, just start the new application, point it at your local [socks client](https://nymtech.net/docs/clients/socks5-client.html) (configured to use your remote `nym-network-requester`), and keep copying URLs from `unknown.list` into `allowed.list` (it may take multiple tries until you get all of them, depending on the complexity of the application). Make sure to restart your network requester! > If you are adding custom domains, please note that whilst they may appear in the logs of your network-requester as something like `api-0.core.keybaseapi.com:443`, you **only need** to include the main domain name, in this instance `keybaseapi.com` @@ -262,7 +276,7 @@ We already know that `allowed.list` is what lets requests go through. All unknow If you *really* want to run an open proxy, perhaps for testing purposes for your own use or among a small group of trusted friends, it is possible to do so. You can disable network checks by passing the flag `--open-proxy` flag when you run it. If you run in this configuration, you do so at your own risk. ## Testing your network requester -1. Add `nymtech.net` to your `allowed.list` (remember to restart your network requester). +1. Make sure `nymtech.net` is in your `allowed.list` (remember to restart your network requester). 2. Ensure that your network-requester is initialized and running. diff --git a/documentation/operators/src/nodes/setup-guides.md b/documentation/operators/src/nodes/setup-guides.md new file mode 100644 index 0000000000..ca86dd99f4 --- /dev/null +++ b/documentation/operators/src/nodes/setup-guides.md @@ -0,0 +1,11 @@ +# Node Setup Guides + +To setup any type of Nym's node, start with building [Nym's platform](../binaries/building-nym.md) on the machine (VPS) where you want to run the node. Nodes will need to be bond to Nym's wallet, setup one [here](https://nymtech.net/docs/wallet/desktop-wallet.html). + +This section contains setup guides for the following node types: +* [Mix node](./mix-node-setup.md) +* [Gateway](./gateway-setup.md) +* [Network Requester](./network-requester-setup.md) +* [Validator](./validator-setup.md) + + diff --git a/documentation/docs/src/nodes/troubleshooting.md b/documentation/operators/src/nodes/troubleshooting.md similarity index 54% rename from documentation/docs/src/nodes/troubleshooting.md rename to documentation/operators/src/nodes/troubleshooting.md index e832edbc39..20b98970df 100644 --- a/documentation/docs/src/nodes/troubleshooting.md +++ b/documentation/operators/src/nodes/troubleshooting.md @@ -1,6 +1,5 @@ # Troubleshooting - ## Binary Build Problems ### I am trying to build from the GitHub archive files and the build fails @@ -20,9 +19,19 @@ Why does this happen? We have scripts which automatically include the Git commit hash and Git tag in the binary for easier debugging later. If you download a .zip and try building from that, it's not a Git repository and build will fail as above. +What to do? + +* Open terminal in the directory where you want to have a git repository +* To get Nym repository for the first time, run: +``` +git clone https://github.com/nymtech/nym.git +``` +* Follow the instructions to build the platform +* To upgrade, pause your nodes, in the same terminal window run `git pull`, follow the upgrade instructions and re-start your nodes. + ## General Node Config -### Where can I find my private and public keys and config? +### Where can I find my private and public keys and config? All config and keys files are stored in a directory named after your `id` which you chose during the `init` process, and can be found at the following PATH: `$HOME/.nym//` where `$HOME` is a home directory of the user (your current user in this case) that launched the node or client. @@ -41,7 +50,7 @@ bob@nym:~$ tree /home/nym/.nym/mixnodes/ | `-- public_sphinx.pem ``` -> If you `cat` the `public_sphinx.pem key`, the output will be different from the public key you will see on Nym [dashboard](https://sandbox-explorer.nymtech.net/). The reason for this is that `.pem` files are encoded in **base64**, however on the web they are in **base58**. Don't be confused if your keys look different. They are the same keys, just with different encoding :) +> If you `cat` the `public_sphinx.pem` key, the output will be different from the public key you will see on Nym [dashboard](https://sandbox-explorer.nymtech.net/). The reason for this is that `.pem` files are encoded in **base64**, however on the web they are in **base58**. Don't be confused if your keys look different. They are the same keys, just with different encoding :) ## Mix Nodes @@ -49,15 +58,17 @@ bob@nym:~$ tree /home/nym/.nym/mixnodes/ ### How can I tell my node is up and running and mixing traffic? First of all check the 'Mixnodes' section of either the Nym Network Explorers: -* [Mainnet](https://explorer.nymtech.net/overview) +* [Mainnet](https://explorer.nymtech.net/) * [Sandbox testnet](https://sandbox-explorer.nymtech.net/) -Enter your **identity key** to find your node. Check the contents of the 'mixnode stats' and 'uptime story' sections. +Enter your **identity key** to find your node. Check the contents of the `Mixnode stats` and `Routing score` sections. There are 2 community explorers currently, which have been created by [Nodes Guru](https://nodes.guru): * [Mainnet](https://mixnet.explorers.guru/) * [Sandbox testnet](https://sandbox.mixnet.explorers.guru/) +[Here](https://github.com/cosmos/chain-registry/blob/master/nyx/chain.json#L158-L187) is a dictionary with Nyx chain registry entry regarding all explorers. + If you want more information, or if your node isn't showing up on the explorer of your choice and you want to double-check, here are some examples on how to check if the node is configured properly. #### Check from your VPS @@ -67,7 +78,7 @@ Additional details can be obtained via various methods after you connect to your ##### Socket statistics with `ss` ``` -sudo ss -s -t | grep 1789 # if you have specified a different port in your mixnode config, change accordingly +sudo ss -s -t | grep 1789 # if you have specified a different port in your mix node config, change accordingly ``` This command should return a lot of data containing `ESTAB`. This command should work on every unix based system. @@ -79,7 +90,7 @@ This command should return a lot of data containing `ESTAB`. This command should lsof -v # install if not installed sudo apt install lsof -# run against mixnode port +# run against mix node port sudo lsof -i TCP:1789 # if you have specified a different port in your mixnode config, change accordingly ``` @@ -99,7 +110,7 @@ nym-mixno 103349 root 57u IPv6 1333229976 0t0 TCP [2a03:b0c0:3:d0::ff3: sudo journalctl -u nym-mixnode -o cat | grep "Since startup mixed" ``` -If you have created `nym-mixnode.service` file (i.e. you are running your mixnode via `systemd`) then this command shows you how many packets have you mixed so far, and should return a list of messages like this: +If you have created `nym-mixnode.service` file (i.e. you are running your mix node via `systemd`) then this command shows you how many packets have you mixed so far, and should return a list of messages like this: ``` 2021-05-18T12:35:24.057Z INFO nym_mixnode::node::metrics > Since startup mixed 233639 packets! @@ -111,6 +122,16 @@ If you have created `nym-mixnode.service` file (i.e. you are running your mixnod You can add ` | tail` to the end of the command to watch for new entries in real time if needed. +##### build-info + +A `build-info` command prints the build information like commit hash, rust version, binary version just like what command `--version` does. However, you can also specify an `--output=json` flag that will format the whole output as a json, making it an order of magnitude easier to parse. + +For example `./target/debug/nym-network-requester --no-banner build-info --output json` will return: + +``` +{"binary_name":"nym-network-requester","build_timestamp":"2023-07-24T15:38:37.00657Z","build_version":"1.1.23","commit_sha":"c70149400206dce24cf20babb1e64f22202672dd","commit_timestamp":"2023-07-24T14:45:45Z","commit_branch":"feature/simplify-cli-parsing","rustc_version":"1.71.0","rustc_channel":"stable","cargo_profile":"debug"} +``` + #### Check from your local machine ##### Scan ports with `nmap`: @@ -119,7 +140,7 @@ You can add ` | tail` to the end of the command to watch for new entries in real nmap -p 1789 -Pn ``` -If your mixnode is configured properly it should output something like this: +If your mix node is configured properly it should output something like this: ``` bob@desktop:~$ nmap -p 1789 95.296.134.220 -Pn @@ -138,58 +159,39 @@ curl --location --request GET 'https://validator.nymtech.net/api/v1/mixnodes/' Will return a list all nodes currently online. -You can query gateways by replacing `mixnodes` with `gateways` in the above command, and can query for the mixnodes and gatways on the Sandbox testnet by replacing `validator` with `sandbox-validator`. +You can query gateways by replacing `mixnodes` with `gateways` in the above command, and can query for the mixnodes and gateways on the Sandbox testnet by replacing `validator` with `sandbox-validator`. #### Check with Network API -We currently have an API set up returning our metrics tests of the network. There are two endpoints to ping for information about your mixnode, `report` and `history`. Find more information about this in the [Mixnodes metrics documentation](../nodes/mix-node-setup.md#metrics--api-endpoints). +We currently have an API set up returning our metrics tests of the network. There are two endpoints to ping for information about your mix node, `report` and `history`. Find more information about this in the [Mixnodes metrics documentation](./maintenance.md#metrics--api-endpoints). ### Why is my node not mixing any packets? If you are still unable to see your node on the dashboard, or your node is declaring it has not mixed any packets, there are several potential issues: -- The firewall on your host machine is not configured properly. +- The firewall on your host machine is not configured properly. Checkout the [instructions](./maintenance.md#configure-your-firewall). - You provided incorrect information when bonding your node. -- You are running your mixnode from a VPS without IPv6 support. -- You did not use the `--announce-host` flag while running the mixnode from your local machine behind NAT. -- You did not configure your router firewall while running the mixnode from your local machine behind NAT, or you are lacking IPv6 support. -- Your mixnode is not running at all, it has either exited / panicked or you closed the session without making the node persistent. +- You are running your mix node from a VPS without IPv6 support. +- You did not use the `--announce-host` flag while running the mix node from your local machine behind NAT. +- You did not configure your router firewall while running the mix node from your local machine behind NAT, or you are lacking IPv6 support. +- Your mix node is not running at all, it has either exited / panicked or you closed the session without making the node persistent. Check out the [instructions](./maintenance.md#automating-your-node-with-tmux-and-systemd). ```admonish caution -Your mixnode **must speak both IPv4 and IPv6** in order to cooperate with other nodes and route traffic. This is a common reason behind many errors we are seeing among node operators, so check with your provider that your VPS is able to do this! -``` - -#### Incorrectly configured firewall - -The most common reason your mixnode might not be mixing packets is due to a poorly configured firewall. The following commands will allow you to set up a firewall using `ufw`. - -``` -# check if you have ufw installed -ufw version -# if it is not installed, install with -sudo apt install ufw -y -# enable ufw -sudo ufw enable -# check the status of the firewall -sudo ufw status -``` - -Finally open your mixnode's p2p port, as well as ports for ssh, http, and https connections, and ports `8000` and `1790` for verloc and measurement pings: - -``` -sudo ufw allow 1789,1790,8000,22,80,443/tcp -# check the status of the firewall -sudo ufw status +Your mix node **must speak both IPv4 and IPv6** in order to cooperate with other nodes and route traffic. This is a common reason behind many errors we are seeing among node operators, so check with your provider that your VPS is able to do this! ``` #### Incorrect bonding information -Check that you have provided the correct information when bonding your mixnode in the web wallet [interface](https://sandbox-wallet.nymtech.net/). When in doubt, unbond and then rebond your node! +Check that you have provided the correct information when bonding your mix node in the web wallet interface. When in doubt, un-bond and then re-bond your node! + +> All delegated stake will be lost when un-bonding! However the mix node must be operational in the first place for the delegation to have any effect. #### Missing `announce-host` flag -On certain cloud providers such as AWS and Google Cloud, you need to do some additional configuration of your firewall and use `--host` with your **local ip** and `--announce-host` with the **public ip** of your mixnode host. +On certain cloud providers such as AWS and Google Cloud, you need to do some additional configuration of your firewall and use `--host` with your **local ip** and `--announce-host` with the **public ip** of your mix node host. + +If the difference between the two is unclear, contact the help desk of your VPS provider. #### No IPv6 connectivity @@ -220,87 +222,33 @@ bob@nym:~$ hostname -I ### Running on a local machine behind NAT with no fixed IP address -Your ISP has to be IPv6 ready if you want to run a mixnode on your local machine. Sadly, in 2020, most of them are not and you won't get an IPv6 address by default from your ISP. Usually it is a extra paid service or they simply don't offer it. +Your ISP has to be IPv6 ready if you want to run a mix node on your local machine. Sadly, in 2020, most of them are not and you won't get an IPv6 address by default from your ISP. Usually it is an extra paid service or they simply don't offer it. -Before you begin, check if you have IPv6 [here](https://test-ipv6.cz/). If not, then don't waste your time to run a node which won't ever be able to mix any packet due to this limitation. Call your ISP and ask for IPv6, there is a plenty of it for everyone! +Before you begin, check if you have IPv6 [here](https://test-ipv6.cz/) or by running command explained in the [section above](./troubleshooting.md#no-ipv6-connectivity). If not, then don't waste your time to run a node which won't ever be able to mix any packet due to this limitation. Call your ISP and ask for IPv6, there is a plenty of it for everyone! -If all goes well and you have IPv6 available, then you will need to `init` the mixnode with an extra flag, `--announce-host`. You will also need to edit your `config.toml` file each time your IPv4 address changes, that could be a few days or a few weeks. +If all goes well and you have IPv6 available, then you will need to `init` the mix node with an extra flag, `--announce-host`. You will also need to edit your `config.toml` file each time your IPv4 address changes, that could be a few days or a few weeks. Check the your IPv4 in the [section above](./troubleshooting.md#no-ipv6-connectivity). Additional configuration on your router might also be needed to allow traffic in and out to port 1789 and IPv6 support. -Here is a sample of the `init` command to create the mixnode config. +Here is a sample of the `init` command example to create the mix node config. ``` -./target/release/nym-mixnode init --id nym-nat --host 0.0.0.0 --announce-host 85.160.12.13 --layer 3 +./nym-mixnode init --id --host 0.0.0.0 --announce-host 85.160.12.13 ``` -- `--host 0.0.0.0` should work everytime even if your local machine IPv4 address changes. For example on Monday your router gives your machine an address `192.168.0.13` and on Wednesday, the DHCP lease will end and you will be asigned `192.168.0.14`. Using `0.0.0.0` should avoid this without having to set any static ip in your router`s configuration. +- `--host 0.0.0.0` should work every time even if your local machine IPv4 address changes. For example on Monday your router gives your machine an address `192.168.0.13` and on Wednesday, the DHCP lease will end and you will be assigned `192.168.0.14`. Using `0.0.0.0` should avoid this without having to set any static IP in your router`s configuration. - you can get your current IPv4 address by either using `curl ipinfo.io` if you're on MacOS or Linux or visiting [whatsmyip site](https://www.whatsmyip.org/). Simply copy it and use it as `--anounce-host` address. -Make sure you check if your node is really mixing. You will need a bit of luck to set this up from your home behind NAT. +Make sure you check if your node is really mixing. We are aiming to improve the setup for operators running locally, however you may need a bit of patience to set this up from your home behind NAT. ### Accidentally killing your node process on exiting session -When you close your current terminal session, you need to make sure you don't kill the mixnode process! There are multiple ways on how to make it persistent even after exiting your ssh session, the easiest solution is to use `nohup`, and the more elegant solution is to run the node with `systemd`. - -### Running your mixnode as a background process with `nohup` - -`nohup` is a command with which your terminal is told to ignore the `HUP` or 'hangup' signal. This will stop the mixnode process ending if you kill your session. - -``` -nohup ./nym-mixnode run --id NYM # where `--id NYM` is the id you set during the `init` command. -``` - -### Running your mixnode as a background process with `systemd` - -The most reliable and elegant solution is to create a `systemd.service` file and run the nym-mixnode with `systemctl` command. - -Create a file with `nano` at `/etc/systemd/system/nym-mixnode.service` containing the following: - -```ini -[Unit] -Description=nym mixnode service -After=network.target - -[Service] -Type=simple -User=nym # change as appropriate -LimitNOFILE=65536 -ExecStart=/home/nym/nym-mixnode run --id nym # change as appropriate -KillSignal=SIGINT -Restart=on-failure -RestartSec=30 -Restart=on-abort -[Install] -WantedBy=multi-user.target -``` - -``` -# enable the service -sudo systemctl enable nym-mixnode -# start the service -sudo systemctl start nym-mixnode -# check if the service is running properly and mixnode is mixing -sudo systemctl status nym-mixnode -``` - -Now your node should be mixing all the time, and restart if you reboot your server! - -Anytime you change your `systemd` service file you need to `sudo systemctl daemon-reload` in order to restart the service. - -### Network configuration seems fine but log still claims `Since startup mixed 0 packets!` - -This behavior is most likely caused by a mismatch between your node configuration and the bonding information. Unbond and then rebond your node. - -Also make sure to enter all the information in the web wallet exactly as it appears in the log when you start the mixnode process. In particular, the `host` field must contain the _port_ on which your mixnode will listen: - -- correct host: `34.12.3.43:1789` -- incorrect host:`34.12.3.43` +When you close your current terminal session, you need to make sure you don't kill the mix node process! There are multiple ways on how to make it persistent even after exiting your ssh session, the easiest solution is to use `tmux` or `nohup`, and the more elegant solution is to run the node with `systemd`. Read the automation manual [here](./maintenance.md#automating-your-node-with-tmux-and-systemd). ### Common errors and warnings -Most of the `ERROR` and `WARN` messages in your node logs are benign - as long as your node outputs `since startup mixed X packets!` in your logs (and this number increases over time), your node is mixing packets. If you want to be sure, check the Nym [dashboard](https://sandbox-explorer.nymtech.net/) or see other ways on how to check if your node is mixing properly as outlined in the section **How can I tell my node is up and running and mixing traffic?** above. +Most of the `ERROR` and `WARN` messages in your node logs are benign - as long as your node outputs `since startup mixed X packets!` (`X` bust be > 0) in your logs (and this number increases over time), your node is mixing packets. If you want to be sure, check the Nym [dashboard](https://sandbox-explorer.nymtech.net/) or see other ways on how to check if your node is mixing properly as outlined in the section [**How can I tell my node is up and running and mixing traffic?**](./troubleshooting.md#how-can-i-tell-my-node-is-up-and-running-and-mixing-traffic?) above. More specific errors and warnings are covered below. @@ -312,57 +260,72 @@ If you are running into issues with an error including the following: thread 'tokio-runtime-worker' panicked at 'Failed to create TCP listener: Os { code: 99, kind: AddrNotAvailable, message: "Cannot assign requested address" }' ``` -Then you need to `--announce-host ` and ``--host ` on startup. This issue arises because of your use of a provider like AWS or Google Cloud, and the fact that your VPS' available bind address is not the same as the public IP address (see [Virtual IPs and hosting via Google and AWS](../nodes/mix-node-setup.md#virtual-ips-and-hosting-via-google--aws) for more information on this issue). +Then you need to `--announce-host ` and `--host ` on startup. This issue is addressed [above](./troubleshooting.md#missing-`announce-host`-flag) + + ### Can I use a port other than 1789 ? Yes! Here is what you will need to do: -Assuming you would like to use port `1337` for your mixnode, you need to open the new port (and close the old one): +Assuming you would like to use port `1337` for your mix node, you need to open the new port (and close the old one): ``` sudo ufw allow 1337 sudo ufw deny 1789 ``` -And then edit the mixnode's config. +And then edit the mix node's config. > If you want to change the port for an already running node, you need to stop the process before editing your config file. -Assuming your node name is `nym`, the config file is located at `~/.nym/mixnodes/nym/config/config.toml`. +The config file is located at `~/.nym/mixnodes//config/config.toml`. + +For example, assuming `` was chosen to be `alice-node`: ``` -nano ~/.nym/mixnodes/nym/config/config.toml +nano ~/.nym/mixnodes/alice-node/config/config.toml ``` You will need to edit two parts of the file. `announce_address` and `listening_address` in the config.toml file. Simply replace `:1789` (the default port) with `:1337` (your new port) after your IP address. -Finally, restart your node. You should see if the mixnode is using the port you have changed in the config.toml file right after you run the node. +Finally, restart your node. You should see if the mix node is using the port you have changed in the config.toml file right after you run the node. -### What is `verloc` and do I have to configure my mixnode to implement it? +### What is `verloc` and do I have to configure my mix node to implement it? `verloc` is short for _verifiable location_. Mixnodes and gateways now measure speed-of-light distances to each other, in an attempt to verify how far apart they are. In later releases, this will allow us to algorithmically verify node locations in a non-fakeable and trustworthy manner. -You don't have to do any additional configuration for your node to implement this, it is a passive process that runs in the background of the mixnet from version `0.10.1` onwards. +You don't have to do any additional configuration for your node to implement this, it is a passive process that runs in the background of the mixnet from version `0.10.1` onward. + +## Validators + +### Common reasons for your validator being jailed + +The most common reason for your validator being jailed is that your validator is out of memory because of bloated syslogs. + +Running the command `df -H` will return the size of the various partitions of your VPS. + +If the `/dev/sda` partition is almost full, try pruning some of the `.gz` syslog archives and restart your validator process. -### Where can I get more help? +## Where can I get more help? -The fastest way to reach one of us or get a help from the community, visit our [Telegram help chat](https://t.me/nymchan_help_chat) or head to our [Discord](https://Discord.gg/nym) +The fastest way to reach one of us or get a help from the community, visit our [Telegram Node Setup Help Chat](https://t.me/nymchan_help_chat) or head to our [Discord](https://Discord.gg/nym). + +For more tech heavy question join our [Matrix core community channel](https://matrix.to/#/#general:nymtech.chat), where you can meet other builders and Nym core team members. -For more tech heavy questions join our Keybase channel. Get Keybase [here](https://keybase.io/), then click Teams -> Join a team. Type nymtech.friends into the team name and hit continue. For general chat, hang out in the #general channel. diff --git a/documentation/docs/src/nodes/validator-setup.md b/documentation/operators/src/nodes/validator-setup.md similarity index 56% rename from documentation/docs/src/nodes/validator-setup.md rename to documentation/operators/src/nodes/validator-setup.md index 053a19253d..ca35c0b2b9 100644 --- a/documentation/docs/src/nodes/validator-setup.md +++ b/documentation/operators/src/nodes/validator-setup.md @@ -1,10 +1,20 @@ # Validators +> Nym has two main codebases: +> - the [Nym platform](https://github.com/nymtech/nym), written in Rust. This contains all of our code except for the validators. +> - the [Nym validators](https://github.com/nymtech/nyxd), written in Go. + The validator is built using [Cosmos SDK](https://cosmos.network) and [Tendermint](https://tendermint.com), with a [CosmWasm](https://cosmwasm.com) smart contract controlling the directory service, node bonding, and delegated mixnet staking. +> We are currently working towards building up a closed set of reputable validators. You can ask us for coins to get in, but please don't be offended if we say no - validators are part of our system's core security and we are starting out with people we already know or who have a solid reputation. + ## Building your validator + +> Any syntax in `<>` brackets is a user's unique variable. Exchange it with a corresponding name without the `<>` brackets. + ### Prerequisites #### `git`, `gcc`, `jq` + * Debian-based systems: ``` apt install git build-essential jq @@ -20,22 +30,15 @@ pacman -S git gcc jq ``` #### `Go` -`Go` can be installed via the following commands (taken from the [Agoric SDK docs](https://github.com/Agoric/agoric-sdk/wiki/Validator-Guide-for-Incentivized-Testnet#install-go)): +`Go` can be installed via the following commands (taken from the [Go Download and install page](https://go.dev/doc/install)): ``` -# First remove any existing old Go installation -sudo rm -rf /usr/local/go +# First remove any existing old Go installation and extract the archive you just downloaded into /usr/local: +# You may need to run the command as root or through sudo +rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.6.linux-amd64.tar.gz -# Install correct Go version -curl https://dl.google.com/go/go1.20.4.linux-amd64.tar.gz | sudo tar -C/usr/local -zxvf - - -# Update environment variables to include go -cat <<'EOF' >>$HOME/.profile -export GOROOT=/usr/local/go -export GOPATH=$HOME/go -export GO111MODULE=on -export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin -EOF +# Add /usr/local/go/bin to the PATH environment variable +export PATH=$PATH:/usr/local/go/bin source $HOME/.profile ``` @@ -61,7 +64,7 @@ The validator binary can be compiled by running the following commands: ``` git clone https://github.com/nymtech/nyxd.git cd nyxd -git checkout release/ +git checkout release/ # Mainnet make build @@ -76,7 +79,46 @@ At this point, you will have a copy of the `nyxd` binary in your `build/` direct ./build/nyxd ``` -You should see help text print out. +You should see a similar help menu printed to you: + +~~~admonish example collapsible=true title="Console output" +``` +Wasm Daemon (server) + +Usage: + nyxd [command] + +Available Commands: + add-genesis-account Add a genesis account to genesis.json + add-wasm-genesis-message Wasm genesis subcommands + collect-gentxs Collect genesis txs and output a genesis.json file + config Create or query an application CLI configuration file + debug Tool for helping with debugging your application + export Export state to JSON + gentx Generate a genesis tx carrying a self delegation + help Help about any command + init Initialize private validator, p2p, genesis, and application configuration files + keys Manage your application's keys + query Querying subcommands + rollback rollback cosmos-sdk and tendermint state by one height + start Run the full node + status Query remote node for status + tendermint Tendermint subcommands + tx Transactions subcommands + validate-genesis validates the genesis file at the default location or at the location passed as an arg + version Print the application binary version information + +Flags: + -h, --help help for nyxd + --home string directory for config and data (default "/home/willow/.nyxd") + --log_format string The logging format (json|plain) (default "plain") + --log_level string The logging level (trace|debug|info|warn|error|fatal|panic) (default "info") + --trace print out full stack trace on errors + +Use "nyxd [command] --help" for more information about a command. + +``` +~~~ ### Linking `nyxd` to `libwasmvm.so` @@ -89,10 +131,10 @@ If you are seeing an error concerning this file when trying to run `nyxd`, then Simply `cp` or `mv` that file to `/lib/x86_64-linux-gnu/` and re-run `nyxd`. ### Adding `nyxd` to your `$PATH` -You'll need to set `LD_LIBRARY_PATH` in your user's `~/.bashrc` file, and add that to our path. Replace `/home/youruser/path/to/nym/binaries` in the command below to the locations of `nyxd` and `libwasmvm.so` and run it. If you have compiled these on the server, they will be in the `build/` folder: +You'll need to set `LD_LIBRARY_PATH` in your user's `~/.bashrc` or `~/.zshrc` file (depends on the terminal you use), and add that to our path. Replace `/home///binaries` in the command below to the locations of `nyxd` and `libwasmvm.so` and run it. If you have compiled these on the server, they will be in the `build/` folder: ``` -NYX_BINARIES=/home/youruser/path/to/validator/binary +NYX_BINARIES=/home/// # if you are using another shell like zsh replace '.bashrc' with the relevant config file echo 'export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:'NYX_BINARIES >> ~/.bashrc @@ -106,7 +148,46 @@ Test everything worked: nyxd ``` -This should return the regular help text. +This should return the regular help menu: + +~~~admonish example collapsible=true title="Console output" +``` +Wasm Daemon (server) + +Usage: + nyxd [command] + +Available Commands: + add-genesis-account Add a genesis account to genesis.json + add-wasm-genesis-message Wasm genesis subcommands + collect-gentxs Collect genesis txs and output a genesis.json file + config Create or query an application CLI configuration file + debug Tool for helping with debugging your application + export Export state to JSON + gentx Generate a genesis tx carrying a self delegation + help Help about any command + init Initialize private validator, p2p, genesis, and application configuration files + keys Manage your application's keys + query Querying subcommands + rollback rollback cosmos-sdk and tendermint state by one height + start Run the full node + status Query remote node for status + tendermint Tendermint subcommands + tx Transactions subcommands + validate-genesis validates the genesis file at the default location or at the location passed as an arg + version Print the application binary version information + +Flags: + -h, --help help for nyxd + --home string directory for config and data (default "/home/willow/.nyxd") + --log_format string The logging format (json|plain) (default "plain") + --log_level string The logging level (trace|debug|info|warn|error|fatal|panic) (default "info") + --trace print out full stack trace on errors + +Use "nyxd [command] --help" for more information about a command. + +``` +~~~ ## Initialising your validator ### Prerequisites: @@ -165,7 +246,7 @@ create_empty_blocks = false laddr = "tcp://0.0.0.0:26656" ``` -These affect the following: +These affect the following: * `persistent_peers = "@.nymtech.net:26666"` allows your validator to start pulling blocks from other validators. **The main sandbox validator listens on `26666` instead of the default `26656` for debugging**. It is recommended you do not change your port from `26656`. * `create_empty_blocks = false` will save space * `laddr = "tcp://0.0.0.0:26656"` is in your p2p configuration options @@ -179,7 +260,7 @@ Optionally, if you want to enable [Prometheus](https://prometheus.io/) metrics t And if you wish to add a human-readable moniker to your node: -- `moniker = "yourname"` +- `moniker = ""` Finally, if you plan on using [Cockpit](https://cockpit-project.org/documentation.html) on your server, change the `grpc` port from `9090` as this is the port used by Cockpit. @@ -232,7 +313,9 @@ If this check passes, you should receive the following output: File at /path/to/genesis.json is a valid genesis file ``` -> If this test did not pass, check that you have replaced the contents of `/path/to/.nymd/config/genesis.json` with that of the correct genesis file. +> If this test did not pass, check that you have replaced the contents of `//.nymd/config/genesis.json` with that of the correct genesis file. + +### Open firewall ports Before starting the validator, we will need to open the firewall ports: @@ -245,9 +328,9 @@ sudo ufw allow 1317,26656,26660,22,80,443/tcp sudo ufw status ``` -Ports `22`, `80`, and `443` are for ssh, http, and https connections respectively. The rest of the ports are documented [here](https://docs.cosmos.network/v0.42/core/grpc_rest.html). +Ports `22`, `80`, and `443` are for ssh, http, and https connections respectively. The rest of the ports are documented [here](https://docs.cosmos.network/main/core/grpc_rest). -For more information about your validator's port configuration, check the [validator port reference table](#validator-port-reference) below. +For more information about your validator's port configuration, check the [validator port reference table](./maintenance.md#ports) below. > If you are planning to use [Cockpit](https://cockpit-project.org/) on your validator server then you will have defined a different `grpc` port in your `config.toml` above: remember to open this port as well. @@ -295,8 +378,8 @@ Once your validator has synced and you have received tokens, you can join consen nyxd tx staking create-validator --amount=10000000unyx --fees=0unyx - --pubkey=$(/home/youruser/path/to/nyxd/binaries/nyxd tendermint show-validator) - --moniker="whatever you called your validator" + --pubkey=$(/home///nyxd/binaries/nyxd tendermint show-validator) + --moniker="" --chain-id=nyx --commission-rate="0.10" --commission-max-rate="0.20" @@ -312,8 +395,8 @@ nyxd tx staking create-validator nyxd tx staking create-validator --amount=10000000unyxt --fees=5000unyxt - --pubkey=$(/home/youruser/path/to/nym/binaries/nyxd tendermint show-validator) - --moniker="whatever you called your validator" + --pubkey=$(/home///nym/binaries/nyxd tendermint show-validator) + --moniker="" --chain-id=sandbox --commission-rate="0.10" --commission-max-rate="0.20" @@ -335,10 +418,10 @@ If you want to edit some details for your node you will use a command like this: # Mainnet nyxd tx staking edit-validator --chain-id=nyx - --moniker="whatever you called your validator" + --moniker="" --details="Nyx validator" - --security-contact="your email" - --identity="your identity" + --security-contact="" + --identity="" --gas="auto" --gas-adjustment=1.15 --from="KEYRING_NAME" @@ -348,10 +431,10 @@ nyxd tx staking edit-validator # Sandbox testnet nyxd tx staking edit-validator --chain-id=sandbox - --moniker="whatever you called your validator" + --moniker="" --details="Sandbox testnet validator" --security-contact="your email" - --identity="your identity" + --identity="" --gas="auto" --gas-adjustment=1.15 --from="KEYRING_NAME" @@ -361,213 +444,18 @@ nyxd tx staking edit-validator With above command you can specify the `gpg` key last numbers (as used in `keybase`) as well as validator details and your email for security contact. ### Automating your validator with systemd -You will most likely want to automate your validator restarting if your server reboots. Below is a systemd unit file to place at `/etc/systemd/system/nymd.service`: - -```ini -[Unit] -Description=Nyxd -StartLimitInterval=350 -StartLimitBurst=10 - -[Service] -User=nyx # change to your user -Type=simple -Environment="LD_LIBRARY_PATH=/home/youruser/path/to/nyx/binaries" # change to correct path -ExecStart=/home/youruser/path/to/nyx/binaries/nymd start # change to correct path -Restart=on-failure -RestartSec=30 -LimitNOFILE=infinity - -[Install] -WantedBy=multi-user.target -``` - -Proceed to start it with: - -``` -systemctl daemon-reload # to pickup the new unit file -systemctl enable nymd # to enable the service -systemctl start nymd # to actually start the service -journalctl -f # to monitor system logs showing the service start -``` +You will most likely want to automate your validator restarting if your server reboots. Checkout the [maintenance page](./maintenance.md#systemd) with a quick tutorial. ### Installing and configuring nginx for HTTPS -#### Setup -[Nginx](https://www.nginx.com/resources/glossary/nginx/#:~:text=NGINX%20is%20open%20source%20software,%2C%20media%20streaming%2C%20and%20more.&text=In%20addition%20to%20its%20HTTP,%2C%20TCP%2C%20and%20UDP%20servers.) is an open source software used for operating high-performance web servers. It allows us to set up reverse proxying on our validator server to improve performance and security. -Install `nginx` and allow the 'Nginx Full' rule in your firewall: - -``` -sudo ufw allow 'Nginx Full' -``` - -Check nginx is running via systemctl: - -``` -systemctl status nginx -``` - -Which should return: - -``` -● nginx.service - A high performance web server and a reverse proxy server - Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) - Active: active (running) since Fri 2018-04-20 16:08:19 UTC; 3 days ago - Docs: man:nginx(8) - Main PID: 2369 (nginx) - Tasks: 2 (limit: 1153) - CGroup: /system.slice/nginx.service - ├─2369 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; - └─2380 nginx: worker process -``` - -#### Configuration - -Proxying your validator's port `26657` to nginx port `80` can then be done by creating a file with the following at `/etc/nginx/conf.d/validator.conf`: - -``` -server { - listen 80; - listen [::]:80; - server_name "domain_name"; - - location / { - proxy_pass http://127.0.0.1:26657; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } -} -``` - -Followed by: - -``` -sudo apt install certbot nginx python3 -certbot --nginx -d nym-validator.yourdomain.com -m you@yourdomain.com --agree-tos --noninteractive --redirect -``` - -```admonish caution title="" -If using a VPS running Ubuntu 20: replace `certbot nginx python3` with `python3-certbot-nginx` -``` - -These commands will get you an https encrypted nginx proxy in front of the API. - -### Configuring Prometheus metrics (optional) - -Configure Prometheus with the following commands (adapted from NodesGuru's [Agoric setup guide](https://nodes.guru/agoric/setup-guide/en)): - -``` -echo 'export OTEL_EXPORTER_PROMETHEUS_PORT=9464' >> $HOME/.bashrc -source ~/.bashrc -sed -i '/\[telemetry\]/{:a;n;/enabled/s/false/true/;Ta}' $HOME/.nymd/config/app.toml -sed -i "s/prometheus-retention-time = 0/prometheus-retention-time = 60/g" $HOME/.nymd/config/app.toml -sudo ufw allow 9464 -echo 'Metrics URL: http://'$(curl -s ifconfig.me)':26660/metrics' -``` - -Your validator's metrics will be available to you at the returned 'Metrics URL'. - -~~~admonish example collapsible=true title="Console output" -``` -# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles. -# TYPE go_gc_duration_seconds summary -go_gc_duration_seconds{quantile="0"} 6.7969e-05 -go_gc_duration_seconds{quantile="0.25"} 7.864e-05 -go_gc_duration_seconds{quantile="0.5"} 8.4591e-05 -go_gc_duration_seconds{quantile="0.75"} 0.000115919 -go_gc_duration_seconds{quantile="1"} 0.001137591 -go_gc_duration_seconds_sum 0.356555301 -go_gc_duration_seconds_count 2448 -# HELP go_goroutines Number of goroutines that currently exist. -# TYPE go_goroutines gauge -go_goroutines 668 -# HELP go_info Information about the Go environment. -# TYPE go_info gauge -go_info{version="go1.15.7"} 1 -# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use. -# TYPE go_memstats_alloc_bytes gauge -go_memstats_alloc_bytes 1.62622216e+08 -# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed. -# TYPE go_memstats_alloc_bytes_total counter -go_memstats_alloc_bytes_total 2.09341707264e+11 -# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table. -# TYPE go_memstats_buck_hash_sys_bytes gauge -go_memstats_buck_hash_sys_bytes 5.612319e+06 -# HELP go_memstats_frees_total Total number of frees. -# TYPE go_memstats_frees_total counter -go_memstats_frees_total 2.828263344e+09 -# HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started. -# TYPE go_memstats_gc_cpu_fraction gauge -go_memstats_gc_cpu_fraction 0.03357798610671518 -# HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata. -# TYPE go_memstats_gc_sys_bytes gauge -go_memstats_gc_sys_bytes 1.3884192e+07 -``` -~~~ +If you want to set up a reverse proxying on the validator server to improve security and performance, using [nginx](https://www.nginx.com/resources/glossary/nginx/#:~:text=NGINX%20is%20open%20source%20software,%2C%20media%20streaming%2C%20and%20more.&text=In%20addition%20to%20its%20HTTP,%2C%20TCP%2C%20and%20UDP%20servers.), follow the manual on the [maintenance page](./maintenance.md#setup). ### Setting the ulimit -Linux machines limit how many open files a user is allowed to have. This is called a `ulimit`. -`ulimit` is 1024 by default on most systems. It needs to be set higher, because validators make and receive a lot of connections to other nodes. - -If you see errors such as: - -``` -Failed to accept incoming connection - Os { code: 24, kind: Other, message: "Too many open files" } -``` - -This means that the operating system is preventing network connections from being made. - -##### Set the ulimit via `systemd` service file -Query the `ulimit` of your validator with: - -``` -grep -i "open files" /proc/$(ps -A -o pid,cmd|grep nymd | grep -v grep |head -n 1 | awk '{print $1}')/limits -``` - -You'll get back the hard and soft limits, which looks something like this: - -``` -Max open files 65536 65536 files -``` - -If your output is **the same as above**, your node will not encounter any `ulimit` related issues. - -However if either value is `1024`, you must raise the limit via the systemd service file. Add the line: - -``` -LimitNOFILE=65536 -``` - -Reload the daemon: - -``` -systemctl daemon-reload -``` - -or execute this as root for system-wide setting of `ulimit`: - -``` -echo "DefaultLimitNOFILE=65535" >> /etc/systemd/system.conf -``` - -Reboot your machine and restart your node. When it comes back, use `cat /proc/$(pidof nym-validator)/limits | grep "Max open files"` to make sure the limit has changed to 65535. - -##### Set the ulimit on `non-systemd` based distributions -Edit `etc/security/conf` and add the following lines: - -``` -# Example hard limit for max opened files -username hard nofile 4096 -# Example soft limit for max opened files -username soft nofile 4096 -``` - -Then reboot your server and restart your validator. +Linux machines limit how many open files a user is allowed to have. This is called a `ulimit`. We need to set it to a higher value than the default 1024. Follow the instructions in the [maintenance page](./maintenance.md#Setting-the-ulimit) to change the `ulimit` value for validators. ## Using your validator -### Unjailing your validator +### Un-jailing your validator If your validator gets jailed, you can fix it with the following command: ``` @@ -593,19 +481,7 @@ nyxd tx slashing unjail ### Upgrading your validator -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.32.0 -- the new wasmvm (it depends on your platform, but most common filename is `libwasmvm.x86_64.so`) - -Wait for the upgrade height to be reached and the chain to halt awaiting upgrade, then: - -* 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/9). - -Note: Cosmovisor will swap the `nyxd` binary, but you'll need to already have the `libwasmvm.x86_64.so` in place. +To upgrade your validator, follow the steps on the [maintenance page](./maintenance.md#setting-the-ulimit). #### Common reasons for your validator being jailed @@ -623,7 +499,7 @@ You can check your current balances with: nymd query bank balances ${ADDRESS} ``` -For example, on the Sanbox testnet this would return: +For example, on the Sandbox testnet this would return: ```yaml balances: @@ -659,11 +535,3 @@ nyxd tx staking delegate VALOPERADDRESS AMOUNTunymt --fees 5000unyxt ``` -## Validator port reference -All validator-specific port configuration can be found in `$HOME/.nymd/config/config.toml`. If you do edit any port configs, remember to restart your validator. - -| Default port | Use | -|--------------|--------------------------------------| -| 1317 | REST API server endpoint | -| 26656 | Listen for incoming peer connections | -| 26660 | Listen for Prometheus connections | diff --git a/documentation/operators/src/not-found.md b/documentation/operators/src/not-found.md new file mode 100644 index 0000000000..aafb70cba5 --- /dev/null +++ b/documentation/operators/src/not-found.md @@ -0,0 +1,11 @@ +# Page Not Found + +You seem to have followed a link to a page that no longer exists. Our documentation is changing and growing over time, so this page has most likely been moved somewhere else. + +For node setup guides, see the [Operator Guides book](https://nymtech.net/operators). + +For developer tutorials, app guides, and demo and community apps, see the [Developer Portal](https://nymtech.net/developers). + +If you are looking for information on setting up and maintaining infrastructure nodes, check the sitemap on the left hand side of the screen. + +If you still can't find what you're looking for get in touch with us via [Matrix](https://matrix.to/#/#nym-community:nymtech.chat). \ No newline at end of file diff --git a/documentation/operators/src/preliminary-steps.md b/documentation/operators/src/preliminary-steps.md new file mode 100644 index 0000000000..2f7cb2008b --- /dev/null +++ b/documentation/operators/src/preliminary-steps.md @@ -0,0 +1,41 @@ +# Preliminary Steps + +> The Nym `mixnode`, `gateway` and `network-requester` binaries were built in the [building nym](./binaries/building-nym.md) section. If you haven't yet built Nym and want to run the code, go there first. + +There are a couple of steps that need completing before starting to set up your mix node, gateway or a network requester: + +- preparing your [desktop wallet](https://nymtech.net/docs/wallet/desktop-wallet.html) or [CLI wallet](https://nymtech.net/docs/wallet/cli-wallet.html). +- requisitioning a VPS (Virtual Private Server) + +### Wallet preparation +#### Mainnet +Before you initialise and run your mix node, head to our [website](https://nymtech.net/download/) and download the Nym wallet for your operating system. If pre-compiled binaries for your operating system aren't available, you can build the wallet yourself with instructions [here](https://nymtech.net/docs/wallet/desktop-wallet.html). + +If you don't already have one, please create a Nym address using the wallet, and fund it with tokens. The minimum amount required to bond a mix node is 100 `NYM`, but make sure you have a bit more to account for gas costs. + +`NYM` can be purchased via Bity from the wallet itself with BTC or fiat, and is currently present on several [exchanges](https://www.coingecko.com/en/coins/nym#markets). + +> Remember that you can **only** use Cosmos `NYM` tokens to bond your mix node. You **cannot** use ERC20 representations of `NYM` to run a node. + + +#### Sandbox testnet +Make sure to download a wallet and create an account as outlined above. Then head to our [token faucet](https://faucet.nymtech.net/) and get some tokens to use to bond it. + +### VPS Hardware Specs +You will need to rent a VPS to run your node on. One key reason for this is that your node **must be able to send TCP data using both IPv4 and IPv6** (as other nodes you talk to may use either protocol). + +For the moment, we haven't put a great amount of effort into optimizing concurrency to increase throughput, so don't bother provisioning a beastly server with multiple cores. This will change when we get a chance to start doing performance optimizations in a more serious way. Sphinx packet decryption is CPU-bound, so once we optimize, more fast cores will be better. + +For now, see the below rough specs: + +- Processors: 2 cores are fine. Get the fastest CPUs you can afford. + +#### For mix node + +- RAM: Memory requirements are very low - typically a mix node may use only a few hundred MB of RAM. +- Disks: The mixnodes require no disk space beyond a few bytes for the configuration files. + +#### For Gateway + +- RAM: Memory requirements depend on the amount of users your Gateway will be serving at any one time. If you're just going to be using it yourself, then minimal RAM is fine. **If you're running your Gateway as part of a Service Grant, get something with at least 4GB RAM.** +- Disks: much like the amount of RAM your Gateway could use, the amount of disk space required will vary with the amount of users your Gateway is serving. **If you're running your Gateway as part of a Service Grant, get something with at least 40GB storage.** diff --git a/documentation/operators/theme/css/chrome.css b/documentation/operators/theme/css/chrome.css new file mode 100644 index 0000000000..febd1d40a6 --- /dev/null +++ b/documentation/operators/theme/css/chrome.css @@ -0,0 +1,538 @@ +/* CSS for UI elements (a.k.a. chrome) */ + +@import 'variables.css'; + +::-webkit-scrollbar { + background: var(--bg); +} +::-webkit-scrollbar-thumb { + background: var(--scrollbar); +} +html { + scrollbar-color: var(--scrollbar) var(--bg); +} +#searchresults a, +.content a:link, +a:visited, +a > .hljs { + color: var(--links); +} + +/* Menu Bar */ + +#menu-bar, +#menu-bar-hover-placeholder { + z-index: 101; + margin: auto calc(0px - var(--page-padding)); +} +#menu-bar { + position: relative; + display: flex; + flex-wrap: wrap; + background-color: var(--bg); + border-bottom-color: var(--bg); + border-bottom-width: 1px; + border-bottom-style: solid; +} +#menu-bar.sticky, +.js #menu-bar-hover-placeholder:hover + #menu-bar, +.js #menu-bar:hover, +.js.sidebar-visible #menu-bar { + position: -webkit-sticky; + position: sticky; + top: 0 !important; +} +#menu-bar-hover-placeholder { + position: sticky; + position: -webkit-sticky; + top: 0; + height: var(--menu-bar-height); +} +#menu-bar.bordered { + border-bottom-color: var(--table-border-color); +} +#menu-bar i, #menu-bar .icon-button { + position: relative; + padding: 0 8px; + z-index: 10; + line-height: var(--menu-bar-height); + cursor: pointer; + transition: color 0.5s; +} +@media only screen and (max-width: 420px) { + #menu-bar i, #menu-bar .icon-button { + padding: 0 5px; + } +} + +.icon-button { + border: none; + background: none; + padding: 0; + color: inherit; +} +.icon-button i { + margin: 0; +} + +.right-buttons { + margin: 0 15px; +} +.right-buttons a { + text-decoration: none; +} + +.left-buttons { + display: flex; + margin: 0 5px; +} +.no-js .left-buttons { + display: none; +} + +.menu-title { + display: inline-block; + font-weight: 200; + font-size: 2.4rem; + line-height: var(--menu-bar-height); + text-align: center; + margin: 0; + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.js .menu-title { + cursor: pointer; +} + +.menu-bar, +.menu-bar:visited, +.nav-chapters, +.nav-chapters:visited, +.mobile-nav-chapters, +.mobile-nav-chapters:visited, +.menu-bar .icon-button, +.menu-bar a i { + color: var(--icons); +} + +.menu-bar i:hover, +.menu-bar .icon-button:hover, +.nav-chapters:hover, +.mobile-nav-chapters i:hover { + color: var(--icons-hover); +} + +/* Nav Icons */ + +.nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + + position: fixed; + top: 0; + bottom: 0; + margin: 0; + max-width: auto; + min-width: auto; + + display: flex; + justify-content: center; + align-content: center; + flex-direction: column; + + transition: color 0.5s, background-color 0.5s; +} + +.nav-chapters:hover { + text-decoration: none; + background-color: var(--theme-hover); + transition: background-color 0.15s, color 0.15s; +} + +.nav-wrapper { + margin-top: 50px; + display: none; +} + +.mobile-nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + width: 90px; + border-radius: 5px; + background-color: var(--sidebar-bg); +} + +.previous { + float: left; +} + +.next { + float: right; + right: var(--page-padding); +} + +@media only screen and (max-width: 1080px) { + .nav-wide-wrapper { display: none; } + .nav-wrapper { display: block; } +} + +@media only screen and (max-width: 1380px) { + .sidebar-visible .nav-wide-wrapper { display: none; } + .sidebar-visible .nav-wrapper { display: block; } +} + +/* Inline code */ + +:not(pre) > .hljs { + display: inline; + padding: 0.1em 0.3em; + border-radius: 3px; +} + +:not(pre):not(a) > .hljs { + color: var(--inline-code-color); + overflow-x: initial; +} + +a:hover > .hljs { + text-decoration: underline; +} + +pre { + position: relative; +} +pre > .buttons { + position: absolute; + z-index: 100; + right: 0px; + top: 2px; + margin: 0px; + padding: 2px 0px; + + color: var(--sidebar-fg); + cursor: pointer; + visibility: hidden; + opacity: 0; + transition: visibility 0.1s linear, opacity 0.1s linear; +} +pre:hover > .buttons { + visibility: visible; + opacity: 1 +} +pre > .buttons :hover { + color: var(--sidebar-active); + border-color: var(--icons-hover); + background-color: var(--theme-hover); +} +pre > .buttons i { + margin-left: 8px; +} +pre > .buttons button { + cursor: inherit; + margin: 0px 5px; + padding: 3px 5px; + font-size: 14px; + + border-style: solid; + border-width: 1px; + border-radius: 4px; + border-color: var(--icons); + background-color: var(--theme-popup-bg); + transition: 100ms; + transition-property: color,border-color,background-color; + color: var(--icons); +} +@media (pointer: coarse) { + pre > .buttons button { + /* On mobile, make it easier to tap buttons. */ + padding: 0.3rem 1rem; + } +} +pre > code { + padding: 1rem; +} + +/* FIXME: ACE editors overlap their buttons because ACE does absolute + positioning within the code block which breaks padding. The only solution I + can think of is to move the padding to the outer pre tag (or insert a div + wrapper), but that would require fixing a whole bunch of CSS rules. +*/ +.hljs.ace_editor { + padding: 0rem 0rem; +} + +pre > .result { + margin-top: 10px; +} + +/* Search */ + +#searchresults a { + text-decoration: none; +} + +mark { + border-radius: 2px; + padding: 0 3px 1px 3px; + margin: 0 -3px -1px -3px; + background-color: var(--search-mark-bg); + transition: background-color 300ms linear; + cursor: pointer; +} + +mark.fade-out { + background-color: rgba(0,0,0,0) !important; + cursor: auto; +} + +.searchbar-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); +} + +#searchbar { + width: 100%; + margin: 5px auto 0px auto; + padding: 10px 16px; + transition: box-shadow 300ms ease-in-out; + border: 1px solid var(--searchbar-border-color); + border-radius: 3px; + background-color: var(--searchbar-bg); + color: var(--searchbar-fg); +} +#searchbar:focus, +#searchbar.active { + box-shadow: 0 0 3px var(--searchbar-shadow-color); +} + +.searchresults-header { + font-weight: bold; + font-size: 1em; + padding: 18px 0 0 5px; + color: var(--searchresults-header-fg); +} + +.searchresults-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); + border-bottom: 1px dashed var(--searchresults-border-color); +} + +ul#searchresults { + list-style: none; + padding-left: 20px; +} +ul#searchresults li { + margin: 10px 0px; + padding: 2px; + border-radius: 2px; +} +ul#searchresults li.focus { + background-color: var(--searchresults-li-bg); +} +ul#searchresults span.teaser { + display: block; + clear: both; + margin: 5px 0 0 20px; + font-size: 0.8em; +} +ul#searchresults span.teaser em { + font-weight: bold; + font-style: normal; +} + +/* Sidebar */ + +.sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: var(--sidebar-width); + font-size: 1em; + box-sizing: border-box; + -webkit-overflow-scrolling: touch; + overscroll-behavior-y: contain; + background-color: var(--sidebar-bg); + color: var(--sidebar-fg); +} +.sidebar-resizing { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} +.js:not(.sidebar-resizing) .sidebar { + transition: transform 0.3s; /* Animation: slide away */ +} +.sidebar code { + line-height: 2em; +} +.sidebar .sidebar-scrollbox { + overflow-y: auto; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + padding: 10px 10px; +} +.sidebar .sidebar-resize-handle { + position: absolute; + cursor: col-resize; + width: 0; + right: 0; + top: 0; + bottom: 0; +} +.js .sidebar .sidebar-resize-handle { + cursor: col-resize; + width: 5px; +} +.sidebar-hidden .sidebar { + transform: translateX(calc(0px - var(--sidebar-width))); +} +.sidebar::-webkit-scrollbar { + background: var(--sidebar-bg); +} +.sidebar::-webkit-scrollbar-thumb { + background: var(--scrollbar); +} + +.sidebar-visible .page-wrapper { + transform: translateX(var(--sidebar-width)); +} +@media only screen and (min-width: 620px) { + .sidebar-visible .page-wrapper { + transform: none; + margin-left: var(--sidebar-width); + } +} + +.chapter { + list-style: none outside none; + padding-left: 0; + line-height: 2em; +} + +.chapter ol { + width: 100%; +} + +.chapter li { + display: flex; + color: var(--sidebar-non-existant); +} +.chapter li a { + display: block; + padding: 0; + text-decoration: none; + color: var(--sidebar-fg); +} + +.chapter li a:hover { + color: var(--sidebar-active); +} + +.chapter li a.active { + color: var(--sidebar-active); +} + +.chapter li > a.toggle { + cursor: pointer; + display: block; + margin-left: auto; + padding: 0 10px; + user-select: none; + opacity: 0.68; +} + +.chapter li > a.toggle div { + transition: transform 0.5s; +} + +/* collapse the section */ +.chapter li:not(.expanded) + li > ol { + display: none; +} + +.chapter li.chapter-item { + line-height: 1.5em; + margin-top: 0.6em; +} + +.chapter li.expanded > a.toggle div { + transform: rotate(90deg); +} + +.spacer { + width: 100%; + height: 3px; + margin: 5px 0px; +} +.chapter .spacer { + background-color: var(--sidebar-spacer); +} + +@media (-moz-touch-enabled: 1), (pointer: coarse) { + .chapter li a { padding: 5px 0; } + .spacer { margin: 10px 0; } +} + +.section { + list-style: none outside none; + padding-left: 20px; + line-height: 1.5em; +} + +/* Theme Menu Popup */ + +.theme-popup { + position: absolute; + left: 10px; + top: var(--menu-bar-height); + z-index: 1000; + border-radius: 4px; + font-size: 0.7em; + color: var(--fg); + background: var(--theme-popup-bg); + border: 1px solid var(--theme-popup-border); + margin: 0; + padding: 0; + list-style: none; + display: none; + /* Don't let the children's background extend past the rounded corners. */ + overflow: hidden; +} +.theme-popup .default { + color: var(--icons); +} +.theme-popup .theme { + width: 100%; + border: 0; + margin: 0; + padding: 2px 20px; + line-height: 25px; + white-space: nowrap; + text-align: left; + cursor: pointer; + color: inherit; + background: inherit; + font-size: inherit; +} +.theme-popup .theme:hover { + background-color: var(--theme-hover); +} + +.theme-selected::before { + display: inline-block; + content: "✓"; + margin-left: -14px; + width: 14px; +} diff --git a/documentation/operators/theme/css/general.css b/documentation/operators/theme/css/general.css new file mode 100644 index 0000000000..a06db43965 --- /dev/null +++ b/documentation/operators/theme/css/general.css @@ -0,0 +1,203 @@ +/* Base styles and content styles */ + +@import 'variables.css'; + +:root { + /* Browser default font-size is 16px, this way 1 rem = 10px */ + font-size: 70%; +} + +html { + font-family: "Open Sans", sans-serif; + color: var(--fg); + background-color: var(--bg); + text-size-adjust: none; + -webkit-text-size-adjust: none; +} + +body { + margin: 0; + font-size: 1.5rem; + overflow-x: hidden; +} + +code { + font-family: var(--mono-font) !important; + font-size: 0.9em; +} + +/* make long words/inline code not x overflow */ +main { + overflow-wrap: break-word; +} + +/* make wide tables scroll if they overflow */ +.table-wrapper { + overflow-x: auto; +} + +/* Don't change font size in headers. */ +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + font-size: unset; +} + +.left { float: left; } +.right { float: right; } +.boring { opacity: 0.6; } +.hide-boring .boring { display: none; } +.hidden { display: none !important; } + +h2, h3 { margin-top: 2.5em; } +h4, h5 { margin-top: 2em; } + +.header + .header h3, +.header + .header h4, +.header + .header h5 { + margin-top: 1em; +} + +h1:target::before, +h2:target::before, +h3:target::before, +h4:target::before, +h5:target::before, +h6:target::before { + display: inline-block; + content: "»"; + margin-left: -30px; + width: 30px; +} + +/* This is broken on Safari as of version 14, but is fixed + in Safari Technology Preview 117 which I think will be Safari 14.2. + https://bugs.webkit.org/show_bug.cgi?id=218076 +*/ +:target { + scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); +} + +.page { + outline: 0; + padding: 0 var(--page-padding); + margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */ +} +.page-wrapper { + box-sizing: border-box; +} +.js:not(.sidebar-resizing) .page-wrapper { + transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ +} + +.content { + overflow-y: auto; + padding: 0 10px; +} +.content main { + margin-left: 10%; + margin-right: 10%; + max-width: var(--content-max-width); +} +.content p { line-height: 1.45em; } +.content ol { line-height: 1.45em; } +.content ul { line-height: 1.45em; } +.content a { text-decoration: none; } +.content a:hover { text-decoration: underline; } +.content img, .content video { max-width: 100%; } +.content .header:link, +.content .header:visited { + color: var(--fg); +} +.content .header:link, +.content .header:visited:hover { + text-decoration: none; +} + +table { + margin: 0 auto; + border-collapse: collapse; +} +table td { + padding: 3px 20px; + border: 1px var(--table-border-color) solid; +} +table thead { + background: var(--table-header-bg); +} +table thead td { + font-weight: 700; + border: none; +} +table thead th { + padding: 3px 20px; +} +table thead tr { + border: 1px var(--table-header-bg) solid; +} +/* Alternate background colors for rows */ +table tbody tr:nth-child(2n) { + background: var(--table-alternate-bg); +} + + +blockquote { + margin: 20px 0; + padding: 0 20px; + color: var(--fg); + background-color: var(--quote-bg); + border-top: .1em solid var(--quote-border); + border-bottom: .1em solid var(--quote-border); +} + +kbd { + background-color: var(--table-border-color); + border-radius: 4px; + border: solid 1px var(--theme-popup-border); + box-shadow: inset 0 -1px 0 var(--theme-hover); + display: inline-block; + font-size: var(--code-font-size); + font-family: var(--mono-font); + line-height: 10px; + padding: 4px 5px; + vertical-align: middle; +} + +:not(.footnote-definition) + .footnote-definition, +.footnote-definition + :not(.footnote-definition) { + margin-top: 2em; +} +.footnote-definition { + font-size: 0.9em; + margin: 0.5em 0; +} +.footnote-definition p { + display: inline; +} + +.tooltiptext { + position: absolute; + visibility: hidden; + color: #fff; + background-color: #333; + transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ + left: -8px; /* Half of the width of the icon */ + top: -35px; + font-size: 0.8em; + text-align: center; + border-radius: 6px; + padding: 5px 8px; + margin: 5px; + z-index: 1000; +} +.tooltipped .tooltiptext { + visibility: visible; +} + +.chapter li.part-title { + color: var(--sidebar-fg); + margin: 5px 0px; + font-weight: bold; +} + +.result-no-output { + font-style: italic; +} diff --git a/documentation/operators/theme/css/variables.css b/documentation/operators/theme/css/variables.css new file mode 100644 index 0000000000..8dfcf92239 --- /dev/null +++ b/documentation/operators/theme/css/variables.css @@ -0,0 +1,3257 @@ + +/* Globals */ + +:root { + --sidebar-width: 300px; + --page-padding: 20px; + --content-max-width: 70%; + --menu-bar-height: 40px; + --mono-font: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace; + --code-font-size: 0.875em /* please adjust the ace font size accordingly in editor.js */ + --pagetoc-width: 13%; + --pagetoc-fontsize: 14.5px; +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +@media only screen and (max-width:1439px) { + :root{ + --content-max-width: 98%; + } +} + +/* Themes */ + +.ayu { + --bg: hsl(210, 25%, 8%); + --fg: #c5c5c5; + + --sidebar-bg: #14191f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #5c6773; + --sidebar-active: #ffb454; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #0096cf; + + --inline-code-color: #ffb454; + + --theme-popup-bg: #14191f; + --theme-popup-border: #5c6773; + --theme-hover: #191f26; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: hsl(210, 25%, 13%); + --table-header-bg: hsl(210, 25%, 28%); + --table-alternate-bg: hsl(210, 25%, 11%); + + --searchbar-border-color: #848484; + --searchbar-bg: #424242; + --searchbar-fg: #fff; + --searchbar-shadow-color: #d4c89f; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #252932; + --search-mark-bg: #e3b171; +} + +.coal { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; +} + +.light { + --bg: hsl(0, 0%, 100%); + --fg: hsl(0, 0%, 0%); + + --sidebar-bg: #fafafa; + --sidebar-fg: hsl(0, 0%, 0%); + --sidebar-non-existant: #aaaaaa; + --sidebar-active: #1f1fff; + --sidebar-spacer: #f4f4f4; + + --scrollbar: #8F8F8F; + + --icons: #747474; + --icons-hover: #000000; + + --links: #1f1fff; + + --inline-code-color: #F42C4C; + + --theme-popup-bg: #fafafa; + --theme-popup-border: #cccccc; + --theme-hover: #e6e6e6; + + --quote-bg: hsl(197, 37%, 96%); + --quote-border: hsl(197, 37%, 91%); + + --table-border-color: hsl(0, 0%, 95%); + --table-header-bg: hsl(0, 0%, 80%); + --table-alternate-bg: hsl(0, 0%, 97%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #e4f2fe; + --search-mark-bg: #a2cff5; +} + +.navy { + --bg: hsl(226, 23%, 11%); + --fg: #bcbdd0; + + --sidebar-bg: #282d3f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505274; + --sidebar-active: #2b79a2; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #161923; + --theme-popup-border: #737480; + --theme-hover: #282e40; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: hsl(226, 23%, 16%); + --table-header-bg: hsl(226, 23%, 31%); + --table-alternate-bg: hsl(226, 23%, 14%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #aeaec6; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #5f5f71; + --searchresults-border-color: #5c5c68; + --searchresults-li-bg: #242430; + --search-mark-bg: #a2cff5; +} + +.rust { + --bg: hsl(60, 9%, 87%); + --fg: #262625; + + --sidebar-bg: #3b2e2a; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505254; + --sidebar-active: #e69f67; + --sidebar-spacer: #45373a; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #262625; + + --links: #2b79a2; + + --inline-code-color: #6e6b5e; + + --theme-popup-bg: #e1e1db; + --theme-popup-border: #b38f6b; + --theme-hover: #99908a; + + --quote-bg: hsl(60, 5%, 75%); + --quote-border: hsl(60, 5%, 70%); + + --table-border-color: hsl(60, 9%, 82%); + --table-header-bg: #b3a497; + --table-alternate-bg: hsl(60, 9%, 84%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #dec2a2; + --search-mark-bg: #e69f67; +} + +@media (prefers-color-scheme: dark) { + .light.no-js { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; + } +} diff --git a/documentation/operators/theme/index.hbs b/documentation/operators/theme/index.hbs new file mode 100644 index 0000000000..06a3731f33 --- /dev/null +++ b/documentation/operators/theme/index.hbs @@ -0,0 +1,1063 @@ + + + + + + {{ title }} + {{#if is_print }} + + {{/if}} + {{#if base_url}} + + {{/if}} + + + + {{> head}} + + + + + + {{#if favicon_svg}} + + {{/if}} + {{#if favicon_png}} + + {{/if}} + + + + {{#if print_enable}} + + {{/if}} + + + + {{#if copy_fonts}} + + {{/if}} + + + + + + + + {{#each additional_css}} + + {{/each}} + + {{#if mathjax_support}} + + + {{/if}} + + + + + + + + + + + + + + +

+ +
+ +
+ {{> header}} + + + + {{#if search_enabled}} + + {{/if}} + + + + +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + {{{ content }}} +
+ + +
+
+ + + +
+ + {{#if live_reload_endpoint}} + + + {{/if}} + + {{#if google_analytics}} + + + {{/if}} + + {{#if playground_line_numbers}} + + {{/if}} + + {{#if playground_copyable}} + + {{/if}} + + {{#if playground_js}} + + + + + + {{/if}} + + {{#if search_js}} + + + + {{/if}} + + + + + + + {{#each additional_js}} + + {{/each}} + + {{#if is_print}} + {{#if mathjax_support}} + + {{else}} + + {{/if}} + {{/if}} + + + diff --git a/documentation/operators/theme/pagetoc.css b/documentation/operators/theme/pagetoc.css new file mode 100644 index 0000000000..a68e5b8e21 --- /dev/null +++ b/documentation/operators/theme/pagetoc.css @@ -0,0 +1,47 @@ +/* src: https://github.com/JorelAli/mdBook-pagetoc */ + +@media only screen and (max-width:1439px) { + .sidetoc { + display: none; + } +} + +@media only screen and (min-width:1440px) { + main { + position: relative; + } + .sidetoc { + margin-left: auto; + margin-right: auto; + /* left: calc(90% + (var(--content-min-width))/4 - 110px); */ + left: 101%; + position: absolute; + font-size: var(--pagetoc-fontsize); + } + .pagetoc { + position: fixed; + width: var(--pagetoc-width); + } + .pagetoc a { + border-left: 1px solid var(--sidebar-bg); + /* color: var(--fg); */ + /* color: var(--sidebar-fg); */ + color: var(--links); + display: block; + padding-bottom: 5px; + padding-top: 5px; + padding-left: 10px; + text-align: left; + text-decoration: none; + font-weight: normal; + background: var(--sidebar-bg); + } + .pagetoc a:hover, + .pagetoc a.active { + background: var(--sidebar-bg); + /* color: var(--sidebar-fg); */ + color: var(--sidebar-active); + font-weight: bold; + font-size: var(--pagetoc-fontsize); + } +} diff --git a/documentation/operators/theme/pagetoc.js b/documentation/operators/theme/pagetoc.js new file mode 100644 index 0000000000..b11052427e --- /dev/null +++ b/documentation/operators/theme/pagetoc.js @@ -0,0 +1,68 @@ +// src: https://github.com/JorelAli/mdBook-pagetoc + +// Un-active everything when you click it +Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el, i) { + el.addEventHandler("click", function() { + Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el, i) { + el.classList.remove("active"); + }); + el.classList.add("active"); + }); +}); + +var updateFunction = function() { + + var id; + var elements = document.getElementsByClassName("header"); + Array.prototype.forEach.call(elements, function(el, i) { + if (window.pageYOffset >= el.offsetTop) { + id = el; + } + }); + + Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el, i) { + el.classList.remove("active"); + }); + + Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function(el, i) { + if (id.href.localeCompare(el.href) == 0) { + el.classList.add("active"); + } + }); +}; + +// Populate sidebar on load +window.addEventListener('load', function() { + var pagetoc = document.getElementsByClassName("pagetoc")[0]; + var elements = document.getElementsByClassName("header"); + Array.prototype.forEach.call(elements, function(el, i) { + var link = document.createElement("a"); + + // Indent shows hierarchy + var indent = ""; + switch (el.parentElement.tagName) { + case "H2": + indent = "20px"; + break; + case "H3": + indent = "40px"; + break; + case "H4": + indent = "60px"; + break; + default: + break; + } + + link.appendChild(document.createTextNode(el.text)); + link.style.paddingLeft = indent; + link.href = el.href; + pagetoc.appendChild(link); + }); + updateFunction.call(); +}); + + + +// Handle active elements on scroll +window.addEventListener("scroll", updateFunction); diff --git a/envs/local.env b/envs/local.env index c7121762d0..0fba848a3e 100644 --- a/envs/local.env +++ b/envs/local.env @@ -1,5 +1,7 @@ CONFIGURED=true +NETWORK_NAME=local + RUST_LOG=info RUST_BACKTRACE=1 @@ -11,11 +13,11 @@ STAKE_DENOM_DISPLAY=nyx DENOMS_EXPONENT=6 MIXNET_CONTRACT_ADDRESS=n1xsml6tm6pnx8nsuz3a54cznh55g6nuwu9ug9t92ucjkq5ewp5w9syxeqsv VESTING_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav -BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1lp5zex6685kd22agzskhqsylpnssxnweyuvsz4edr4p4ta92qf3q3dhmsx GROUP_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m MULTISIG_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m COCONUT_DKG_CONTRACT_ADDRESS=n1vwtgazgpancsfel04y7syc95ausmat47cjtldqzkdmx6phyrwx2qvkv32p +EPHEMERA_CONTRACT_ADDRESS=n1vwtgazgpancsfel04y7syc95ausmat47cjtldqzkdmx6phyrwx2qvkv32p REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0" NYXD="http://127.0.0.1:26657" diff --git a/envs/mainnet.env b/envs/mainnet.env index 967534f006..9dc5b5272d 100644 --- a/envs/mainnet.env +++ b/envs/mainnet.env @@ -1,5 +1,7 @@ CONFIGURED=true +NETWORK_NAME=mainnet + RUST_LOG=info RUST_BACKTRACE=1 @@ -11,14 +13,15 @@ STAKE_DENOM_DISPLAY=nyx DENOMS_EXPONENT=6 MIXNET_CONTRACT_ADDRESS=n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw -BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 GROUP_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_DKG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 +EPHEMERA_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090" NYXD="https://rpc.nymtech.net" NYM_API="https://validator.nymtech.net/api/" +EXPLORER_API="https://explorer.nymtech.net/api/" DKG_TIME_CONFIGURATION="259200,300,300,60,60,1209600" diff --git a/envs/qa-qwerty.env b/envs/qa-qwerty.env index 2a6a5da732..9080c9d37b 100644 --- a/envs/qa-qwerty.env +++ b/envs/qa-qwerty.env @@ -1,5 +1,7 @@ CONFIGURED=true +NETWORK_NAME=qa-qwerty + RUST_LOG=info RUST_BACKTRACE=1 @@ -11,16 +13,16 @@ STAKE_DENOM_DISPLAY=nyx DENOMS_EXPONENT=6 MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw -BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19d2nwj7fdhxqmyvgy8lf3ad49a6vmww4shryhrkj2mqk36att66s6xzszw GROUP_CONTRACT_ADDRESS=n1fqquzw4mk0pkamgr2ywt2v7h2j9nuyjjn4gvpy8zlpp6xn0uyuzqfm28l5 MULTISIG_CONTRACT_ADDRESS=n1gaq3666chd5348apj8cka8t2mckv7azp9espyr7wgpxyuzur5d0sazpysy COCONUT_DKG_CONTRACT_ADDRESS=n18yadscxw8v35dds7ksv3j0svmjh3h6e7tmxpadk96mvgz27zygkshuf4vs +EPHEMERA_CONTRACT_ADDRESS=n18yadscxw8v35dds7ksv3j0svmjh3h6e7tmxpadk96mvgz27zygkshuf4vs REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS=n1qsn2655eflc0nx2uwqtwyv5kad5dwm4c0gn72yr4q4de5r3jaz2slvqjgt NAME_SERVICE_CONTRACT_ADDRESS=n1cm2u5vfjd3zalfw0p65xyh4tcrw3hjlm0960gzhewga449h4mgas77mjkl STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090" NYXD="https://qwerty-validator.qa.nymte.ch/" -NYM_API="https://qwerty-validator-api.qa.nymte.ch/api" +NYM_API="https://qwerty-validator-api.qa.nymte.ch/api/" DKG_TIME_CONFIGURATION="600,300,300,60,60,1209600" diff --git a/envs/qa.env b/envs/qa.env index a5442b7600..82bb8b9d3c 100644 --- a/envs/qa.env +++ b/envs/qa.env @@ -1,5 +1,7 @@ CONFIGURED=true +NETWORK_NAME=qa + RUST_LOG=info RUST_BACKTRACE=1 @@ -11,14 +13,14 @@ STAKE_DENOM_DISPLAY=nyx DENOMS_EXPONENT=6 MIXNET_CONTRACT_ADDRESS=n1rjzps6qrmdqmf0xz4cn4x4rcmqeqzq6hnzqg4wcvd0r2lyasdq5sepn5s8 VESTING_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav -BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0 COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw GROUP_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m MULTISIG_CONTRACT_ADDRESS=n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs COCONUT_DKG_CONTRACT_ADDRESS=n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs +EPHEMERA_CONTRACT_ADDRESS=n1fc7nakzuyfn2qzkclafcsc54asamnclg064962lwne40w2lq558qftzjza REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0" NYXD="https://qa-validator.nymtech.net" -NYM_API="https://qa-validator-api.nymtech.net/api" +NYM_API="https://qa-validator-api.nymtech.net/api/" -DKG_TIME_CONFIGURATION="600,300,300,60,60,1209600" \ No newline at end of file +DKG_TIME_CONFIGURATION="600,300,300,60,60,1209600" diff --git a/envs/sandbox.env b/envs/sandbox.env index f42a91a97d..b4a66a70d1 100644 --- a/envs/sandbox.env +++ b/envs/sandbox.env @@ -1,5 +1,7 @@ CONFIGURED=true +NETWORK_NAME=sandbox + RUST_LOG=info RUST_BACKTRACE=1 @@ -13,12 +15,13 @@ DENOMS_EXPONENT=6 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 +EPHEMERA_CONTRACT_ADDRESS="nymt1gwk6muhmzeuxje7df7rjvqwl2vex0kj4t2hwuzmyx5k62kfusu5qk4k5z4" STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0" NYXD="https://sandbox-validator1.nymtech.net" -NYM_API="https://sandbox-nym-api1.nymtech.net/api" \ No newline at end of file +NYM_API="https://sandbox-nym-api1.nymtech.net/api/" +EXPLORER_API="https://sandbox-explorer.nymtech.net/api/" diff --git a/ephemera/Cargo.toml b/ephemera/Cargo.toml new file mode 100644 index 0000000000..50743df2fe --- /dev/null +++ b/ephemera/Cargo.toml @@ -0,0 +1,65 @@ +[package] +name = "ephemera" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "ephemera" +path = "bin/main.rs" + +[dependencies] +## internal +nym-task = { path = "../common/task" } + +actix-web = "4" +anyhow = { version = "1.0.66", features = ["backtrace"] } +array-bytes = "6.0.0" +async-trait = "0.1.59" +asynchronous-codec = "0.6.1" +blake2 = "0.10.6" +bs58 = "0.4.0" +bytes = "1.3.0" +cfg-if = "1.0.0" +chrono = { version = "0.4.24", default-features = false, features = ["clock"] } +clap = { version = "4.0.32", features = ["derive"] } +config = { version = "0.13", default-features = false, features = ["toml"] } +digest = "0.10.6" +dirs = "5.0.0" +futures = "0.3.18" +futures-util = "0.3.25" +lazy_static = "1.4.0" +libp2p = { version = "0.51.3", default-features = false, features = ["dns", "gossipsub", "kad", "macros", "noise", "request-response", "serde", "tcp", "tokio", "yamux"] } +libp2p-identity = "0.1.0" +log = "0.4.14" +lru = "0.10.0" +nym-config = { path = "../common/config" } +nym-ephemera-common = { path = "../common/cosmwasm-smart-contracts/ephemera" } +pretty_env_logger = "0.4" +refinery = { version = "0.8.7", features = ["rusqlite"], optional = true } +reqwest = { version = "0.11.6", features = ["json"] } +rocksdb = { version = "0.21.0", optional = true } +rusqlite = { version = "0.27.0", features = ["bundled"], optional = true } +serde = { version = "1.0", features = ["derive"] } +serde_derive = "1.0.149" +serde_json = "1.0.91" +thiserror = "1.0.37" +tokio = { version = "1", features = ["macros", "net","rt-multi-thread"] } +tokio-tungstenite = "0.18.0" +tokio-util = { version = "0.7.4", features = ["full"] } +toml = "0.7.0" +unsigned-varint = "0.7.1" +utoipa = { version = "3.0.1", features = ["actix_extras"] } +utoipa-swagger-ui = { version = "3.0.2", features = ["actix-web"] } +uuid = { version = "1.2.2", features = ["v4"] } + +# Temporary fix to https://github.com/bluejekyll/trust-dns/issues/1946 +enum-as-inner = "=0.5.1" + +[dev-dependencies] +assert_matches = "1.5.0" +rand = "0.8.5" + +[features] +default = ["sqlite_storage"] +rocksdb_storage = ["rocksdb"] +sqlite_storage = ["rusqlite", "refinery"] diff --git a/ephemera/README.md b/ephemera/README.md new file mode 100644 index 0000000000..5d4a9a6de7 --- /dev/null +++ b/ephemera/README.md @@ -0,0 +1,142 @@ +# Ephemera - reliable broadcast protocol implementation + +Ephemera does reliable broadcast for blocks. + +## Short Overview + +All Ephemera nodes accept messages submitted by clients. Node then gossips these to other nodes in the cluster. After certain interval, +a node collects messages and produces a block. Then it does reliable broadcast for the block with other nodes in the cluster. + +Ephemera doesn't have the concept of (decentralised) leader at the moment. It's up to an _Application_ to decide which block to use. +For example in case of Nym-Api, it is the first block submitted to a "Smart Contract". + +At the same time, the purpose of blocks is to reach consensus about which messages are included. It's just that Ephemera doesn't make the final decision, +instead it leaves that to an _Application_. + +## Main concepts + +- **Node** - a single instance of Ephemera. +- **Cluster** - a set of nodes participating in reliable broadcast. +- **EphemeraMessage** - a message submitted by a client. +- **Block** - a set of messages collected by a node. +- **Application(ABCI)** - a trait which Ephemera users implement to accept messages and blocks. + - check_tx + - check_block + - accept_block + +## How to run + +[README](../scripts/README.md) + +## HTTP API + +See [Rust](src/api/http/mod.rs) + +### Endpoints + +**NODE** +- `/ephemera/node/health` +- `/ephemera/node/config` + +**BLOCKS** +- `/ephemera/broadcast/block/{hash}` +- `/ephemera/broadcast/block/height/{height}` +- `/ephemera/broadcast/blocks/last` +- `/ephemera/broadcast/block/certificates/{hash}` +- `/ephemera/broadcast/block/broadcast_info/{hash}` + +**GROUP** +- `/ephemera/broadcast/group/info` + +**MESSAGES** +- `/ephemera/broadcast/submit_message` + +**DHT** +- `/ephemera/dht/query/{key}` +- `/ephemera/dht/store` + +## Rust API + +Almost identical to HTTP API. + +See [Rust](src/api/mod.rs) + +## Application(Ephemera ABCI) + +Cosmos style ABCI application hook +- `check_tx` +- `check_block` +- `deliver_block` + +See [Rust](src/api/application.rs) + +## Examples + +### Ephemera HTTP and WS external interfaces example/tests + +See [README.md](../examples/http-ws-sync/README.md) + +### Nym Api simulation + +See [README.md](../examples/nym-api/README.md) + +### http API example/tests + +See [README.md](../examples/cluster-http-api/README.md) + +### Membership over HTTP API example/tests + +See [README.md](../examples/members_provider_http/README.md) + +## About reliable broadcast and consensus + +In blockchain technology blocks have two main purposes: +1. To maintain chain of blocks, so that the validity of each block can be cryptographically verified by the previous blocks +2. As a unit of consensus, each block contains a set of transactions/messages/actions that are agreed upon by + the network. This set of transactions is chosen from the global set of all possible transactions that are pending. + We call the set of transactions in a block consensus because the set of nodes trying to achieve global shared state + agreed on this particular set of transactions. + +Ephemera is not a blockchain. But it uses blocks to agree on the set of transactions in a block. +But at the same time it doesn't behave like a blockchain consensus algorithm. +We may say that it allows each application that uses Ephemera to "propose" something what can be +afterwards to be used to achieve consensus. + +### In Summary + +1. Ephemera provides functionality to reach agreement on a single value between a set of nodes. +2. Ephemera also provides the concept of a block, which application can take advantage of to reach consensus externally. + +### Reliable broadcast, consensus and blocks + +In distributed systems(including byzantine), we try to solve the problem of reaching to a commons state between a set of nodes. + +One way to define this problem is using the following properties: +1. + 1) Agreement: All nodes agree on the same value.(TODO clarify) + 2) Consensus: All nodes agree on the same value.(TODO clarify) +2. Validity: All nodes agree on a value that is valid. +3. Termination: All nodes eventually agree on a value. + +Reliable broadcast ensures the properties of 1.1 and 1.2. It's left to a particular consensus algorithm to ensure the termination property. + +One important feature of consensus in blockchain is that it guarantees total ordering of transactions. +Reliable broadcast with blocks helps to ensure this total ordering. + +### Ephemera specific properties + +Because Ephemera doesn't use the idea of leader, we can say that it solves consensus partially. +It allows each instance to create a block. And then it's up to an application to decide which block to use. + +Also, as it doesn't implement a full consensus algorithm, it doesn't ensure the termination. +There's no algorithm in place what tries to reach a consensus about a single block globally and sequentially +in time. + +When a block contains a single message, then it's semantically equivalent to a reliable broadcast. + +But when a block contains multiple messages, then it can be part of a consensus process. Except that in Ephemera each node +can create a block. To achieve consensus in a more traditional sense, it needs an application help if more strict +consensus is required. + +For example, Nym-Api allows each node to create a block but uses external coordinator(a smart contract) +to decide which block to use. \ No newline at end of file diff --git a/ephemera/bin/main.rs b/ephemera/bin/main.rs new file mode 100644 index 0000000000..5f7d12976c --- /dev/null +++ b/ephemera/bin/main.rs @@ -0,0 +1,12 @@ +use clap::Parser; + +use ephemera::cli::Cli; +use ephemera::logging; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + logging::init(); + + Cli::parse().execute().await?; + Ok(()) +} diff --git a/ephemera/migrations/V1__block.sql b/ephemera/migrations/V1__block.sql new file mode 100644 index 0000000000..512f9c3af1 --- /dev/null +++ b/ephemera/migrations/V1__block.sql @@ -0,0 +1,24 @@ +CREATE TABLE IF NOT EXISTS blocks ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + block_hash TEXT NOT NULL UNIQUE, + height TEXT NOT NULL UNIQUE, + block BLOB NOT NULL +); + +CREATE TABLE IF NOT EXISTS block_certificates ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + block_hash TEXT NOT NULL UNIQUE, + certificates BLOB NOT NULL +); + +CREATE TABLE IF NOT EXISTS block_broadcast_group ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + block_hash TEXT NOT NULL UNIQUE, + members BLOB NOT NULL +); + +CREATE TABLE IF NOT EXISTS block_merkle_tree ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + block_hash TEXT NOT NULL UNIQUE, + merkle_tree BLOB NOT NULL +); \ No newline at end of file diff --git a/ephemera/src/api/application.rs b/ephemera/src/api/application.rs new file mode 100644 index 0000000000..4d605273f0 --- /dev/null +++ b/ephemera/src/api/application.rs @@ -0,0 +1,104 @@ +use log::trace; +use thiserror::Error; + +use crate::api::types::{ApiBlock, ApiEphemeraMessage}; + +#[derive(Debug, Clone, PartialEq)] +pub enum RemoveMessages { + /// Remove all messages from the mempool + All, + /// Remove only inclued messages from the mempool + Selected(Vec), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum CheckBlockResult { + /// Accept the block + Accept, + /// Reject the block with a reason. + Reject, + /// Reject the block and remove messages from the mempool + RejectAndRemoveMessages(RemoveMessages), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct CheckBlockResponse { + pub accept: bool, + pub reason: Option, +} + +#[derive(Error, Debug)] +pub enum Error { + //Just a placeholder for now + #[error("ApplicationError: {0}")] + Application(#[from] anyhow::Error), +} + +pub type Result = std::result::Result; + +/// Cosmos style ABCI application hook +/// +/// These functions should be relatively fast, as they are called synchronously by Ephemera main loop. +pub trait Application { + /// It's called when receiving a new message from network before adding it to the mempool. + /// It's up to the application to decide whether the message is valid or not. + /// Basic check could be for example signature verification. + /// + /// # Arguments + /// * `message` - message to be checked + /// + /// # Returns + /// * `true` - if the message is valid + /// * `false` - if the message is invalid + /// + /// # Errors + /// * `Error::General` - if there was an error during validation + fn check_tx(&self, message: ApiEphemeraMessage) -> Result; + + /// Ephemera produces new blocks with configured interval. + /// Application can decide whether to accept the block or not. + /// For example, if the block doesn't contain any transactions, it can be rejected. + /// + /// # Arguments + /// * `block` - block to be checked + /// + /// # Returns + /// * `CheckBlockResult::Accept` - if the block is valid + /// * `CheckBlockResult::Reject` - if the block is invalid + /// * `CheckBlockResult::RejectAndRemoveMessages` - if the block is invalid and some messages should be removed from the mempool + /// + /// # Errors + /// * `Error::General` - if there was an error during validation + fn check_block(&self, block: &ApiBlock) -> Result; + + /// Deliver Block is called after block is confirmed by Ephemera and persisted to the storage. + /// + /// # Arguments + /// * `block` - block to be delivered + /// + /// # Errors + /// * `Error::General` - if there was an error during validation + fn deliver_block(&self, block: ApiBlock) -> Result<()>; +} + +/// Dummy application which doesn't do any validation. +/// Might be useful for testing. +#[derive(Default)] +pub struct Dummy; + +impl Application for Dummy { + fn check_tx(&self, tx: ApiEphemeraMessage) -> Result { + trace!("check_tx: {tx:?}"); + Ok(true) + } + + fn check_block(&self, block: &ApiBlock) -> Result { + trace!("accept_block: {block:?}"); + Ok(CheckBlockResult::Accept) + } + + fn deliver_block(&self, block: ApiBlock) -> Result<()> { + trace!("deliver_block: {block:?}"); + Ok(()) + } +} diff --git a/ephemera/src/api/http/client.rs b/ephemera/src/api/http/client.rs new file mode 100644 index 0000000000..593ee5d0f6 --- /dev/null +++ b/ephemera/src/api/http/client.rs @@ -0,0 +1,497 @@ +use std::time::Duration; + +use thiserror::Error; + +use crate::api::types::{ApiBlockBroadcastInfo, ApiBroadcastInfo, ApiHealth}; +use crate::ephemera_api::{ + ApiBlock, ApiCertificate, ApiDhtQueryRequest, ApiDhtQueryResponse, ApiDhtStoreRequest, + ApiEphemeraConfig, ApiEphemeraMessage, ApiVerifyMessageInBlock, +}; + +#[derive(Error, Debug)] +pub enum Error { + #[error(transparent)] + Internal(#[from] reqwest::Error), + #[error("Unexpected response: {status} {body}")] + UnexpectedResponse { + status: reqwest::StatusCode, + body: String, + }, +} + +pub type Result = std::result::Result; + +/// Client to interact with the node over HTTP api. +pub struct Client { + pub(crate) client: reqwest::Client, + pub(crate) url: String, +} + +impl Client { + /// Create a http new client. + /// + /// # Arguments + /// * `url` - The url of the node api endpoint. + #[must_use] + pub fn new(url: String) -> Self { + let client = reqwest::Client::new(); + Self { client, url } + } + + /// Create a new client. + /// + /// # Arguments + /// * `url` - The url of the node api endpoint. + /// * `timeout_sec` - Request timeout in seconds. + /// + /// # Panics + /// If the client cannot be created. + #[must_use] + pub fn new_with_timeout(url: String, timeout_sec: u64) -> Self { + let client = reqwest::ClientBuilder::new() + .timeout(Duration::from_secs(timeout_sec)) + .build() + .unwrap(); + Self { client, url } + } + + /// Get the health of the node. + /// + /// # Example + /// ```no_run + /// use ephemera::ephemera_api::{Client, ApiHealth}; + /// + /// #[tokio::main] + ///async fn main() -> Result<(), Box> { + /// let client = Client::new("http://localhost:7000".to_string()); + /// let health = client.health().await.unwrap(); + /// Ok(()) + /// } + /// ``` + /// + /// # Returns + /// * [`ApiHealth`] - The health of the node. + /// + /// # Errors + /// If the request fails. + pub async fn health(&self) -> Result { + self.query("ephemera/node/health").await + } + + /// Get the block by hash. + /// + /// # Example + /// ```no_run + /// use ephemera::ephemera_api::{ApiBlock, Client}; + /// + /// #[tokio::main] + ///async fn main() -> Result<(), Box> { + /// let client = Client::new("http://localhost:7000".to_string()); + /// let block = client.get_block_by_hash("9D2LaY17rbnxfgKUbvcsJ5cB2BRHEd8fPJwsBnDHNGBX").await?; + /// Ok(()) + /// } + /// ``` + /// + /// # Returns + /// * Option<[`ApiBlock`]> - The block. + /// + /// # Errors + /// If the request fails. + pub async fn get_block_by_hash(&self, hash: &str) -> Result> { + let url = format!("ephemera/broadcast/block/{hash}",); + self.query_optional(&url).await + } + + /// Get the block certificates by hash. + /// + /// # Example + /// ```no_run + /// use ephemera::ephemera_api::{ApiCertificate, Client}; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// let client = Client::new("http://localhost:7000".to_string()); + /// let certificates = client.get_block_certificates("9D2LaY17rbnxfgKUbvcsJ5cB2BRHEd8fPJwsBnDHNGBX").await?; + /// Ok(()) + /// } + /// ``` + /// + /// # Arguments + /// * `hash` - The hash of the block. + /// + /// # Returns + /// * Option> - The block certificates. + /// + /// # Errors + /// If the request fails. + pub async fn get_block_certificates(&self, hash: &str) -> Result>> { + let url = format!("ephemera/broadcast/block/certificates/{hash}",); + self.query_optional(&url).await + } + + /// Get the block by height. + /// + /// # Example + /// ```no_run + /// use ephemera::ephemera_api::{ApiBlock, Client}; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// let client = Client::new("http://localhost:7000/".to_string()); + /// let block = client.get_block_by_height(1).await?; + /// Ok(()) + /// } + /// ``` + /// + /// # Arguments + /// * `height` - The height of the block. + /// + /// # Returns + /// * Option<[`ApiBlock`]> - The block. + /// + /// # Errors + /// If the request fails. + pub async fn get_block_by_height(&self, height: u64) -> Result> { + let url = format!("ephemera/broadcast/block/height/{height}",); + self.query_optional(&url).await + } + + /// Get the last block. + /// + /// # Example + /// ```no_run + /// use ephemera::ephemera_api::{ApiBlock, Client}; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// let client = Client::new("http://localhost:7000/".to_string()); + /// let block = client.get_last_block().await?; + /// Ok(()) + /// } + /// ``` + /// + /// # Returns + /// * [`ApiBlock`] - The last block. + /// + /// # Errors + /// If the request fails. + pub async fn get_last_block(&self) -> Result { + self.query("ephemera/broadcast/blocks/last").await + } + + /// Get the node configuration. + /// + /// # Example + /// ```no_run + /// use ephemera::ephemera_api::{ApiEphemeraConfig, Client}; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// let client = Client::new("http://localhost:7000/".to_string()); + /// let config = client.get_ephemera_config().await?; + /// Ok(()) + /// } + /// ``` + /// + /// # Returns + /// * [`ApiEphemeraConfig`] - The node configuration. + /// + /// # Errors + /// If the request fails. + pub async fn get_ephemera_config(&self) -> Result { + self.query("ephemera/node/config").await + } + + /// Submit a message to the node. + /// + /// # Example + /// ```no_run + /// use ephemera::ephemera_api::{ApiEphemeraMessage, Client}; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// let client = Client::new("http://localhost:7000/".to_string()); + /// let message = unimplemented!("See how to create a ApiEphemeraMessage"); + /// client.submit_message(message).await?; + /// Ok(()) + /// } + /// + /// ``` + /// + /// # Arguments + /// * `message` - The message to submit. + /// + /// # Errors + /// If the request fails. + pub async fn submit_message(&self, message: ApiEphemeraMessage) -> Result<()> { + let url = format!("{}/{}", self.url, "ephemera/broadcast/submit_message"); + let response = self.client.post(&url).json(&message).send().await?; + if response.status().is_success() { + Ok(()) + } else { + Err(Error::UnexpectedResponse { + status: response.status(), + body: response.text().await?, + }) + } + } + + ///Store Key Value pair in the DHT. + /// + /// # Example + ///```no_run + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// use ephemera::ephemera_api::Client; + /// let client = Client::new("http://localhost:7000/".to_string()); + /// let request = unimplemented!("See how to create a ApiDhtStoreRequest"); + /// client.store_in_dht(request).await?; + /// Ok(()) + /// } + /// ``` + /// + /// # Arguments + /// * `request` - Key Value pair to store. + /// + /// # Errors + /// If the request fails. + pub async fn store_in_dht(&self, request: ApiDhtStoreRequest) -> Result<()> { + let url = format!("{}/{}", self.url, "ephemera/dht/store"); + let response = self.client.post(&url).json(&request).send().await?; + if response.status().is_success() { + Ok(()) + } else { + Err(Error::UnexpectedResponse { + status: response.status(), + body: response.text().await?, + }) + } + } + + ///Store Key Value pair in the DHT. + /// + /// # Example + ///```no_run + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// use ephemera::ephemera_api::Client; + /// let client = Client::new("http://localhost:7000/".to_string()); + /// let key = &[1, 2, 3]; + /// let value = &[4, 5, 6]; + /// client.store_in_dht_key_value(key, value).await?; + /// Ok(()) + /// } + /// ``` + /// # Arguments + /// * `key` - Key to use to store the value. + /// * `value` - Value to store. + /// + /// # Errors + /// If the request fails. + pub async fn store_in_dht_key_value(&self, key: &[u8], value: &[u8]) -> Result<()> { + let request = ApiDhtStoreRequest::new(key, value); + self.store_in_dht(request).await + } + + /// Query the DHT for a given key. + /// + /// # Example + ///```no_run + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// use ephemera::ephemera_api::{ApiDhtQueryRequest, Client}; + /// let client = Client::new("http://localhost:7000/".to_string()); + /// let request = ApiDhtQueryRequest::new(&[1, 2, 3]); + /// let response = client.query_dht(request).await?; + /// Ok(()) + /// } + /// ``` + /// + /// # Arguments + /// * `request` - Key to query. + /// + /// # Returns + /// * Option<[`ApiDhtQueryResponse`]> - The value stored in the DHT for the given key. + /// + /// # Errors + /// If the request fails. + pub async fn query_dht( + &self, + request: ApiDhtQueryRequest, + ) -> Result> { + let url = format!("ephemera/dht/query/{}", request.key_encoded()); + self.query_optional(&url).await + } + + /// Query the DHT for a given key. + /// + /// # Example + /// ```no_run + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// use ephemera::ephemera_api::Client; + /// let client = Client::new("http://localhost:7000/".to_string()); + /// let key = &[1, 2, 3]; + /// let response = client.query_dht_key(key).await?; + /// Ok(()) + /// } + /// ``` + /// + /// # Arguments + /// * `key` - Key to query. + /// + /// # Returns + /// * Option<[`ApiDhtQueryResponse`]> - The value stored in the DHT for the given key. + /// + /// # Errors + /// If the request fails. + pub async fn query_dht_key(&self, key: &[u8]) -> Result> { + let request = ApiDhtQueryRequest::new(key); + self.query_dht(request).await + } + + /// Get broadcast group info. + /// + /// # Example + /// ```no_run + /// use ephemera::ephemera_api::Client; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// let client = Client::new("http://localhost:7000/".to_string()); + /// let info = client.broadcast_info().await?; + /// Ok(()) + /// } + /// ``` + /// + /// # Returns + /// * [`ApiBroadcastInfo`] - The broadcast group info. + /// + /// # Errors + /// If the request fails. + pub async fn broadcast_info(&self) -> Result { + self.query("ephemera/broadcast/group/info").await + } + + /// Get block broadcast info + /// + /// # Example + /// ```no_run + /// use ephemera::ephemera_api::Client; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// let client = Client::new("http://localhost:7000/".to_string()); + /// let info = client.get_block_broadcast_info("hash").await?; + /// Ok(()) + /// } + ///``` + /// + /// # Arguments + /// * `hash` - Hash of the block to query. + /// + /// # Returns + /// * Some([`ApiBlockBroadcastInfo`]) - The block broadcast info. + /// * None - If the block is not found. + /// + /// # Errors + /// If the request fails. + pub async fn get_block_broadcast_info( + &self, + hash: &str, + ) -> Result> { + let url = format!("ephemera/broadcast/block/broadcast_info/{hash}",); + self.query_optional(&url).await + } + + /// Verifies if given message is in block identified by block hash + /// + /// # Example + /// ```no_run + /// use ephemera::ephemera_api::Client; + /// + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { + /// let client = Client::new("http://localhost:7000/".to_string()); + /// let is_in_block = client.verify_message_in_block("block_hash", "message_hash", 0).await?; + /// Ok(()) + /// } + /// ``` + /// + /// # Arguments + /// * `block_hash` - Hash of the block to query. + /// * `message_hash` - Hash of the message to query. + /// * `message_index` - Index of the message in the block. + /// + /// # Returns + /// * bool - True if the message is in the block, false otherwise. + /// + /// # Errors + /// If the request fails. + pub async fn verify_message_in_block( + &self, + block_hash: &str, + message_hash: &str, + message_index: usize, + ) -> Result { + let request = ApiVerifyMessageInBlock::new( + block_hash.to_string(), + message_hash.to_string(), + message_index, + ); + + let url = format!("{}/{}", self.url, "ephemera/messages/verify"); + let response = self.client.post(&url).json(&request).send().await?; + if response.status().is_success() { + let body = response.json::().await?; + Ok(body) + } else { + Err(Error::UnexpectedResponse { + status: response.status(), + body: response.text().await?, + }) + } + } + + async fn query_optional serde::Deserialize<'de>>( + &self, + path: &str, + ) -> Result> { + let url = format!("{}/{}", self.url, path); + match self.client.get(&url).send().await { + Ok(response) => { + if response.status().is_success() { + let body = response.json::().await?; + Ok(Some(body)) + } else if response.status() == reqwest::StatusCode::NOT_FOUND { + Ok(None) + } else { + return Err(Error::UnexpectedResponse { + status: response.status(), + body: response.text().await?, + }); + } + } + Err(err) => Err(err.into()), + } + } + + async fn query serde::Deserialize<'de>>(&self, path: &str) -> Result { + let url = format!("{}/{}", self.url, path); + match self.client.get(&url).send().await { + Ok(response) => { + if response.status().is_success() { + let body = response.json::().await?; + Ok(body) + } else { + Err(Error::UnexpectedResponse { + status: response.status(), + body: response.text().await?, + }) + } + } + Err(err) => Err(err.into()), + } + } +} diff --git a/ephemera/src/api/http/mod.rs b/ephemera/src/api/http/mod.rs new file mode 100644 index 0000000000..010e397aec --- /dev/null +++ b/ephemera/src/api/http/mod.rs @@ -0,0 +1,88 @@ +use actix_web::{dev::Server, http::KeepAlive, web::Data, App, HttpServer}; +use log::info; +use utoipa::OpenApi; +use utoipa_swagger_ui::SwaggerUi; + +use crate::api::CommandExecutor; +use crate::core::builder::NodeInfo; + +pub(crate) mod client; +pub(crate) mod query; +pub(crate) mod submit; + +/// Starts the HTTP server. +pub(crate) fn init(node_info: &NodeInfo, api: CommandExecutor) -> anyhow::Result { + print_startup_messages(node_info); + + let server = HttpServer::new(move || { + App::new() + .app_data(Data::new(api.clone())) + .service(query::health) + .service(query::block_by_hash) + .service(query::block_certificates) + .service(query::block_by_height) + .service(query::block_broadcast_group) + .service(query::last_block) + .service(query::node_config) + .service(query::query_dht) + .service(query::broadcast_info) + .service(submit::submit_message) + .service(submit::store_in_dht) + .service(submit::verify_message_in_block) + .service(swagger_ui()) + }) + .keep_alive(KeepAlive::Os) + .bind((node_info.ip.as_str(), node_info.initial_config.http.port))? + .run(); + Ok(server) +} + +/// Builds the Swagger UI. +/// +/// Note that all routes you want Swagger docs for must be in the `paths` annotation. +fn swagger_ui() -> SwaggerUi { + use crate::api::types; + #[derive(OpenApi)] + #[openapi( + paths( + query::health, + query::block_by_hash, + query::block_certificates, + query::block_by_height, + query::last_block, + query::block_broadcast_group, + query::node_config, + query::query_dht, + query::broadcast_info, + submit::submit_message, + submit::store_in_dht, + submit::verify_message_in_block + ), + components(schemas( + types::ApiBlock, + types::ApiEphemeraMessage, + types::ApiCertificate, + types::ApiSignature, + types::ApiPublicKey, + types::ApiHealth, + types::HealthStatus, + types::ApiEphemeraConfig, + types::ApiDhtStoreRequest, + types::ApiDhtQueryRequest, + types::ApiDhtQueryResponse, + types::ApiBroadcastInfo, + types::ApiVerifyMessageInBlock, + )) + )] + struct ApiDoc; + SwaggerUi::new("/swagger-ui/{_:.*}").url("/api-doc/openapi.json", ApiDoc::openapi()) +} + +/// Prints messages saying which ports HTTP is running on, and some helpful pointers +/// `OpenAPI` and `Swagger UI` endpoints. +fn print_startup_messages(info: &NodeInfo) { + let http_root = info.api_address_http(); + info!("Server running on {}", http_root); + info!("Swagger UI: {}/swagger-ui/", http_root); + info!("OpenAPI spec is at: {}/api-doc/openapi.json", http_root); +} diff --git a/ephemera/src/api/http/query.rs b/ephemera/src/api/http/query.rs new file mode 100644 index 0000000000..cb7f53288e --- /dev/null +++ b/ephemera/src/api/http/query.rs @@ -0,0 +1,182 @@ +use actix_web::{get, web, HttpResponse, Responder}; +use log::error; + +use crate::{ + api::{types::ApiHealth, types::HealthStatus::Healthy, CommandExecutor}, + ephemera_api::{ApiDhtQueryRequest, ApiDhtQueryResponse}, +}; + +#[utoipa::path( +responses( +(status = 200, description = "Endpoint to check if the server is running")), +)] +#[get("/ephemera/node/health")] +#[allow(clippy::unused_async)] +pub(crate) async fn health() -> impl Responder { + HttpResponse::Ok().json(ApiHealth { status: Healthy }) +} + +#[utoipa::path( +responses( +(status = 200, description = "Get current broadcast group"), +(status = 500, description = "Server failed to process request")), +)] +#[get("/ephemera/broadcast/group/info")] +pub(crate) async fn broadcast_info(api: web::Data) -> impl Responder { + match api.get_broadcast_info().await { + Ok(group) => HttpResponse::Ok().json(group), + Err(err) => { + error!("Failed to get current broadcast group: {err}",); + HttpResponse::InternalServerError().json("Server failed to process request") + } + } +} + +#[utoipa::path( +responses( +(status = 200, description = "GET block by hash"), +(status = 404, description = "Block not found"), +(status = 500, description = "Server failed to process request")), +params(("hash", description = "Block hash")), +)] +#[get("/ephemera/broadcast/block/{hash}")] +pub(crate) async fn block_by_hash( + hash: web::Path, + api: web::Data, +) -> impl Responder { + match api.get_block_by_id(hash.into_inner()).await { + Ok(Some(block)) => HttpResponse::Ok().json(block), + Ok(_) => HttpResponse::NotFound().json("Block not found"), + Err(err) => { + error!("Failed to get block by hash: {err}",); + HttpResponse::InternalServerError().json("Server failed to process request") + } + } +} + +#[utoipa::path( +responses( +(status = 200, description = "Get block signatures"), +(status = 404, description = "Certificates not found"), +(status = 500, description = "Server failed to process request")), +params(("hash", description = "Block hash")), +)] +#[get("/ephemera/broadcast/block/certificates/{hash}")] +pub(crate) async fn block_certificates( + hash: web::Path, + api: web::Data, +) -> impl Responder { + let id = hash.into_inner(); + match api.get_block_certificates(id.clone()).await { + Ok(Some(signatures)) => HttpResponse::Ok().json(signatures), + Ok(_) => HttpResponse::NotFound().json("Certificates not found"), + Err(err) => { + error!("Failed to get signatures {err}",); + HttpResponse::InternalServerError().json("Server failed to process request") + } + } +} + +#[utoipa::path( +responses( +(status = 200, description = "Get block by height"), +(status = 404, description = "Block not found"), +(status = 500, description = "Server failed to process request")), +params(("height", description = "Block height")), +)] +#[get("/ephemera/broadcast/block/height/{height}")] +pub(crate) async fn block_by_height( + height: web::Path, + api: web::Data, +) -> impl Responder { + match api.get_block_by_height(height.into_inner()).await { + Ok(Some(block)) => HttpResponse::Ok().json(block), + Ok(_) => HttpResponse::NotFound().json("Block not found"), + Err(err) => { + error!("Failed to get block {err}",); + HttpResponse::InternalServerError().json("Server failed to process request") + } + } +} + +#[utoipa::path( +responses( +(status = 200, description = "Get last block"), +(status = 500, description = "Server failed to process request")), +)] +//Need to use plural(blocks), otherwise overlaps with block_by_id route +#[get("/ephemera/broadcast/blocks/last")] +pub(crate) async fn last_block(api: web::Data) -> impl Responder { + match api.get_last_block().await { + Ok(block) => HttpResponse::Ok().json(block), + Err(err) => { + error!("Failed to get block {err}",); + HttpResponse::InternalServerError().json("Server failed to process request") + } + } +} + +#[utoipa::path( +responses( +(status = 200, description = "Get block broadcast group"), +(status = 404, description = "Block not found"), +(status = 500, description = "Server failed to process request")), +params(("hash", description = "Block hash")), +)] +#[get("/ephemera/broadcast/block/broadcast_info/{hash}")] +pub(crate) async fn block_broadcast_group( + hash: web::Path, + api: web::Data, +) -> impl Responder { + let hash = hash.into_inner(); + match api.get_block_broadcast_info(hash).await { + Ok(Some(group)) => HttpResponse::Ok().json(group), + Ok(_) => HttpResponse::NotFound().json("Block not found"), + Err(err) => { + error!("Failed to get block broadcast group {err}",); + HttpResponse::InternalServerError().json("Server failed to process request") + } + } +} + +#[utoipa::path( +responses( +(status = 200, description = "Get node config"), +(status = 500, description = "Server failed to process request")), +)] +#[get("/ephemera/node/config")] +pub(crate) async fn node_config(api: web::Data) -> impl Responder { + match api.get_node_config().await { + Ok(config) => HttpResponse::Ok().json(config), + Err(err) => { + error!("Failed to get node config {err}",); + HttpResponse::InternalServerError().json("Server failed to process request") + } + } +} + +#[utoipa::path( +responses( +(status = 200, description = "Query dht"), +(status = 500, description = "Server failed to process request")), +params(("query", description = "Dht query")), +)] +#[get("/ephemera/dht/query/{key}")] +pub(crate) async fn query_dht( + api: web::Data, + key: web::Path, +) -> impl Responder { + let key = ApiDhtQueryRequest::parse_key(key.into_inner().as_str()); + + match api.query_dht(key).await { + Ok(Some((key, value))) => { + let response = ApiDhtQueryResponse::new(key, value); + HttpResponse::Ok().json(response) + } + Ok(_) => HttpResponse::NotFound().json("Not found"), + Err(err) => { + error!("Failed to query dht {err}",); + HttpResponse::InternalServerError().json("Server failed to process request") + } + } +} diff --git a/ephemera/src/api/http/submit.rs b/ephemera/src/api/http/submit.rs new file mode 100644 index 0000000000..7142c2ad9b --- /dev/null +++ b/ephemera/src/api/http/submit.rs @@ -0,0 +1,88 @@ +use actix_web::{post, web, HttpResponse}; +use log::{debug, error}; + +use crate::api::types::ApiVerifyMessageInBlock; +use crate::api::{ + types::{ApiDhtStoreRequest, ApiEphemeraMessage}, + ApiError, CommandExecutor, +}; + +#[utoipa::path( +request_body = ApiEphemeraMessage, +responses( +(status = 200, description = "Send a message to an Ephemera node which will be broadcast to the network"), +(status = 500, description = "Server failed to process request")), +params(("message", description = "Message to send")) +)] +#[post("/ephemera/broadcast/submit_message")] +pub(crate) async fn submit_message( + message: web::Json, + api: web::Data, +) -> HttpResponse { + match api.send_ephemera_message(message.into_inner()).await { + Ok(_) => HttpResponse::Ok().json("Message submitted"), + Err(err) => { + if let ApiError::DuplicateMessage = err { + debug!("Message already submitted {err:?}"); + HttpResponse::BadRequest().json("Message already submitted") + } else { + error!("Error submitting message: {}", err); + HttpResponse::InternalServerError().json("Server failed to process request") + } + } + } +} + +#[utoipa::path( +request_body = ApiDhtStoreRequest, +responses( +(status = 200, description = "Request to store a value in the DHT"), +(status = 500, description = "Server failed to process request")), +params( +("request", description = "Dht store request") +) +)] +#[post("/ephemera/dht/store")] +pub(crate) async fn store_in_dht( + request: web::Json, + api: web::Data, +) -> HttpResponse { + let request = request.into_inner(); + + let key = request.key(); + let value = request.value(); + + match api.store_in_dht(key, value).await { + Ok(_) => HttpResponse::Ok().json("Store request submitted"), + Err(err) => { + error!("Error storing in dht: {}", err); + HttpResponse::InternalServerError().json("Server failed to process request") + } + } +} + +#[utoipa::path( +request_body = ApiVerifyMessageInBlock, +responses( +(status = 200, description = "Verifies if given message is in block identified by block hash.\ +Returns true if message is in block, false otherwise. False can also mean that block or message \ +does not exist in that block."), +(status = 500, description = "Server failed to process request")), +params( +("request", description = "Verify message request") +) +)] +#[post("/ephemera/messages/verify")] +pub(crate) async fn verify_message_in_block( + request: web::Json, + api: web::Data, +) -> HttpResponse { + let request = request.into_inner(); + match api.verify_message_in_block(request).await { + Ok(valid) => HttpResponse::Ok().json(valid), + Err(err) => { + error!("Error verifying message: {}", err); + HttpResponse::InternalServerError().json("Server failed to process request") + } + } +} diff --git a/ephemera/src/api/mod.rs b/ephemera/src/api/mod.rs new file mode 100644 index 0000000000..ae2b4f6de4 --- /dev/null +++ b/ephemera/src/api/mod.rs @@ -0,0 +1,309 @@ +//! # Ephemera API +//! +//! This module contains all the types and functions available as part of Ephemera public API. +//! +//! This API is also available over HTTP. + +use std::fmt::Display; + +use log::{error, trace}; +use tokio::sync::{ + mpsc::{channel, Receiver, Sender}, + oneshot, +}; + +use crate::api::types::{ + ApiBlock, ApiBlockBroadcastInfo, ApiBroadcastInfo, ApiCertificate, ApiEphemeraConfig, + ApiEphemeraMessage, ApiError, ApiVerifyMessageInBlock, +}; + +pub(crate) mod application; +pub(crate) mod http; +pub(crate) mod types; + +/// Kademlia DHT key +pub(crate) type DhtKey = Vec; + +/// Kademlia DHT value +pub(crate) type DhtValue = Vec; + +/// Kademlia DHT key/value pair +pub(crate) type DhtKV = (DhtKey, DhtValue); + +pub(crate) type Result = std::result::Result; + +#[derive(Debug)] +pub(crate) enum ToEphemeraApiCmd { + SubmitEphemeraMessage(Box, oneshot::Sender>), + QueryBlockByHeight(u64, oneshot::Sender>>), + QueryBlockByHash(String, oneshot::Sender>>), + QueryLastBlock(oneshot::Sender>), + QueryBlockCertificates(String, oneshot::Sender>>>), + QueryDht(DhtKey, oneshot::Sender>>), + StoreInDht(DhtKey, DhtValue, oneshot::Sender>), + QueryEphemeraConfig(oneshot::Sender>), + QueryBroadcastGroup(oneshot::Sender>), + QueryBlockBroadcastInfo( + String, + oneshot::Sender>>, + ), + VerifyMessageInBlock(String, String, usize, oneshot::Sender>), +} + +impl Display for ToEphemeraApiCmd { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ToEphemeraApiCmd::SubmitEphemeraMessage(message, _) => { + write!(f, "SubmitEphemeraMessage({message})",) + } + ToEphemeraApiCmd::QueryBlockByHeight(height, _) => { + write!(f, "QueryBlockByHeight({height})",) + } + ToEphemeraApiCmd::QueryBlockByHash(hash, _) => write!(f, "QueryBlockByHash({hash})",), + ToEphemeraApiCmd::QueryLastBlock(_) => write!(f, "QueryLastBlock"), + ToEphemeraApiCmd::QueryBlockCertificates(id, _) => { + write!(f, "QueryBlockSignatures{id}") + } + ToEphemeraApiCmd::QueryDht(_, _) => { + write!(f, "QueryDht") + } + ToEphemeraApiCmd::StoreInDht(_, _, _) => { + write!(f, "StoreInDht") + } + ToEphemeraApiCmd::QueryEphemeraConfig(_) => { + write!(f, "EphemeraConfig") + } + ToEphemeraApiCmd::QueryBroadcastGroup(_) => { + write!(f, "BroadcastGroup") + } + ToEphemeraApiCmd::QueryBlockBroadcastInfo(hash, ..) => { + write!(f, "BlockBroadcastInfo({hash})") + } + ToEphemeraApiCmd::VerifyMessageInBlock(block_id, message_id, height, _) => { + write!( + f, + "VerifyMessageInBlock({block_id}, {message_id}, {height})", + ) + } + } + } +} + +pub(crate) struct ApiListener { + pub(crate) messages_rcv: Receiver, +} + +impl ApiListener { + pub(crate) fn new(messages_rcv: Receiver) -> Self { + Self { messages_rcv } + } +} + +#[derive(Clone)] +pub struct CommandExecutor { + pub(crate) commands_channel: Sender, +} + +impl CommandExecutor { + pub(crate) fn new() -> (CommandExecutor, ApiListener) { + let (commands_channel, signed_messages_rcv) = channel(100); + let api_listener = ApiListener::new(signed_messages_rcv); + let api = CommandExecutor { commands_channel }; + (api, api_listener) + } + + /// Returns block with given id if it exists + /// + /// # Arguments + /// * `block_id` - Block id + /// + /// # Returns + /// * `ApiBlock` - Block + /// + /// # Errors + /// * `ApiError::InternalError` - If there is an internal error + pub async fn get_block_by_id(&self, block_id: String) -> Result> { + trace!("get_block_by_id({:?})", block_id); + self.send_and_wait_response(|tx| ToEphemeraApiCmd::QueryBlockByHash(block_id, tx)) + .await + } + + /// Returns block with given height if it exists + /// + /// # Arguments + /// * `height` - Block height + /// + /// # Returns + /// * `ApiBlock` - Block + /// + /// # Errors + /// * `ApiError::InternalError` - If there is an internal error + pub async fn get_block_by_height(&self, height: u64) -> Result> { + trace!("get_block_by_height({:?})", height); + self.send_and_wait_response(|tx| ToEphemeraApiCmd::QueryBlockByHeight(height, tx)) + .await + } + + /// Returns last block. Which has maximum height and is stored in database + /// + /// # Returns + /// * `ApiBlock` - Last block + /// + /// # Errors + /// * `ApiError::InternalError` - If there is an internal error + pub async fn get_last_block(&self) -> Result { + trace!("get_last_block()"); + self.send_and_wait_response(ToEphemeraApiCmd::QueryLastBlock) + .await + } + + /// Returns signatures for given block id + /// + /// # Arguments + /// * `block_hash` - Block id + /// + /// # Returns + /// * `Vec` - Certificates + /// + /// # Errors + /// * `ApiError::InternalError` - If there is an internal error + pub async fn get_block_certificates( + &self, + block_hash: String, + ) -> Result>> { + trace!("get_block_certificates({block_hash:?})",); + self.send_and_wait_response(|tx| ToEphemeraApiCmd::QueryBlockCertificates(block_hash, tx)) + .await + } + + /// Queries DHT for given key + /// + /// # Arguments + /// * `key` - DHT key + /// + /// # Errors + /// * `ApiError::InternalError` - If there is an internal error + /// + /// # Returns + /// * `Some((key, value))` - If key is found + /// * `None` - If key is not found + pub async fn query_dht(&self, key: DhtKey) -> Result> { + trace!("get_dht({key:?})"); + //TODO: this needs timeout(somewhere around dht query functionality) + self.send_and_wait_response(|tx| ToEphemeraApiCmd::QueryDht(key, tx)) + .await + } + + /// Stores given key-value pair in DHT + /// + /// # Arguments + /// * `key` - DHT key + /// * `value` - DHT value + /// + /// # Errors + /// * `ApiError::InternalError` - If there is an internal error + pub async fn store_in_dht(&self, key: DhtKey, value: DhtValue) -> Result<()> { + trace!("store_in_dht({key:?}, {value:?})"); + self.send_and_wait_response(|tx| ToEphemeraApiCmd::StoreInDht(key, value, tx)) + .await + } + + /// Returns node configuration + /// + /// # Returns + /// * `ApiEphemeraConfig` - Node configuration + /// + /// # Errors + /// * `ApiError::InternalError` - If there is an internal error + pub async fn get_node_config(&self) -> Result { + trace!("get_node_config()"); + self.send_and_wait_response(ToEphemeraApiCmd::QueryEphemeraConfig) + .await + } + + /// Returns broadcast group + /// + /// # Errors + /// * `ApiError::InternalError` - If there is an internal error + /// + /// # Return + /// * `ApiBroadcastInfo` - Broadcast group + pub async fn get_broadcast_info(&self) -> Result { + trace!("get_broadcast_group()"); + self.send_and_wait_response(ToEphemeraApiCmd::QueryBroadcastGroup) + .await + } + + /// Returns block broadcast info. + /// + /// # Arguments + /// * `block_hash` - Block hash + /// + /// # Return + /// * `ApiBlockBroadcastInfo` - Block broadcast info + /// + /// # Errors + /// * `ApiError::InternalError` - If there is an internal error + pub async fn get_block_broadcast_info( + &self, + block_hash: String, + ) -> Result> { + trace!("get_broadcast_group()"); + self.send_and_wait_response(|tx| ToEphemeraApiCmd::QueryBlockBroadcastInfo(block_hash, tx)) + .await + } + + /// Send a message to Ephemera which should then be included in mempool and broadcast to all peers + /// + /// # Arguments + /// * `message` - Message to be sent + /// + /// # Errors + /// * `ApiError::InternalError` - If there is an internal error + pub async fn send_ephemera_message(&self, message: ApiEphemeraMessage) -> Result<()> { + trace!("send_ephemera_message({message})",); + self.send_and_wait_response(|tx| { + ToEphemeraApiCmd::SubmitEphemeraMessage(message.into(), tx) + }) + .await + } + + /// Verifies if given message is in block identified by block hash + /// Returns true if message is in block, false otherwise. False can also mean that block or message + /// does not exist. + /// + /// # Arguments + /// * `request` - Message and block hash + /// + /// # Errors + /// * `ApiError::InternalError` - If there is an internal error + pub async fn verify_message_in_block(&self, request: ApiVerifyMessageInBlock) -> Result { + trace!("verify_message_in_block({request})",); + let block_hash = request.block_hash; + let message_hash = request.message_hash; + let index = request.message_index; + self.send_and_wait_response(|tx| { + ToEphemeraApiCmd::VerifyMessageInBlock(block_hash, message_hash, index, tx) + }) + .await + } + + async fn send_and_wait_response(&self, f: F) -> Result + where + F: FnOnce(oneshot::Sender>) -> ToEphemeraApiCmd, + R: Send + 'static, + { + let (tx, rcv) = oneshot::channel(); + let cmd = f(tx); + if let Err(e) = self.commands_channel.send(cmd).await { + error!("Failed to send command to Ephemera: {e:?}",); + return Err(ApiError::Internal( + "Failed to receive response from Ephemera".to_string(), + )); + } + rcv.await.map_err(|e| { + error!("Failed to receive response from Ephemera: {e:?}",); + ApiError::Internal("Failed to receive response from Ephemera".to_string()) + })? + } +} diff --git a/ephemera/src/api/types.rs b/ephemera/src/api/types.rs new file mode 100644 index 0000000000..cfd99a300d --- /dev/null +++ b/ephemera/src/api/types.rs @@ -0,0 +1,659 @@ +//! This module contains all the types that are used in the API. +//! +//! - `ApiEphemeraMessage` +//! - `RawApiEphemeraMessage` +//! - `ApiBlock` +//! - `ApiCertificate` +//! - `Health` +//! - `ApiError` +//! - `ApiEphemeraConfig` +//! - `ApiDhtQueryRequest` +//! - `ApiDhtQueryResponse` +//! - `ApiDhtStoreRequest` +//! - `ApiBroadcastInfo` +//! - `ApiBlockBroadcastInfo` +//! - `ApiVerifyMessageInBlock` + +use std::collections::HashSet; +use std::fmt::Display; + +use array_bytes::{bytes2hex, hex2bytes}; +use log::error; +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use utoipa::ToSchema; + +use crate::peer::{PeerId, ToPeerId}; +use crate::utilities::codec::{Codec, DecodingError, EncodingError, EphemeraCodec}; +use crate::{ + block::types::{block::Block, block::BlockHeader, message::EphemeraMessage}, + codec::{Decode, Encode}, + crypto::{Keypair, PublicKey}, + ephemera_api, + utilities::{ + crypto::{Certificate, Signature}, + time::EphemeraTime, + }, +}; + +#[derive(Error, Debug)] +pub enum ApiError { + #[error("Application rejected ephemera message")] + ApplicationRejectedMessage, + #[error("Duplicate message")] + DuplicateMessage, + #[error("Invalid hash: {0}")] + InvalidHash(String), + #[error("ApplicationError: {0}")] + Application(#[from] ephemera_api::ApplicationError), + #[error("Internal error: {0}")] + Internal(String), +} + +/// # Ephemera message. +/// +/// A message submitted to an Ephemera node will be gossiped to other nodes. +/// And will be eventually included in a Ephemera block. +/// +/// It needs to signed by the sender. The signature is included in the certificate. +/// +/// The fields of the message what are signed: +/// - timestamp +/// - label +/// - data +/// +/// Currently it's up provided [`ephemera_api::application::Application`] to verify the signature. +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiEphemeraMessage { + /// The timestamp of the message. + pub timestamp: u64, + /// The label of the message. It can be used to identify the type of a message for example. + pub label: String, + /// The data of the message. It is application specific. + pub data: Vec, + /// The certificate of the message. All messages are required to be signed. + pub certificate: ApiCertificate, +} + +impl ApiEphemeraMessage { + #[must_use] + pub fn new(raw_message: RawApiEphemeraMessage, certificate: ApiCertificate) -> Self { + Self { + timestamp: raw_message.timestamp, + label: raw_message.label, + data: raw_message.data, + certificate, + } + } + + /// Generates the message hash. + /// + /// # Errors + /// - If internal hash function fails. + pub fn hash(&self) -> anyhow::Result { + let em = EphemeraMessage::from(self.clone()); + let hash = em.hash_with_default_hasher()?.to_string(); + Ok(hash) + } +} + +/// `RawApiEphemeraMessage` contains the fields of the `ApiEphemeraMessage` that are signed. +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct RawApiEphemeraMessage { + /// The timestamp of the message. It's initialized when the message is created. + /// It uses UTC time. + pub timestamp: u64, + /// The label of the message. It can be used to identify the type of a message without decoding full data. + pub label: String, + /// The data of the message. It is application specific. + pub data: Vec, +} + +impl RawApiEphemeraMessage { + #[must_use] + pub fn new(label: String, data: Vec) -> Self { + Self { + timestamp: EphemeraTime::now(), + label, + data, + } + } + + /// Signs the message with the given keypair. + /// + /// # Signing example + /// + /// ``` + /// use ephemera::codec::Encode; + /// use ephemera::crypto::{EphemeraKeypair, EphemeraPublicKey, Keypair}; + /// use ephemera::ephemera_api::{ApiEphemeraMessage, RawApiEphemeraMessage}; + /// + /// let keypair = Keypair::generate(None); + /// let raw_message = RawApiEphemeraMessage::new("test".to_string(), vec![]); + /// + /// let signed_message:ApiEphemeraMessage = raw_message.sign(&keypair).unwrap(); + /// + /// assert_eq!(signed_message.certificate.public_key, keypair.public_key().into()); + /// + /// let bytes = raw_message.encode().unwrap(); + /// assert!(keypair.public_key().verify(&bytes, &signed_message.certificate.signature.into())); + /// ``` + /// + /// # Errors + /// - If the message can't be encoded. + /// - If the message can't be signed. + pub fn sign(&self, keypair: &Keypair) -> anyhow::Result { + let certificate = Certificate::prepare(keypair, &self)?; + let message = ApiEphemeraMessage::new(self.clone(), certificate.into()); + Ok(message) + } +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub struct ApiBlockHeader { + /// The timestamp of the block. It's initialized when the block is created. + /// It uses UTC time. + pub timestamp: u64, + /// The PeerId of the block producer instance. + pub creator: PeerId, + /// The height of the block. + pub height: u64, + /// The hash of the current block. + pub hash: String, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiBlock { + pub header: ApiBlockHeader, + pub messages: Vec, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub struct ApiRawBlock { + pub(crate) header: ApiBlockHeader, + pub(crate) messages: Vec, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiCertificate { + pub signature: ApiSignature, + pub public_key: ApiPublicKey, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiSignature(pub(crate) Signature); + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiPublicKey(pub(crate) PublicKey); + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiEphemeraConfig { + /// The address of the node. It's the address what Ephemera instance uses to communicate with other nodes. + pub protocol_address: String, + /// The HTTP API address of the node. + pub api_address: String, + /// The WebSocket address of the node. + pub websocket_address: String, + /// Node's public key. + /// + /// # Converting to string and back example + /// ``` + /// use ephemera::crypto::{EphemeraKeypair, Keypair, PublicKey}; + /// + /// let keypair = Keypair::generate(None); + /// let public_key = keypair.public_key().to_string(); + /// + /// let from_str = public_key.parse::().unwrap(); + /// + /// assert_eq!(keypair.public_key(), from_str); + /// ``` + pub public_key: String, + /// True if the node is a block producer. It's a configuration option. + pub block_producer: bool, + /// The interval of block creation in seconds. It's a configuration option. + pub block_creation_interval_sec: u64, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiDhtQueryRequest { + /// The key to query for in hex format. + key: String, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiDhtQueryResponse { + /// The key that was queried for in hex format. + key: String, + /// The value that was stored under the queried key in hex format. + value: String, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub enum HealthStatus { + Healthy, + Unhealthy, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiHealth { + pub(crate) status: HealthStatus, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiDhtStoreRequest { + /// The key to store the value under in hex format. + key: String, + /// The value to store in hex format. + value: String, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiBroadcastInfo { + /// The PeerId of the local node. + pub local_peer_id: PeerId, + /// The list of the current members of the network. + pub current_members: HashSet, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiBlockBroadcastInfo { + pub local_peer_id: PeerId, + pub broadcast_group: Vec, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ToSchema)] +pub struct ApiVerifyMessageInBlock { + pub block_hash: String, + pub message_hash: String, + pub message_index: usize, +} + +impl ApiVerifyMessageInBlock { + #[must_use] + pub fn new(block_hash: String, message_hash: String, message_index: usize) -> Self { + Self { + block_hash, + message_hash, + message_index, + } + } +} + +impl Display for ApiVerifyMessageInBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{{ block_hash: {}, message_hash: {} }}", + self.block_hash, self.message_hash + ) + } +} + +impl ApiBlockBroadcastInfo { + pub(crate) fn new(local_peer_id: PeerId, broadcast_group: Vec) -> Self { + Self { + local_peer_id, + broadcast_group, + } + } +} + +impl ApiBroadcastInfo { + pub(crate) fn new(current_members: HashSet, local_peer_id: PeerId) -> Self { + Self { + local_peer_id, + current_members, + } + } +} + +impl Display for ApiBroadcastInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let current_members = self.current_members.iter().map(ToString::to_string); + write!( + f, + "{{ local_peer_id: {}, current_members: {current_members:?} }}", + self.local_peer_id, + ) + } +} + +impl Display for ApiEphemeraMessage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "ApiEphemeraMessage(timestamp: {}, label: {})", + self.timestamp, self.label, + ) + } +} + +impl From for RawApiEphemeraMessage { + fn from(message: ApiEphemeraMessage) -> Self { + RawApiEphemeraMessage { + timestamp: message.timestamp, + label: message.label, + data: message.data, + } + } +} + +impl From for EphemeraMessage { + fn from(message: ApiEphemeraMessage) -> Self { + Self { + timestamp: message.timestamp, + label: message.label, + data: message.data, + certificate: message.certificate.into(), + } + } +} + +impl Decode for RawApiEphemeraMessage { + type Output = Self; + + fn decode(bytes: &[u8]) -> Result { + Codec::decode(bytes) + } +} + +impl Encode for RawApiEphemeraMessage { + fn encode(&self) -> Result, EncodingError> { + Codec::encode(self) + } +} + +impl Encode for &RawApiEphemeraMessage { + fn encode(&self) -> Result, EncodingError> { + Codec::encode(self) + } +} + +impl Display for ApiBlockHeader { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "ApiBlockHeader(timestamp: {}, creator: {}, height: {}, hash: {})", + self.timestamp, self.creator, self.height, self.hash, + ) + } +} + +impl ApiBlock { + #[must_use] + pub fn as_raw_block(&self) -> ApiRawBlock { + ApiRawBlock { + header: self.header.clone(), + messages: self.messages.clone(), + } + } + + #[must_use] + pub fn message_count(&self) -> usize { + self.messages.len() + } + + #[must_use] + pub fn hash(&self) -> String { + self.header.hash.clone() + } + + /// # Errors + /// - If the block is invalid. + /// - If the block's certificate is invalid. + /// - If the block's certificate is not signed by the block's creator. + pub fn verify(&self, certificate: &ApiCertificate) -> Result { + let block: Block = self.clone().try_into()?; + let valid = block.verify(&(certificate.clone()).into()).map_err(|e| { + error!("Failed to verify block: {}", e); + ApiError::Internal("Failed to verify block certificate".to_string()) + })?; + Ok(valid) + } +} + +impl Display for ApiBlock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "ApiBlock(header: {}, message_count: {})", + self.header, + self.message_count() + ) + } +} + +impl ApiRawBlock { + pub fn new(header: ApiBlockHeader, messages: Vec) -> Self { + Self { header, messages } + } +} + +impl ApiCertificate { + /// # Errors + /// - `EncodingError` if the message cannot be encoded. + /// - `KeyPairError` if the message cannot be signed. + pub fn prepare(key_pair: &Keypair, data: &D) -> anyhow::Result { + Certificate::prepare(key_pair, data).map(Into::into) + } + + /// # Errors + /// -`EncodingError` if the message cannot be encoded. + pub fn verify(&self, data: &D) -> anyhow::Result { + let certificate: Certificate = (self.clone()).into(); + Certificate::verify(&certificate, data) + } +} + +impl From for ApiEphemeraMessage { + fn from(ephemera_message: EphemeraMessage) -> Self { + Self { + timestamp: ephemera_message.timestamp, + label: ephemera_message.label, + data: ephemera_message.data, + certificate: ApiCertificate { + signature: ephemera_message.certificate.signature.into(), + public_key: ephemera_message.certificate.public_key.into(), + }, + } + } +} + +impl From for ApiCertificate { + fn from(signature: Certificate) -> Self { + Self { + signature: signature.signature.into(), + public_key: signature.public_key.into(), + } + } +} + +impl From for Certificate { + fn from(value: ApiCertificate) -> Self { + Certificate { + signature: value.signature.into(), + public_key: value.public_key.into(), + } + } +} + +impl From<&Block> for &ApiBlock { + fn from(block: &Block) -> Self { + let api_block: ApiBlock = block.clone().into(); + Box::leak(Box::new(api_block)) + } +} + +impl From for ApiSignature { + fn from(signature: Signature) -> Self { + Self(signature) + } +} + +impl From for Signature { + fn from(signature: ApiSignature) -> Self { + signature.0 + } +} + +impl ApiPublicKey { + pub fn peer_id(&self) -> String { + self.0.peer_id().to_string() + } +} + +impl From for ApiPublicKey { + fn from(public_key: PublicKey) -> Self { + Self(public_key) + } +} + +impl From for PublicKey { + fn from(public_key: ApiPublicKey) -> Self { + public_key.0 + } +} + +impl From for ApiBlock { + fn from(block: Block) -> Self { + Self { + header: ApiBlockHeader { + timestamp: block.header.timestamp, + creator: block.header.creator, + height: block.header.height, + hash: block.header.hash.to_string(), + }, + messages: block.messages.into_iter().map(Into::into).collect(), + } + } +} + +impl TryFrom for Block { + type Error = ApiError; + + fn try_from(api_block: ApiBlock) -> Result { + let messages: Vec = api_block + .messages + .into_iter() + .map(Into::into) + .collect::>(); + Ok(Self { + header: BlockHeader { + timestamp: api_block.header.timestamp, + creator: api_block.header.creator, + height: api_block.header.height, + hash: api_block.header.hash.parse().map_err(|e| { + error!("Failed to parse block hash: {}", e); + ApiError::Internal("Failed to parse block hash".to_string()) + })?, + }, + messages, + }) + } +} + +impl ApiDhtStoreRequest { + #[must_use] + pub fn new(key: &[u8], value: &[u8]) -> Self { + let key = bytes2hex("0x", key); + let value = bytes2hex("0x", value); + Self { key, value } + } + + #[allow(clippy::missing_panics_doc)] + #[must_use] + pub fn key(&self) -> Vec { + //We can unwrap here because the key is always valid. + hex2bytes(&self.key).unwrap() + } + + #[allow(clippy::missing_panics_doc)] + #[must_use] + pub fn value(&self) -> Vec { + //We can unwrap here because the value is always valid. + hex2bytes(&self.value).unwrap() + } +} + +impl ApiDhtQueryRequest { + #[must_use] + pub fn new(key: &[u8]) -> Self { + let key = bytes2hex("0x", key); + Self { key } + } + + #[must_use] + pub fn key_encoded(&self) -> String { + self.key.clone() + } + + #[allow(clippy::missing_panics_doc)] + #[must_use] + pub fn key(&self) -> Vec { + //We can unwrap here because the value is always valid. + hex2bytes(&self.key).unwrap() + } + + pub(crate) fn parse_key(key: &str) -> Vec { + hex2bytes(key).unwrap() + } +} + +impl ApiDhtQueryResponse { + pub(crate) fn new(key: Vec, value: Vec) -> Self { + let key = bytes2hex("0x", key); + let value = bytes2hex("0x", value); + Self { key, value } + } + + #[allow(clippy::missing_panics_doc)] + #[must_use] + pub fn key(&self) -> Vec { + //We can unwrap here because the key is always valid. + hex2bytes(&self.key).unwrap() + } + + #[allow(clippy::missing_panics_doc)] + #[must_use] + pub fn value(&self) -> Vec { + //We can unwrap here because the value is always valid. + hex2bytes(&self.value).unwrap() + } +} + +#[cfg(test)] +mod test { + use crate::crypto::EphemeraKeypair; + use crate::crypto::Keypair; + + use super::*; + + #[test] + fn test_message_sign_ok() { + let message_signing_keypair = Keypair::generate(None); + + let message = RawApiEphemeraMessage::new("test".to_string(), vec![1, 2, 3]); + let signed_message = message + .sign(&message_signing_keypair) + .expect("Failed to sign message"); + + let certificate = signed_message.certificate; + + assert!(certificate.verify(&message).unwrap()); + } + + #[test] + fn test_message_sign_fail() { + let message_signing_keypair = Keypair::generate(None); + + let message = RawApiEphemeraMessage::new("test1".to_string(), vec![1, 2, 3]); + let signed_message = message + .sign(&message_signing_keypair) + .expect("Failed to sign message"); + + let certificate = signed_message.certificate; + + let modified_message = RawApiEphemeraMessage::new("test2".to_string(), vec![1, 2, 3]); + assert!(!certificate.verify(&modified_message).unwrap()); + } +} diff --git a/ephemera/src/block/builder.rs b/ephemera/src/block/builder.rs new file mode 100644 index 0000000000..e74530ae81 --- /dev/null +++ b/ephemera/src/block/builder.rs @@ -0,0 +1,74 @@ +use std::collections::HashSet; +use std::{sync::Arc, time::Duration}; + +use log::{debug, info}; + +use crate::block::manager::State; +use crate::peer::ToPeerId; +use crate::{ + block::{ + manager::{BlockChainState, BlockManager}, + message_pool::MessagePool, + producer::BlockProducer, + types::block::Block, + }, + broadcast::signing::BlockSigner, + config::BlockManagerConfiguration, + crypto::Keypair, + storage::EphemeraDatabase, +}; + +pub(crate) struct BlockManagerBuilder { + config: BlockManagerConfiguration, + block_producer: BlockProducer, + keypair: Arc, +} + +impl BlockManagerBuilder { + pub(crate) fn new(config: BlockManagerConfiguration, keypair: Arc) -> Self { + let block_producer = BlockProducer::new(keypair.peer_id()); + Self { + config, + block_producer, + keypair, + } + } + + pub(crate) fn build( + self, + storage: &mut D, + ) -> anyhow::Result { + let mut most_recent_block = storage.get_last_block()?; + if most_recent_block.is_none() { + //Although Ephemera is not a blockchain(chain of historically dependent blocks), + //it's helpful to have some sort of notion of progress in time. So we use the concept of height. + //The genesis block helps to define the start of it. + + info!("No last block found in database. Creating genesis block."); + + let genesis_block = Block::new_genesis_block(self.block_producer.peer_id); + storage.store_block(&genesis_block, HashSet::new(), HashSet::new())?; + most_recent_block = Some(genesis_block); + } + + let last_created_block = most_recent_block.expect("Block should be present"); + debug!("Most recent block: {:?}", last_created_block); + + let block_signer = BlockSigner::new(self.keypair.clone()); + let message_pool = MessagePool::new(); + let block_chain_state = BlockChainState::new(last_created_block); + let block_creation_interval = + tokio::time::interval(Duration::from_secs(self.config.creation_interval_sec)); + + Ok(BlockManager { + config: self.config, + block_producer: self.block_producer, + block_signer, + message_pool, + block_chain_state, + state: State::Paused, + backoff: None, + block_creation_interval, + }) + } +} diff --git a/ephemera/src/block/manager.rs b/ephemera/src/block/manager.rs new file mode 100644 index 0000000000..481cd92418 --- /dev/null +++ b/ephemera/src/block/manager.rs @@ -0,0 +1,688 @@ +use std::collections::HashSet; +use std::future::Future; +use std::task::Poll; +use std::time::Duration; +use std::{ + num::NonZeroUsize, + pin::Pin, + task, + task::Poll::{Pending, Ready}, +}; + +use anyhow::anyhow; +use futures::Stream; +use futures_util::FutureExt; +use log::{debug, error, info, trace}; +use lru::LruCache; +use thiserror::Error; +use tokio::time; +use tokio::time::{Instant, Interval}; + +use crate::network::PeerId; +use crate::peer::ToPeerId; +use crate::{ + api::application::RemoveMessages, + block::{ + message_pool::MessagePool, + producer::BlockProducer, + types::{block::Block, message::EphemeraMessage}, + }, + broadcast::signing::BlockSigner, + config::BlockManagerConfiguration, + utilities::{crypto::Certificate, hash::Hash}, +}; + +pub(crate) type Result = std::result::Result; + +#[derive(Error, Debug)] +pub(crate) enum BlockManagerError { + #[error("Message is already in pool: {0}")] + DuplicateMessage(String), + //Just a placeholder for now + #[error("BlockManagerError: {0}")] + BlockManager(#[from] anyhow::Error), +} + +/// It helps to use atomic state management for new blocks. +pub(crate) struct BlockChainState { + pub(crate) last_blocks: LruCache, + /// Last block that we created. + /// It's not Option because we always have genesis block + last_produced_block: Option, + /// Last block that we accepted + /// It's not Option because we always have genesis block + last_committed_block: Block, +} + +impl BlockChainState { + pub(crate) fn new(last_committed_block: Block) -> Self { + Self { + //1000 is just a "big enough". + last_blocks: LruCache::new(NonZeroUsize::new(1000).unwrap()), + last_produced_block: None, + last_committed_block, + } + } + + fn mark_last_produced_block_as_committed(&mut self) { + self.last_committed_block = self + .last_produced_block + .take() + .expect("Block should be present"); + } + + fn is_last_produced_block(&self, hash: Hash) -> bool { + match self.last_produced_block.as_ref() { + Some(block) => block.get_hash() == hash, + None => false, + } + } + + fn is_last_produced_block_is_pending(&self) -> bool { + self.last_produced_block.is_some() + } + + fn next_block_height(&self) -> u64 { + self.last_committed_block.get_height() + 1 + } + + fn remove_last_produced_block(&mut self) -> Block { + self.last_produced_block + .take() + .expect("Block should be present") + } +} + +pub(crate) enum State { + Paused, + Running, +} + +#[derive(Debug)] +pub(crate) struct BackOffInterval { + /// Maximum number of attempts before this backoff expires. + maximum_times: u32, + /// Number of attempts that have been made so far. + nr_of_attempts: u32, + /// Backoff rate. Previous delay is multiplied by this rate to get next delay. + backoff_rate: u32, + /// Delay between before next attempt. + delay: Interval, +} + +impl BackOffInterval { + fn new(maximum_times: u32, backoff_rate: u32, initial_wait: Duration) -> Self { + let delay = time::interval_at(Instant::now() + initial_wait, initial_wait); + Self { + maximum_times, + nr_of_attempts: 0, + backoff_rate, + delay, + } + } + + fn is_expired(&self) -> bool { + self.nr_of_attempts >= self.maximum_times + } +} + +impl Future for BackOffInterval { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { + if self.nr_of_attempts >= self.maximum_times { + debug!("Backoff expired after {} attempts", self.nr_of_attempts); + return Pending; + } + + match Pin::new(&mut self.delay).poll_tick(cx) { + Ready(_) => { + self.nr_of_attempts += 1; + let next_tick = Instant::now() + + self.delay.period() * self.backoff_rate.pow(self.nr_of_attempts); + debug!("Backoff attempt: {}", self.nr_of_attempts); + self.delay = time::interval_at(next_tick, self.delay.period()); + Ready(()) + } + Pending => Pending, + } + } +} + +pub(crate) struct BlockManager { + pub(crate) config: BlockManagerConfiguration, + /// Block producer. Simple helper that creates blocks + pub(crate) block_producer: BlockProducer, + /// Message pool. Contains all messages that we received from the network and not included in any(committed) block yet. + pub(crate) message_pool: MessagePool, + /// Delay between block creation attempts. + pub(crate) block_creation_interval: Interval, + /// Backoff between block creation attempts. When `last_produced_block` is not committed during + /// certain time window, and normal delay is not passed yet, we use backoff delay to try again. + pub(crate) backoff: Option, + /// Signs and verifies blocks + pub(crate) block_signer: BlockSigner, + /// State management for new blocks + pub(crate) block_chain_state: BlockChainState, + /// Current state of the block manager + pub(crate) state: State, +} + +impl BlockManager { + pub(crate) fn on_new_message(&mut self, msg: EphemeraMessage) -> Result<()> { + trace!("Message received: {:?}", msg); + + let message_hash = msg.hash_with_default_hasher()?; + if self.message_pool.contains(&message_hash) { + return Err(BlockManagerError::DuplicateMessage( + message_hash.to_string(), + )); + } + + self.message_pool.add_message(msg)?; + Ok(()) + } + + pub(crate) fn on_block( + &mut self, + sender: &PeerId, + block: &Block, + certificate: &Certificate, + ) -> Result<()> { + let hash = block.hash_with_default_hasher()?; + + trace!( + "Received block: {:?} from peer {sender:?}", + block.get_hash() + ); + + //Reject blocks with invalid hash + if block.header.hash != hash { + return Err(anyhow!("Block hash is invalid: {} != {hash}", block.header.hash).into()); + } + + //Block signer should be also its sender + let signer_peer_id = certificate.public_key.peer_id(); + if *sender != signer_peer_id { + return Err(anyhow!( + "Block signer is not the block sender: {sender:?} != {signer_peer_id:?}", + ) + .into()); + } + + //Verify that block signature is valid + if self.block_signer.verify_block(block, certificate).is_err() { + return Err(anyhow!("Block signature is invalid: {hash}").into()); + } + + self.block_chain_state.last_blocks.put(hash, block.clone()); + Ok(()) + } + + pub(crate) fn sign_block(&mut self, block: &Block) -> Result { + let hash = block.hash_with_default_hasher()?; + + trace!("Signing block: {block}"); + + let certificate = self.block_signer.sign_block(block, &hash)?; + + trace!("Block certificate: {certificate:?}",); + + Ok(certificate) + } + + pub(crate) fn on_application_rejected_block( + &mut self, + messages_to_remove: RemoveMessages, + ) -> Result<()> { + debug!("Application rejected last created block"); + + let last_produced_block = self.block_chain_state.remove_last_produced_block(); + match messages_to_remove { + RemoveMessages::All => { + let messages = last_produced_block + .messages + .into_iter() + .map(Into::into) + .collect::>(); + + debug!("Removing block messages from pool: all: {messages:?}",); + self.message_pool.remove_messages(&messages)?; + } + RemoveMessages::Selected(messages) => { + debug!("Removing block messages from pool: selected: {messages:?}",); + let messages = messages.into_iter().map(Into::into).collect::>(); + self.message_pool.remove_messages(messages.as_slice())?; + } + }; + Ok(()) + } + + /// After a block gets committed, clear up mempool from its messages + pub(crate) fn on_block_committed(&mut self, block: &Block) -> Result<()> { + info!("Block committed: {}", block); + + let hash = &block.header.hash; + + if !self.block_chain_state.is_last_produced_block(*hash) { + let last_produced_block = self + .block_chain_state + .last_produced_block + .as_ref() + .expect("Last produced block should be present"); + log::error!( + "Received unexpected committed block: {hash}, was expecting: {}", + last_produced_block.get_hash() + ); + panic!("Received committed block which isn't last produced block, this is a bug!"); + } + + match self.message_pool.remove_messages(&block.messages) { + Ok(_) => { + self.block_chain_state + .mark_last_produced_block_as_committed(); + } + Err(e) => { + return Err(anyhow!("Failed to remove messages from mempool: {}", e).into()); + } + } + Ok(()) + } + + pub(crate) fn get_block_by_hash(&mut self, block_id: &Hash) -> Option { + self.block_chain_state.last_blocks.get(block_id).cloned() + } + + pub(crate) fn get_block_certificates(&mut self, hash: &Hash) -> Option<&HashSet> { + self.block_signer.get_block_certificates(hash) + } + + pub(crate) fn stop(&mut self) { + debug!("Stopping block creation"); + self.state = State::Paused; + self.backoff = None; + } + + pub(crate) fn start(&mut self) { + if !self.config.producer { + return; + } + if let State::Running = self.state { + return; + } + debug!("Starting block creation"); + self.state = State::Running; + self.block_creation_interval = + tokio::time::interval(Duration::from_secs(self.config.creation_interval_sec)); + } +} + +//Produces blocks at a predefined interval. +//If blocks will be actually broadcast depends on the application. +impl Stream for BlockManager { + type Item = (Block, Certificate); + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { + //Optionally it is possible to turn off block production and let the node behave just as voter. + //For example for testing purposes. + if !self.config.producer { + return Pending; + } + + //It is dynamically turned off when node is not part of most recent broadcast group. + if let State::Paused = self.state { + return Pending; + } + + let is_previous_pending = self.block_chain_state.is_last_produced_block_is_pending(); + if !is_previous_pending { + self.backoff = None; + } + + if self.block_creation_interval.poll_tick(cx).is_pending() { + if let Some(mut backoff) = self.backoff.take() { + if backoff.is_expired() { + return Pending; + } + if backoff.poll_unpin(cx).is_pending() { + self.backoff = Some(backoff); + return Pending; + } + self.backoff = Some(backoff); + } else { + return Pending; + } + } else { + self.backoff = None; + } + + //If backoff is expired and we still don't have previous block committed + let repeat_previous = is_previous_pending && self.config.repeat_last_block_messages; + + let pending_messages = if repeat_previous { + let block = self + .block_chain_state + .last_produced_block + .clone() + .expect("Block should be present"); + + //Use only previous block messages but create new block with new timestamp. + debug!("Producing block with previous messages"); + block.messages + } else { + debug!("Producing block with new messages"); + self.message_pool.get_messages() + }; + + let new_height = self.block_chain_state.next_block_height(); + let created_block = self + .block_producer + .create_block(new_height, pending_messages); + + if let Ok(block) = created_block { + info!("Created block: {}", block); + + let hash = block.get_hash(); + self.block_chain_state.last_produced_block = Some(block.clone()); + self.block_chain_state.last_blocks.put(hash, block.clone()); + + let certificate = self + .block_signer + .sign_block(&block, &hash) + .expect("Failed to sign block"); + + if self.backoff.is_none() { + let backoff = BackOffInterval::new(100, 2, Duration::from_secs(10)); + self.backoff = Some(backoff); + } + + Ready(Some((block, certificate))) + } else { + error!("Error producing block: {:?}", created_block); + Pending + } + } +} + +#[cfg(test)] +mod test { + use std::sync::Arc; + use std::time::Duration; + + use assert_matches::assert_matches; + use futures_util::StreamExt; + + use crate::crypto::{EphemeraKeypair, Keypair}; + use crate::ephemera_api::RawApiEphemeraMessage; + + use super::*; + + #[tokio::test] + async fn test_add_message() { + let (mut manager, _) = block_manager_with_defaults(); + + let signed_message = message("test"); + let hash = signed_message.hash_with_default_hasher().unwrap(); + + manager.on_new_message(signed_message).unwrap(); + + assert!(manager.message_pool.contains(&hash)); + } + + #[tokio::test] + async fn test_add_duplicate_message() { + let (mut manager, _) = block_manager_with_defaults(); + + let signed_message = message("test"); + + manager.on_new_message(signed_message.clone()).unwrap(); + + assert_matches!( + manager.on_new_message(signed_message), + Err(BlockManagerError::DuplicateMessage(_)) + ); + } + + #[tokio::test] + async fn test_accept_valid_block() { + let (mut manager, peer_id) = block_manager_with_defaults(); + + let block = block(); + let certificate = manager.sign_block(&block).unwrap(); + + let result = manager.on_block(&peer_id, &block, &certificate); + + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_reject_invalid_sender() { + let (mut manager, _) = block_manager_with_defaults(); + + let block = block(); + let certificate = manager.sign_block(&block).unwrap(); + + let invalid_peer_id = PeerId::from_public_key(&Keypair::generate(None).public_key()); + let result = manager.on_block(&invalid_peer_id, &block, &certificate); + + assert!(result.is_err()); + } + + #[tokio::test] + async fn test_reject_invalid_hash() { + let (mut manager, peer_id) = block_manager_with_defaults(); + + let mut block = block(); + let certificate = manager.sign_block(&block).unwrap(); + + block.header.hash = Hash::new([0; 32]); + let result = manager.on_block(&peer_id, &block, &certificate); + + assert!(result.is_err()); + } + + #[tokio::test] + async fn test_reject_invalid_signature() { + let (mut manager, peer_id) = block_manager_with_defaults(); + + let correct_block = block(); + let fake_block = block(); + + let fake_certificate = manager.sign_block(&fake_block).unwrap(); + let correct_certificate = manager.sign_block(&correct_block).unwrap(); + + let result = manager.on_block(&peer_id, &correct_block, &fake_certificate); + assert!(result.is_err()); + + let result = manager.on_block(&peer_id, &fake_block, &correct_certificate); + assert!(result.is_err()); + } + + #[tokio::test] + async fn test_next_block_empty() { + let (mut manager, _) = block_manager_with_defaults(); + + let (block, _) = manager.next().await.unwrap(); + assert_eq!(block.header.height, 1); + assert!(block.messages.is_empty()); + } + + #[tokio::test] + async fn test_next_block_with_message() { + let (mut manager, _) = block_manager_with_defaults(); + + let signed_message = message("test"); + manager.on_new_message(signed_message).unwrap(); + + match manager.next().await { + Some((block, _)) => { + assert_eq!(block.header.height, 1); + assert_eq!(block.messages.len(), 1); + } + None => { + panic!("No block produced"); + } + } + } + + #[tokio::test] + async fn test_next_block_previous_not_committed_repeat() { + let (mut manager, _) = block_manager_with_defaults(); + + let signed_message = message("test"); + manager.on_new_message(signed_message).unwrap(); + + let (block1, _) = manager.next().await.unwrap(); + + let signed_message = message("test"); + manager.on_new_message(signed_message).unwrap(); + + let (block2, _) = manager.next().await.unwrap(); + + assert_eq!(block1.messages.len(), block2.messages.len()); + assert_eq!(block1.header.height, block2.header.height); + } + + #[tokio::test] + async fn test_next_block_previous_not_committed_repeat_false() { + let config = BlockManagerConfiguration::new(true, 0, false); + let (mut manager, _) = block_manager_with_config(config); + + let signed_message = message("test"); + manager.on_new_message(signed_message).unwrap(); + + let (block1, _) = manager.next().await.unwrap(); + + let signed_message = message("test"); + manager.on_new_message(signed_message).unwrap(); + + let (block2, _) = manager.next().await.unwrap(); + + assert_eq!(block1.messages.len(), 1); + assert_eq!(block2.messages.len(), 2); + //We create new block but don't leave gap + assert_eq!(block1.header.height, block2.header.height); + } + + #[tokio::test] + async fn test_on_committed_with_correct_pending_block() { + let (mut manager, _) = block_manager_with_defaults(); + + let signed_message = message("test"); + manager.on_new_message(signed_message).unwrap(); + + let (block, _) = manager.next().await.unwrap(); + + let result = manager.on_block_committed(&block); + + assert!(result.is_ok()); + assert!(manager.message_pool.get_messages().is_empty()); + } + + #[tokio::test] + #[should_panic] + async fn test_on_committed_with_invalid_pending_block() { + let (mut manager, _) = block_manager_with_defaults(); + + let signed_message = message("test"); + manager.on_new_message(signed_message).unwrap(); + + manager.next().await.unwrap(); + + //Create invalid block + let wrong_block = block(); + + //This shouldn't remove messages from the pool + manager.on_block_committed(&wrong_block).unwrap(); + } + + #[tokio::test] + async fn application_rejected_messages_all() { + let (mut manager, _) = block_manager_with_defaults(); + + //Add messages to pool + let signed_message = message("test"); + manager.on_new_message(signed_message).unwrap(); + + let signed_message = message("test"); + manager.on_new_message(signed_message).unwrap(); + + //Produce new block + manager.next().await.unwrap(); + + //Application Rejects the block with ALL messages + manager + .on_application_rejected_block(RemoveMessages::All) + .unwrap(); + + assert!(manager.message_pool.get_messages().is_empty()); + } + + #[tokio::test] + async fn application_rejected_messages_selected() { + let (mut manager, _) = block_manager_with_defaults(); + + //Add messages to pool + let signed_message1 = message("test"); + manager.on_new_message(signed_message1.clone()).unwrap(); + + let signed_message2 = message("test"); + manager.on_new_message(signed_message2.clone()).unwrap(); + + //Produce new block + manager.next().await.unwrap(); + + //Application Rejects the block with ALL messages + manager + .on_application_rejected_block(RemoveMessages::Selected(vec![signed_message2.into()])) + .unwrap(); + + assert_eq!(manager.message_pool.get_messages().len(), 1); + let message = manager + .message_pool + .get_messages() + .into_iter() + .next() + .unwrap(); + assert_eq!(message, signed_message1); + } + + fn block_manager_with_defaults() -> (BlockManager, PeerId) { + let config = BlockManagerConfiguration::new(true, 0, true); + block_manager_with_config(config) + } + + fn block_manager_with_config(config: BlockManagerConfiguration) -> (BlockManager, PeerId) { + let keypair: Arc = Keypair::generate(None).into(); + let peer_id = keypair.public_key().peer_id(); + let genesis_block = Block::new_genesis_block(peer_id); + let block_chain_state = BlockChainState::new(genesis_block); + ( + BlockManager { + config, + block_producer: BlockProducer::new(peer_id), + message_pool: MessagePool::new(), + block_creation_interval: tokio::time::interval(Duration::from_millis(1)), + backoff: None, + block_signer: BlockSigner::new(keypair), + block_chain_state, + state: State::Running, + }, + peer_id, + ) + } + + fn block() -> Block { + let keypair: Arc = Keypair::generate(None).into(); + let peer_id = keypair.public_key().peer_id(); + let mut producer = BlockProducer::new(peer_id); + producer.create_block(1, vec![]).unwrap() + } + + fn message(label: &str) -> EphemeraMessage { + let message1 = RawApiEphemeraMessage::new(label.into(), vec![1, 2, 3]); + let keypair = Keypair::generate(None); + let signed_message1 = message1.sign(&keypair).expect("Failed to sign message"); + signed_message1.into() + } +} diff --git a/ephemera/src/block/message_pool.rs b/ephemera/src/block/message_pool.rs new file mode 100644 index 0000000000..bf1e062be2 --- /dev/null +++ b/ephemera/src/block/message_pool.rs @@ -0,0 +1,87 @@ +//! Message pool for Ephemera messages +//! +//! It stores pending Ephemera messages which will be added to a future block. +//! It doesn't have any other logic than just storing messages. +//! +//! It's up to the user provided [`crate::ephemera_api::Application::check_tx`] to decide which messages to include. + +use std::collections::HashMap; + +use log::{trace, warn}; + +use crate::block::types::message::EphemeraMessage; +use crate::utilities::hash::Hash; + +pub(crate) struct MessagePool { + pending_messages: HashMap, +} + +impl MessagePool { + pub(super) fn new() -> Self { + Self { + pending_messages: HashMap::default(), + } + } + + pub(crate) fn contains(&self, hash: &Hash) -> bool { + self.pending_messages.contains_key(hash) + } + + pub(super) fn add_message(&mut self, msg: EphemeraMessage) -> anyhow::Result<()> { + trace!("Adding message to pool: {:?}", msg); + + let msg_hash = msg.hash_with_default_hasher()?; + + self.pending_messages.insert(msg_hash, msg); + + trace!("Message pool size: {:?}", self.pending_messages.len()); + Ok(()) + } + + pub(super) fn remove_messages(&mut self, messages: &[EphemeraMessage]) -> anyhow::Result<()> { + trace!( + "Mempool size before removing messages {}", + self.pending_messages.len() + ); + for msg in messages { + let hash = msg.hash_with_default_hasher()?; + if self.pending_messages.remove(&hash).is_none() { + warn!("Message not found in pool: {:?}", msg); + } + } + trace!( + "Mempool size after removing messages {}", + self.pending_messages.len() + ); + Ok(()) + } + + /// Returns a `Vec` of all `EphemeraMessage`s in the message pool. + /// The message pool is not cleared. + pub(super) fn get_messages(&self) -> Vec { + self.pending_messages.values().cloned().collect() + } +} + +#[cfg(test)] +mod test { + use crate::block::message_pool::MessagePool; + use crate::block::types::message::EphemeraMessage; + use crate::crypto::{EphemeraKeypair, Keypair}; + use crate::ephemera_api::RawApiEphemeraMessage; + + #[test] + fn test_add_remove() { + let keypair = Keypair::generate(None); + + let message = RawApiEphemeraMessage::new("test".to_string(), vec![1, 2, 3]); + let signed_message = message.sign(&keypair).expect("Failed to sign message"); + let signed_message: EphemeraMessage = signed_message.into(); + + let mut pool = MessagePool::new(); + pool.add_message(signed_message.clone()).unwrap(); + pool.remove_messages(&[signed_message]).unwrap(); + + assert_eq!(pool.get_messages().len(), 0); + } +} diff --git a/ephemera/src/block/mod.rs b/ephemera/src/block/mod.rs new file mode 100644 index 0000000000..b6d3684d80 --- /dev/null +++ b/ephemera/src/block/mod.rs @@ -0,0 +1,26 @@ +//! # Block manager +//! +//! Block manager is quite simple. It keeps pending messages in memory and puts all of them into a block +//! at predefined intervals. That's all it does. +//! +//! If the block actually will be broadcast or not is decided by the application. If not, it will produce next block with +//! the same messages plus the new ones. +//! +//! When application shuts down, pending messages are lost. +//! +//! When a block gets accepted by reliable broadcast then Block Manager will remove all messages included in the block from the +//! pending messages queue. +//! +//! # Synchronization and duplicate messages in sequence of blocks +//! +//! When previous block hasn't been accepted yet, then the next block will contain the same messages as the previous one. +//! One way to solve this is that an application itself keeps track of duplicate messages and discards them if necessary. +//! +//! But it seems a reasonable assumption that in general duplicate messages are unwanted. Therefore, Ephemera solves this +//! by dropping previous blocks which get Finalised/Committed after a new block has been created. + +pub(crate) mod builder; +pub(crate) mod manager; +pub(crate) mod message_pool; +pub(crate) mod producer; +pub(crate) mod types; diff --git a/ephemera/src/block/producer.rs b/ephemera/src/block/producer.rs new file mode 100644 index 0000000000..20f82bc328 --- /dev/null +++ b/ephemera/src/block/producer.rs @@ -0,0 +1,92 @@ +use crate::block::{ + types::block::{Block, RawBlock, RawBlockHeader}, + types::message::EphemeraMessage, +}; +use crate::peer::PeerId; +use log::trace; + +pub(crate) struct BlockProducer { + pub(crate) peer_id: PeerId, +} + +impl BlockProducer { + pub(super) fn new(peer_id: PeerId) -> Self { + Self { peer_id } + } + + pub(super) fn create_block( + &mut self, + height: u64, + pending_messages: Vec, + ) -> anyhow::Result { + trace!("Pending messages for new block: {:?}", pending_messages); + let block = self.new_block(height, pending_messages)?; + Ok(block) + } + + fn new_block(&self, height: u64, mut messages: Vec) -> anyhow::Result { + //Ordering is fundamental for block hash. Simple sort is fine for now. + messages.sort(); + + let raw_header = RawBlockHeader::new(self.peer_id, height); + let raw_block = RawBlock::new(raw_header, messages); + + //Better idea is probably combine header hash with Merkle tree root hash + let block_hash = raw_block.hash_with_default_hasher()?; + + let block = Block::new(raw_block, block_hash); + Ok(block) + } +} + +#[cfg(test)] +mod test { + use crate::crypto::{EphemeraKeypair, Keypair}; + use crate::ephemera_api::RawApiEphemeraMessage; + use std::cmp::Ordering; + + use super::*; + + #[test] + fn test_produce_block() { + let peer_id = PeerId::random(); + + let mut block_producer = BlockProducer::new(peer_id); + + let message = RawApiEphemeraMessage::new("test".to_string(), vec![1, 2, 3]); + let signed_message = message + .sign(&Keypair::generate(None)) + .expect("Failed to sign message"); + let signed_message1: EphemeraMessage = signed_message.into(); + + let message = RawApiEphemeraMessage::new("test".to_string(), vec![1, 2, 3]); + let signed_message = message + .sign(&Keypair::generate(None)) + .expect("Failed to sign message"); + let signed_message2: EphemeraMessage = signed_message.into(); + + let messages = vec![signed_message1.clone(), signed_message2.clone()]; + + let block = block_producer.create_block(1, messages).unwrap(); + + assert_eq!(block.header.height, 1); + assert_eq!(block.header.creator, peer_id); + assert_eq!(block.messages.len(), 2); + + //Nondeterministic because of timestamp + match signed_message1.cmp(&signed_message2) { + Ordering::Less => { + assert_eq!(block.messages[0], signed_message1); + assert_eq!(block.messages[1], signed_message2); + } + Ordering::Greater => { + assert_eq!(block.messages[0], signed_message2); + assert_eq!(block.messages[1], signed_message1); + } + + Ordering::Equal => { + panic!("Messages are equal"); + } + } + } +} diff --git a/ephemera/src/block/types/block.rs b/ephemera/src/block/types/block.rs new file mode 100644 index 0000000000..7b1538a5a5 --- /dev/null +++ b/ephemera/src/block/types/block.rs @@ -0,0 +1,327 @@ +use std::fmt::{Debug, Display}; + +use serde::{Deserialize, Serialize}; + +use crate::utilities::merkle::MerkleTree; +use crate::{ + block::types::message::EphemeraMessage, + codec::{Decode, Encode}, + crypto::Keypair, + peer::PeerId, + utilities::{ + codec::{Codec, DecodingError, EncodingError, EphemeraCodec}, + crypto::Certificate, + hash::{EphemeraHash, EphemeraHasher}, + hash::{Hash, Hasher}, + time::EphemeraTime, + }, +}; + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +pub(crate) struct BlockHeader { + pub(crate) timestamp: u64, + pub(crate) creator: PeerId, + pub(crate) height: u64, + pub(crate) hash: Hash, +} + +impl BlockHeader { + pub(crate) fn new(raw_header: &RawBlockHeader, hash: Hash) -> Self { + Self { + timestamp: raw_header.timestamp, + creator: raw_header.creator, + height: raw_header.height, + hash, + } + } +} + +impl Display for BlockHeader { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let hash = &self.hash; + let time = self.timestamp; + let creator = &self.creator; + let height = self.height; + write!( + f, + "hash: {hash}, timestamp: {time}, creator: {creator}, height: {height}", + ) + } +} + +impl Encode for BlockHeader { + fn encode(&self) -> Result, EncodingError> { + Codec::encode(&self) + } +} + +impl Decode for BlockHeader { + type Output = Self; + + fn decode(bytes: &[u8]) -> Result { + Codec::decode(bytes) + } +} + +impl EphemeraHash for BlockHeader { + fn hash(&self, state: &mut H) -> anyhow::Result<()> { + let bytes = Codec::encode(&self)?; + state.update(&bytes); + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub(crate) struct RawBlockHeader { + pub(crate) timestamp: u64, + pub(crate) creator: PeerId, + pub(crate) height: u64, +} + +impl RawBlockHeader { + pub(crate) fn new(creator: PeerId, height: u64) -> Self { + Self { + timestamp: EphemeraTime::now(), + creator, + height, + } + } + + pub(crate) fn hash_with_default_hasher(&self) -> anyhow::Result { + let mut hasher = Hasher::default(); + self.hash(&mut hasher)?; + let header_hash = hasher.finish().into(); + Ok(header_hash) + } +} + +impl Display for RawBlockHeader { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let creator = &self.creator; + let height = self.height; + write!(f, "creator: {creator}, height: {height}",) + } +} + +impl From for RawBlockHeader { + fn from(block_header: BlockHeader) -> Self { + Self { + timestamp: block_header.timestamp, + creator: block_header.creator, + height: block_header.height, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +pub(crate) struct Block { + pub(crate) header: BlockHeader, + pub(crate) messages: Vec, +} + +impl Block { + pub(crate) fn new(raw_block: RawBlock, block_hash: Hash) -> Self { + let header = BlockHeader::new(&raw_block.header, block_hash); + Self { + header, + messages: raw_block.messages, + } + } + + pub(crate) fn get_hash(&self) -> Hash { + self.header.hash + } + + pub(crate) fn get_height(&self) -> u64 { + self.header.height + } + + pub(crate) fn new_genesis_block(creator: PeerId) -> Self { + let mut block = Self { + header: BlockHeader { + timestamp: EphemeraTime::now(), + creator, + height: 0, + hash: Hash::new([0; 32]), + }, + messages: Vec::new(), + }; + + let hash = block + .hash_with_default_hasher() + .expect("Failed to hash genesis block"); + block.header.hash = hash; + block + } + + pub(crate) fn sign(&self, keypair: &Keypair) -> anyhow::Result { + let raw_block: RawBlock = self.clone().into(); + let certificate = Certificate::prepare(keypair, &raw_block)?; + Ok(certificate) + } + + pub(crate) fn verify(&self, certificate: &Certificate) -> anyhow::Result { + let raw_block: RawBlock = self.clone().into(); + certificate.verify(&raw_block) + } + + pub(crate) fn hash_with_default_hasher(&self) -> anyhow::Result { + let raw_block: RawBlock = self.clone().into(); + raw_block.hash_with_default_hasher() + } + + pub(crate) fn merkle_tree(&self) -> anyhow::Result { + merkle_tree(&self.messages) + } +} + +impl Display for Block { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let header = &self.header; + write!(f, "{header}, nr of messages: {}", self.messages.len()) + } +} + +impl Encode for Block { + fn encode(&self) -> Result, EncodingError> { + Codec::encode(&self) + } +} + +impl Decode for Block { + type Output = Block; + + fn decode(bytes: &[u8]) -> Result { + Codec::decode(bytes) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub(crate) struct RawBlock { + pub(crate) header: RawBlockHeader, + pub(crate) messages: Vec, +} + +impl RawBlock { + pub(crate) fn new(header: RawBlockHeader, messages: Vec) -> Self { + Self { header, messages } + } + + pub(crate) fn hash_with_default_hasher(&self) -> anyhow::Result { + let header_hash = self.header.hash_with_default_hasher()?; + let merkle_root = merkle_tree(&self.messages)?.root_hash(); + let block_hash = Hasher::digest(&[header_hash.inner(), merkle_root.inner()].concat()); + Ok(block_hash.into()) + } +} + +impl From for RawBlock { + fn from(block: Block) -> Self { + Self { + header: block.header.into(), + messages: block.messages, + } + } +} + +impl Encode for RawBlockHeader { + fn encode(&self) -> Result, EncodingError> { + Codec::encode(&self) + } +} + +impl Decode for RawBlockHeader { + type Output = RawBlockHeader; + + fn decode(bytes: &[u8]) -> Result { + Codec::decode(bytes) + } +} + +impl Encode for RawBlock { + fn encode(&self) -> Result, EncodingError> { + Codec::encode(&self) + } +} + +impl Decode for RawBlock { + type Output = RawBlock; + + fn decode(bytes: &[u8]) -> Result { + Codec::decode(bytes) + } +} + +impl EphemeraHash for RawBlockHeader { + fn hash(&self, state: &mut H) -> anyhow::Result<()> { + state.update(&self.encode()?); + Ok(()) + } +} + +impl EphemeraHash for RawBlock { + fn hash(&self, state: &mut H) -> anyhow::Result<()> { + self.header.hash(state)?; + for message in &self.messages { + message.hash(state)?; + } + Ok(()) + } +} + +pub(crate) fn merkle_tree(messages: &[EphemeraMessage]) -> anyhow::Result { + let message_hashes = messages + .iter() + .map(EphemeraMessage::hash_with_default_hasher) + .collect::>>()?; + let merkle_tree = MerkleTree::build_tree(&message_hashes); + Ok(merkle_tree) +} + +#[cfg(test)] +mod test { + use crate::block::types::message::RawEphemeraMessage; + use crate::crypto::EphemeraKeypair; + + use super::*; + + #[test] + fn test_block_hash_no_messages() { + let block = Block::new_genesis_block(PeerId::random()); + let block_hash = block.hash_with_default_hasher().unwrap(); + assert_eq!(block_hash, block.get_hash()); + } + + #[test] + fn test_block_hash_with_messages() { + let messages = create_ephemera_messages(10); + let message_hashes = messages + .iter() + .map(EphemeraMessage::hash_with_default_hasher) + .collect::>>() + .unwrap(); + + let raw_block = RawBlock::new(RawBlockHeader::new(PeerId::random(), 0), messages); + let block_hash = raw_block.hash_with_default_hasher().unwrap(); + + let header_hash = raw_block.header.hash_with_default_hasher().unwrap(); + let merkle_root = MerkleTree::build_tree(&message_hashes).root_hash(); + let expected_block_hash = + Hasher::digest(&[header_hash.inner(), merkle_root.inner()].concat()); + + assert_eq!(block_hash, expected_block_hash.into()); + } + + fn create_ephemera_messages(n: usize) -> Vec { + let keypair = Keypair::generate(None); + let mut messages = Vec::new(); + for i in 0..n { + let label = format!("test {i}",); + let message = RawEphemeraMessage::new(label, vec![0; 32]); + let certificate = Certificate::prepare(&keypair, &message).unwrap(); + let ephemera_message = EphemeraMessage::new(message, certificate); + messages.push(ephemera_message); + } + messages + } +} diff --git a/ephemera/src/block/types/message.rs b/ephemera/src/block/types/message.rs new file mode 100644 index 0000000000..0cf271ba46 --- /dev/null +++ b/ephemera/src/block/types/message.rs @@ -0,0 +1,99 @@ +use serde::{Deserialize, Serialize}; + +use crate::utilities::codec::{Codec, DecodingError, EncodingError, EphemeraCodec}; +use crate::{ + codec::{Decode, Encode}, + utilities::{ + crypto::Certificate, + hash::{EphemeraHash, EphemeraHasher}, + hash::{Hash, Hasher}, + }, +}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] +pub(crate) struct EphemeraMessage { + ///Timestamp of the message + pub(crate) timestamp: u64, + ///Application specific logical identifier of the message + pub(crate) label: String, + ///Application specific data + pub(crate) data: Vec, + ///Signature of the raw message + pub(crate) certificate: Certificate, +} + +impl EphemeraMessage { + #[cfg(test)] + pub(crate) fn new(raw_message: RawEphemeraMessage, certificate: Certificate) -> Self { + Self { + timestamp: raw_message.timestamp, + label: raw_message.label, + data: raw_message.data, + certificate, + } + } + + pub(crate) fn hash_with_default_hasher(&self) -> anyhow::Result { + let mut hasher = Hasher::default(); + self.hash(&mut hasher)?; + let hash = hasher.finish().into(); + Ok(hash) + } +} + +impl Encode for EphemeraMessage { + fn encode(&self) -> Result, EncodingError> { + Codec::encode(&self) + } +} + +impl Decode for EphemeraMessage { + type Output = Self; + + fn decode(bytes: &[u8]) -> Result { + Codec::decode(bytes) + } +} + +impl EphemeraHash for EphemeraMessage { + fn hash(&self, state: &mut H) -> anyhow::Result<()> { + state.update(&self.encode()?); + Ok(()) + } +} + +/// Raw message represents all the data what will be signed. +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +pub(crate) struct RawEphemeraMessage { + pub(crate) timestamp: u64, + pub(crate) label: String, + pub(crate) data: Vec, +} + +impl RawEphemeraMessage { + #[cfg(test)] + pub(crate) fn new(label: String, data: Vec) -> Self { + use crate::utilities::time::EphemeraTime; + Self { + timestamp: EphemeraTime::now(), + label, + data, + } + } +} + +impl From for RawEphemeraMessage { + fn from(message: EphemeraMessage) -> Self { + Self { + timestamp: message.timestamp, + label: message.label, + data: message.data, + } + } +} + +impl Encode for RawEphemeraMessage { + fn encode(&self) -> Result, EncodingError> { + Codec::encode(&self) + } +} diff --git a/ephemera/src/block/types/mod.rs b/ephemera/src/block/types/mod.rs new file mode 100644 index 0000000000..21c274a325 --- /dev/null +++ b/ephemera/src/block/types/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod block; +pub(crate) mod message; diff --git a/ephemera/src/broadcast/bracha/broadcast.rs b/ephemera/src/broadcast/bracha/broadcast.rs new file mode 100644 index 0000000000..19e3e00d4b --- /dev/null +++ b/ephemera/src/broadcast/bracha/broadcast.rs @@ -0,0 +1,320 @@ +use std::num::NonZeroUsize; + +use log::{debug, trace}; +use lru::LruCache; + +use crate::broadcast::bracha::quorum::BrachaMessageType; +use crate::peer::PeerId; +use crate::{ + block::types::block::Block, + broadcast::{ + bracha::quorum::Quorum, + MessageType::{Echo, Vote}, + ProtocolContext, RawRbMsg, + }, + utilities::hash::Hash, +}; + +#[allow(clippy::large_enum_variant)] +#[derive(Debug)] +pub(crate) enum BroadcastResponse { + Broadcast(RawRbMsg), + Deliver(Hash), + Drop(Hash), +} + +pub(crate) struct Broadcaster { + /// Local peer id + local_peer_id: PeerId, + /// We keep a context for each block we are processing. + contexts: LruCache, + /// Current cluster size + cluster_size: usize, +} + +impl Broadcaster { + pub fn new(peer_id: PeerId) -> Broadcaster { + Broadcaster { + //At any given time we are processing in parallel about n messages, where n is the number of peers in the group. + //This is just large enough buffer. + contexts: LruCache::new(NonZeroUsize::new(1000).unwrap()), + cluster_size: 0, + local_peer_id: peer_id, + } + } + + pub(crate) fn new_broadcast(&mut self, block: Block) -> anyhow::Result { + debug!("Starting broadcast for new block {:?}", block.get_hash()); + self.handle(&RawRbMsg::new(block, self.local_peer_id)) + } + + pub(crate) fn handle(&mut self, rb_msg: &RawRbMsg) -> anyhow::Result { + trace!("Processing new broadcast message: {:?}", rb_msg); + + let block = rb_msg.block(); + let hash = block.hash_with_default_hasher()?; + + let ctx = self.contexts.get_or_insert(hash, || { + ProtocolContext::new(hash, self.local_peer_id, Quorum::new(self.cluster_size)) + }); + + if ctx.delivered { + trace!("Block {hash:?} already delivered"); + return Ok(BroadcastResponse::Drop(hash)); + } + + match rb_msg.message_type { + Echo(_) => { + trace!("Processing ECHO {:?}", rb_msg.id); + Ok(self.process_echo(rb_msg, hash)) + } + Vote(_) => { + trace!("Processing VOTE {:?}", rb_msg.id); + Ok(self.process_vote(rb_msg, hash)) + } + } + } + + fn process_echo(&mut self, rb_msg: &RawRbMsg, hash: Hash) -> BroadcastResponse { + let ctx = self.contexts.get_mut(&hash).expect("Context not found"); + + if self.local_peer_id != rb_msg.original_sender { + trace!("Adding echo from {:?}", rb_msg.original_sender); + ctx.add_echo(rb_msg.original_sender); + } + + if !ctx.echoed() { + ctx.add_echo(self.local_peer_id); + + trace!("Sending echo reply for {hash:?}",); + return BroadcastResponse::Broadcast( + rb_msg.echo_reply(self.local_peer_id, rb_msg.block()), + ); + } + + if !ctx.voted() + && ctx + .quorum + .check_threshold(ctx, BrachaMessageType::Echo) + .is_vote() + { + ctx.add_vote(self.local_peer_id); + + trace!("Sending vote reply for {hash:?}",); + return BroadcastResponse::Broadcast( + rb_msg.vote_reply(self.local_peer_id, rb_msg.block()), + ); + } + + BroadcastResponse::Drop(hash) + } + + fn process_vote(&mut self, rb_msg: &RawRbMsg, hash: Hash) -> BroadcastResponse { + let block = rb_msg.block(); + let ctx = self.contexts.get_mut(&hash).expect("Context not found"); + + if self.local_peer_id != rb_msg.original_sender { + trace!("Adding vote from {:?}", rb_msg.original_sender); + ctx.add_vote(rb_msg.original_sender); + } + + if ctx + .quorum + .check_threshold(ctx, BrachaMessageType::Vote) + .is_vote() + { + ctx.add_vote(self.local_peer_id); + + trace!("Sending vote reply for {hash:?}",); + return BroadcastResponse::Broadcast(rb_msg.vote_reply(self.local_peer_id, block)); + } + + if ctx + .quorum + .check_threshold(ctx, BrachaMessageType::Vote) + .is_deliver() + { + trace!("Commit complete for {:?}", rb_msg.id); + + ctx.delivered = true; + + return BroadcastResponse::Deliver(hash); + } + + BroadcastResponse::Drop(hash) + } + + pub(crate) fn group_updated(&mut self, size: usize) { + self.cluster_size = size; + } +} + +#[cfg(test)] +mod tests { + + //1.make sure before voting enough echo messages are received + //2.make sure before delivering enough vote messages are received + //a)Either f + 1 + //b)Or n - f + + //3.make sure that duplicate messages doesn't have impact + + //4. "Ideally" make sure that when group changes, the ongoing broadcast can deal with it + + use std::iter; + + use assert_matches::assert_matches; + + use crate::broadcast::bracha::broadcast::BroadcastResponse; + use crate::peer::PeerId; + use crate::utilities::hash::Hash; + use crate::{ + block::types::block::{Block, RawBlock, RawBlockHeader}, + broadcast::{self, bracha::broadcast::Broadcaster, RawRbMsg}, + }; + + #[test] + fn test_state_transitions_from_start_to_end() { + let peers: Vec = iter::repeat_with(PeerId::random).take(10).collect(); + let local_peer_id = peers[0]; + let block_creator_peer_id = peers[1]; + + let mut broadcaster = Broadcaster::new(local_peer_id); + broadcaster.group_updated(peers.len()); + + let (block_hash, block) = create_block(block_creator_peer_id); + + //After this echo set contains local and block creator(msg sender) + receive_echo_first_message(&mut broadcaster, &block, block_creator_peer_id); + + let ctx = broadcaster.contexts.get(&block_hash).unwrap(); + assert_eq!(ctx.echo.len(), 2); + assert!(ctx.echoed()); + assert!(!ctx.voted()); + + receive_nr_of_echo_messages_below_vote_threshold(&mut broadcaster, &block, &peers[2..6]); + + let ctx = broadcaster.contexts.get(&block_hash).unwrap(); + assert_eq!(ctx.echo.len(), 6); + assert!(ctx.echoed()); + assert!(!ctx.voted()); + + receive_echo_threshold_message(&mut broadcaster, &block, *peers.get(7).unwrap()); + + let ctx = broadcaster.contexts.get(&block_hash).unwrap(); + assert_eq!(ctx.echo.len(), 7); + assert_eq!(ctx.vote.len(), 1); + assert!(ctx.echoed()); + assert!(ctx.voted()); + + receive_nr_of_vote_messages_below_deliver_threshold(&mut broadcaster, &block, &peers[2..7]); + + let ctx = broadcaster.contexts.get(&block_hash).unwrap(); + assert_eq!(ctx.echo.len(), 7); + assert_eq!(ctx.vote.len(), 6); + assert!(ctx.echoed()); + assert!(ctx.voted()); + + receive_threshold_vote_message_for_deliver( + &mut broadcaster, + &block, + *peers.get(8).unwrap(), + ); + } + + fn receive_threshold_vote_message_for_deliver( + broadcaster: &mut Broadcaster, + block: &Block, + peer_id: PeerId, + ) { + let rb_msg = RawRbMsg::new(block.clone(), PeerId::random()); + let rb_msg = rb_msg.vote_reply(peer_id, block.clone()); + + let response = handle_double(broadcaster, &rb_msg); + + assert_matches!(response, BroadcastResponse::Deliver(_)); + } + + fn receive_nr_of_echo_messages_below_vote_threshold( + broadcaster: &mut Broadcaster, + block: &Block, + peers: &[PeerId], + ) { + for peer_id in peers { + let rb_msg = RawRbMsg::new(block.clone(), *peer_id); + + let response = handle_double(broadcaster, &rb_msg); + + assert_matches!(response, BroadcastResponse::Drop(_)); + } + } + + fn receive_nr_of_vote_messages_below_deliver_threshold( + broadcaster: &mut Broadcaster, + block: &Block, + peers: &[PeerId], + ) { + for peer_id in peers { + let rb_msg = RawRbMsg::new(block.clone(), PeerId::random()); + let rb_msg = rb_msg.vote_reply(*peer_id, block.clone()); + + let response = handle_double(broadcaster, &rb_msg); + assert_matches!(response, BroadcastResponse::Drop(_)); + } + } + + fn receive_echo_first_message( + broadcaster: &mut Broadcaster, + block: &Block, + block_creator: PeerId, + ) { + let rb_msg = RawRbMsg::new(block.clone(), block_creator); + let response = handle_double(broadcaster, &rb_msg); + + assert_matches!( + response, + BroadcastResponse::Broadcast(RawRbMsg { + id: _, + request_id: _, + original_sender: _, + timestamp: _, + message_type: broadcast::MessageType::Echo(_), + }) + ); + } + + fn receive_echo_threshold_message( + broadcaster: &mut Broadcaster, + block: &Block, + peer_id: PeerId, + ) { + let rb_msg = RawRbMsg::new(block.clone(), peer_id); + + let response = handle_double(broadcaster, &rb_msg); + assert_matches!( + response, + BroadcastResponse::Broadcast(RawRbMsg { + id: _, + request_id: _, + original_sender: _, + timestamp: _, + message_type: broadcast::MessageType::Vote(_), + }) + ); + } + + fn create_block(block_creator_peer_id: PeerId) -> (Hash, Block) { + let header = RawBlockHeader::new(block_creator_peer_id, 0); + let raw_block = RawBlock::new(header, vec![]); + let block_hash = raw_block.hash_with_default_hasher().unwrap(); + let block = Block::new(raw_block, block_hash); + (block_hash, block) + } + + //make sure that duplicate messages doesn't have impact + fn handle_double(broadcaster: &mut Broadcaster, rb_msg: &RawRbMsg) -> BroadcastResponse { + let response = broadcaster.handle(rb_msg).unwrap(); + broadcaster.handle(rb_msg).unwrap(); + response + } +} diff --git a/ephemera/src/broadcast/bracha/mod.rs b/ephemera/src/broadcast/bracha/mod.rs new file mode 100644 index 0000000000..ddbf36eb64 --- /dev/null +++ b/ephemera/src/broadcast/bracha/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod broadcast; +pub(crate) mod quorum; diff --git a/ephemera/src/broadcast/bracha/quorum.rs b/ephemera/src/broadcast/bracha/quorum.rs new file mode 100644 index 0000000000..35bb935ae6 --- /dev/null +++ b/ephemera/src/broadcast/bracha/quorum.rs @@ -0,0 +1,254 @@ +use log::trace; + +use crate::broadcast::{MessageType, ProtocolContext}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum BrachaMessageType { + Echo, + Vote, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum BrachaAction { + Vote, + Deliver, + Ignore, +} + +impl BrachaAction { + pub(crate) fn is_vote(self) -> bool { + matches!(self, BrachaAction::Vote) + } + + pub(crate) fn is_deliver(self) -> bool { + matches!(self, BrachaAction::Deliver) + } +} + +impl From for BrachaMessageType { + fn from(message_type: MessageType) -> Self { + match message_type { + MessageType::Echo(_) => BrachaMessageType::Echo, + MessageType::Vote(_) => BrachaMessageType::Vote, + } + } +} + +const MAX_FAULTY_RATIO: f64 = 1.0 / 3.0; + +#[derive(Debug, Clone, Copy)] +pub(crate) struct Quorum { + pub(crate) cluster_size: usize, + pub(crate) max_faulty_nodes: usize, +} + +impl Quorum { + pub fn new(cluster_size: usize) -> Self { + let max_faulty_nodes = Quorum::max_faulty_nodes(cluster_size); + Self { + cluster_size, + max_faulty_nodes, + } + } + + pub(crate) fn check_threshold( + &self, + ctx: &ProtocolContext, + phase: BrachaMessageType, + ) -> BrachaAction { + if self.cluster_size == 0 { + return BrachaAction::Ignore; + } + + match phase { + BrachaMessageType::Echo => { + if ctx.echo.len() >= self.cluster_size - self.max_faulty_nodes { + trace!( + "Echo threshold reached: Echoed:{} / Threshold:{} for Block:{}", + ctx.echo.len(), + self.cluster_size - self.max_faulty_nodes, + ctx.hash + ); + BrachaAction::Vote + } else { + trace!( + "Echo threshold not reached: Echoed:{} / Threshold:{} for Block:{}", + ctx.echo.len(), + self.cluster_size - self.max_faulty_nodes, + ctx.hash + ); + BrachaAction::Ignore + } + } + BrachaMessageType::Vote => { + if !ctx.voted() { + // f + 1 votes are enough to send our vote + if ctx.vote.len() >= self.max_faulty_nodes { + trace!( + "Vote send threshold reached: Voted:{} / Threshold:{} for Block:{}", + ctx.vote.len(), + self.max_faulty_nodes + 1, + ctx.hash + ); + return BrachaAction::Vote; + } + } + + if ctx.voted() { + // n-f votes are enough to deliver the value + if ctx.vote.len() >= self.cluster_size - self.max_faulty_nodes { + trace!( + "Deliver threshold reached: Voted:{} / Threshold:{} for Block:{}", + ctx.vote.len(), + self.cluster_size - self.max_faulty_nodes, + ctx.hash + ); + return BrachaAction::Deliver; + } + } + + trace!( + "Vote threshold not reached: Voted:{} / Threshold:{} for Block:{}", + ctx.vote.len(), + self.max_faulty_nodes + 1, + ctx.hash + ); + BrachaAction::Ignore + } + } + } + + pub(crate) fn cluster_size_info(cluster_size: usize) -> String { + let max_faulty_nodes = Quorum::max_faulty_nodes(cluster_size); + format!("Cluster size: {cluster_size} / Max faulty nodes: {max_faulty_nodes}",) + } + + #[allow( + clippy::cast_precision_loss, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] + fn max_faulty_nodes(cluster_size: usize) -> usize { + (cluster_size as f64 * MAX_FAULTY_RATIO).floor() as usize + } +} + +#[cfg(test)] +mod test { + use std::collections::HashSet; + + use crate::broadcast::{ + bracha::quorum::{BrachaAction, BrachaMessageType, Quorum}, + ProtocolContext, + }; + use crate::peer::PeerId; + + #[test] + fn test_max_faulty_nodes() { + let quorum = Quorum::new(10); + assert_eq!(quorum.max_faulty_nodes, 3); + } + + #[test] + fn test_vote_threshold_from_n_minus_f_peers() { + let quorum = Quorum::new(10); + + let ctx = ctx_with_nr_echoes(0); + assert_eq!( + quorum.check_threshold(&ctx, BrachaMessageType::Echo), + BrachaAction::Ignore + ); + + let ctx = ctx_with_nr_echoes(3); + assert_eq!( + quorum.check_threshold(&ctx, BrachaMessageType::Echo), + BrachaAction::Ignore + ); + + let ctx = ctx_with_nr_echoes(8); + assert_eq!( + quorum.check_threshold(&ctx, BrachaMessageType::Echo), + BrachaAction::Vote + ); + } + + #[test] + fn test_vote_threshold_from_f_plus_one_peers() { + let quorum = Quorum::new(10); + + let ctx = ctx_with_nr_votes(0, None); + assert_eq!( + quorum.check_threshold(&ctx, BrachaMessageType::Vote), + BrachaAction::Ignore + ); + + let ctx = ctx_with_nr_votes(2, None); + assert_eq!( + quorum.check_threshold(&ctx, BrachaMessageType::Vote), + BrachaAction::Ignore + ); + + let ctx = ctx_with_nr_votes(5, None); + assert_eq!( + quorum.check_threshold(&ctx, BrachaMessageType::Vote), + BrachaAction::Vote + ); + } + + #[test] + fn test_deliver_threshold_from_n_minus_f_peers() { + let quorum = Quorum::new(10); + + let local_peer_id = PeerId::random(); + let ctx = ctx_with_nr_votes(0, local_peer_id.into()); + assert_eq!( + quorum.check_threshold(&ctx, BrachaMessageType::Vote), + BrachaAction::Ignore + ); + + let ctx = ctx_with_nr_votes(3, local_peer_id.into()); + assert_eq!( + quorum.check_threshold(&ctx, BrachaMessageType::Vote), + BrachaAction::Ignore + ); + + let ctx = ctx_with_nr_votes(7, local_peer_id.into()); + assert_eq!( + quorum.check_threshold(&ctx, BrachaMessageType::Vote), + BrachaAction::Deliver + ); + } + + fn ctx_with_nr_echoes(n: usize) -> ProtocolContext { + let mut ctx = ProtocolContext { + local_peer_id: PeerId::random(), + hash: [0; 32].into(), + echo: HashSet::default(), + vote: HashSet::default(), + quorum: Quorum::new(10), + delivered: false, + }; + for _ in 0..n { + ctx.echo.insert(PeerId::random()); + } + ctx + } + + fn ctx_with_nr_votes(n: usize, local_peer_id: Option) -> ProtocolContext { + let mut ctx = ProtocolContext { + local_peer_id: local_peer_id.unwrap_or(PeerId::random()), + hash: [0; 32].into(), + echo: HashSet::default(), + vote: HashSet::default(), + quorum: Quorum::new(10), + delivered: false, + }; + for _ in 0..n { + ctx.vote.insert(PeerId::random()); + } + if let Some(id) = local_peer_id { + ctx.vote.insert(id); + } + ctx + } +} diff --git a/ephemera/src/broadcast/group.rs b/ephemera/src/broadcast/group.rs new file mode 100644 index 0000000000..d8483b8aaf --- /dev/null +++ b/ephemera/src/broadcast/group.rs @@ -0,0 +1,249 @@ +use std::collections::HashSet; +use std::num::NonZeroUsize; + +use log::warn; +use lru::LruCache; + +use crate::peer::PeerId; +use crate::utilities::hash::Hash; + +pub(crate) struct BroadcastGroup { + /// The id of current group. Incremented every time a new snapshot is added. + pub(crate) current_id: u64, + /// A cache of the group snapshots. + pub(crate) snapshots: LruCache>, + /// A cache of the groups for each block. + pub(crate) broadcast_groups: LruCache, +} + +impl BroadcastGroup { + pub(crate) fn new() -> BroadcastGroup { + let mut snapshots = LruCache::new(NonZeroUsize::new(100).unwrap()); + snapshots.put(0, HashSet::new()); + BroadcastGroup { + current_id: 0, + snapshots, + broadcast_groups: LruCache::new(NonZeroUsize::new(100).unwrap()), + } + } + + pub(crate) fn add_snapshot(&mut self, snapshot: HashSet) { + self.current_id += 1; + self.snapshots.put(self.current_id, snapshot); + } + + pub(crate) fn is_member(&mut self, id: u64, peer_id: &PeerId) -> bool { + self.snapshots + .get(&id) + .map_or(false, |s| s.contains(peer_id)) + } + + pub(crate) fn is_empty(&mut self) -> bool { + self.snapshots + .get(&self.current_id) + .map_or(true, HashSet::is_empty) + } + + // Returns empty snapshots(inserted in 'new' fn) if we haven't received any yet. + pub(crate) fn current(&mut self) -> &HashSet { + self.snapshots + .get(&self.current_id) + .expect("Current group should always exist") + } + + // Checks if creator and sender are part of the expected group. + // If we see hash first time, it checks against the current group. And if check passes, it + // associates the hash with the current group. + pub(crate) fn check_membership( + &mut self, + hash: Hash, + block_creator: &PeerId, + message_sender: &PeerId, + ) -> bool { + //We see this block first time + if !self.broadcast_groups.contains(&hash) { + //This can happen at startup for example when node is not ready yet(caught up with the network) + if self.is_empty() { + warn!( + "Received new block {:?} but current group is empty, rejecting the block", + hash + ); + return false; + } + } + + //Make sure that the sender peer_id and block peer_id are part of the block initial group + //1. If the block is new, the group is the current one + //2. If the block is old, the group is the one that was used when the block was created + + //It's needed to make sure that + //1. The peer is authenticated(part of the network) + //2. Block processing is consistent regarding the group across rounds + + let membership_id = *self.broadcast_groups.get(&hash).unwrap_or(&self.current_id); + + //Node is excluded from group for some reason(for example health checks failed) + if !self.is_member(membership_id, message_sender) { + warn!( + "Received new block {} but sender {} is not part of the current group", + hash, message_sender + ); + return false; + } + + //Node is excluded from group for some reason(for example health checks failed) + if !self.is_member(membership_id, block_creator) { + warn!( + "Received new block {} but sender {} is not part of the current group", + hash, message_sender + ); + return false; + } + + self.broadcast_groups.put(hash, membership_id); + + true + } + + pub(crate) fn get_group_by_block_hash(&mut self, hash: Hash) -> Option<&HashSet> { + let membership_id = *self.broadcast_groups.get(&hash)?; + self.snapshots.get(&membership_id) + } +} + +#[cfg(test)] +mod test { + use std::collections::HashSet; + + use crate::broadcast::group::BroadcastGroup; + use crate::peer::PeerId; + use crate::utilities::hash::Hash; + + #[test] + fn test_no_snapshot() { + let group = BroadcastGroup::new(); + assert_eq!(group.current_id, 0); + //Including initial default snapshot + assert_eq!(group.snapshots.len(), 1); + } + + #[test] + fn test_multiple_snapshots() { + let (mut group, snapshots) = group_with_snapshots(10); + assert_eq!(group.current_id, 10); + //Including initial default snapshot + assert_eq!(group.snapshots.len(), 11); + + for (i, sn) in snapshots + .iter() + .enumerate() + .map(|(i, sn)| ((i + 1) as u64, sn)) + { + let gsn = group.snapshots.get(&i).unwrap(); + assert_eq!(sn, gsn); + } + } + + #[test] + fn check_membership_empty_group() { + let mut group = BroadcastGroup::new(); + let hash = Hash::new([0; 32]); + assert!(!group.check_membership(hash, &PeerId::random(), &PeerId::random())); + assert!(!group.broadcast_groups.contains(&hash)); + } + + #[test] + fn check_membership_creator_nor_sender_not_member() { + let (mut group, _snapshots) = group_with_snapshots(1); + assert!(!group.check_membership(Hash::new([0; 32]), &PeerId::random(), &PeerId::random())); + assert!(!group.broadcast_groups.contains(&Hash::new([0; 32]))); + } + + #[test] + fn check_membership_creator_not_member() { + let (mut group, snapshots) = group_with_snapshots(1); + let sender = snapshots[0].clone().into_iter().next().unwrap(); + + let hash = Hash::new([0; 32]); + assert!(!group.check_membership(hash, &PeerId::random(), &sender)); + assert!(!group.broadcast_groups.contains(&hash)); + } + + #[test] + fn check_membership_sender_not_member() { + let (mut group, snapshots) = group_with_snapshots(1); + let creator = snapshots[0].clone().into_iter().next().unwrap(); + let hash = Hash::new([0; 32]); + assert!(!group.check_membership(hash, &creator, &PeerId::random())); + assert!(!group.broadcast_groups.contains(&hash)); + } + + #[test] + fn check_snapshot_membership_both_are_members() { + let (mut group, snapshots) = group_with_snapshots(1); + let creator = snapshots[0].clone().into_iter().next().unwrap(); + let sender = creator; + let hash = Hash::new([0; 32]); + assert!(group.check_membership(hash, &creator, &sender)); + assert!(group.broadcast_groups.contains(&hash)); + } + + #[test] + fn check_snapshot_membership_of_current_snapshot() { + let (mut group, snapshots) = group_with_snapshots(2); + let current_snapshot = snapshots[1].clone(); + let creator = current_snapshot.into_iter().next().unwrap(); + let sender = creator; + + let hash = Hash::new([0; 32]); + assert!(group.check_membership(hash, &creator, &sender)); + assert!(group.broadcast_groups.contains(&hash)); + + //Remove the current snapshot + group.snapshots.pop(&group.current_id); + + //Membership should fail + assert!(!group.check_membership(hash, &creator, &sender)); + } + + #[test] + fn check_snapshot_membership_of_previous_snapshot() { + let mut group = BroadcastGroup::new(); + let first_snapshot = create_snapshot(); + group.add_snapshot(first_snapshot.clone()); + + let creator = first_snapshot.into_iter().next().unwrap(); + let sender = creator; + let hash = Hash::new([0; 32]); + assert!(group.check_membership(hash, &creator, &sender)); + assert!(group.broadcast_groups.contains(&hash)); + + //Add second snapshot + group.add_snapshot(create_snapshot()); + + //Remove the current snapshot + group.snapshots.pop(&group.current_id); + + //Membership should still pass + assert!(group.broadcast_groups.contains(&hash)); + assert!(group.check_membership(hash, &creator, &sender)); + } + + fn group_with_snapshots(count: usize) -> (BroadcastGroup, Vec>) { + let mut group = BroadcastGroup::new(); + let mut snapshots = Vec::new(); + for _ in 0..count { + let snapshot = create_snapshot(); + snapshots.push(snapshot.clone()); + group.add_snapshot(snapshot); + } + (group, snapshots) + } + + fn create_snapshot() -> HashSet { + let mut snapshot = HashSet::new(); + let peer_id = PeerId::random(); + snapshot.insert(peer_id); + snapshot + } +} diff --git a/ephemera/src/broadcast/mod.rs b/ephemera/src/broadcast/mod.rs new file mode 100644 index 0000000000..ca930c451d --- /dev/null +++ b/ephemera/src/broadcast/mod.rs @@ -0,0 +1,191 @@ +//! Simple reliable broadcast protocol(Bracha) implementation +//! +use std::collections::HashSet; +use std::fmt::{Debug, Display}; + +use serde_derive::{Deserialize, Serialize}; + +use crate::broadcast::bracha::quorum::Quorum; +use crate::{ + block::types::block::Block, + peer::PeerId, + utilities::{ + crypto::Certificate, + hash::Hash, + id::{EphemeraId, EphemeraIdentifier}, + time::EphemeraTime, + }, +}; + +pub(crate) mod bracha; +pub(crate) mod group; +pub(crate) mod signing; + +/// Context keeps the broadcast state for a block +#[derive(Debug, Clone)] +pub(crate) struct ProtocolContext { + pub(crate) local_peer_id: PeerId, + /// Block hash + pub(crate) hash: Hash, + /// Peers that sent prepare message(this peer included) + pub(crate) echo: HashSet, + /// Peers that sent commit message(this peer included) + pub(crate) vote: HashSet, + /// Quorum logic for Bracha protocol + pub(crate) quorum: Quorum, + /// Flag indicating if the message was delivered to the client + pub(crate) delivered: bool, +} + +impl ProtocolContext { + pub(crate) fn new(hash: Hash, local_peer_id: PeerId, quorum: Quorum) -> ProtocolContext { + ProtocolContext { + local_peer_id, + hash, + echo: HashSet::new(), + vote: HashSet::new(), + quorum, + delivered: false, + } + } + + fn add_echo(&mut self, peer: PeerId) { + self.echo.insert(peer); + } + + fn add_vote(&mut self, peer: PeerId) { + self.vote.insert(peer); + } + + fn echoed(&self) -> bool { + self.echo.contains(&self.local_peer_id) + } + + fn voted(&self) -> bool { + self.vote.contains(&self.local_peer_id) + } +} + +#[derive(Clone, PartialEq, Eq, Deserialize, Serialize)] +pub(crate) struct RbMsg { + ///Unique id of the message which stays the same throughout the protocol + pub(crate) id: EphemeraId, + ///Distinct id of the message which changes when the message is rebroadcast + pub(crate) request_id: EphemeraId, + ///Id of the peer that CREATED the message(not necessarily the one that sent it, with gossip it can come through a different peer) + pub(crate) original_sender: PeerId, + ///When the message was created by the sender. + pub(crate) timestamp: u64, + ///Current phase of the protocol(Echo, Vote) + pub(crate) phase: MessageType, + ///Signature of the message + pub(crate) certificate: Certificate, +} + +impl RbMsg { + pub(crate) fn new(raw: RawRbMsg, signature: Certificate) -> RbMsg { + RbMsg { + id: raw.id, + request_id: raw.request_id, + original_sender: raw.original_sender, + timestamp: raw.timestamp, + phase: raw.message_type, + certificate: signature, + } + } + + pub(crate) fn block(&self) -> &Block { + match &self.phase { + MessageType::Echo(block) | MessageType::Vote(block) => block, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +pub(crate) struct RawRbMsg { + pub(crate) id: EphemeraId, + pub(crate) request_id: EphemeraId, + pub(crate) original_sender: PeerId, + pub(crate) timestamp: u64, + pub(crate) message_type: MessageType, +} + +impl RawRbMsg { + pub(crate) fn new(block: Block, original_sender: PeerId) -> RawRbMsg { + RawRbMsg { + id: EphemeraId::generate(), + request_id: EphemeraId::generate(), + original_sender, + timestamp: EphemeraTime::now(), + message_type: MessageType::Echo(block), + } + } + + pub(crate) fn block(&self) -> Block { + match &self.message_type { + MessageType::Echo(block) | MessageType::Vote(block) => block.clone(), + } + } + + pub(crate) fn reply(&self, local_id: PeerId, phase: MessageType) -> Self { + RawRbMsg { + id: self.id.clone(), + request_id: EphemeraId::generate(), + original_sender: local_id, + timestamp: EphemeraTime::now(), + message_type: phase, + } + } + + pub(crate) fn echo_reply(&self, local_id: PeerId, data: Block) -> Self { + self.reply(local_id, MessageType::Echo(data)) + } + + pub(crate) fn vote_reply(&self, local_id: PeerId, data: Block) -> Self { + self.reply(local_id, MessageType::Vote(data)) + } +} + +impl From for RawRbMsg { + fn from(msg: RbMsg) -> Self { + RawRbMsg { + id: msg.id, + request_id: msg.request_id, + original_sender: msg.original_sender, + timestamp: msg.timestamp, + message_type: msg.phase, + } + } +} + +impl Display for RbMsg { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "[id: {}, peer: {}, block: {}, phase: {:?}]", + self.id, + self.original_sender, + self.block().get_hash(), + self.phase + ) + } +} + +impl Debug for RbMsg { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "[id: {}, peer: {}, block: {}, phase: {:?}]", + self.id, + self.original_sender, + self.block().get_hash(), + self.phase + ) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +pub(crate) enum MessageType { + Echo(Block), + Vote(Block), +} diff --git a/ephemera/src/broadcast/signing.rs b/ephemera/src/broadcast/signing.rs new file mode 100644 index 0000000000..c3003365f3 --- /dev/null +++ b/ephemera/src/broadcast/signing.rs @@ -0,0 +1,150 @@ +use std::collections::HashSet; +use std::num::NonZeroUsize; +use std::sync::Arc; + +use log::trace; +use lru::LruCache; + +use crate::{ + block::types::block::{Block, RawBlock}, + crypto::Keypair, + utilities::{codec::Encode, crypto::Certificate, crypto::EphemeraPublicKey, hash::Hash}, +}; + +pub(crate) struct BlockSigner { + /// All signatures of the last blocks that we received from the network(+ our own) + verified_signatures: LruCache>, + /// Our own keypair + signing_keypair: Arc, +} + +impl BlockSigner { + pub fn new(keypair: Arc) -> Self { + Self { + verified_signatures: LruCache::new(NonZeroUsize::new(1000).unwrap()), + signing_keypair: keypair, + } + } + + pub(crate) fn get_block_certificates( + &mut self, + block_id: &Hash, + ) -> Option<&HashSet> { + self.verified_signatures.get(block_id) + } + + pub(crate) fn sign_block(&mut self, block: &Block, hash: &Hash) -> anyhow::Result { + trace!("Signing block: {:?}", block); + + let certificate = block.sign(self.signing_keypair.as_ref())?; + self.add_certificate(hash, certificate.clone()); + Ok(certificate) + } + + /// This verification is part of reliable broadcast and verifies only the + /// signature of the sender. + pub(crate) fn verify_block( + &mut self, + block: &Block, + certificate: &Certificate, + ) -> anyhow::Result<()> { + trace!("Verifying block: {block:?} against certificate {certificate:?}"); + + let raw_block: RawBlock = (*block).clone().into(); + let raw_block = raw_block.encode()?; + + if certificate + .public_key + .verify(&raw_block, &certificate.signature) + { + self.add_certificate(&block.header.hash, certificate.clone()); + Ok(()) + } else { + anyhow::bail!("Invalid block certificate"); + } + } + + fn add_certificate(&mut self, hash: &Hash, certificate: Certificate) { + trace!("Adding certificate to block: {}", hash); + self.verified_signatures + .get_or_insert_mut(*hash, HashSet::new) + .insert(certificate); + } +} + +#[cfg(test)] +mod test { + use crate::block::types::block::RawBlockHeader; + use crate::block::types::message::{EphemeraMessage, RawEphemeraMessage}; + use crate::crypto::EphemeraKeypair; + use crate::peer::ToPeerId; + + use super::*; + + #[test] + fn test_sign_verify_block_ok() { + let mut signer = BlockSigner::new(Arc::new(Keypair::generate(None))); + + let message_signing_keypair = Keypair::generate(None); + + let block = new_block(&message_signing_keypair, "label1"); + let hash = block.hash_with_default_hasher().unwrap(); + + let certificate = signer.sign_block(&block, &hash).unwrap(); + + assert!(signer.verify_block(&block, &certificate).is_ok()); + } + + #[test] + fn test_sign_signatures_cached_correctly() { + let mut signer = BlockSigner::new(Arc::new(Keypair::generate(None))); + + let block = new_block(&Keypair::generate(None), "label1"); + let hash = block.hash_with_default_hasher().unwrap(); + + //Signed by node 1 + let certificate1 = block.sign(&Keypair::generate(None)).unwrap(); + signer.verify_block(&block, &certificate1).unwrap(); + //Signed by node 2 + let certificate2 = block.sign(&Keypair::generate(None)).unwrap(); + signer.verify_block(&block, &certificate2).unwrap(); + + let block_certificates = signer.get_block_certificates(&hash).unwrap(); + assert_eq!(block_certificates.len(), 2); + } + + #[test] + fn test_sign_verify_block_fail() { + let mut signer = BlockSigner::new(Arc::new(Keypair::generate(None))); + let message_signing_keypair = Keypair::generate(None); + + let block = new_block(&message_signing_keypair, "label1"); + let certificate = block.sign(&message_signing_keypair).unwrap(); + + let modified_block = new_block(&message_signing_keypair, "label2"); + + assert!(signer.verify_block(&modified_block, &certificate).is_err()); + } + + fn new_block(keypair: &Keypair, message_label: &str) -> Block { + let peer_id = keypair.public_key().peer_id(); + + let raw_ephemera_message = + RawEphemeraMessage::new(message_label.to_string(), "payload".as_bytes().to_vec()); + + let message_certificate = Certificate::prepare(keypair, &raw_ephemera_message).unwrap(); + let messages = vec![EphemeraMessage::new( + raw_ephemera_message, + message_certificate, + )]; + + let raw_block_header = RawBlockHeader::new(peer_id, 0); + let raw_block = RawBlock::new(raw_block_header, messages); + + let block_hash = raw_block + .hash_with_default_hasher() + .expect("Hashing failed"); + + Block::new(raw_block, block_hash) + } +} diff --git a/ephemera/src/cli/config.rs b/ephemera/src/cli/config.rs new file mode 100644 index 0000000000..1fca81afcc --- /dev/null +++ b/ephemera/src/cli/config.rs @@ -0,0 +1,132 @@ +use std::fs; +use std::path::PathBuf; +use std::str::FromStr; + +use clap::Parser; +use log::{error, info}; +use toml::{Table, Value}; + +use crate::config::Configuration; + +#[derive(Debug, Clone, Parser)] +pub struct UpdateConfigCmd { + #[clap(long)] + pub config_path: String, + #[clap(long)] + pub property: String, + #[clap(long)] + pub value: String, +} + +impl UpdateConfigCmd { + /// # Panics + /// Panics if the config file does not exist. + pub fn execute(self) { + let path: PathBuf = self.config_path.clone().into(); + match Configuration::try_load(path.clone()) { + Ok(_) => { + info!("Updating config: {:?}", self); + + let toml_str = fs::read_to_string(path.clone()).unwrap(); + let table = toml_str.parse::().unwrap(); + + let keys = self.property.split('.').collect::>(); + + if !table.contains_key(keys[0]) { + println!("Key '{}' does not exist", keys[0]); + return; + } + + let mut visitor = ConfigVisitor::new(keys, self.value); + let table = visitor.process(table); + + let toml_str = toml::to_string(&table).unwrap(); + fs::write(path, toml_str).unwrap(); + } + Err(err) => { + error!("Error loading configuration file: {err:?}"); + } + } + } +} + +struct ConfigVisitor<'a> { + keys: Vec<&'a str>, + value: String, + in_array: bool, + data_in_array: Vec, +} + +impl<'a> ConfigVisitor<'a> { + fn new(keys: Vec<&'a str>, value: String) -> Self { + Self { + keys, + value, + in_array: false, + data_in_array: vec![], + } + } + + #[allow(clippy::needless_pass_by_value)] + fn process(&mut self, table: Table) -> Table { + let key = self.keys[0]; + let value = table.get(key).unwrap(); + self.process_value(table.clone(), value.clone(), 0) + } + + fn process_value(&mut self, mut table: Table, value: Value, key_index: usize) -> Table { + let root_key = self.keys[key_index]; + match value { + Value::String(str) => { + println!("Old value: {str}",); + let value = String::from(&self.value); + println!("New value: {value}",); + table.insert(root_key.to_string(), Value::String(value)); + } + Value::Integer(i) => { + println!("Old value: {i}",); + let value = i64::from_str(&self.value).unwrap(); + println!("New value: {value}",); + table.insert(root_key.to_string(), Value::Integer(value)); + } + Value::Float(f) => { + println!("Old value: {f}",); + let value = f64::from_str(&self.value).unwrap(); + println!("New value: {value}",); + table.insert(root_key.to_string(), Value::Float(value)); + } + Value::Boolean(b) => { + println!("Old value: {b}",); + let value = bool::from_str(&self.value).unwrap(); + println!("New value: {value}",); + table.insert(root_key.to_string(), Value::Boolean(value)); + } + Value::Datetime(_) => { + println!("Datetime not supported"); + } + Value::Array(ar) => { + self.in_array = true; + for value in ar { + self.process_value(table.clone(), value, key_index); + } + table.remove(root_key); + table.insert( + root_key.to_string(), + Value::Array(self.data_in_array.clone()), + ); + self.data_in_array.clear(); + self.in_array = false; + } + Value::Table(t) => { + let value = t.get(self.keys[key_index + 1]).cloned().unwrap(); + let updated_table = self.process_value(t, value, key_index + 1); + if self.in_array { + self.data_in_array.push(updated_table.into()); + return table.clone(); + } + table.insert(root_key.to_string(), updated_table.into()); + } + } + table + } +} diff --git a/ephemera/src/cli/crypto.rs b/ephemera/src/cli/crypto.rs new file mode 100644 index 0000000000..fc6f3e989b --- /dev/null +++ b/ephemera/src/cli/crypto.rs @@ -0,0 +1,15 @@ +use crate::crypto::{EphemeraKeypair, Keypair}; +use clap::Parser; + +use crate::utilities::crypto::EphemeraPublicKey; + +#[derive(Debug, Clone, Parser)] +pub struct GenerateKeypairCmd; + +impl GenerateKeypairCmd { + pub fn execute() { + let keypair = Keypair::generate(None); + println!("Keypair: {:>5}", keypair.to_base58()); + println!("Public key: {:>5}", keypair.public_key().to_base58()); + } +} diff --git a/ephemera/src/cli/init.rs b/ephemera/src/cli/init.rs new file mode 100644 index 0000000000..60d245d279 --- /dev/null +++ b/ephemera/src/cli/init.rs @@ -0,0 +1,165 @@ +use clap::{Args, Parser}; +use serde::{Deserialize, Serialize}; + +use crate::config::{ + BlockManagerConfiguration, Configuration, DatabaseConfiguration, HttpConfiguration, + Libp2pConfiguration, MembershipKind as ConfigMembershipKind, NodeConfiguration, + WebsocketConfiguration, +}; +use crate::crypto::{EphemeraKeypair, Keypair}; + +//network settings +const DEFAULT_LISTEN_ADDRESS: &str = "127.0.0.1"; +const DEFAULT_PROT_LISTEN_PORT: u16 = 3000; +const DEFAULT_WS_LISTEN_PORT: u16 = 3001; +const DEFAULT_HTTP_LISTEN_PORT: u16 = 3002; + +//libp2p settings +const DEFAULT_MESSAGES_TOPIC_NAME: &str = "nym-ephemera-proposed"; +const DEFAULT_HEARTBEAT_INTERVAL_SEC: u64 = 1; + +#[derive(Args, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[group(required = true, multiple = false)] +pub struct MembershipKind { + /// Requires the threshold of peers returned by membership provider to be online + #[clap(long)] + threshold: Option, + /// Requires that all peers returned by membership provider peers to be online + #[clap(long)] + all: bool, + /// Membership is just all online peers from the set returned by membership provider + #[clap(long)] + any: bool, +} + +impl Default for MembershipKind { + fn default() -> Self { + MembershipKind { + threshold: None, + all: false, + any: true, + } + } +} + +impl From for ConfigMembershipKind { + fn from(kind: MembershipKind) -> Self { + match (kind.threshold, kind.all, kind.any) { + //FIXME: use threshold value + (Some(_), false, false) => ConfigMembershipKind::Threshold, + (None, true, false) => ConfigMembershipKind::AllOnline, + (None, false, true) => ConfigMembershipKind::AnyOnline, + _ => panic!("Invalid membership kind"), + } + } +} + +#[derive(Parser, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct Cmd { + #[clap(long)] + /// The IP address to listen on + pub ephemera_ip: Option, + /// The port which Ephemera uses for peer to peer communication + #[clap(long)] + pub ephemera_protocol_port: Option, + /// The port which Ephemera listens on for websocket subscriptions + #[clap(long)] + pub ephemera_websocket_port: Option, + /// The port which Ephemera listens on for http api + #[clap(long)] + pub ephemera_http_api_port: Option, + /// Either this node produces blocks or not + #[clap(skip)] + pub block_producer: bool, + /// At which interval to produce blocks + #[clap(skip)] + pub block_creation_interval_sec: u64, + /// When next block is created before previous one is finished, should we repeat it with the same messages + #[clap(skip)] + pub repeat_last_block_messages: bool, + /// The interval at which Ephemera requests the list of members + #[clap(skip)] + pub members_provider_delay_sec: u64, + /// A rule how to choose members based on their online status + #[clap(skip)] + pub membership_kind: MembershipKind, +} + +impl Default for Cmd { + fn default() -> Self { + Cmd { + ephemera_ip: Some(String::from(DEFAULT_LISTEN_ADDRESS)), + ephemera_protocol_port: Some(DEFAULT_PROT_LISTEN_PORT), + ephemera_websocket_port: Some(DEFAULT_WS_LISTEN_PORT), + ephemera_http_api_port: Some(DEFAULT_HTTP_LISTEN_PORT), + block_producer: true, + block_creation_interval_sec: 30, + repeat_last_block_messages: false, + members_provider_delay_sec: 60 * 60, + membership_kind: MembershipKind::default(), + } + } +} + +impl Cmd { + /// # Panics + /// Panics if the config file already exists. + pub fn execute(self, id: Option<&str>) { + assert!( + Configuration::try_load_from_home_dir().is_err(), + "Configuration file already exists", + ); + + let path = Configuration::ephemera_root_dir(id).unwrap(); + println!("Creating ephemera node configuration in: {path:?}",); + + let db_dir = path.join("db"); + let rocksdb_path = db_dir.join("rocksdb"); + let sqlite_path = db_dir.join("ephemera.sqlite"); + std::fs::create_dir_all(&rocksdb_path).unwrap(); + std::fs::File::create(&sqlite_path).unwrap(); + + let keypair = Keypair::generate(None); + let private_key = keypair.to_base58(); + + let default_cfg = Self::default(); + + let configuration = Configuration { + node: NodeConfiguration { + ip: self.ephemera_ip.unwrap_or(default_cfg.ephemera_ip.unwrap()), + private_key, + }, + libp2p: Libp2pConfiguration { + port: self + .ephemera_protocol_port + .unwrap_or(default_cfg.ephemera_protocol_port.unwrap()), + ephemera_msg_topic_name: DEFAULT_MESSAGES_TOPIC_NAME.to_string(), + heartbeat_interval_sec: DEFAULT_HEARTBEAT_INTERVAL_SEC, + members_provider_delay_sec: self.members_provider_delay_sec, + membership_kind: self.membership_kind.into(), + }, + storage: DatabaseConfiguration { + rocksdb_path: rocksdb_path.as_os_str().to_str().unwrap().to_string(), + sqlite_path: sqlite_path.as_os_str().to_str().unwrap().to_string(), + create_if_not_exists: true, + }, + websocket: WebsocketConfiguration { + port: self + .ephemera_websocket_port + .unwrap_or(default_cfg.ephemera_websocket_port.unwrap()), + }, + http: HttpConfiguration { + port: self + .ephemera_http_api_port + .unwrap_or(default_cfg.ephemera_http_api_port.unwrap()), + }, + block_manager: BlockManagerConfiguration { + producer: self.block_producer, + creation_interval_sec: self.block_creation_interval_sec, + repeat_last_block_messages: self.repeat_last_block_messages, + }, + }; + + configuration.try_write_home_dir(id).ok(); + } +} diff --git a/ephemera/src/cli/mod.rs b/ephemera/src/cli/mod.rs new file mode 100644 index 0000000000..1828d89347 --- /dev/null +++ b/ephemera/src/cli/mod.rs @@ -0,0 +1,49 @@ +use crate::cli::crypto::GenerateKeypairCmd; +use clap::Parser; + +pub mod config; +mod crypto; +pub mod init; +pub mod peers; +pub mod run_node; + +pub const PEERS_CONFIG_FILE: &str = "peers.toml"; + +#[derive(Parser)] +#[command()] +pub struct Cli { + #[command(subcommand)] + pub subcommand: Subcommand, +} + +#[derive(clap::Subcommand)] +pub enum Subcommand { + InitConfig(init::Cmd), + InitLocalPeersConfig(peers::CreateLocalPeersConfiguration), + RunNode(run_node::RunExternalNodeCmd), + GenerateKeypair(crypto::GenerateKeypairCmd), + UpdateConfig(config::UpdateConfigCmd), +} + +impl Cli { + /// # Errors + /// Returns an error if the subcommand fails. + pub async fn execute(self) -> anyhow::Result<()> { + match self.subcommand { + Subcommand::InitConfig(init) => { + init.execute(None); + } + Subcommand::InitLocalPeersConfig(add_local_peers) => { + add_local_peers.execute(); + } + Subcommand::RunNode(run_node) => run_node.execute().await?, + Subcommand::GenerateKeypair(_) => { + GenerateKeypairCmd::execute(); + } + Subcommand::UpdateConfig(update_config) => { + update_config.execute(); + } + } + Ok(()) + } +} diff --git a/ephemera/src/cli/peers.rs b/ephemera/src/cli/peers.rs new file mode 100644 index 0000000000..c77f4974e2 --- /dev/null +++ b/ephemera/src/cli/peers.rs @@ -0,0 +1,65 @@ +//! This is used to create local peers configuration file. Useful for local cluster development. + +use crate::cli::PEERS_CONFIG_FILE; +use clap::Parser; + +use crate::config::Configuration; +use crate::crypto::{EphemeraKeypair, EphemeraPublicKey, Keypair}; +use crate::membership::PeerSetting; +use crate::network::members::ConfigPeers; + +#[derive(Debug, Clone, Parser)] +pub struct CreateLocalPeersConfiguration; + +impl CreateLocalPeersConfiguration { + /// # Panics + /// Panics if the configuration file cannot be written. + pub fn execute(self) { + let peers = Self::from_ephemera_dev_cluster_conf().unwrap(); + let config_peers = ConfigPeers::new(peers); + + let peers_conf_path = Configuration::ephemera_root_dir(None) + .unwrap() + .join(PEERS_CONFIG_FILE); + + config_peers.try_write(peers_conf_path).unwrap(); + } + + //LOCAL DEV CLUSTER ONLY + //Get peers from dev Ephemera cluster config files + pub(crate) fn from_ephemera_dev_cluster_conf() -> anyhow::Result> { + let ephemera_root_dir = Configuration::ephemera_root_dir(None).unwrap(); + + let mut peers = vec![]; + + let home_dir = std::fs::read_dir(ephemera_root_dir)?; + for entry in home_dir { + let path = entry?.path(); + if path.is_dir() { + let cosmos_address = path.file_name().unwrap().to_str().unwrap(); + + println!("Reading peer info config from node {cosmos_address}",); + + let conf = Configuration::try_load_from_home_dir().unwrap_or_else(|_| { + panic!("Error loading configuration for node {cosmos_address}") + }); + + let node_info = conf.node; + + let keypair = bs58::decode(&node_info.private_key).into_vec().unwrap(); + let keypair = Keypair::from_bytes(&keypair).unwrap(); + + let peer = PeerSetting { + cosmos_address: cosmos_address.to_string(), + address: format!("/ip4/{}/tcp/{}", node_info.ip, conf.libp2p.port), + public_key: keypair.public_key().to_base58(), + }; + peers.push(peer); + + println!("Loaded config for node {cosmos_address}",); + } + } + + Ok(peers) + } +} diff --git a/ephemera/src/cli/run_node.rs b/ephemera/src/cli/run_node.rs new file mode 100644 index 0000000000..a33854f52d --- /dev/null +++ b/ephemera/src/cli/run_node.rs @@ -0,0 +1,140 @@ +use anyhow::anyhow; +use std::str::FromStr; +use std::sync::Arc; + +use clap::Parser; +use log::trace; +use nym_task::TaskManager; +use reqwest::Url; + +use crate::ephemera_api::ApplicationResult; +use crate::utilities::codec::{Codec, EphemeraCodec}; +use crate::{ + api::application::CheckBlockResult, + cli::PEERS_CONFIG_FILE, + config::Configuration, + crypto::EphemeraKeypair, + crypto::Keypair, + ephemera_api::{ApiBlock, ApiEphemeraMessage, Application, Dummy, RawApiEphemeraMessage}, + network::members::ConfigMembersProvider, + EphemeraStarterInit, +}; + +#[derive(Clone, Debug)] +pub struct HttpMembersProviderArg { + pub url: Url, +} + +impl FromStr for HttpMembersProviderArg { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + Ok(HttpMembersProviderArg { url: s.parse()? }) + } +} + +#[derive(Parser)] +pub struct RunExternalNodeCmd { + #[clap(short, long)] + pub config_file: String, + #[clap(short, long)] + pub peers_config: String, +} + +impl RunExternalNodeCmd { + /// # Errors + /// If the members provider cannot be created. + /// + /// # Panics + /// If the ephemera cannot be created. + pub async fn execute(&self) -> anyhow::Result<()> { + let ephemera_conf = match Configuration::try_load(self.config_file.clone()) { + Ok(conf) => conf, + Err(err) => anyhow::bail!("Error loading configuration file: {err:?}"), + }; + + let members_provider = Self::config_members_provider_with_path(self.peers_config.clone())?; + let ephemera = EphemeraStarterInit::new(ephemera_conf.clone()) + .unwrap() + .with_application(Dummy) + .with_members_provider(members_provider)? + .build(); + + let shutdown = TaskManager::new(10); + + let shutdown_listener = shutdown.subscribe(); + tokio::spawn(ephemera.run(shutdown_listener)); + + if let Err(err) = shutdown.catch_interrupt().await { + Err(anyhow!("Shutdown error {:?}", err)) + } else { + Ok(()) + } + } + + #[allow(dead_code)] + fn config_members_provider() -> anyhow::Result { + let peers_conf_path = Configuration::ephemera_root_dir(None) + .unwrap() + .join(PEERS_CONFIG_FILE); + + let peers_conf = match ConfigMembersProvider::init(peers_conf_path) { + Ok(conf) => conf, + Err(err) => anyhow::bail!("Error loading peers file: {err:?}"), + }; + Ok(peers_conf) + } + + #[allow(dead_code)] + fn config_members_provider_with_path( + peers_conf_path: String, + ) -> anyhow::Result { + let peers_conf = match ConfigMembersProvider::init(peers_conf_path) { + Ok(conf) => conf, + Err(err) => anyhow::bail!("Error loading peers file: {err:?}"), + }; + Ok(peers_conf) + } +} + +pub struct SignatureVerificationApplication { + keypair: Arc, +} + +impl SignatureVerificationApplication { + #[must_use] + pub fn new(keypair: Arc) -> Self { + Self { keypair } + } + + pub(crate) fn verify_message(&self, msg: ApiEphemeraMessage) -> anyhow::Result<()> { + let signature = msg.certificate.clone(); + let raw_message: RawApiEphemeraMessage = msg.into(); + let encoded_message = Codec::encode(&raw_message)?; + if self + .keypair + .verify(&encoded_message, &signature.signature.into()) + { + Ok(()) + } else { + anyhow::bail!("Invalid signature") + } + } +} + +impl Application for SignatureVerificationApplication { + fn check_tx(&self, tx: ApiEphemeraMessage) -> ApplicationResult { + trace!("SignatureVerificationApplicationHook::check_tx"); + self.verify_message(tx)?; + Ok(true) + } + + fn check_block(&self, _block: &ApiBlock) -> ApplicationResult { + Ok(CheckBlockResult::Accept) + } + + fn deliver_block(&self, _block: ApiBlock) -> ApplicationResult<()> { + trace!("SignatureVerificationApplicationHook::deliver_block"); + Ok(()) + } +} diff --git a/ephemera/src/config/mod.rs b/ephemera/src/config/mod.rs new file mode 100644 index 0000000000..bf30850928 --- /dev/null +++ b/ephemera/src/config/mod.rs @@ -0,0 +1,334 @@ +//! Configuration for Ephemeris node. It contains mandatory settings for a node to start. +//! +//! Default location for the configuration file is `~/.nym/ephemera/ephemera.toml`. +//! Or relative to a node specific directory `~/.nym/ephemera//ephemera.toml`. + +use std::io::Write; +use std::path::PathBuf; + +use config::ConfigError; +use log::{error, info}; +use nym_config::{DEFAULT_NYM_APIS_DIR, NYM_DIR}; +use serde_derive::{Deserialize, Serialize}; +use thiserror::Error; + +//TODO - validate configuration at load time +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct Configuration { + /// Configuration related to node instance identity + pub node: NodeConfiguration, + /// Configuration for libp2p network + pub libp2p: Libp2pConfiguration, + /// Configuration for Ephemera embedded database + pub storage: DatabaseConfiguration, + /// Configuration for websocket + pub websocket: WebsocketConfiguration, + /// Configuration for embedded http API server + pub http: HttpConfiguration, + /// Configuration related to block creation + pub block_manager: BlockManagerConfiguration, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct NodeConfiguration { + /// Node IP shared by all Ephemera services like libp2p, websocket, http. + /// If separate IP/DNS is needed for each service, it should be configured outside of Ephemera. + pub ip: String, + //FIXME: dev only + /// If private key is stored in configuration as plain text, read it from here. + /// Private key is mandatory for a node to be able to function in the network. + /// It is used to signe protocol messages and identify node in the network. + pub private_key: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum MembershipKind { + /// Mandatory minimum membership size is defined by threshold of all peers returned by membership provider. + /// Threshold value is defined the ratio of peers that need to be available. + /// For example, if the threshold is 0.5, then at least 50% of the peers need to be available. + Threshold, + /// Mandatory minimum membership size is all peers who are online. + AnyOnline, + /// Mandatory minimum membership size is all peers returned by membership provider. + AllOnline, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct Libp2pConfiguration { + /// Port to listen on for libp2p internal connections + pub port: u16, + /// Gossipsub topic to gossip Ephemera messages between peers. Ephemera listens messages + /// only from this topic. Invalid topic configuration means that Ephemera is not able to + /// reach messages from other peers. + pub ephemera_msg_topic_name: String, + /// Gossipsub interval to check its mesh health. + pub heartbeat_interval_sec: u64, + /// How often Ephemera checks its membership rendezvous endpoint. It's configurable with second granularity. + /// But in general it's up to the user and depends how rendezvous endpoint is configured. + /// + /// Ephemera uses rendezvous endpoint as authority to tell which nodes are authorized to participate. + /// So it should be configured and implemented in a manner that nodes always have the most up to date and + /// accurate information. + pub members_provider_delay_sec: u64, + /// Defines how the actual membership is decided. See `[ephemera:]` for more details. + pub membership_kind: MembershipKind, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct DatabaseConfiguration { + /// Path to the RocksDb database directory + pub rocksdb_path: String, + /// Path to the SQLite database file + pub sqlite_path: String, + /// If to create database if it does not exist + pub create_if_not_exists: bool, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct WebsocketConfiguration { + /// Port to listen on for WebSocket subscriptions. + pub port: u16, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct HttpConfiguration { + /// Port to listen on for HTTP API requests + pub port: u16, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct BlockManagerConfiguration { + /// By default every node is block producer. + /// + /// But in trusted settings it might be useful configure only one or selected nodes to be block producer. + /// The rest of nodes still participate in the message gossiping and reliable broadcast. + pub producer: bool, + /// Interval in seconds between block creation + /// Blocks are "proposed" at this interval. + pub creation_interval_sec: u64, + /// Ephemera creates blocks at fixed interval and doesn't have any consensus algorithm to make progress + /// if the most recent block fails to go through reliable broadcast. + /// This flag tells what to do when at next interval previous block is not yet delivered. + /// + /// If true, Ephemera will repeat messages from the previous block. Otherwise it will take all messages + /// from mempool as normally. + pub repeat_last_block_messages: bool, +} + +impl BlockManagerConfiguration { + pub fn new(producer: bool, creation_interval_sec: u64, repeat_last_block: bool) -> Self { + BlockManagerConfiguration { + producer, + creation_interval_sec, + repeat_last_block_messages: repeat_last_block, + } + } +} + +#[derive(Debug, Error)] +pub enum Error { + /// This is returned if configuration file exists and user tries to create new one. + #[error("Configuration file exists: '{0}'")] + Exists(String), + /// This is returned if configuration file does not exist and user tries to load it. + #[error("Configuration file does not exists: '{0}'")] + NotFound(String), + #[error("Configuration file does not exist")] + /// This is returned if IoError happens during configuration file read/write. + IoError(#[from] std::io::Error), + /// This is returned if configuration file is invalid. + #[error("Configuration file is invalid: '{0}'")] + InvalidFormat(String), + /// This is returned if configuration file path is invalid. + #[error("Configuration file path is invalid: '{0}'")] + InvalidPath(String), + /// Technical error happens during parsing. + #[error("{}", .0)] + Other(String), +} + +impl From for Error { + fn from(err: ConfigError) -> Self { + match err { + ConfigError::NotFound(err) => Error::NotFound(err), + ConfigError::PathParse(err) => { + Error::InvalidPath(format!("Invalid path to configuration file: {err:?}",)) + } + ConfigError::FileParse { uri, cause } => { + Error::InvalidFormat(format!("Invalid configuration file: {uri:?}: {cause:?}",)) + } + _ => Error::Other(err.to_string()), + } + } +} + +const EPHEMERA_DIR_NAME: &str = "ephemera"; +const EPHEMERA_CONFIG_FILE: &str = "ephemera.toml"; + +type Result = std::result::Result; + +impl Configuration { + /// Tries to read Ephemera node configuration file (`ephemera.toml`) from the given path. + /// + /// # Arguments + /// * `path` - Path to the configuration file. + /// + /// # Errors + /// Returns an error if the configuration file does not exist or is invalid. + /// + /// # Example + /// ```no_run + /// use ephemera::configuration::Configuration; + /// + /// let config = Configuration::try_load("/ephemera/ephemera.toml"); + /// ``` + pub fn try_load>(path: P) -> Result { + let buf = path.into(); + log::debug!("Loading configuration from {:?}", buf); + let config = config::Config::builder() + .add_source(config::File::from(buf)) + .build() + .map_err(Error::from)?; + + config.try_deserialize().map_err(Error::from) + } + + /// Tries to read Ephemera node configuration from default + /// default Ephemera configuration directory(`~.nym/ephemera`). Full path resolves + /// as `~/.nym/ephemera//`. + /// + /// # Arguments + /// * `node_name` - Name of the node. + /// * `file` - Name of the configuration file. + /// + /// # Errors + /// Returns an error if the configuration file does not exist or is invalid. + pub fn try_load_node_from_home_dir(file: &str) -> Result { + let file_path = Self::ephemera_node_dir(None)?.join(file); + Configuration::try_load(file_path) + } + + /// Tries to read Ephemera node configuration from default Ephemera configuration directory(`~.nym/ephemera`). + /// Full path resolves as `~/.nym/ephemera//ephemera.toml`. + /// + /// # Arguments + /// * `node_name` - Name of the node. + /// + /// # Errors + /// Returns an error if the configuration file does not exist or is invalid. + pub fn try_load_from_home_dir() -> Result { + let file_path = Configuration::ephemera_config_file_home(None)?; + let config = config::Config::builder() + .add_source(config::File::from(file_path)) + .build() + .map_err(Error::from)?; + + config.try_deserialize().map_err(Error::from) + } + + /// Tries to write(create) Ephemera node configuration file (`ephemera.toml`) relative to default + /// Ephemera configuration directory(`~.nym/ephemera`). Full path resolves as `~/.nym/ephemera//ephemera.toml`. + /// + /// # Arguments + /// * `id` - Id of the node. + /// + /// # Errors + /// Returns an error if the configuration file already exists. + /// + /// # Panics + /// Panics if the configuration file cannot be written. + pub fn try_write_home_dir(&self, id: Option<&str>) -> Result<()> { + let conf_path = Configuration::ephemera_node_dir(id)?; + if !conf_path.exists() { + std::fs::create_dir_all(conf_path)?; + } + + let file_path = Configuration::ephemera_config_file_home(id)?; + if file_path.exists() { + return Err(Error::Exists(file_path.to_str().unwrap().to_string())); + } + + self.write(&file_path)?; + Ok(()) + } + + /// Tries to write(update) Ephemera node configuration file (`ephemera.toml`) relative to default + /// Ephemera configuration directory(`~.nym/ephemera`). Full path resolves as `~/.nym/ephemera//ephemera.toml`. + /// If the file does not exist, update will be refused. + /// + /// # Arguments + /// * `node_name` - Name of the node. + /// + /// # Errors + /// Returns an error if the configuration file does not exist. + /// + /// # Panics + /// Panics if the configuration file cannot be written. + pub fn try_update_home_dir(&self) -> Result<()> { + let file_path = Configuration::ephemera_config_file_home(None)?; + if !file_path.exists() { + error!( + "Configuration file does not exist {}", + file_path.to_str().unwrap() + ); + return Err(Error::NotFound(file_path.to_str().unwrap().to_string())); + } + self.write(&file_path)?; + Ok(()) + } + + /// Returns node configuration file path relative to default Ephemera configuration directory(`~.nym/ephemera`). + /// Full path resolves as `~/.nym/ephemera//ephemera.toml`. + /// + /// # Arguments + /// * `id` - Id of the node. + /// + /// # Errors + /// Returns an error if the configuration file path cannot be resolved. + pub fn ephemera_config_file_home(id: Option<&str>) -> Result { + Ok(Self::ephemera_node_dir(id)?.join(EPHEMERA_CONFIG_FILE)) + } + + /// Returns default Ephemera configuration directory(`~.nym/ephemera`). + /// + /// # Errors + /// Returns an error if the configuration directory cannot be resolved. + pub fn ephemera_root_dir(id: Option<&str>) -> Result { + let id = id.unwrap_or_default(); + dirs::home_dir() + .map(|home| { + home.join(NYM_DIR) + .join(DEFAULT_NYM_APIS_DIR) + .join(id) + .join(EPHEMERA_DIR_NAME) + }) + .ok_or(Error::Other("Could not find home directory".to_string())) + } + + pub(crate) fn ephemera_node_dir(id: Option<&str>) -> Result { + Self::ephemera_root_dir(id) + } + + fn write(&self, file_path: &PathBuf) -> Result<()> { + //TODO: use toml or config crate, not both + let config = toml::to_string(&self).map_err(|e| { + Error::InvalidFormat(format!("Failed to serialize configuration: {e}",)) + })?; + + let config = format!( + "#This file is generated by cli and automatically overwritten every time when cli is run\n{config}", + ); + + if file_path.exists() { + info!("Updating configuration file: '{}'", file_path.display()); + } else { + info!("Writing configuration to file: '{}'", file_path.display()); + } + + let mut file = std::fs::File::create(file_path)?; + file.write_all(config.as_bytes())?; + + Ok(()) + } +} diff --git a/ephemera/src/core/api_cmd.rs b/ephemera/src/core/api_cmd.rs new file mode 100644 index 0000000000..2d5647adec --- /dev/null +++ b/ephemera/src/core/api_cmd.rs @@ -0,0 +1,390 @@ +use std::num::NonZeroUsize; + +use log::{debug, error, trace}; +use lru::LruCache; +use tokio::sync::oneshot::Sender; + +use crate::api::types::{ApiBlockBroadcastInfo, ApiBroadcastInfo}; +use crate::api::{DhtKV, DhtKey, DhtValue}; +use crate::ephemera_api::ApiEphemeraMessage; +use crate::peer::ToPeerId; +use crate::{ + api::{ + self, + application::Application, + types::{ApiBlock, ApiCertificate, ApiError}, + ToEphemeraApiCmd, + }, + block::{manager::BlockManagerError, types::message}, + crypto::EphemeraKeypair, + ephemera_api::ApiEphemeraConfig, + network::libp2p::ephemera_sender::EphemeraEvent, + Ephemera, +}; + +type DhtPendingQueryReply = Sender, Vec)>, ApiError>>; + +pub(crate) struct ApiCmdProcessor { + pub(crate) dht_query_cache: LruCache, Vec>, +} + +impl ApiCmdProcessor { + pub(crate) fn new() -> Self { + Self { + dht_query_cache: LruCache::new(NonZeroUsize::new(1000).unwrap()), + } + } + + pub(crate) async fn process_api_requests( + ephemera: &mut Ephemera, + cmd: ToEphemeraApiCmd, + ) -> api::Result<()> { + trace!("Processing API request: {:?}", cmd); + match cmd { + ToEphemeraApiCmd::SubmitEphemeraMessage(api_msg, reply) => { + // Ask application to decide if we should accept this message. + Self::submit_message(ephemera, api_msg, reply).await?; + } + + ToEphemeraApiCmd::QueryBlockByHash(block_hash, reply) => { + Self::query_block_by_hash(ephemera, &block_hash, reply).await; + } + + ToEphemeraApiCmd::QueryBlockByHeight(height, reply) => { + Self::query_block_by_height(ephemera, height, reply).await; + } + + ToEphemeraApiCmd::QueryLastBlock(reply) => { + Self::query_last_block(ephemera, reply).await; + } + + ToEphemeraApiCmd::QueryBlockCertificates(block_id, reply) => { + Self::query_block_certificates(ephemera, &block_id, reply).await; + } + + ToEphemeraApiCmd::QueryDht(key, reply) => { + Self::query_dht(ephemera, key, reply).await; + } + + ToEphemeraApiCmd::StoreInDht(key, value, reply) => { + Self::store_in_dht(ephemera, key, value, reply).await; + } + + ToEphemeraApiCmd::QueryEphemeraConfig(reply) => { + Self::ephemera_config(ephemera, reply); + } + + ToEphemeraApiCmd::QueryBroadcastGroup(reply) => { + Self::broadcast_group(ephemera, reply); + } + ToEphemeraApiCmd::QueryBlockBroadcastInfo(hash, reply) => { + Self::query_block_broadcast_info(ephemera, &hash, reply).await; + } + ToEphemeraApiCmd::VerifyMessageInBlock(block_hash, message_hash, index, reply) => { + Self::verify_message_in_block(ephemera, block_hash, message_hash, index, reply) + .await; + } + } + Ok(()) + } + + fn broadcast_group( + ephemera: &mut Ephemera, + reply: Sender>, + ) { + let group_peers = ephemera.broadcast_group.current(); + + let bc = ApiBroadcastInfo::new(group_peers.clone(), ephemera.node_info.peer_id); + reply + .send(Ok(bc)) + .expect("Error sending BroadcastGroup response to api"); + } + + fn ephemera_config( + ephemera: &mut Ephemera, + reply: Sender>, + ) { + let node_info = ephemera.node_info.clone(); + let api_config = ApiEphemeraConfig { + protocol_address: node_info.protocol_address(), + api_address: node_info.api_address_http(), + websocket_address: node_info.ws_address_ws(), + public_key: node_info.keypair.public_key().to_string(), + block_producer: node_info.initial_config.block_manager.producer, + block_creation_interval_sec: node_info + .initial_config + .block_manager + .creation_interval_sec, + }; + reply + .send(Ok(api_config)) + .expect("Error sending EphemeraConfig response to api"); + } + + async fn store_in_dht( + ephemera: &mut Ephemera, + key: DhtKey, + value: DhtValue, + reply: Sender>, + ) { + let response = match ephemera + .to_network + .send_ephemera_event(EphemeraEvent::StoreInDht { key, value }) + .await + { + Ok(_) => Ok(()), + Err(err) => { + error!("Error sending StoreInDht to network: {:?}", err); + Err(ApiError::Internal("Failed to store in DHT".to_string())) + } + }; + reply + .send(response) + .expect("Error sending StoreInDht response to api"); + } + + async fn query_dht( + ephemera: &mut Ephemera, + key: DhtKey, + reply: Sender>>, + ) { + match ephemera + .to_network + .send_ephemera_event(EphemeraEvent::QueryDht { key: key.clone() }) + .await + { + Ok(_) => { + //Save the reply channel in a map and send the reply when we get the response from the network + ephemera + .api_cmd_processor + .dht_query_cache + .get_or_insert_mut(key, Vec::new) + .push(reply); + } + Err(err) => { + error!("Error sending QueryDht to network: {:?}", err); + reply + .send(Err(ApiError::Internal("Failed to query DHT".to_string()))) + .expect("Error sending QueryDht response to api"); + } + }; + } + + async fn query_block_certificates( + ephemera: &mut Ephemera, + block_id: &str, + reply: Sender>>>, + ) { + let response = match ephemera + .storage + .lock() + .await + .get_block_certificates(block_id) + { + Ok(signatures) => { + let certificates = signatures.map(|s| { + s.into_iter() + .map(Into::into) + .collect::>() + }); + Ok(certificates) + } + Err(err) => { + error!("Error querying block certificates: {:?}", err); + Err(ApiError::Internal( + "Failed to query block certificates".to_string(), + )) + } + }; + reply + .send(response) + .expect("Error sending QueryBlockSignatures response to api"); + } + + async fn query_last_block( + ephemera: &mut Ephemera, + reply: Sender>, + ) { + let response = match ephemera.storage.lock().await.get_last_block() { + Ok(Some(block)) => Ok(block.into()), + Ok(None) => Err(ApiError::Internal( + "No blocks found, this is a bug!".to_string(), + )), + Err(err) => { + error!("Error querying last block: {:?}", err); + Err(ApiError::Internal("Failed to query last block".to_string())) + } + }; + reply + .send(response) + .expect("Error sending QueryLastBlock response to api"); + } + + async fn query_block_by_height( + ephemera: &mut Ephemera, + height: u64, + reply: Sender>>, + ) { + let response = match ephemera.storage.lock().await.get_block_by_height(height) { + Ok(Some(block)) => { + let api_block: ApiBlock = block.into(); + Ok(api_block.into()) + } + Ok(None) => Ok(None), + Err(err) => { + error!("Error querying block by height: {:?}", err); + Err(ApiError::Internal( + "Failed to query block by height".to_string(), + )) + } + }; + reply + .send(response) + .expect("Error sending QueryBlockByHeight response to api"); + } + + async fn query_block_by_hash( + ephemera: &mut Ephemera, + block_hash: &str, + reply: Sender>>, + ) { + let response = match ephemera.storage.lock().await.get_block_by_hash(block_hash) { + Ok(Some(block)) => { + let api_block: ApiBlock = block.into(); + Ok(api_block.into()) + } + Ok(None) => Ok(None), + Err(err) => { + error!("Error querying block by id: {:?}", err); + Err(ApiError::Internal( + "Failed to query block by id".to_string(), + )) + } + }; + reply + .send(response) + .expect("Error sending QueryBlockByHash response to api"); + } + + async fn submit_message( + ephemera: &mut Ephemera, + api_msg: Box, + reply: Sender>, + ) -> api::Result<()> { + let response = match ephemera.application.check_tx(*api_msg.clone()) { + Ok(true) => { + trace!("Application accepted ephemera message: {:?}", api_msg); + + // Send to BlockManager to verify it and put into memory pool + let ephemera_msg: message::EphemeraMessage = (*api_msg).into(); + match ephemera.block_manager.on_new_message(ephemera_msg.clone()) { + Ok(_) => { + //Gossip to network for other nodes to receive + match ephemera + .to_network + .send_ephemera_event(EphemeraEvent::EphemeraMessage( + ephemera_msg.into(), + )) + .await + { + Ok(_) => Ok(()), + Err(err) => { + error!("Error sending EphemeraMessage to network: {:?}", err); + Err(ApiError::Internal("Failed to submit message".to_string())) + } + } + } + Err(err) => match err { + BlockManagerError::DuplicateMessage(_) => Err(ApiError::DuplicateMessage), + BlockManagerError::BlockManager(err) => { + error!("Error submitting message to block manager: {:?}", err); + Err(ApiError::Internal("Failed to submit message".to_string())) + } + }, + } + } + Ok(false) => { + debug!("Application rejected ephemera message: {:?}", api_msg); + Err(ApiError::ApplicationRejectedMessage) + } + Err(err) => { + error!("Application rejected ephemera message: {:?}", err); + Err(ApiError::Application(err)) + } + }; + reply + .send(response) + .expect("Error sending SubmitEphemeraMessage response to api"); + Ok(()) + } + + async fn query_block_broadcast_info( + ephemera: &mut Ephemera, + block_id: &str, + reply: Sender>>, + ) { + let response = match ephemera + .storage + .lock() + .await + .get_block_broadcast_group(block_id) + { + Ok(Some(peers)) => { + let local_peer = ephemera.node_info.keypair.peer_id(); + Ok(Some(ApiBlockBroadcastInfo::new(local_peer, peers))) + } + Ok(None) => Ok(None), + Err(err) => { + error!("Error querying block broadcast info: {:?}", err); + Err(ApiError::Internal( + "Failed to query block broadcast info".to_string(), + )) + } + }; + reply + .send(response) + .expect("Error sending QueryBlockBroadcastGroup response to api"); + } + async fn verify_message_in_block( + ephemera: &mut Ephemera, + block_hash: String, + message_hash: String, + index: usize, + reply: Sender>, + ) { + let message_hash_hash = message_hash.parse(); + if message_hash_hash.is_err() { + reply + .send(Err(ApiError::InvalidHash( + "Failed to parse message hash".to_string(), + ))) + .expect("Error sending VerifyMessageInBlock response to api"); + return; + } + + let message_hash_hash = message_hash_hash.unwrap(); + + let storage = ephemera.storage.lock().await; + match storage.get_block_merkle_tree(&block_hash) { + Ok(Some(tree)) => { + let result = tree.verify_leaf_at_index(message_hash_hash, index); + reply + .send(Ok(result)) + .expect("Error sending VerifyMessageInBlock response to api"); + } + Ok(None) => { + reply + .send(Ok(false)) + .expect("Error sending VerifyMessageInBlock response to api"); + } + Err(err) => { + error!("Error querying block merkle tree: {:?}", err); + reply + .send(Err(ApiError::Internal( + "Failed to verify message".to_string(), + ))) + .expect("Error sending VerifyMessageInBlock response to api"); + } + } + } +} diff --git a/ephemera/src/core/builder.rs b/ephemera/src/core/builder.rs new file mode 100644 index 0000000000..58d4b7cd08 --- /dev/null +++ b/ephemera/src/core/builder.rs @@ -0,0 +1,413 @@ +use std::fmt::Display; +use std::future::Future; +use std::sync::Arc; + +use futures_util::future::BoxFuture; +use futures_util::FutureExt; +use log::{debug, error, info}; +use tokio::sync::Mutex; + +use crate::core::shutdown::Shutdown; +#[cfg(feature = "rocksdb_storage")] +use crate::storage::rocksdb::RocksDbStorage; +#[cfg(feature = "sqlite_storage")] +use crate::storage::sqlite::SqliteStorage; +use crate::{ + api::{application::Application, http, ApiListener, CommandExecutor}, + block::{builder::BlockManagerBuilder, manager::BlockManager}, + broadcast::bracha::broadcast::Broadcaster, + broadcast::group::BroadcastGroup, + config::Configuration, + core::{ + api_cmd::ApiCmdProcessor, + shutdown::{Handle, ShutdownManager}, + }, + crypto::Keypair, + membership, + membership::PeerInfo, + network::libp2p::{ + ephemera_sender::EphemeraToNetworkSender, network_sender::NetCommunicationReceiver, + swarm_network::SwarmNetwork, + }, + peer::{PeerId, ToPeerId}, + storage::EphemeraDatabase, + utilities::crypto::key_manager::KeyManager, + websocket::ws_manager::{WsManager, WsMessageBroadcaster}, + Ephemera, +}; + +#[derive(Clone)] +pub(crate) struct NodeInfo { + pub(crate) ip: String, + pub(crate) protocol_port: u16, + pub(crate) http_port: u16, + pub(crate) ws_port: u16, + pub(crate) peer_id: PeerId, + pub(crate) keypair: Arc, + pub(crate) initial_config: Configuration, +} + +impl NodeInfo { + pub(crate) fn new(config: Configuration) -> anyhow::Result { + let keypair = KeyManager::read_keypair_from_str(&config.node.private_key)?; + let info = Self { + ip: config.node.ip.clone(), + protocol_port: config.libp2p.port, + http_port: config.http.port, + ws_port: config.websocket.port, + peer_id: keypair.peer_id(), + keypair, + initial_config: config, + }; + Ok(info) + } + + pub(crate) fn protocol_address(&self) -> String { + format!("/ip4/{}/tcp/{}", self.ip, self.protocol_port) + } + + pub(crate) fn api_address_http(&self) -> String { + format!("http://{}:{}", self.ip, self.http_port) + } + + pub(crate) fn ws_address_ws(&self) -> String { + format!("ws://{}:{}", self.ip, self.ws_port) + } + + pub(crate) fn ws_address_ip_port(&self) -> String { + format!("{}:{}", self.ip, self.ws_port) + } +} + +impl Display for NodeInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "NodeInfo {{ ip: {}, protocol_port: {}, http_port: {}, ws_port: {}, peer_id: {}, keypair: {} }}", + self.ip, + self.protocol_port, + self.http_port, + self.ws_port, + self.peer_id, + self.keypair + ) + } +} + +#[derive(Clone)] +pub struct EphemeraHandle { + /// Ephemera API + pub api: CommandExecutor, + /// Allows to send shutdown signal to the node + pub shutdown: Handle, +} + +pub struct EphemeraStarterInit { + config: Configuration, + node_info: NodeInfo, + broadcaster: Broadcaster, + api_listener: ApiListener, + api: CommandExecutor, +} + +impl EphemeraStarterInit { + /// Initialize Ephemera builder + /// + /// # Arguments + /// * `config` - [Configuration] + /// + /// # Returns + /// [`EphemeraStarterInit`] + /// + /// # Errors + /// * If the node configuration is invalid + pub fn new(config: Configuration) -> anyhow::Result { + let instance_info = NodeInfo::new(config.clone())?; + let broadcaster = Broadcaster::new(instance_info.peer_id); + let (api, api_listener) = CommandExecutor::new(); + + let builder = EphemeraStarterInit { + config, + node_info: instance_info, + broadcaster, + api_listener, + api, + }; + Ok(builder) + } + + pub fn with_application( + self, + application: A, + ) -> EphemeraStarterWithApplication { + EphemeraStarterWithApplication { + init: self, + application, + } + } +} + +#[derive(Default)] +struct ServiceInfo { + ws_message_broadcast: Option, + from_network: Option, + to_network: Option, +} + +pub struct EphemeraStarterWithApplication { + init: EphemeraStarterInit, + application: A, +} + +impl EphemeraStarterWithApplication { + /// Initialize Ephemera with the given application. + /// It also tries to open the database connection. + /// + /// # Arguments + /// * `application` - [Application] to be used + /// + /// # Returns + /// [`EphemeraStarterWithApplication`] + /// + /// # Errors + /// * If the node configuration is invalid or the database connection cannot be opened + pub fn with_members_provider< + P: Future>> + Send + Unpin + 'static, + >( + mut self, + provider: P, + ) -> anyhow::Result> { + cfg_if::cfg_if! { + if #[cfg(feature = "sqlite_storage")] { + let mut storage = self.connect_sqlite()?; + debug!("Connected to sqlite database") + } else if #[cfg(feature = "rocksdb_storage")] { + let mut storage = self.connect_rocksdb()?; + debug!("Connected to rocksdb database") + } else { + compile_error!("Must enable either sqlite or rocksdb feature"); + } + } + + let block_manager = self.init_block_manager(&mut storage)?; + + let (mut shutdown_manager, shutdown_handle) = ShutdownManager::init(); + + let mut service_data = ServiceInfo::default(); + let services = self.init_services(&mut service_data, &mut shutdown_manager, provider)?; + + Ok(EphemeraStarterWithProvider { + with_application: self, + block_manager: Some(block_manager), + service_data, + services, + storage: Some(Box::new(storage)), + shutdown_manager: Some(shutdown_manager), + shutdown_handle: Some(shutdown_handle), + }) + } + + //allocate database connection + #[cfg(feature = "rocksdb_storage")] + fn connect_rocksdb(&self) -> anyhow::Result { + info!("Opening database..."); + RocksDbStorage::open(&self.init.config.storage) + .map_err(|e| anyhow::anyhow!("Failed to open database: {}", e)) + } + + #[cfg(feature = "sqlite_storage")] + fn connect_sqlite(&mut self) -> anyhow::Result { + info!("Opening database..."); + SqliteStorage::open(self.init.config.storage.clone()) + .map_err(|e| anyhow::anyhow!("Failed to open database: {}", e)) + } + + fn init_block_manager( + &mut self, + db: &mut D, + ) -> anyhow::Result { + let block_manager_configuration = self.init.config.block_manager.clone(); + let keypair = self.init.node_info.keypair.clone(); + let builder = BlockManagerBuilder::new(block_manager_configuration, keypair); + builder.build(db) + } + + fn init_services< + P: Future>> + Send + Unpin + 'static, + >( + &mut self, + service_data: &mut ServiceInfo, + shutdown_manager: &mut ShutdownManager, + provider: P, + ) -> anyhow::Result>>> { + let services = vec![ + self.init_libp2p(service_data, shutdown_manager.subscribe(), provider)?, + self.init_http(shutdown_manager.subscribe())?, + self.init_websocket(service_data, shutdown_manager.subscribe()), + ]; + Ok(services) + } + + fn init_websocket( + &mut self, + service_data: &mut ServiceInfo, + mut shutdown: Shutdown, + ) -> BoxFuture<'static, anyhow::Result<()>> { + let (mut websocket, ws_message_broadcast) = + WsManager::new(self.init.node_info.ws_address_ip_port()); + + service_data.ws_message_broadcast = Some(ws_message_broadcast); + + async move { + websocket.listen().await?; + + tokio::select! { + _ = shutdown.shutdown_signal_rcv.recv() => { + info!("Shutting down websocket manager"); + } + ws_stopped = websocket.run() => { + match ws_stopped { + Ok(_) => info!("Websocket stopped unexpectedly"), + Err(e) => error!("Websocket stopped with error: {}", e), + } + } + } + info!("Websocket task finished"); + Ok(()) + } + .boxed() + } + + fn init_http( + &mut self, + mut shutdown: Shutdown, + ) -> anyhow::Result>> { + let http = http::init(&self.init.node_info, self.init.api.clone())?; + + let fut = async move { + let server_handle = http.handle(); + tokio::select! { + _ = shutdown.shutdown_signal_rcv.recv() => { + info!("Shutting down http server"); + server_handle.stop(true).await; + } + http_stopped = http => { + match http_stopped { + Ok(_) => info!("Http server stopped unexpectedly"), + Err(e) => error!("Http server stopped with error: {}", e), + } + } + } + info!("Http task finished"); + Ok(()) + } + .boxed(); + Ok(fut) + } + + fn init_libp2p< + P: Future>> + Send + Unpin + 'static, + >( + &mut self, + service_data: &mut ServiceInfo, + mut shutdown: Shutdown, + provider: P, + ) -> anyhow::Result>> { + info!("Starting network...",); + + let (mut network, from_network, to_network) = + SwarmNetwork::new(self.init.node_info.clone(), provider)?; + + service_data.from_network = Some(from_network); + service_data.to_network = Some(to_network); + + let libp2p = async move { + network.listen()?; + + tokio::select! { + _ = shutdown.shutdown_signal_rcv.recv() => { + info!("Shutting down network"); + } + nw_stopped = network.start() => { + match nw_stopped { + Ok(_) => info!("Network stopped unexpectedly"), + Err(e) => error!("Network stopped with error: {e}",), + } + } + } + info!("Network task finished"); + Ok(()) + } + .boxed(); + Ok(libp2p) + } +} + +pub struct EphemeraStarterWithProvider +where + A: Application + 'static, +{ + with_application: EphemeraStarterWithApplication, + block_manager: Option, + service_data: ServiceInfo, + storage: Option>, + services: Vec>>, + shutdown_manager: Option, + shutdown_handle: Option, +} + +impl EphemeraStarterWithProvider +where + A: Application + 'static, +{ + pub fn build(self) -> Ephemera { + self.ephemera() + } + + fn ephemera(mut self) -> Ephemera { + let ephemera_handle = EphemeraHandle { + api: self.with_application.init.api, + shutdown: self.shutdown_handle.take().unwrap(), + }; + + let node_info = self.with_application.init.node_info; + let application = self.with_application.application; + let block_manager = self.block_manager.expect("Block manager not initialized"); + let broadcaster = self.with_application.init.broadcaster; + let from_network = self + .service_data + .from_network + .expect("From network not initialized"); + let to_network = self + .service_data + .to_network + .expect("To network not initialized"); + let storage = self.storage.expect("Storage not initialized"); + let ws_message_broadcast = self + .service_data + .ws_message_broadcast + .expect("WS message broadcast not initialized"); + let api_listener = self.with_application.init.api_listener; + let shutdown_manager = self + .shutdown_manager + .expect("Shutdown manager not initialized"); + let services = self.services; + + Ephemera { + node_info, + block_manager, + broadcaster, + from_network, + to_network, + broadcast_group: BroadcastGroup::new(), + storage: Arc::new(Mutex::new(storage)), + ws_message_broadcast, + api_listener, + api_cmd_processor: ApiCmdProcessor::new(), + application: application.into(), + ephemera_handle, + shutdown_manager, + services, + } + } +} diff --git a/ephemera/src/core/ephemera.rs b/ephemera/src/core/ephemera.rs new file mode 100644 index 0000000000..9c970b9962 --- /dev/null +++ b/ephemera/src/core/ephemera.rs @@ -0,0 +1,421 @@ +use std::sync::Arc; + +use anyhow::anyhow; +use futures_util::future::BoxFuture; +use futures_util::StreamExt; +use log::{debug, error, info, trace}; +use nym_task::TaskClient; +use thiserror::Error; +use tokio::sync::Mutex; + +use crate::broadcast::bracha::quorum::Quorum; +use crate::storage::DatabaseError; +use crate::{ + api::{application::Application, application::CheckBlockResult, ApiListener}, + block::{manager::BlockManager, types::block::Block}, + broadcast::{ + bracha::broadcast::BroadcastResponse, bracha::broadcast::Broadcaster, + group::BroadcastGroup, RbMsg, + }, + core::{ + api_cmd::ApiCmdProcessor, + builder::{EphemeraHandle, NodeInfo}, + shutdown::ShutdownManager, + }, + network::{ + libp2p::network_sender::GroupChangeEvent, + libp2p::{ + ephemera_sender::{EphemeraEvent, EphemeraToNetworkSender}, + network_sender::{NetCommunicationReceiver, NetworkEvent}, + }, + }, + storage::EphemeraDatabase, + utilities::crypto::Certificate, + websocket::ws_manager::WsMessageBroadcaster, +}; + +#[derive(Error, Debug)] +enum EphemeraCoreError { + #[error("DatabaseFailure: {0}")] + DatabaseFailure(DatabaseError), + //Just a placeholder now + #[error("EphemeraCore: {0}")] + EphemeraCore(#[from] anyhow::Error), +} + +type Result = std::result::Result; + +pub struct Ephemera { + /// Node info + pub(crate) node_info: NodeInfo, + + /// Block manager responsibility includes: + /// - Block production and signing + /// - Block verification for externally received blocks + /// - Message verification sent by clients and gossiped other nodes + pub(crate) block_manager: BlockManager, + + /// Broadcaster is making sure that blocks are deterministically agreed by all nodes. + pub(crate) broadcaster: Broadcaster, + + /// A component which receives messages from network. + pub(crate) from_network: NetCommunicationReceiver, + + /// A component which sends messages to network. + pub(crate) to_network: EphemeraToNetworkSender, + + /// A component which keeps track of broadcast group over time. + pub(crate) broadcast_group: BroadcastGroup, + + /// A component which has mutable access to database. + pub(crate) storage: Arc>>, + + /// A component which broadcasts messages to websocket clients. + pub(crate) ws_message_broadcast: WsMessageBroadcaster, + + /// A component which listens API requests. + pub(crate) api_listener: ApiListener, + + /// A component which processes API requests. + pub(crate) api_cmd_processor: ApiCmdProcessor, + + /// An implementation of Application trait. Provides callbacks to broadcast. + pub(crate) application: Arc, + + ///Interface to external Rust code + pub(crate) ephemera_handle: EphemeraHandle, + + /// A component which handles shutdown. + pub(crate) shutdown_manager: ShutdownManager, + + /// A list of services which are running in background. + pub(crate) services: Vec>>, +} + +impl Ephemera { + ///Provides external api for Rust code to interact with ephemera node. + #[must_use] + pub fn handle(&self) -> EphemeraHandle { + self.ephemera_handle.clone() + } + + /// Main loop of ephemera node. + /// 1. Block manager generates new blocks or receives blocks from network. + /// 2. Run reliable broadcast. + /// 3. Process reliable broadcast result. + /// 4. Process http api request + /// 5. Process rust api request + /// 6. Publish(gossip) messages to network + /// 7. Publish blocks to network + /// 8. Broadcast messages to websocket clients + pub async fn run(mut self, mut shutdown: TaskClient) { + info!("Starting ephemera services"); + for service in self.services.drain(..) { + let handle = tokio::spawn(service); + self.shutdown_manager.add_handle(handle); + } + + info!("Starting ephemera main loop"); + + while !shutdown.is_shutdown() { + tokio::select! { + biased; + _ = shutdown.recv() => { + trace!("UpdateHandler: Received shutdown"); + self.shutdown_manager.stop().await; + break; + } + + // GENERATING NEW BLOCKS + Some((new_block, certificate)) = self.block_manager.next() => { + if let Err(err) = self.process_new_local_block(new_block, certificate).await{ + error!("Error processing new block: {:?}", err); + } + } + + // PROCESSING NETWORK EVENTS + Some(net_event) = self.from_network.net_event_rcv.recv() => { + if let Err(err) = self.process_network_event(net_event).await{ + error!("Error processing network event: {:?}", err); + if let EphemeraCoreError::DatabaseFailure(_) = err { + info!("Database failure. Shutting down ephemera"); + self.shutdown_manager.stop().await; + break; + } + } + } + + //PROCESSING EXTERNAL API REQUESTS + api = self.api_listener.messages_rcv.recv() => { + match api { + Some(api_msg) => { + if let Err(err) = ApiCmdProcessor::process_api_requests(&mut self, api_msg).await{ + error!("Error processing api request: {:?}", err); + } + } + None => { + error!("Error: Api listener channel closed"); + //TODO: handle shutdown + } + } + } + } + } + info!("Ephemera main loop finished"); + } + + async fn process_network_event(&mut self, net_event: NetworkEvent) -> Result<()> { + trace!("New network event: {:?}", net_event); + + match net_event { + NetworkEvent::EphemeraMessage(em) => { + let api_msg = (*em.clone()).into(); + trace!("New ephemera message from network: {:?}", api_msg); + + //Only Application checks if messages are valid(possibly message origin). + //For messages we don't check if sender belongs to group. + + // Ask application to decide if we should accept this message. + match self.application.check_tx(api_msg) { + Ok(true) => { + trace!("Application accepted message: {:?}", em); + + // Send to BlockManager to store in mempool. + if let Err(err) = self.block_manager.on_new_message(*em) { + error!("Error sending signed message to block manager: {:?}", err); + } + } + Ok(false) => { + trace!("Application rejected message: {:?}", em); + } + Err(err) => { + error!("Application check_tx failed: {:?}", err); + } + } + } + NetworkEvent::BroadcastMessage(rb_msg) => { + self.process_block_from_network(*rb_msg).await?; + } + NetworkEvent::GroupUpdate(event) => { + self.process_group_update(event); + } + NetworkEvent::QueryDhtResponse { key, value } => { + match self.api_cmd_processor.dht_query_cache.pop(&key) { + Some(replies) => { + for reply in replies { + let response = Ok(Some((key.clone(), value.clone()))); + if let Err(err) = reply.send(response) { + error!("Error sending dht query response: {:?}", err); + } + } + } + None => { + trace!( + "No dht query cache found for key: {:?}", + String::from_utf8(key) + ); + } + } + } + } + Ok(()) + } + + fn process_group_update(&mut self, event: GroupChangeEvent) { + match event { + GroupChangeEvent::PeersUpdated(peers) => { + info!("New group: {:?}", peers); + info!("{}", Quorum::cluster_size_info(peers.len())); + self.broadcaster.group_updated(peers.len()); + self.broadcast_group.add_snapshot(peers); + self.block_manager.start(); + } + GroupChangeEvent::LocalPeerRemoved(peers) | GroupChangeEvent::NotEnoughPeers(peers) => { + info!("New group: {:?}", peers); + info!("Group update: Local peer removed or not enough peers"); + self.broadcaster.group_updated(0); + self.broadcast_group.add_snapshot(peers); + self.block_manager.stop(); + } + } + } + + async fn process_new_local_block( + &mut self, + new_block: Block, + certificate: Certificate, + ) -> Result<()> { + debug!("New block from block manager: {:?}", new_block.get_hash()); + + let hash = new_block.header.hash; + let block_creator = &self.node_info.peer_id; + let sender = &self.node_info.peer_id; + + // Check if block matches group membership. + if !self + .broadcast_group + .check_membership(hash, block_creator, sender) + { + debug!("Membership check rejected block: {:?}", new_block); + return Ok(()); + } + + //Ephemera ABCI + match self.application.check_block(&new_block.clone().into()) { + Ok(response) => match response { + CheckBlockResult::Accept => { + debug!("Application accepted new block: {hash:?}",); + } + CheckBlockResult::Reject => { + debug!("Application rejected block: {hash:?}",); + return Ok(()); + } + CheckBlockResult::RejectAndRemoveMessages(messages_to_remove) => { + debug!("Application rejected block: {:?}", messages_to_remove); + self.block_manager + .on_application_rejected_block(messages_to_remove) + .map_err(|err| { + anyhow!("Error rejecting block from block manager: {:?}", err) + })?; + } + }, + Err(err) => { + return Err(anyhow!("Application check_block failed: {:?}", err).into()); + } + } + + //Block manager generated new block that nobody hasn't seen yet. + //We start reliable broadcaster protocol to broadcaster it to other nodes. + match self.broadcaster.new_broadcast(new_block) { + Ok(resp) => { + if let BroadcastResponse::Broadcast(msg) = resp { + trace!("Broadcasting new block: {:?}", msg); + + let rb_msg = RbMsg::new(msg, certificate); + self.to_network + .send_ephemera_event(EphemeraEvent::ProtocolMessage(rb_msg.into())) + .await?; + } + } + Err(err) => { + error!("Error starting new broadcast: {:?}", err); + } + } + Ok(()) + } + + //TODO: should we accept more blocks(certificates) from peers after its committed? + async fn process_block_from_network(&mut self, msg: RbMsg) -> Result<()> { + let msg_id = msg.id.clone(); + let block = msg.block(); + let block_creator = &block.header.creator; + let sender = &msg.original_sender; + let hash = block.header.hash; + let certificate = msg.certificate.clone(); + + trace!("New broadcast message from network: {:?}", msg); + + if !self + .broadcast_group + .check_membership(hash, block_creator, sender) + { + return Err(anyhow!("Block doesn't match broacast group").into()); + } + + if let Err(err) = self.block_manager.on_block(sender, block, &certificate) { + return Err(anyhow!("Error sending block to block manager: {:?}", err).into()); + } + let raw_mgs = msg.into(); + match self.broadcaster.handle(&raw_mgs) { + Ok(resp) => { + match resp { + BroadcastResponse::Broadcast(msg) => { + trace!("Broadcasting block to network: {:?}", msg); + + match self.block_manager.sign_block(&msg.block()) { + Ok(certificate) => { + let rb_msg = RbMsg::new(msg, certificate); + self.to_network + .send_ephemera_event(EphemeraEvent::ProtocolMessage( + rb_msg.into(), + )) + .await?; + } + Err(err) => { + return Err(anyhow!("Error signing block: {:?}", err).into()); + } + } + } + BroadcastResponse::Deliver(hash) => { + trace!("Block broadcast complete: {hash:?}",); + let block = self.block_manager.get_block_by_hash(&hash); + match block { + Some(block) => { + if block.header.creator == self.node_info.peer_id { + info!("Block committed, ready to deliver...: {hash:?}",); + + //BlockManager + self.block_manager.on_block_committed(&block).map_err(|e| { + anyhow!( + "Error: BlockManager failed to process block: {e:?}", + ) + })?; + + //Save to database + let certificates = self + .block_manager + .get_block_certificates(&block.header.hash) + .ok_or(anyhow!( + "Error: Block certificates not found for block: {hash:?}" + ))?; + let members = self + .broadcast_group + .get_group_by_block_hash(block.get_hash()) + .ok_or(anyhow!( + "Error: Group not found for block: {hash:?}" + ))?; + + if let Err(e) = self.storage.lock().await.store_block( + &block, + certificates.clone(), + members.clone(), + ) { + return Err(EphemeraCoreError::DatabaseFailure(e)); + } + + // It is open question how much Application `deliver_block` failure should affect + // continuing with next block. + //Application(ABCI) + self.application + .deliver_block(Into::into(block.clone())) + .map_err(|e| { + anyhow!( + "Error: Deliver block to Application failed: {e:?}", + ) + })?; + + //WS + self.ws_message_broadcast.send_block(&block)?; + info!("Block broadcast complete: {hash:?}",); + } + } + None => { + return Err( + anyhow!("Error: Block not found in block manager").into() + ); + } + } + } + BroadcastResponse::Drop(hash) => { + trace!("Ignoring broadcast message {:?}[block {:?}]", msg_id, hash); + return Ok(()); + } + } + } + Err(err) => { + error!("Error handling broadcast message: {:?}", err); + } + } + Ok(()) + } +} diff --git a/ephemera/src/core/mod.rs b/ephemera/src/core/mod.rs new file mode 100644 index 0000000000..f6bd82aa49 --- /dev/null +++ b/ephemera/src/core/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod api_cmd; +pub(crate) mod builder; +pub(crate) mod ephemera; +pub(crate) mod shutdown; diff --git a/ephemera/src/core/shutdown.rs b/ephemera/src/core/shutdown.rs new file mode 100644 index 0000000000..ee2e9c3c70 --- /dev/null +++ b/ephemera/src/core/shutdown.rs @@ -0,0 +1,77 @@ +use log::info; + +use tokio::sync::broadcast; +use tokio::task::JoinHandle; + +pub(crate) struct ShutdownManager { + pub(crate) shutdown_tx: broadcast::Sender<()>, + pub(crate) _shutdown_rcv: broadcast::Receiver<()>, + handles: Vec>>, +} + +pub(crate) struct Shutdown { + pub(crate) shutdown_signal_rcv: broadcast::Receiver<()>, +} + +#[derive(Clone)] +pub struct Handle { + pub(crate) shutdown_started: bool, +} + +impl Handle { + /// Shutdown the node. + /// This will send a shutdown signal to all tasks and wait for them to finish. + /// + /// # Errors + /// This will return an error if shutdown signal can't be sent. + /// + /// # Panics + /// This will panic if shutdown signal can't be sent. + pub fn shutdown(&mut self) { + self.shutdown_started = true; + } +} + +impl ShutdownManager { + pub(crate) fn init() -> (ShutdownManager, Handle) { + let (shutdown_tx, shutdown_rcv) = broadcast::channel(1); + let shutdown_handle = Handle { + shutdown_started: false, + }; + let manager = Self { + shutdown_tx, + _shutdown_rcv: shutdown_rcv, + handles: vec![], + }; + (manager, shutdown_handle) + } + + pub async fn stop(self) { + info!("Starting Ephemera shutdown"); + self.shutdown_tx.send(()).unwrap(); + info!("Waiting for tasks to finish"); + for (i, handle) in self + .handles + .into_iter() + .enumerate() + .map(|(i, h)| (i + 1, h)) + { + match handle.await.unwrap() { + Ok(_) => info!("Task {i} finished successfully"), + Err(e) => info!("Task {i} finished with error: {e}",), + } + } + info!("All tasks finished"); + } + + pub(crate) fn subscribe(&self) -> Shutdown { + let shutdown = self.shutdown_tx.subscribe(); + Shutdown { + shutdown_signal_rcv: shutdown, + } + } + + pub(crate) fn add_handle(&mut self, handle: JoinHandle>) { + self.handles.push(handle); + } +} diff --git a/ephemera/src/lib.rs b/ephemera/src/lib.rs new file mode 100644 index 0000000000..db506a96b3 --- /dev/null +++ b/ephemera/src/lib.rs @@ -0,0 +1,118 @@ +//! # Ephemera Node + +//! An Ephemera node does reliable broadcast of inbound messages to all other Ephemera nodes in the cluster. +//! +//! Each node has a unique ID, and each message is signed by the node that first received it. +//! Messages are then re-broadcast and re-signed by all other nodes in the cluster. +//! +//! At the end of the process, each message is signed by every node in the cluster, and each node has also +//! signed all messages that were broadcast by other nodes. This means that nodes are unable to repudiate messages +//! once they are seen and signed, so there is a strong guarantee of message integrity within the cluster. +//! +//! # Why would I want this? +//! +//! Let's say you have blockchain system that needs to ship large amounts of information around, but the information +//! is relatively short-lived. You could use a blockchain to store the information, but that would be expensive, +//! slow, and wasteful. Instead, you could use Ephemera to broadcast the information to all nodes in the cluster, +//! and then store only a cryptographic commitment in the blockchain's data store. +//! +//! Ephemera nodes then keep messages around for inspection in a data availability layer (accessible over HTTP) +//! so that interested parties can verify correctness. Ephemeral information can then be automatically discarded +//! once it's no longer useful. +//! +//! This gives very similar guarantees to a blockchain, but without incurring the permanent storage costs. +//! +//! Note that it *requires* a blockchain to be present. + +//'Denying' everything and allowing exceptions seems better than other way around. +#![deny(clippy::pedantic)] + +// PUBLIC MODULES + +pub use crate::core::builder::{ + EphemeraStarterInit, EphemeraStarterWithApplication, EphemeraStarterWithProvider, +}; +pub use crate::core::ephemera::Ephemera; +pub use crate::core::shutdown::Handle as ShutdownHandle; + +/// Ephemera API. Public interface and types. +pub mod ephemera_api { + pub use crate::api::{ + application::{ + Application, CheckBlockResult, Dummy, Error as ApplicationError, RemoveMessages, + Result as ApplicationResult, + }, + http::client::{Client, Error as HttpClientError, Result as HttpClientResult}, + types::{ + ApiBlock, ApiBlockBroadcastInfo, ApiBroadcastInfo, ApiCertificate, ApiDhtQueryRequest, + ApiDhtQueryResponse, ApiDhtStoreRequest, ApiEphemeraConfig, ApiEphemeraMessage, + ApiError, ApiHealth, ApiVerifyMessageInBlock, RawApiEphemeraMessage, + }, + CommandExecutor, + }; +} + +/// Peer identification +#[allow(clippy::module_name_repetitions)] +pub mod peer { + pub use super::network::{PeerId, PeerIdError, ToPeerId}; +} + +/// Ephemera membership. How to find other nodes in the cluster. +pub mod membership { + pub use super::network::members::{ + ConfigMembersProvider, DummyMembersProvider, PeerInfo, PeerSetting, ProviderError, Result, + }; +} + +/// Ephemera keypair and public key +pub mod crypto { + pub use super::utilities::crypto::{ + EphemeraKeypair, EphemeraPublicKey, KeyPairError, Keypair, PublicKey, + }; +} + +/// Ephemera codec to encode and decode messages +pub mod codec { + pub use super::utilities::codec::{Decode, Encode}; +} + +/// Ephemera node configuration +pub mod configuration { + pub use super::config::Configuration; +} + +/// Ephemera CLI. Helpers for creating configuration, running node, etc. +pub mod cli; + +/// Utilities to set up logging. +pub mod logging; + +// PRIVATE MODULES + +/// External interface for Ephemera +mod api; + +/// Block creation code +mod block; + +/// Ephemera reliable broadcast +mod broadcast; + +/// Ephemera configuration +mod config; + +/// Ephemera core. Ephemera builder and instance. +mod core; + +/// Ephemera networking with peers +mod network; + +/// Ephemera storage. Block storage and certificate storage. +mod storage; + +/// Ephemera utilities. Crypto, codec, etc. +mod utilities; + +/// Ephemera websocket. Websocket server where external clients can subscribe. +mod websocket; diff --git a/ephemera/src/logging/mod.rs b/ephemera/src/logging/mod.rs new file mode 100644 index 0000000000..79968347b0 --- /dev/null +++ b/ephemera/src/logging/mod.rs @@ -0,0 +1,19 @@ +pub fn init() { + if let Ok(directives) = ::std::env::var("RUST_LOG") { + println!("Logging enabled with directives: {directives}",); + pretty_env_logger::formatted_timed_builder() + .parse_filters(&directives) + .format_timestamp_millis() + .init(); + } else { + println!("Logging disabled"); + } +} + +pub fn init_with_directives(directives: &str) { + println!("Logging enabled with directives: {directives}",); + pretty_env_logger::formatted_timed_builder() + .parse_filters(directives) + .format_timestamp_millis() + .init(); +} diff --git a/ephemera/src/network/libp2p/behaviours/membership/behaviour.rs b/ephemera/src/network/libp2p/behaviours/membership/behaviour.rs new file mode 100644 index 0000000000..65c3a36dc8 --- /dev/null +++ b/ephemera/src/network/libp2p/behaviours/membership/behaviour.rs @@ -0,0 +1,556 @@ +//! # Membership behaviour. +//! +//! Ephemera `reliable broadcast` needs to know the list of peers who participate in the protocol. +//! Also it's not enough to have just the list but to make sure that they are actually online. +//! When the list of available peers changes, `reliable broadcast` needs to be notified so that it can adjust accordingly. +//! +//! This behaviour is responsible for keeping membership up to date. +//! +//! `MembersProviderFuture`: `Future>> + Send + 'static`. +//! +//! User provides a [`MembersProviderFuture`] implementation to the [Behaviour] which is responsible for fetching the list of peers. +//! +//! [Behaviour] accepts only peers that are actually online. +//! +//! When peers become available or unavailable, [Behaviour] adjusts the list of connected peers accordingly and notifies `reliable broadcast` +//! about the membership change. +//! +//! It is configurable what `threshold` of peers(from the total list provided by [`MembersProviderFuture`]) should be available at any given time. +//! Or if just to use all peers who are online. See [`MembershipKind`] for more details. +//! +//! Ideally [`MembersProviderFuture`] can depend on a resource that gives reliable results. Some kind of registry which itself keeps track of actually online nodes. +//! As Ephemera uses only peers provided by [`MembersProviderFuture`], it depends on its accuracy. +//! At the same time it tries to be flexible and robust to handle less reliable [`MembersProviderFuture`] implementations. + +// When peer gets disconnected, we try to dial it and if that fails, we update group. +// (it may connect us meanwhile). +// Although we can retry to connect to disconnected peers, it's simpler if we just assume that when +// they come online again, they will connect us. + +//So the main goal is to remove offline peers from the group so that rb can make progress. + +//a)when peer disconnects, we try to dial it and if that fails, we update group. +//b)when peer connects, we will update the group. + +use std::future::Future; +use std::time::Duration; +use std::{ + collections::HashMap, + collections::HashSet, + fmt::Debug, + task::{Context, Poll}, +}; + +use futures_util::FutureExt; +use libp2p::core::Endpoint; +use libp2p::swarm::{ConnectionDenied, NotifyHandler, THandler}; +use libp2p::{ + swarm::ToSwarm, + swarm::{ + behaviour::ConnectionEstablished, + dial_opts::{DialOpts, PeerCondition}, + ConnectionClosed, ConnectionId, DialFailure, FromSwarm, NetworkBehaviour, PollParameters, + THandlerInEvent, THandlerOutEvent, + }, + Multiaddr, +}; +use libp2p_identity::PeerId; +use log::{debug, error, trace, warn}; +use tokio::time; +use tokio::time::{Instant, Interval}; + +use crate::network::libp2p::behaviours::membership::handler::ToHandler; +use crate::network::libp2p::behaviours::membership::{Membership, MEMBERSHIP_SYNC_INTERVAL_SEC}; +use crate::network::Peer; +use crate::{ + membership, + network::{ + libp2p::behaviours::{ + membership::connections::ConnectedPeers, + membership::protocol::ProtocolMessage, + membership::{handler::Handler, MAX_DIAL_ATTEMPT_ROUNDS}, + membership::{MembershipKind, Memberships}, + }, + members::PeerInfo, + }, +}; + +/// [`MembersProviderFuture`] state when we are trying to connect to new peers. +/// +/// We try to connect few times before giving up. Generally speaking an another peer is either online or offline +/// at any given time. But it has been helpful for testing when whole cluster comes up around the same time. +#[derive(Debug, Default)] +struct PendingPeersUpdate { + /// Peers that we are haven't tried to connect to yet. + waiting_to_dial: HashSet, + /// Number of dial attempts per round. + dial_attempts: usize, + /// How long we wait between dial attempts. + interval_between_dial_attempts: Option, +} + +#[derive(Debug, Default)] +struct SyncPeers { + pending_peers: Vec, +} + +impl SyncPeers { + fn new(pending_peers: Vec) -> Self { + Self { pending_peers } + } +} + +/// Behaviour states. +enum State { + /// Waiting for new peers from the members provider trait. + WaitingPeers, + /// Trying to connect to new peers. + WaitingDial(PendingPeersUpdate), + /// We have finished trying to connect to new peers and going to report it. + NotifyPeersUpdated, + /// Notify other members that we have updated members. + SyncPeers(SyncPeers), +} + +/// Events that can be emitted by the `Behaviour`. +pub(crate) enum Event { + /// We have received new peers from the members provider trait. + /// We are going to try to connect to them. + PeerUpdatePending, + /// We have finished trying to connect to new peers and going to report it. + PeersUpdated(HashSet), + /// MembersProviderFuture reported us new peers and this set doesn't contain our local peer. + LocalRemoved(HashSet), + /// MembersProviderFuture reported us new peers and we failed to connect to enough of them. + NotEnoughPeers(HashSet), +} + +pub(crate) struct Behaviour

+where + P: Future>> + Send + 'static, +{ + /// All peers that are part of the current group. + memberships: Memberships, + /// Local peer id. + local_peer_id: PeerId, + /// Future that provides new peers. + members_provider: P, + /// Interval between requesting new peers from the members provider. + members_provider_interval: Option, + /// Delay between dial attempts. + members_provider_delay: Duration, + /// Current behaviour state. + state: State, + /// Current state of all incoming and outgoing connections. + all_connections: ConnectedPeers, + /// Membership kind. + membership_kind: MembershipKind, + /// Last time we broadcast SYNC + last_sync_time: Instant, + /// Minimum time between members provider updates. + minimum_time_between_sync: Duration, +} + +impl

Behaviour

+where + P: Future>> + Send + Unpin + 'static, +{ + pub(crate) fn new( + members_provider: P, + members_provider_delay: Duration, + local_peer_id: PeerId, + membership_kind: MembershipKind, + ) -> Self { + let initial_delay = Instant::now() + Duration::from_secs(5); + let delay = tokio::time::interval_at(initial_delay, members_provider_delay); + Behaviour { + memberships: Memberships::new(), + local_peer_id, + members_provider, + members_provider_interval: Some(delay), + members_provider_delay, + state: State::WaitingPeers, + all_connections: ConnectedPeers::default(), + membership_kind, + last_sync_time: Instant::now(), + minimum_time_between_sync: Duration::from_secs(MEMBERSHIP_SYNC_INTERVAL_SEC), + } + } + + /// Returns the list of peers that are part of current group. + pub(crate) fn active_peer_ids(&mut self) -> &HashSet { + self.memberships.current().connected_peers() + } + + pub(crate) fn active_peer_ids_with_local(&mut self) -> HashSet { + self.memberships.current().connected_peer_ids_with_local() + } + + fn waiting_peers(&mut self, cx: &mut Context) -> Poll> { + if let Some(mut tick) = self.members_provider_interval.take() { + if !tick.poll_tick(cx).is_ready() { + self.members_provider_interval = Some(tick); + return Poll::Pending; + } + } + let peers = match self.members_provider.poll_unpin(cx) { + Poll::Ready(peers) => { + let wait_time = Instant::now() + self.members_provider_delay; + self.members_provider_interval = + time::interval_at(wait_time, self.members_provider_delay).into(); + self.last_sync_time = Instant::now(); + peers + } + Poll::Pending => { + return Poll::Pending; + } + }; + + match peers { + Ok(peers) => { + if peers.is_empty() { + //Not sure what to do here. Tempted to think that if this happens + //we should ignore it and assume that this is probably a bug in the membership service. + + warn!("Received empty peers from provider. To try again before preconfigured interval, please restart the node."); + return Poll::Ready(ToSwarm::GenerateEvent(Event::NotEnoughPeers( + HashSet::default(), + ))); + } + + let mut new_peers = HashMap::new(); + + for peer_info in peers { + match >::try_into(peer_info) { + Ok(peer) => { + debug!( + "Received peer: {:?}, {:?}", + peer.peer_id.inner(), + peer.cosmos_address + ); + new_peers.insert(*peer.peer_id.inner(), peer); + } + Err(err) => { + error!("Error while converting peer info to peer: {}", err); + } + } + } + + //If we are not part of the new membership, notify immediately + if !new_peers.contains_key(&self.local_peer_id) { + debug!( + "Local peer {:?} is not part of the new membership. Notifying immediately.", + self.local_peer_id + ); + let pending_membership = Membership::new(new_peers.clone()); + self.memberships.set_pending(pending_membership); + self.state = State::NotifyPeersUpdated; + return Poll::Pending; + } + + let mut pending_membership = + Membership::new_with_local(new_peers.clone(), self.local_peer_id); + let mut pending_update = PendingPeersUpdate::default(); + + for peer_id in new_peers.keys() { + if self.all_connections.is_peer_connected(peer_id) { + pending_membership.peer_connected(*peer_id); + } else { + pending_update.waiting_to_dial.insert(*peer_id); + } + } + + self.memberships.set_pending(pending_membership); + + //It seems that all peers from updated membership set are already connected + if pending_update.waiting_to_dial.is_empty() { + self.state = State::NotifyPeersUpdated; + Poll::Pending + } else { + self.state = State::WaitingDial(pending_update); + + //Just let the rest of the system to know that we are in the middle of updating membership + Poll::Ready(ToSwarm::GenerateEvent(Event::PeerUpdatePending)) + } + } + Err(err) => { + error!("Error while getting peers from provider: {:?}", err); + Poll::Ready(ToSwarm::GenerateEvent(Event::NotEnoughPeers( + HashSet::default(), + ))) + } + } + } + + fn waiting_dial( + &mut self, + cx: &mut Context<'_>, + ) -> Poll>> { + if let State::WaitingDial(PendingPeersUpdate { + waiting_to_dial, + dial_attempts, + interval_between_dial_attempts, + }) = &mut self.state + { + //Refresh the list of connected peers + for peer_id in self.all_connections.all_connected_peers_ref() { + waiting_to_dial.remove(peer_id); + } + + //FIXME + waiting_to_dial.remove(&self.local_peer_id); + + let pending_membership = self + .memberships + .pending() + .expect("Pending membership should be set"); + + //With each 'poll' we can tell Swarm to dial one peer + let next_waiting = waiting_to_dial.iter().next().copied(); + + if let Some(peer_id) = next_waiting { + waiting_to_dial.remove(&peer_id); + + let address = pending_membership + .peer_address(&peer_id) + .expect("Peer should exist"); + + trace!("Dialing peer: {:?} {:?}", peer_id, address); + + let opts = DialOpts::peer_id(peer_id) + .condition(PeerCondition::NotDialing) + .addresses(vec![address.clone()]) + .build(); + + Poll::Ready(ToSwarm::Dial { opts }) + } else { + let all_peers = pending_membership.all_peer_ids(); + let connected_peers = pending_membership.connected_peers(); + + //Exclude local peer + let all_connected = connected_peers.len() == all_peers.len() - 1; + + if all_connected || *dial_attempts >= MAX_DIAL_ATTEMPT_ROUNDS { + interval_between_dial_attempts.take(); + self.state = State::NotifyPeersUpdated; + return Poll::Pending; + } + //Try again few times before notifying the rest of the system about membership update. + //Dialing attempt + if let Some(interval) = interval_between_dial_attempts { + if interval.poll_tick(cx) == Poll::Pending { + return Poll::Pending; + } + *dial_attempts += 1; + trace!("Next attempt({dial_attempts:?}) to dial failed peers"); + } else { + let start_at = Instant::now() + Duration::from_secs(5); + *interval_between_dial_attempts = + Some(time::interval_at(start_at, Duration::from_secs(10))); + } + if *dial_attempts > 0 { + waiting_to_dial.extend(all_peers.difference(connected_peers).copied()); + } + + Poll::Pending + } + } else { + unreachable!() + } + } + + fn notify_peers_updated(&mut self) -> Poll> { + if let Some(membership) = self.memberships.remove_pending() { + self.memberships.update(membership); + } + + let membership = self.memberships.current(); + let membership_connected_peers = membership.connected_peer_ids(); + + let event = if membership.includes_local() { + if self.membership_kind.accept(membership) { + debug!("Membership accepted by kind: {:?}", self.membership_kind); + Event::PeersUpdated(membership_connected_peers) + } else { + debug!("Membership rejected by kind: {:?}", self.membership_kind); + Event::NotEnoughPeers(membership_connected_peers) + } + } else { + debug!("Membership does not include local peer"); + Event::LocalRemoved(membership_connected_peers) + }; + + //TODO: this list should also include "old" peers(peers who aren't part of new membership). + let connected_peers = membership + .connected_peer_ids() + .into_iter() + .collect::>(); + self.state = State::SyncPeers(SyncPeers::new(connected_peers)); + Poll::Ready(ToSwarm::GenerateEvent(event)) + } + + fn sync_peers(&mut self) -> Poll> { + if let State::SyncPeers(SyncPeers { pending_peers }) = &mut self.state { + match pending_peers.pop() { + None => { + self.state = State::WaitingPeers; + Poll::Pending + } + Some(peer_id) => { + debug!("Notifying {peer_id:?} about membership update",); + Poll::Ready(ToSwarm::NotifyHandler { + peer_id, + handler: NotifyHandler::Any, + event: ToHandler::Message(ProtocolMessage::Sync), + }) + } + } + } else { + unreachable!("State should be SyncPeers") + } + } +} + +impl

NetworkBehaviour for Behaviour

+where + P: Future>> + Send + Unpin + 'static, +{ + type ConnectionHandler = Handler; + type OutEvent = Event; + + fn handle_pending_inbound_connection( + &mut self, + _connection_id: ConnectionId, + _local_addr: &Multiaddr, + _remote_addr: &Multiaddr, + ) -> Result<(), ConnectionDenied> { + //TODO: we can refuse connections from peers that are not part of the current membership. + Ok(()) + } + + fn handle_established_inbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + _local_addr: &Multiaddr, + _remote_addr: &Multiaddr, + ) -> Result, ConnectionDenied> { + trace!("Established inbound connection with peer: {:?}", peer); + Ok(Handler::new()) + } + + fn handle_pending_outbound_connection( + &mut self, + _connection_id: ConnectionId, + maybe_peer: Option, + _addresses: &[Multiaddr], + _effective_role: Endpoint, + ) -> Result, ConnectionDenied> { + //FIXME: deprecated + #[allow(deprecated)] + match maybe_peer { + Some(peer_id) => Ok(self.addresses_of_peer(&peer_id)), + None => Ok(vec![]), + } + } + + fn handle_established_outbound_connection( + &mut self, + _connection_id: ConnectionId, + peer: PeerId, + addr: &Multiaddr, + _role_override: Endpoint, + ) -> Result, ConnectionDenied> { + trace!( + "Established outbound connection with peer: {:?} {:?}", + peer, + addr + ); + Ok(Handler::new()) + } + + ///Membership behaviour is responsible for providing addresses to another Swarm behaviours. + fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { + self.memberships + .current() + .peer_address(peer_id) + .cloned() + .map_or(vec![], |addr| vec![addr]) + } + + fn on_swarm_event(&mut self, event: FromSwarm) { + match event { + FromSwarm::ConnectionEstablished(ConnectionEstablished { + peer_id, + connection_id: _, + endpoint, + failed_addresses: _, + other_established: _, + }) => { + self.all_connections + .insert(peer_id, endpoint.clone().into()); + if let Some(pending) = self.memberships.pending_mut() { + pending.peer_connected(peer_id); + } + trace!("{:?}", self.all_connections); + } + + FromSwarm::ConnectionClosed(ConnectionClosed { + peer_id, + connection_id: _, + endpoint, + handler: _h, + remaining_established: _, + }) => { + self.all_connections + .remove(&peer_id, &endpoint.clone().into()); + if let Some(pending) = self.memberships.pending_mut() { + pending.peer_disconnected(&peer_id); + } + debug!("{:?}", self.all_connections); + } + FromSwarm::DialFailure(DialFailure { + peer_id: Some(peer_id), + error, + connection_id: _, + }) => { + trace!("Dial failure: {:?} {:?}", peer_id, error); + } + _ => {} + } + } + + fn on_connection_handler_event( + &mut self, + peer_id: PeerId, + _connection_id: ConnectionId, + event: THandlerOutEvent, + ) { + trace!( + "Received event from connection handler: {:?} from peer: {:?}", + event, + peer_id + ); + + //TODO: we may need to check who sent the update: probably we should accept only updates from members who we already know + if let State::WaitingPeers = self.state { + if self.last_sync_time + self.minimum_time_between_sync < Instant::now() { + self.members_provider_interval = None; + debug!("Received sync notification from peer {peer_id:?}, requesting membership update"); + } + } + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + _params: &mut impl PollParameters, + ) -> Poll>> { + match &mut self.state { + State::WaitingPeers => self.waiting_peers(cx), + State::WaitingDial(_) => self.waiting_dial(cx), + State::NotifyPeersUpdated => self.notify_peers_updated(), + State::SyncPeers(_) => self.sync_peers(), + } + } +} diff --git a/ephemera/src/network/libp2p/behaviours/membership/connections.rs b/ephemera/src/network/libp2p/behaviours/membership/connections.rs new file mode 100644 index 0000000000..1e5bab908d --- /dev/null +++ b/ephemera/src/network/libp2p/behaviours/membership/connections.rs @@ -0,0 +1,88 @@ +use std::collections::{HashMap, HashSet}; +use std::fmt::Debug; + +use libp2p::core::ConnectedPoint; +use libp2p::Multiaddr; +use serde::Serialize; + +#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize)] +pub(crate) enum Endpoint { + Dialer { + address: Multiaddr, + }, + Listener { + local_address: Multiaddr, + remote_address: Multiaddr, + }, +} + +impl From for Endpoint { + fn from(connected_point: ConnectedPoint) -> Self { + match connected_point { + ConnectedPoint::Dialer { address, .. } => Endpoint::Dialer { address }, + ConnectedPoint::Listener { + local_addr, + send_back_addr, + } => Endpoint::Listener { + local_address: local_addr, + remote_address: send_back_addr, + }, + } + } +} + +#[derive(Debug, Default, Serialize)] +pub(crate) struct Connections { + dialer: HashSet, + listener: HashSet, +} + +impl Connections { + fn insert(&mut self, connected_point: Endpoint) { + match connected_point { + Endpoint::Dialer { .. } => { + self.dialer.insert(connected_point); + } + Endpoint::Listener { .. } => { + self.listener.insert(connected_point); + } + } + } + + fn remove(&mut self, connected_point: &Endpoint) { + match connected_point { + Endpoint::Dialer { .. } => { + self.dialer.remove(connected_point); + } + Endpoint::Listener { .. } => { + self.listener.remove(connected_point); + } + } + } +} + +#[derive(Debug, Default, Serialize)] +pub(crate) struct ConnectedPeers { + connections: HashMap, +} + +impl ConnectedPeers { + pub(crate) fn is_peer_connected(&self, peer_id: &libp2p_identity::PeerId) -> bool { + self.connections.contains_key(peer_id) + } + + pub(crate) fn all_connected_peers_ref(&self) -> Vec<&libp2p_identity::PeerId> { + self.connections.keys().collect() + } + + pub(crate) fn insert(&mut self, peer_id: libp2p_identity::PeerId, connected_point: Endpoint) { + let connections = self.connections.entry(peer_id).or_default(); + connections.insert(connected_point); + } + + pub(crate) fn remove(&mut self, peer_id: &libp2p_identity::PeerId, connected_point: &Endpoint) { + if let Some(connections) = self.connections.get_mut(peer_id) { + connections.remove(connected_point); + } + } +} diff --git a/ephemera/src/network/libp2p/behaviours/membership/handler.rs b/ephemera/src/network/libp2p/behaviours/membership/handler.rs new file mode 100644 index 0000000000..9735d5cea9 --- /dev/null +++ b/ephemera/src/network/libp2p/behaviours/membership/handler.rs @@ -0,0 +1,340 @@ +//! Because each Ephemera instance requests peers at arbitrary time, a node needs to notify other +//! peers when it has just requested an update. That helps to keep the whole cluster in sync and avoid +//! nodes' membership diverging. +//! +//! Overall this synchronizes all nodes' view of the membership. +//! +//! Current approach is a bit 'burst'. It makes all nodes to request membership info at the same time. +//! +//! TODO +//! Because we actually can verify peers' membership, it would be possible that one peer(or subset of peers) requests the +//! peers from a rendezvous point and then sends the list to the other peers. Or possibly only the difference. +use std::pin::Pin; +use std::task::{Context, Poll}; + +use asynchronous_codec::Framed; +use futures::Sink; +use futures_util::StreamExt; +use libp2p::{ + swarm::handler::{ + DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, ListenUpgradeError, + }, + swarm::NegotiatedSubstream, + swarm::{ + handler::ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, KeepAlive, + SubstreamProtocol, + }, +}; +use log::{debug, error}; +use thiserror::Error; + +use crate::network::libp2p::behaviours::membership::protocol::{ + MembershipCodec, Protocol, ProtocolMessage, +}; + +#[derive(Error, Debug)] +pub(crate) enum Error { + #[error("HandlerError: {0}")] + Handler(#[from] anyhow::Error), +} + +//Because we keep long lived connections, we need to restrict number of substream attempts. +//Here we don't need more than 1 because it's a 'quiet' protocol. +const MAX_SUBSTREAM_ATTEMPTS: usize = 1; + +enum InboundSubstreamState { + WaitingInput(Framed), + Closing(Framed), +} + +enum OutboundSubstreamState { + WaitingOutput(Framed), + PendingSend( + Framed, + ProtocolMessage, + ), + PendingFlush(Framed), +} + +pub(crate) struct Handler { + outbound_substream: Option, + inbound_substream: Option, + send_queue: Vec, + outbound_substream_establishing: bool, + outbound_substream_attempts: usize, + inbound_substream_attempts: usize, +} + +impl Handler { + pub(crate) fn new() -> Self { + Self { + outbound_substream: None, + inbound_substream: None, + send_queue: vec![], + outbound_substream_establishing: false, + outbound_substream_attempts: 0, + inbound_substream_attempts: 0, + } + } + + //Process inbound stream messages + //WAITING_INPUT + // - if message received, send it to behaviour + // - if receive error or None, close substream + // + //CLOSING + // - Wait buffer to be flushed + // - Close substream + fn process_inbound_stream( + &mut self, + cx: &mut Context, + ) -> Option>> { + loop { + match std::mem::take(&mut self.inbound_substream) { + // inbound idle state + Some(InboundSubstreamState::WaitingInput(mut substream)) => { + match substream.poll_next_unpin(cx) { + Poll::Ready(Some(Ok(message))) => { + self.inbound_substream = + Some(InboundSubstreamState::WaitingInput(substream)); + + let from_handler = FromHandler::Message(message); + + return Poll::Ready(ConnectionHandlerEvent::Custom(from_handler)) + .into(); + } + Poll::Ready(Some(Err(err))) => { + error!("Failed to read from substream: {err}",); + self.inbound_substream = + Some(InboundSubstreamState::Closing(substream)); + } + Poll::Ready(None) => { + debug!("Inbound stream closed by remote"); + self.inbound_substream = + Some(InboundSubstreamState::Closing(substream)); + } + Poll::Pending => { + self.inbound_substream = + Some(InboundSubstreamState::WaitingInput(substream)); + break; + } + } + } + Some(InboundSubstreamState::Closing(mut substream)) => { + match Sink::poll_close(Pin::new(&mut substream), cx) { + Poll::Ready(res) => { + if let Err(e) = res { + error!("Inbound substream error while closing: {e}"); + } + self.inbound_substream = None; + break; + } + Poll::Pending => { + self.inbound_substream = + Some(InboundSubstreamState::Closing(substream)); + break; + } + } + } + None => { + self.inbound_substream = None; + break; + } + } + } + None + } + + //Process outbound stream messages + //WAITING_OUTPUT + // - if send queue is not empty, go to PENDING_SEND + // + //PENDING_SEND + // - send message to substream + // - if send error, mark substream as Closing + // + //PENDING_FLUSH + // - flush substream + // - if flush error, mark substream as Closing + fn process_outbound_stream(&mut self, cx: &mut Context) { + loop { + match std::mem::take(&mut self.outbound_substream) { + // outbound idle state + Some(OutboundSubstreamState::WaitingOutput(substream)) => { + if let Some(message) = self.send_queue.pop() { + self.outbound_substream = + Some(OutboundSubstreamState::PendingSend(substream, message)); + continue; + } + + self.outbound_substream = + Some(OutboundSubstreamState::WaitingOutput(substream)); + break; + } + Some(OutboundSubstreamState::PendingSend(mut substream, message)) => { + match Sink::poll_ready(Pin::new(&mut substream), cx) { + Poll::Ready(Ok(())) => { + match Sink::start_send(Pin::new(&mut substream), message) { + Ok(()) => { + self.outbound_substream = + Some(OutboundSubstreamState::PendingFlush(substream)); + } + Err(e) => { + debug!("Failed to send message on outbound stream: {e}"); + self.outbound_substream = None; + break; + } + } + } + Poll::Ready(Err(e)) => { + debug!("Failed to send message on outbound stream: {e}"); + self.outbound_substream = None; + break; + } + Poll::Pending => { + self.outbound_substream = + Some(OutboundSubstreamState::PendingSend(substream, message)); + break; + } + } + } + Some(OutboundSubstreamState::PendingFlush(mut substream)) => { + match Sink::poll_flush(Pin::new(&mut substream), cx) { + Poll::Ready(Ok(())) => { + self.outbound_substream = + Some(OutboundSubstreamState::WaitingOutput(substream)); + } + Poll::Ready(Err(e)) => { + debug!("Failed to flush outbound stream: {e}"); + self.outbound_substream = None; + break; + } + Poll::Pending => { + self.outbound_substream = + Some(OutboundSubstreamState::PendingFlush(substream)); + break; + } + } + } + None => { + self.outbound_substream = None; + break; + } + } + } + } +} + +#[derive(Debug)] +pub(crate) enum FromHandler { + Message(ProtocolMessage), +} + +#[derive(Debug)] +pub(crate) enum ToHandler { + Message(ProtocolMessage), +} + +impl ConnectionHandler for Handler { + type InEvent = ToHandler; + type OutEvent = FromHandler; + type Error = Error; + type InboundProtocol = Protocol; + type OutboundProtocol = Protocol; + type InboundOpenInfo = (); + type OutboundOpenInfo = (); + + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(Protocol, ()) + } + + fn connection_keep_alive(&self) -> KeepAlive { + //we could add idle timeout here + KeepAlive::Yes + } + + fn poll( + &mut self, + cx: &mut Context<'_>, + ) -> Poll< + ConnectionHandlerEvent< + Self::OutboundProtocol, + Self::OutboundOpenInfo, + Self::OutEvent, + Self::Error, + >, + > { + // poll STATE_MACHINE + //- Request outbound substream if neccessary + //- poll inbound substream + //- poll outbound substream + + //Establish new connection when behaviour wants to send a message and we don't have an outbound substream yet + if !self.send_queue.is_empty() + && self.outbound_substream.is_none() + && !self.outbound_substream_establishing + { + self.outbound_substream_establishing = true; + return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest { + protocol: SubstreamProtocol::new(Protocol, ()), + }); + } + + if let Some(res) = self.process_inbound_stream(cx) { + return res; + } + + self.process_outbound_stream(cx); + + Poll::Pending + } + + fn on_behaviour_event(&mut self, event: Self::InEvent) { + match event { + ToHandler::Message(message) => { + self.send_queue.push(message); + } + } + } + + fn on_connection_event( + &mut self, + event: ConnectionEvent< + Self::InboundProtocol, + Self::OutboundProtocol, + Self::InboundOpenInfo, + Self::OutboundOpenInfo, + >, + ) { + match event { + ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { + protocol: stream, + info: _, + }) => { + if self.inbound_substream_attempts > MAX_SUBSTREAM_ATTEMPTS { + log::warn!("Too many inbound substream attempts, refusing stream"); + return; + } + self.inbound_substream_attempts += 1; + self.inbound_substream = Some(InboundSubstreamState::WaitingInput(stream)); + } + ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { + protocol, + info: _, + }) => { + if self.outbound_substream_attempts > MAX_SUBSTREAM_ATTEMPTS { + log::warn!("Too many outbound substream attempts, refusing stream"); + return; + } + self.outbound_substream = Some(OutboundSubstreamState::WaitingOutput(protocol)); + } + ConnectionEvent::DialUpgradeError(DialUpgradeError { info, error }) => { + error!("DialUpgradeError: info: {:?}, error: {:?}", info, error); + } + ConnectionEvent::ListenUpgradeError(ListenUpgradeError { info, error }) => { + error!("ListenUpgradeError: info: {:?}, error: {:?}", info, error); + } + ConnectionEvent::AddressChange(_) => {} + } + } +} diff --git a/ephemera/src/network/libp2p/behaviours/membership/mod.rs b/ephemera/src/network/libp2p/behaviours/membership/mod.rs new file mode 100644 index 0000000000..6a4f6e6178 --- /dev/null +++ b/ephemera/src/network/libp2p/behaviours/membership/mod.rs @@ -0,0 +1,190 @@ +//! In Ephemera, membership of reliable broadcast protocol is decided by membership provider. +//! Only peers who are returned by [`crate::membership::MembersProviderFut`] are allowed to participate. + +use std::collections::{HashMap, HashSet}; +use std::num::NonZeroUsize; + +use libp2p_identity::PeerId; +use lru::LruCache; + +use crate::network::Peer; + +pub(crate) mod behaviour; +mod connections; +mod handler; +mod protocol; + +const MAX_DIAL_ATTEMPT_ROUNDS: usize = 6; + +/// Minimum percentage of available nodes to consider the network healthy. +//TODO: make this configurable +const MEMBERSHIP_MINIMUM_AVAILABLE_NODES_RATIO: f64 = 0.8; + +/// Minimum time between syncs of membership. +const MEMBERSHIP_SYNC_INTERVAL_SEC: u64 = 60; + +/// Maximum percentage of nodes that can change in a single membership update. +/// In general it should be considered a security risk if it has changed too much. +/// //TODO: make this configurable +const _MEMBERSHIP_MAXIMUM_ALLOWED_CHANGE_RATIO: f64 = 0.2; + +/// Membership provider returns list of peers. But it is up to the Ephemera user to decide +/// how reliable the list is. For example, it can contain peers who are offline. + +/// This enum defines how the actual membership is decided. +#[derive(Debug)] +pub(crate) enum MembershipKind { + /// Mandatory minimum membership size is defined by threshold of all peers returned by membership provider. + Threshold(f64), + /// Mandatory minimum membership size is all peers who are online. + AnyOnline, + /// Mandatory minimum membership size is all peers returned by membership provider. + AllOnline, +} + +impl MembershipKind { + #[allow( + clippy::cast_precision_loss, + clippy::cast_sign_loss, + clippy::cast_possible_truncation + )] + pub(crate) fn accept(&self, membership: &Membership) -> bool { + let total_number_of_peers = membership.all_members.len(); + let connected_peers = membership.connected_peers_ids.len(); + match self { + MembershipKind::Threshold(threshold) => { + let minimum_available_nodes = (total_number_of_peers as f64 * threshold) as usize; + connected_peers >= minimum_available_nodes + } + MembershipKind::AnyOnline => connected_peers > 0, + MembershipKind::AllOnline => connected_peers == total_number_of_peers, + } + } +} + +pub(crate) struct Memberships { + snapshots: LruCache, + current: u64, + /// This is set when we get new peers set from [crate::membership::MembersProviderFut] + /// but haven't yet activated it. + pending_membership: Option, +} + +impl Memberships { + pub(crate) fn new() -> Self { + let mut snapshots = LruCache::new(NonZeroUsize::new(1000).unwrap()); + snapshots.put(0, Membership::new(HashMap::default())); + Self { + snapshots, + current: 0, + pending_membership: None, + } + } + + pub(crate) fn current(&mut self) -> &Membership { + //Unwrap is safe because we always have current membership + self.snapshots.get(&self.current).unwrap() + } + + pub(crate) fn update(&mut self, membership: Membership) { + self.current += 1; + self.snapshots.put(self.current, membership); + } + + pub(crate) fn set_pending(&mut self, membership: Membership) { + self.pending_membership = Some(membership); + } + + pub(crate) fn remove_pending(&mut self) -> Option { + self.pending_membership.take() + } + + pub(crate) fn pending(&self) -> Option<&Membership> { + self.pending_membership.as_ref() + } + + pub(crate) fn pending_mut(&mut self) -> Option<&mut Membership> { + self.pending_membership.as_mut() + } +} + +#[derive(Debug)] +pub(crate) struct Membership { + local_peer_id: PeerId, + all_members: HashMap, + all_peers_ids: HashSet, + connected_peers_ids: HashSet, +} + +impl Membership { + pub(crate) fn new_with_local( + all_members: HashMap, + local_peer_id: PeerId, + ) -> Self { + let all_peers_ids = all_members.keys().copied().collect(); + Self { + local_peer_id, + all_members, + all_peers_ids, + connected_peers_ids: HashSet::new(), + } + } + + pub(crate) fn new(all_members: HashMap) -> Self { + let all_peers_ids = all_members.keys().copied().collect(); + Self { + local_peer_id: PeerId::random(), + all_members, + all_peers_ids, + connected_peers_ids: HashSet::new(), + } + } + + pub(crate) fn includes_local(&self) -> bool { + self.all_members.contains_key(&self.local_peer_id) + } + + pub(crate) fn peer_connected(&mut self, peer_id: PeerId) { + self.connected_peers_ids.insert(peer_id); + } + + pub(crate) fn peer_disconnected(&mut self, peer_id: &PeerId) { + self.connected_peers_ids.remove(peer_id); + } + + pub(crate) fn all_peer_ids(&self) -> &HashSet { + &self.all_peers_ids + } + + pub(crate) fn connected_peer_ids(&self) -> HashSet { + self.connected_peers_ids.clone() + } + + pub(crate) fn connected_peer_ids_with_local(&self) -> HashSet { + let mut active_peers = self.connected_peers_ids.clone(); + active_peers.insert(self.local_peer_id); + active_peers + } + + pub(crate) fn connected_peers(&self) -> &HashSet { + &self.connected_peers_ids + } + + pub(crate) fn peer_address(&self, peer_id: &PeerId) -> Option<&libp2p::Multiaddr> { + self.all_members + .get(peer_id) + .map(|peer| peer.address.inner()) + } +} + +impl From for MembershipKind { + fn from(kind: crate::config::MembershipKind) -> Self { + match kind { + crate::config::MembershipKind::Threshold => { + MembershipKind::Threshold(MEMBERSHIP_MINIMUM_AVAILABLE_NODES_RATIO) + } + crate::config::MembershipKind::AnyOnline => MembershipKind::AnyOnline, + crate::config::MembershipKind::AllOnline => MembershipKind::AllOnline, + } + } +} diff --git a/ephemera/src/network/libp2p/behaviours/membership/protocol.rs b/ephemera/src/network/libp2p/behaviours/membership/protocol.rs new file mode 100644 index 0000000000..cb2c1d631c --- /dev/null +++ b/ephemera/src/network/libp2p/behaviours/membership/protocol.rs @@ -0,0 +1,100 @@ +use std::future::Future; +use std::iter; +use std::pin::Pin; + +use asynchronous_codec::{Decoder, Encoder, Framed}; +use bytes::BytesMut; +use futures::{AsyncRead, AsyncWrite}; +use futures_util::future; +use libp2p::core::UpgradeInfo; +use libp2p::{InboundUpgrade, OutboundUpgrade}; +use log::trace; +use serde::{Deserialize, Serialize}; + +use crate::utilities::codec::varint_bytes::{read_length_prefixed, write_length_prefixed}; + +pub const PROTOCOL_NAME: &[u8] = b"/ephemera/membership/1.0.0"; + +pub(crate) struct Protocol; + +impl UpgradeInfo for Protocol { + type Info = &'static [u8]; + type InfoIter = iter::Once; + + fn protocol_info(&self) -> Self::InfoIter { + iter::once(PROTOCOL_NAME) + } +} + +impl InboundUpgrade for Protocol +where + C: AsyncRead + AsyncWrite + Unpin + Send + 'static, +{ + type Output = Framed; + type Error = anyhow::Error; + type Future = Pin> + Send>>; + + fn upgrade_inbound(self, socket: C, _: Self::Info) -> Self::Future { + trace!( + "Inbound upgrade for protocol: {}", + String::from_utf8_lossy(PROTOCOL_NAME) + ); + Box::pin(future::ok(Framed::new(socket, MembershipCodec {}))) + } +} + +impl OutboundUpgrade for Protocol +where + C: AsyncRead + AsyncWrite + Unpin + Send + 'static, +{ + type Output = Framed; + type Error = anyhow::Error; + type Future = Pin> + Send>>; + + fn upgrade_outbound(self, socket: C, _: Self::Info) -> Self::Future { + trace!( + "Outbound upgrade for protocol: {}", + String::from_utf8_lossy(PROTOCOL_NAME) + ); + Box::pin(future::ok(Framed::new(socket, MembershipCodec {}))) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) enum ProtocolMessage { + Sync, +} + +pub(crate) struct MembershipCodec {} + +impl Encoder for MembershipCodec { + type Item = ProtocolMessage; + type Error = anyhow::Error; + + fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { + //FIXME: switch to binary + let data = serde_json::to_vec(&item).unwrap(); + write_length_prefixed(dst, data); + Ok(()) + } +} + +impl Decoder for MembershipCodec { + type Item = ProtocolMessage; + type Error = anyhow::Error; + + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + if src.is_empty() { + return Ok(None); + } + let data = read_length_prefixed(src, 1024 * 1024)?; + match data { + None => Ok(None), + Some(data) => { + //FIXME: switch to binary + let msg = serde_json::from_slice(&data)?; + Ok(msg) + } + } + } +} diff --git a/ephemera/src/network/libp2p/behaviours/mod.rs b/ephemera/src/network/libp2p/behaviours/mod.rs new file mode 100644 index 0000000000..fdd1b4bfa5 --- /dev/null +++ b/ephemera/src/network/libp2p/behaviours/mod.rs @@ -0,0 +1,183 @@ +use std::future::Future; +use std::{iter, sync::Arc, time::Duration}; + +use libp2p::{ + core::{muxing::StreamMuxerBox, transport::Boxed}, + dns, gossipsub, + gossipsub::{IdentTopic as Topic, MessageAuthenticity, ValidationMode}, + kad, noise, request_response as libp2p_request_response, + swarm::NetworkBehaviour, + tcp::{tokio::Transport as TokioTransport, Config as TokioConfig}, + yamux, PeerId as Libp2pPeerId, Transport, +}; +use log::info; + +use crate::membership::PeerInfo; +use crate::network::libp2p::behaviours::membership::MembershipKind; +use crate::{ + broadcast::RbMsg, + crypto::Keypair, + network::libp2p::behaviours::request_response::{ + RbMsgMessagesCodec, RbMsgProtocol, RbMsgResponse, + }, + peer::{PeerId, ToPeerId}, + utilities::hash::{EphemeraHasher, Hasher}, +}; + +pub(crate) mod membership; +pub(crate) mod request_response; + +#[derive(NetworkBehaviour)] +#[behaviour(out_event = "GroupBehaviourEvent")] +pub(crate) struct GroupNetworkBehaviour

+where + P: Future>> + Send + 'static, +{ + pub(crate) members_provider: membership::behaviour::Behaviour

, + pub(crate) gossipsub: gossipsub::Behaviour, + pub(crate) request_response: libp2p_request_response::Behaviour, + pub(crate) kademlia: kad::Kademlia, +} + +#[allow(clippy::large_enum_variant)] +pub(crate) enum GroupBehaviourEvent { + Gossipsub(gossipsub::Event), + RequestResponse(libp2p_request_response::Event), + Membership(membership::behaviour::Event), + Kademlia(kad::KademliaEvent), +} + +impl From for GroupBehaviourEvent { + fn from(event: gossipsub::Event) -> Self { + GroupBehaviourEvent::Gossipsub(event) + } +} + +impl From> for GroupBehaviourEvent { + fn from(event: libp2p_request_response::Event) -> Self { + GroupBehaviourEvent::RequestResponse(event) + } +} + +impl From for GroupBehaviourEvent { + fn from(event: membership::behaviour::Event) -> Self { + GroupBehaviourEvent::Membership(event) + } +} + +impl From for GroupBehaviourEvent { + fn from(event: kad::KademliaEvent) -> Self { + GroupBehaviourEvent::Kademlia(event) + } +} + +//Create combined behaviour. +//Gossipsub takes care of message delivery semantics +//Membership takes care of providing peers who are part of the reliable broadcast group +//Kademlia takes provides closest neighbours and general DHT functionality +pub(crate) fn create_behaviour

( + keypair: &Arc, + ephemera_msg_topic: &Topic, + members_provider: P, + members_provider_delay: Duration, + membership_kind: MembershipKind, +) -> GroupNetworkBehaviour

+where + P: Future>> + Send + Unpin + 'static, +{ + //TODO: review behaviours config(eg. gossipsub minimum peers, kademlia ttl, request-response timeouts etc.) + let local_peer_id = keypair.peer_id(); + let gossipsub = create_gossipsub(keypair, ephemera_msg_topic); + let request_response = create_request_response(); + let rendezvous_behaviour = create_membership( + members_provider, + members_provider_delay, + membership_kind, + local_peer_id, + ); + let kademlia = create_kademlia(keypair); + + GroupNetworkBehaviour { + members_provider: rendezvous_behaviour, + gossipsub, + request_response, + kademlia, + } +} + +// Configure networking messaging stack(Gossipsub) +pub(crate) fn create_gossipsub(local_key: &Arc, topic: &Topic) -> gossipsub::Behaviour { + let gossipsub_config = gossipsub::ConfigBuilder::default() + //TODO: settings from config + .heartbeat_interval(Duration::from_secs(5)) + .message_id_fn(|msg: &gossipsub::Message| Hasher::digest(&msg.data).into()) + .validation_mode(ValidationMode::Strict) + .build() + .expect("Valid config"); + + let mut behaviour = gossipsub::Behaviour::new( + MessageAuthenticity::Signed(local_key.inner().clone()), + gossipsub_config, + ) + .expect("Correct configuration"); + + info!("Subscribing to topic: {}", topic); + behaviour.subscribe(topic).expect("Valid topic"); + behaviour +} + +pub(crate) fn create_request_response() -> libp2p_request_response::Behaviour { + let config = libp2p_request_response::Config::default(); + libp2p_request_response::Behaviour::new( + RbMsgMessagesCodec, + iter::once(( + RbMsgProtocol, + libp2p_request_response::ProtocolSupport::Full, + )), + config, + ) +} + +pub(crate) fn create_membership

( + members_provider: P, + members_provider_delay: Duration, + membership_kind: MembershipKind, + local_peer_id: PeerId, +) -> membership::behaviour::Behaviour

+where + P: Future>> + Send + Unpin + 'static, +{ + membership::behaviour::Behaviour::new( + members_provider, + members_provider_delay, + local_peer_id.into(), + membership_kind, + ) +} + +pub(super) fn create_kademlia(local_key: &Arc) -> kad::Kademlia { + let peer_id = local_key.peer_id(); + let mut cfg = kad::KademliaConfig::default(); + cfg.set_query_timeout(Duration::from_secs(5 * 60)); + let store = kad::store::MemoryStore::new(peer_id.0); + kad::Kademlia::with_config(*peer_id.inner(), store, cfg) +} + +//Configure networking connection stack(Tcp, Noise, Yamux) +//Tcp protocol for networking +//Noise protocol for encryption +//Yamux protocol for multiplexing +pub(crate) fn create_transport( + local_key: &Arc, +) -> anyhow::Result> { + let transport = TokioTransport::new(TokioConfig::default().nodelay(true)); + let transport = dns::TokioDnsConfig::system(transport)?; + + let noise_config = noise::Config::new(local_key.inner())?; + Ok(transport + .upgrade(libp2p::core::upgrade::Version::V1) + .authenticate(noise_config) + .multiplex(yamux::Config::default()) + .timeout(Duration::from_secs(20)) + .boxed()) +} diff --git a/ephemera/src/network/libp2p/behaviours/request_response/mod.rs b/ephemera/src/network/libp2p/behaviours/request_response/mod.rs new file mode 100644 index 0000000000..ca70b7b788 --- /dev/null +++ b/ephemera/src/network/libp2p/behaviours/request_response/mod.rs @@ -0,0 +1,101 @@ +use async_trait::async_trait; +use futures::{AsyncRead, AsyncWrite}; +use libp2p::request_response; +use log::trace; +use serde::{Deserialize, Serialize}; + +use crate::broadcast::RbMsg; +use crate::utilities::codec::varint_async::{read_length_prefixed, write_length_prefixed}; +use crate::utilities::id::EphemeraId; + +#[derive(Clone)] +pub(crate) struct RbMsgMessagesCodec; + +impl RbMsgMessagesCodec {} + +#[derive(Clone)] +pub(crate) struct RbMsgProtocol; + +impl request_response::ProtocolName for RbMsgProtocol { + fn protocol_name(&self) -> &[u8] { + "/ephemera/reliable_broadcast/1.0.0".as_bytes() + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct RbMsgResponse { + pub(crate) id: EphemeraId, +} + +impl RbMsgResponse { + pub(crate) fn new(id: EphemeraId) -> Self { + Self { id } + } +} + +#[async_trait] +impl request_response::Codec for RbMsgMessagesCodec { + type Protocol = RbMsgProtocol; + type Request = RbMsg; + type Response = RbMsgResponse; + + async fn read_request( + &mut self, + _: &Self::Protocol, + io: &mut T, + ) -> Result + where + T: AsyncRead + Unpin + Send, + { + //FIXME: max size + let data = read_length_prefixed(io, 1024 * 1024).await?; + //FIXME: switch to binary + let msg = serde_json::from_slice(&data)?; + trace!("Received request {:?}", msg); + Ok(msg) + } + + async fn read_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + ) -> std::io::Result + where + T: AsyncRead + Unpin + Send, + { + //FIXME: max size + let response = read_length_prefixed(io, 1024 * 1024).await?; + //FIXME: switch to binary + let response = serde_json::from_slice(&response)?; + trace!("Received response {:?}", response); + Ok(response) + } + + async fn write_request( + &mut self, + _: &Self::Protocol, + io: &mut T, + req: Self::Request, + ) -> Result<(), std::io::Error> + where + T: AsyncWrite + Unpin + Send, + { + let data = serde_json::to_vec(&req).unwrap(); + write_length_prefixed(io, data).await?; + Ok(()) + } + + async fn write_response( + &mut self, + _: &Self::Protocol, + io: &mut T, + response: Self::Response, + ) -> std::io::Result<()> + where + T: AsyncWrite + Unpin + Send, + { + let response = serde_json::to_vec(&response).unwrap(); + write_length_prefixed(io, response).await?; + Ok(()) + } +} diff --git a/ephemera/src/network/libp2p/ephemera_sender.rs b/ephemera/src/network/libp2p/ephemera_sender.rs new file mode 100644 index 0000000000..c117a15539 --- /dev/null +++ b/ephemera/src/network/libp2p/ephemera_sender.rs @@ -0,0 +1,58 @@ +use log::trace; +use tokio::sync::mpsc; + +use crate::block::types::message::EphemeraMessage; +use crate::broadcast::RbMsg; + +#[derive(Debug, Clone, PartialEq)] +pub(crate) enum EphemeraEvent { + EphemeraMessage(Box), + ProtocolMessage(Box), + StoreInDht { key: Vec, value: Vec }, + QueryDht { key: Vec }, +} + +pub(crate) struct EphemeraToNetwork; + +impl EphemeraToNetwork { + pub(crate) fn init() -> (EphemeraToNetworkSender, EphemeraToNetworkReceiver) { + let (net_event_tx, net_event_rcv) = mpsc::channel(1000); + + let receiver = EphemeraToNetworkReceiver::new(net_event_rcv); + let sender = EphemeraToNetworkSender::new(net_event_tx); + + (sender, receiver) + } +} + +//Receives messages from the network +pub(crate) struct EphemeraToNetworkReceiver { + pub(crate) net_event_rcv: mpsc::Receiver, +} + +impl EphemeraToNetworkReceiver { + pub(crate) fn new(net_event_rcv: mpsc::Receiver) -> Self { + Self { net_event_rcv } + } +} + +//Sends messages to the network +pub(crate) struct EphemeraToNetworkSender { + pub(crate) network_event_sender_tx: mpsc::Sender, +} + +impl EphemeraToNetworkSender { + pub(crate) fn new(network_event_sender_tx: mpsc::Sender) -> Self { + Self { + network_event_sender_tx, + } + } + + pub(crate) async fn send_ephemera_event(&mut self, event: EphemeraEvent) -> anyhow::Result<()> { + trace!("Network event: {:?}", event); + self.network_event_sender_tx + .send(event) + .await + .map_err(|e| anyhow::anyhow!(e)) + } +} diff --git a/ephemera/src/network/libp2p/mod.rs b/ephemera/src/network/libp2p/mod.rs new file mode 100644 index 0000000000..bbd1c5b5ad --- /dev/null +++ b/ephemera/src/network/libp2p/mod.rs @@ -0,0 +1,4 @@ +mod behaviours; +pub(crate) mod ephemera_sender; +pub(crate) mod network_sender; +pub(crate) mod swarm_network; diff --git a/ephemera/src/network/libp2p/network_sender.rs b/ephemera/src/network/libp2p/network_sender.rs new file mode 100644 index 0000000000..e00782f6a0 --- /dev/null +++ b/ephemera/src/network/libp2p/network_sender.rs @@ -0,0 +1,67 @@ +use log::trace; +use std::collections::HashSet; +use tokio::sync::mpsc; + +use crate::block::types::message::EphemeraMessage; +use crate::broadcast::RbMsg; +use crate::peer::PeerId; + +#[derive(Debug, Clone, PartialEq)] +pub(crate) enum GroupChangeEvent { + PeersUpdated(HashSet), + LocalPeerRemoved(HashSet), + NotEnoughPeers(HashSet), +} + +#[derive(Debug, Clone, PartialEq)] +pub(crate) enum NetworkEvent { + EphemeraMessage(Box), + BroadcastMessage(Box), + GroupUpdate(GroupChangeEvent), + QueryDhtResponse { key: Vec, value: Vec }, +} + +pub(crate) struct EphemeraNetworkCommunication; + +impl EphemeraNetworkCommunication { + pub(crate) fn init() -> (NetCommunicationSender, NetCommunicationReceiver) { + let (net_event_tx, net_event_rcv) = mpsc::channel(1000); + + let receiver = NetCommunicationReceiver::new(net_event_rcv); + let sender = NetCommunicationSender::new(net_event_tx); + + (sender, receiver) + } +} + +//Receives messages from the network +pub(crate) struct NetCommunicationReceiver { + pub(crate) net_event_rcv: mpsc::Receiver, +} + +impl NetCommunicationReceiver { + pub(crate) fn new(net_event_rcv: mpsc::Receiver) -> Self { + Self { net_event_rcv } + } +} + +//Sends messages to the network +pub(crate) struct NetCommunicationSender { + pub(crate) network_event_sender_tx: mpsc::Sender, +} + +impl NetCommunicationSender { + pub(crate) fn new(network_event_sender_tx: mpsc::Sender) -> Self { + Self { + network_event_sender_tx, + } + } + + pub(crate) async fn send_network_event(&mut self, event: NetworkEvent) -> anyhow::Result<()> { + trace!("Network event: {:?}", event); + self.network_event_sender_tx + .send(event) + .await + .map_err(|e| anyhow::anyhow!(e)) + } +} diff --git a/ephemera/src/network/libp2p/swarm_network.rs b/ephemera/src/network/libp2p/swarm_network.rs new file mode 100644 index 0000000000..3e9dc04776 --- /dev/null +++ b/ephemera/src/network/libp2p/swarm_network.rs @@ -0,0 +1,604 @@ +use std::collections::HashSet; +use std::future::Future; +use std::str::FromStr; + +use futures::StreamExt; +use libp2p::kad::{GetClosestPeersResult, GetRecordResult}; +use libp2p::swarm::{NetworkBehaviour, SwarmBuilder}; +use libp2p::{ + gossipsub, gossipsub::IdentTopic as Topic, kad, request_response, swarm::SwarmEvent, Multiaddr, + Swarm, +}; +use log::{debug, error, info, trace}; + +use crate::membership::PeerInfo; +use crate::{ + block::types::message::EphemeraMessage, + broadcast::RbMsg, + codec::Encode, + core::builder::NodeInfo, + network::libp2p::behaviours, + network::libp2p::{ + behaviours::{ + create_behaviour, create_transport, request_response::RbMsgResponse, + GroupBehaviourEvent, GroupNetworkBehaviour, + }, + ephemera_sender::{ + EphemeraEvent, EphemeraToNetwork, EphemeraToNetworkReceiver, EphemeraToNetworkSender, + }, + network_sender::{ + EphemeraNetworkCommunication, GroupChangeEvent, + GroupChangeEvent::{LocalPeerRemoved, NotEnoughPeers}, + NetCommunicationReceiver, NetCommunicationSender, NetworkEvent, + }, + }, +}; + +pub(crate) type InitSwarm

= ( + SwarmNetwork

, + NetCommunicationReceiver, + EphemeraToNetworkSender, +); + +pub struct SwarmNetwork

+where + P: Future>> + Send + Unpin + 'static, +{ + node_info: NodeInfo, + swarm: Swarm>, + from_ephemera_rcv: EphemeraToNetworkReceiver, + to_ephemera_tx: NetCommunicationSender, + ephemera_msg_topic: Topic, +} + +impl

SwarmNetwork

+where + P: Future>> + Send + Unpin + 'static, +{ + pub(crate) fn new(node_info: NodeInfo, members_provider: P) -> anyhow::Result> + where + P: Future>> + Send + 'static, + { + let (from_ephemera_tx, from_ephemera_rcv) = EphemeraToNetwork::init(); + let (to_ephemera_tx, to_ephemera_rcv) = EphemeraNetworkCommunication::init(); + + let libp2p_configuration = node_info.initial_config.libp2p.clone(); + + let local_key = node_info.keypair.clone(); + let peer_id = node_info.peer_id; + let ephemera_msg_topic = Topic::new(&libp2p_configuration.ephemera_msg_topic_name); + + let transport = create_transport(&local_key)?; + + let members_provider_delay = + std::time::Duration::from_secs(libp2p_configuration.members_provider_delay_sec); + let behaviour = create_behaviour( + &local_key, + &ephemera_msg_topic, + members_provider, + members_provider_delay, + libp2p_configuration.membership_kind.into(), + ); + + let swarm = SwarmBuilder::with_tokio_executor(transport, behaviour, peer_id.into()).build(); + + let network = SwarmNetwork { + node_info, + swarm, + from_ephemera_rcv, + to_ephemera_tx, + ephemera_msg_topic, + }; + + Ok((network, to_ephemera_rcv, from_ephemera_tx)) + } + + pub(crate) fn listen(&mut self) -> anyhow::Result<()> { + let address = + Multiaddr::from_str(&self.node_info.protocol_address()).expect("Invalid multi-address"); + self.swarm.listen_on(address.clone())?; + + info!("Listening on {address:?}"); + Ok(()) + } + + pub(crate) async fn start(mut self) -> anyhow::Result<()> { + loop { + tokio::select! { + swarm_event = self.swarm.next() => { + match swarm_event{ + Some(event) => { + if let Err(err) = self.handle_incoming_messages(event).await{ + error!("Error handling swarm event: {:?}", err); + } + } + None => { + anyhow::bail!("Swarm event channel closed"); + } + } + }, + Some(event) = self.from_ephemera_rcv.net_event_rcv.recv() => { + self.process_ephemera_events(event); + } + } + } + } + + fn process_ephemera_events(&mut self, event: EphemeraEvent) { + match event { + EphemeraEvent::EphemeraMessage(em) => { + self.send_ephemera_message(em.as_ref()); + } + EphemeraEvent::ProtocolMessage(pm) => { + self.send_broadcast_message(pm.as_ref()); + } + EphemeraEvent::StoreInDht { key, value } => { + let record = kad::Record::new(key, value); + let quorum = kad::Quorum::One; + match self + .swarm + .behaviour_mut() + .kademlia + .put_record(record, quorum) + { + Ok(ok) => { + trace!("StoreDht: {:?}", ok); + } + Err(err) => { + error!("StoreDht: {:?}", err); + } + } + } + EphemeraEvent::QueryDht { key } => { + let kad_key = kad::record::Key::new::>(key.as_ref()); + let query_id = self.swarm.behaviour_mut().kademlia.get_record(kad_key); + trace!("QueryDht: {:?}", query_id); + } + } + } + + async fn handle_incoming_messages( + &mut self, + swarm_event: SwarmEvent, + ) -> anyhow::Result<()> { + if let SwarmEvent::Behaviour(b) = swarm_event { + if let Err(err) = self.process_group_behaviour_event(b).await { + error!("Error handling behaviour event: {:?}", err); + } + } else { + Self::process_other_swarm_events(swarm_event); + } + Ok(()) + } + + async fn process_group_behaviour_event( + &mut self, + event: GroupBehaviourEvent, + ) -> anyhow::Result<()> { + match event { + GroupBehaviourEvent::Gossipsub(gs) => { + if let Err(err) = self.process_gossipsub_event(gs).await { + error!("Error processing gossipsub event: {:?}", err); + } + } + GroupBehaviourEvent::RequestResponse(request_response) => { + if let Err(err) = self.process_request_response(request_response).await { + error!("Error processing request response: {:?}", err); + } + } + + GroupBehaviourEvent::Membership(event) => { + if let Err(err) = self.process_members_provider_event(event).await { + error!("Error processing rendezvous event: {:?}", err); + } + } + GroupBehaviourEvent::Kademlia(ev) => { + if let Err(err) = self.process_kad_event(ev).await { + error!("Error processing kademlia event: {:?}", err); + } + } // GroupBehaviourEvent::Ping(_) => {} + } + Ok(()) + } + + async fn process_gossipsub_event(&mut self, event: gossipsub::Event) -> anyhow::Result<()> { + match event { + gossipsub::Event::Message { + propagation_source: _, + message_id: _, + message, + } => { + let msg: EphemeraMessage = serde_json::from_slice(&message.data[..])?; + self.to_ephemera_tx + .send_network_event(NetworkEvent::EphemeraMessage(msg.into())) + .await?; + } + + gossipsub::Event::Subscribed { peer_id, topic } => { + trace!("Peer {peer_id:?} subscribed to topic {topic:?}"); + } + gossipsub::Event::Unsubscribed { peer_id, topic } => { + trace!("Peer {peer_id:?} unsubscribed from topic {topic:?}"); + } + gossipsub::Event::GossipsubNotSupported { peer_id } => { + trace!("Peer {peer_id:?} does not support gossipsub"); + } + } + Ok(()) + } + + async fn process_request_response( + &mut self, + event: request_response::Event, + ) -> anyhow::Result<()> { + match event { + request_response::Event::Message { peer, message } => match message { + request_response::Message::Request { + request_id: _, + request, + channel, + } => { + let rb_id = request.id.clone(); + trace!("Received request {:?}", request); + self.to_ephemera_tx + .send_network_event(NetworkEvent::BroadcastMessage(request.into())) + .await?; + if let Err(err) = self + .swarm + .behaviour_mut() + .request_response + .send_response(channel, RbMsgResponse::new(rb_id)) + { + error!("Error sending response: {:?}", err); + } + } + request_response::Message::Response { + request_id, + response, + } => { + trace!("Received response {response:?} from peer: {peer:?}, request_id: {request_id:?}",); + } + }, + request_response::Event::OutboundFailure { + peer, + request_id, + error, + } => { + error!("Outbound failure: {error:?}, peer:{peer:?}, request_id:{request_id:?}",); + } + request_response::Event::InboundFailure { + peer, + request_id, + error, + } => { + error!("Inbound failure: {error:?}, peer:{peer:?}, request_id:{request_id:?}",); + } + request_response::Event::ResponseSent { peer, request_id } => { + trace!("Response sent to peer: {peer:?}, {request_id:?}",); + } + } + Ok(()) + } + + async fn process_members_provider_event( + &mut self, + event: behaviours::membership::behaviour::Event, + ) -> anyhow::Result<()> { + match event { + behaviours::membership::behaviour::Event::PeersUpdated(peers_ids) => { + info!("Peers updated: {:?}", peers_ids); + + let local_peer_id = *self.swarm.local_peer_id(); + for peer_id in peers_ids { + if peer_id == local_peer_id { + continue; + } + //FIXME: deprecated + #[allow(deprecated)] + let address = self + .swarm + .behaviour_mut() + .members_provider + .addresses_of_peer(&peer_id); + if let Some(address) = address.first() { + self.swarm + .behaviour_mut() + .kademlia + .add_address(&peer_id, address.clone()); + } + } + + let query_id = self + .swarm + .behaviour_mut() + .kademlia + .get_closest_peers(libp2p::PeerId::random()); + debug!("Neighbours: {:?}", query_id); + } + behaviours::membership::behaviour::Event::PeerUpdatePending => { + info!("Peer update pending"); + } + behaviours::membership::behaviour::Event::LocalRemoved(peers_ids) => { + //TODO: should pause all network block and message activities...? + let peers_ids = peers_ids.into_iter().map(Into::into).collect(); + let update = NetworkEvent::GroupUpdate(LocalPeerRemoved(peers_ids)); + self.to_ephemera_tx.send_network_event(update).await?; + } + behaviours::membership::behaviour::Event::NotEnoughPeers(peers_ids) => { + //TODO: should pause all network block and message activities...? + let peers_ids = peers_ids.into_iter().map(Into::into).collect(); + let update = NetworkEvent::GroupUpdate(NotEnoughPeers(peers_ids)); + self.to_ephemera_tx.send_network_event(update).await?; + } + } + Ok(()) + } + + async fn process_kad_event(&mut self, event: kad::KademliaEvent) -> anyhow::Result<()> { + match event { + kad::KademliaEvent::OutboundQueryProgressed { + id, + result, + stats, + step, + } => { + trace!( + "Outbound query progressed: id:{:?}, result:{:?}, stats:{:?}, step:{:?}", + id, + result, + stats, + step + ); + match result { + kad::QueryResult::GetClosestPeers(gcp) => { + self.process_closest_peers(gcp).await?; + } + kad::QueryResult::GetRecord(get_res) => { + self.process_get_record(get_res).await?; + } + kad::QueryResult::Bootstrap(bt) => { + trace!("Bootstrap: {:?}", bt); + } + kad::QueryResult::GetProviders(gp) => { + trace!("GetProviders: {:?}", gp); + } + kad::QueryResult::StartProviding(sp) => { + trace!("StartProviding: {:?}", sp); + } + kad::QueryResult::RepublishProvider(rp) => { + trace!("RepublishProvider: {:?}", rp); + } + kad::QueryResult::PutRecord(pr) => { + trace!("PutRecord: {:?}", pr); + } + kad::QueryResult::RepublishRecord(rr) => { + trace!("RepublishRecord: {:?}", rr); + } + } + } + + kad::KademliaEvent::InboundRequest { request } => { + trace!("Inbound request: {:?}", request); + } + + kad::KademliaEvent::RoutingUpdated { + peer: peer_id, + is_new_peer: _, + addresses, + bucket_range: _, + old_peer: _, + } => { + trace!("Routing updated: peer:{peer_id}, addresses:{addresses:?}",); + } + kad::KademliaEvent::UnroutablePeer { peer } => { + trace!("Unroutable peer: {:?}", peer); + } + kad::KademliaEvent::RoutablePeer { peer, address } => { + trace!("Routable peer: {:?}, address: {:?}", peer, address); + } + kad::KademliaEvent::PendingRoutablePeer { peer, address } => { + trace!("Pending routable peer: {:?}, address: {:?}", peer, address); + } + } + Ok(()) + } + + async fn process_get_record(&mut self, get_res: GetRecordResult) -> anyhow::Result<()> { + trace!("GetRecord: {:?}", get_res); + match get_res { + Ok(ok) => match ok { + kad::GetRecordOk::FoundRecord(fr) => { + let record = fr.record; + let event = NetworkEvent::QueryDhtResponse { + key: record.key.to_vec(), + value: record.value, + }; + self.to_ephemera_tx.send_network_event(event).await?; + } + kad::GetRecordOk::FinishedWithNoAdditionalRecord { .. } => { + trace!("FinishedWithNoAdditionalRecord"); + } + }, + Err(err) => { + trace!("Not getting record: {:?}", err); + } + } + Ok(()) + } + + async fn process_closest_peers(&mut self, gcp: GetClosestPeersResult) -> anyhow::Result<()> { + trace!("GetClosestPeers: {:?}", gcp); + //TODO: we need also to make sure that we have enough peers + // (Repeat if not enough, may need to wait network to stabilize) + match gcp { + Ok(cp) => { + if cp.peers.is_empty() { + log::warn!("No peers found"); + return Ok(()); + } + + let gossipsub = &mut self.swarm.behaviour_mut().gossipsub; + for peer_id in cp.peers { + gossipsub.add_explicit_peer(&peer_id); + } + + let active_peers = self + .swarm + .behaviour_mut() + .members_provider + .active_peer_ids_with_local(); + let active_peers = active_peers + .into_iter() + .map(Into::into) + .collect::>(); + let group_update = + NetworkEvent::GroupUpdate(GroupChangeEvent::PeersUpdated(active_peers)); + self.to_ephemera_tx.send_network_event(group_update).await?; + } + Err(err) => { + error!("Error getting closest peers: {:?}", err); + } + } + Ok(()) + } + + fn send_broadcast_message(&mut self, msg: &RbMsg) { + trace!("Sending broadcast message: {:?}", msg); + let local_peer_id = *self.swarm.local_peer_id(); + let behaviours = self.swarm.behaviour_mut(); + for peer in behaviours.members_provider.active_peer_ids() { + trace!("Sending broadcast message: {:?} to peer: {peer:?}", msg.id,); + if *peer == local_peer_id { + continue; + } + behaviours.request_response.send_request(peer, msg.clone()); + } + } + + fn send_ephemera_message(&mut self, msg: &EphemeraMessage) { + trace!("Sending Ephemera message: {:?}", msg); + match msg.encode() { + Ok(vec) => { + let topic = self.ephemera_msg_topic.clone(); + if let Err(err) = self.swarm.behaviour_mut().gossipsub.publish(topic, vec) { + error!("Error publishing message: {}", err); + } + } + Err(err) => { + error!("Error serializing message: {}", err); + } + } + } + + //Just logging + #[allow(clippy::too_many_lines)] + fn process_other_swarm_events(swarm_event: SwarmEvent) { + match swarm_event { + SwarmEvent::ConnectionEstablished { + peer_id, + endpoint, + num_established, + concurrent_dial_errors, + established_in, + } => { + trace!("Connection established: peer_id:{:?}, endpoint:{:?}, num_established:{:?}, concurrent_dial_errors:{:?}, established_in:{:?}", peer_id, endpoint, num_established, concurrent_dial_errors, established_in); + } + SwarmEvent::ConnectionClosed { + peer_id, + endpoint, + num_established, + cause: _, + } => { + trace!( + "Connection closed: peer_id:{:?}, endpoint:{:?}, num_established:{:?}", + peer_id, + endpoint, + num_established + ); + } + SwarmEvent::IncomingConnection { + local_addr, + send_back_addr, + } => { + trace!( + "Incoming connection: local_addr:{:?}, send_back_addr:{:?}", + local_addr, + send_back_addr + ); + } + SwarmEvent::IncomingConnectionError { + local_addr, + send_back_addr, + error, + } => { + trace!( + "Incoming connection error: local_addr:{:?}, send_back_addr:{:?}, error:{:?}", + local_addr, + send_back_addr, + error + ); + } + SwarmEvent::OutgoingConnectionError { peer_id, error } => { + trace!( + "Outgoing connection error: peer_id:{:?}, error:{:?}", + peer_id, + error + ); + } + #[allow(deprecated)] + SwarmEvent::BannedPeer { peer_id, endpoint } => { + trace!( + "Banned peer: peer_id:{:?}, endpoint:{:?}", + peer_id, + endpoint + ); + } + SwarmEvent::NewListenAddr { + listener_id, + address, + } => { + trace!( + "New listen address: listener_id:{:?}, address:{:?}", + listener_id, + address + ); + } + SwarmEvent::ExpiredListenAddr { + listener_id, + address, + } => { + trace!( + "Expired listen address: listener_id:{:?}, address:{:?}", + listener_id, + address + ); + } + SwarmEvent::ListenerClosed { + listener_id, + addresses, + reason, + } => { + trace!( + "Listener closed: listener_id:{:?}, addresses:{:?}, reason:{:?}", + listener_id, + addresses, + reason + ); + } + SwarmEvent::ListenerError { listener_id, error } => { + trace!( + "Listener error: listener_id:{:?}, error:{:?}", + listener_id, + error + ); + } + SwarmEvent::Dialing(peer_id) => { + trace!("Dialing: {peer_id:?}",); + } + + SwarmEvent::Behaviour(_) => { + trace!("Unexpected behaviour event"); + } + } + } +} diff --git a/ephemera/src/network/members/mod.rs b/ephemera/src/network/members/mod.rs new file mode 100644 index 0000000000..efb99c802a --- /dev/null +++ b/ephemera/src/network/members/mod.rs @@ -0,0 +1,252 @@ +use std::fmt::Display; +use std::future::Future; +use std::io::Write; +use std::path::PathBuf; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use futures_util::{future, FutureExt}; +use log::error; +use nym_ephemera_common::types::JsonPeerInfo; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use crate::crypto::PublicKey; +use crate::network::{Address, Peer}; +use crate::peer::PeerId; + +/// Information about an Ephemera peer. +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct PeerInfo { + /// The cosmos address of the peer, used in interacting with the chain. + pub cosmos_address: String, + /// The address of the peer. + /// Expected formats: + /// 1. `:` + /// 2. `/ip4//tcp/` - this is the format used by libp2p multiaddr + pub address: String, + /// The public key of the peer. It uniquely identifies the peer. + /// Public key is used to derive the peer id. + pub pub_key: PublicKey, +} + +impl Display for PeerInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "cosmos address {}, address {}, public key {}", + self.cosmos_address, self.address, self.pub_key + ) + } +} + +impl TryFrom for Peer { + type Error = anyhow::Error; + + fn try_from(value: PeerInfo) -> std::result::Result { + let address: Address = value.address.parse()?; + let public_key = value.pub_key; + Ok(Self { + cosmos_address: value.cosmos_address, + address, + public_key: public_key.clone(), + peer_id: PeerId::from_public_key(&public_key), + }) + } +} + +#[derive(Error, Debug)] +pub enum ProviderError { + #[error("ResourceUnavailable: {0}")] + ResourceUnavailable(String), + #[error("MembersProvider: {0}")] + MembersProvider(#[from] anyhow::Error), + #[error("Could not get peers - {0}")] + GetPeers(String), +} + +pub type Result = std::result::Result; + +/// A membership provider that does nothing. +/// Might be useful for testing. +pub struct DummyMembersProvider; + +#[allow(clippy::missing_errors_doc, clippy::unused_async)] +impl DummyMembersProvider { + pub async fn empty_peers_list() -> Result> { + Ok(vec![]) + } +} + +#[derive(Error, Debug)] +pub enum ConfigMembersProviderError { + #[error("ConfigDoesNotExist: '{0}'")] + NotExist(String), + #[error("ParsingFailed: {0}")] + ParsingFailed(#[from] config::ConfigError), + #[error("TomlError: {0}")] + TomlError(#[from] toml::ser::Error), + #[error("IoError: {0}")] + IoError(#[from] std::io::Error), +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct PeerSetting { + /// The cosmos address of the peer, used in interacting with the chain. + pub cosmos_address: String, + /// The address of the peer. + /// Expected formats: + /// 1. `:` + /// 2. `/ip4//tcp/` - this is the format used by libp2p multiaddr + pub address: String, + ///Serialized public key. + /// + /// # Converting to string and back example + ///``` + /// use ephemera::crypto::{EphemeraKeypair, EphemeraPublicKey, Keypair, PublicKey}; + /// + /// let public_key = Keypair::generate(None).public_key(); + /// + /// let public_key_str = public_key.to_string(); + /// + /// let public_key_parsed = public_key_str.parse::().unwrap(); + /// + /// assert_eq!(public_key, public_key_parsed); + /// ``` + pub public_key: String, +} + +impl TryFrom for PeerInfo { + type Error = anyhow::Error; + + fn try_from(setting: PeerSetting) -> std::result::Result { + let pub_key = setting.public_key.parse::()?; + Ok(PeerInfo { + cosmos_address: setting.cosmos_address, + address: setting.address, + pub_key, + }) + } +} + +///[`ProviderFut`] that reads the peers from a toml config file. +/// +/// # Configuration example +/// ```toml +/// [[peers]] +/// name = "node1" +/// address = "/ip4/127.0.0.1/tcp/3000" +/// pub_key = "4XTTMEghav9LZThm6opUaHrdGEEYUkrfkakVg4VAetetBZDWJ" +/// +/// [[peers]] +/// name = "node2" +/// address = "/ip4/127.0.0.1/tcp/3001" +/// pub_key = "4XTTMFQt2tgNRmwRgEAaGQe2NXygsK6Vr3pkuBfYezhDfoVty" +/// ``` +pub struct ConfigMembersProvider { + config_location: PathBuf, +} + +impl ConfigMembersProvider { + /// Creates a new [`ConfigMembersProvider`] instance. + /// + /// # Arguments + /// * `path` - Path to the peers toml config file. + /// + /// # Errors + /// Returns [`ConfigMembersProviderError::NotExist`] if the file does not exist. + /// Returns [`ConfigMembersProviderError::ParsingFailed`] if the file is not a valid members file. + pub fn init>( + path: I, + ) -> std::result::Result { + let path_buf = path.into(); + if !path_buf.exists() { + return Err(ConfigMembersProviderError::NotExist( + path_buf.to_string_lossy().to_string(), + )); + } + + let provider = Self { + config_location: path_buf, + }; + + if provider.read_config().is_err() { + return Err(ConfigMembersProviderError::ParsingFailed( + config::ConfigError::Message("Failed to parse config".to_string()), + )); + } + + Ok(provider) + } + + pub(crate) fn read_config(&self) -> Result> { + let config_peers = ConfigPeers::try_load(self.config_location.clone()) + .map_err(|err| anyhow::anyhow!(err))?; + + let peers = config_peers + .peers + .iter() + .map(|peer| PeerInfo::try_from(peer.clone())) + .collect::>>()?; + Ok(peers) + } +} + +impl Future for ConfigMembersProvider { + type Output = Result>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + future::ready(self.read_config()).poll_unpin(cx) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ConfigPeers { + peers: Vec, +} + +impl ConfigPeers { + pub(crate) fn new(peers: Vec) -> Self { + Self { peers } + } + + pub(crate) fn try_load>( + path: I, + ) -> std::result::Result { + let path = path.into(); + let config = config::Config::builder() + .add_source(config::File::from(path)) + .build()?; + + config.try_deserialize().map_err(Into::into) + } + + pub(crate) fn try_write>( + &self, + path: I, + ) -> std::result::Result<(), ConfigMembersProviderError> { + let config = toml::to_string(&self)?; + + let config = format!( + "#This file is generated by cli and automatically overwritten every time when cli is §\n{config}", + ); + + let mut file = std::fs::File::create(path.into())?; + file.write_all(config.as_bytes())?; + + Ok(()) + } +} + +impl TryFrom for PeerInfo { + type Error = anyhow::Error; + + fn try_from(json_peer_info: JsonPeerInfo) -> std::result::Result { + let pub_key = json_peer_info.public_key.parse::()?; + Ok(PeerInfo { + cosmos_address: json_peer_info.cosmos_address.to_string(), + address: json_peer_info.ip_address, + pub_key, + }) + } +} diff --git a/ephemera/src/network/mod.rs b/ephemera/src/network/mod.rs new file mode 100644 index 0000000000..205e147903 --- /dev/null +++ b/ephemera/src/network/mod.rs @@ -0,0 +1,237 @@ +use std::fmt::Display; +use std::net::IpAddr; +use std::str::FromStr; + +use ::libp2p::{multiaddr::Protocol, Multiaddr}; +use libp2p_identity::PeerId as Libp2pPeerId; +use log::info; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use crate::crypto::PublicKey; + +pub(crate) mod libp2p; +pub(crate) mod members; + +pub(crate) type PeerIdType = Libp2pPeerId; + +#[derive(Debug, Error)] +pub enum PeerIdError { + #[error("Invalid peer ID: {0}")] + InvalidPeerId(String), +} + +/// Unique identifier of a peer. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct PeerId(pub(crate) PeerIdType); + +impl PeerId { + #[must_use] + pub fn random() -> Self { + Self(PeerIdType::random()) + } + + /// Returns the internal representation of the peer ID. + pub(crate) fn inner(&self) -> &PeerIdType { + &self.0 + } + + /// Returns a raw representation of the peer ID. + #[must_use] + pub fn to_bytes(&self) -> Vec { + self.0.to_bytes() + } + + /// Returns a peer ID from a raw representation. + /// + /// # Returns + /// A `PeerId` if the bytes are valid. + /// + /// # Errors + /// An error if input has wrong format. This function is reverse to [`PeerId::to_bytes`]. + pub fn from_bytes(bytes: &[u8]) -> Result { + Ok(Self(PeerIdType::from_bytes(bytes).map_err(|e| { + PeerIdError::InvalidPeerId(format!("Invalid peer ID: {e}")) + })?)) + } + + /// Builds a `PeerId` from a public key. + #[must_use] + pub fn from_public_key(public_key: &PublicKey) -> Self { + Self(PeerIdType::from_public_key(public_key.inner())) + } +} + +impl From for libp2p_identity::PeerId { + fn from(peer_id: PeerId) -> Self { + peer_id.0 + } +} + +impl From for PeerId { + fn from(peer_id: libp2p_identity::PeerId) -> Self { + Self(peer_id) + } +} + +impl Display for PeerId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +pub trait ToPeerId { + fn peer_id(&self) -> PeerId; +} + +/// A peer of the network. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct Peer { + /// The peer's ID. It identifies the peer uniquely and is derived from its public key. + /// + /// # Deriving PeerId from PublicKey example + /// + /// ``` + /// use ephemera::crypto::{EphemeraKeypair, Keypair, PublicKey}; + /// use ephemera::peer::{PeerId, ToPeerId}; + /// + /// let public_key = Keypair::generate(None).public_key(); + /// + /// let peer_id = PeerId::from_public_key(&public_key); + /// + /// assert_eq!(peer_id, public_key.peer_id()); + /// + /// ``` + pub peer_id: PeerId, + /// The peer's public key. It matches PeerId. + pub public_key: PublicKey, + /// The peer's address. + pub address: Address, + /// The cosmos address of the peer, used in interacting with the chain. + pub cosmos_address: String, +} + +#[derive(Error, Debug)] +pub enum AddressError { + #[error("Failed to parse address: {0}")] + ParsingError(String), +} + +/// Ephemera node address. +/// +/// Supported formats: +/// 1. `:` +/// 2. `/ip4//tcp/` - this is format used by libp2p multiaddr. +/// 3. `/dns4//tcp/` - this is format used by libp2p multiaddr. +/// See [libp2p/multiaddress](https://github.com/libp2p/specs/blob/master/addressing/README.md) for more details. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Address(pub Multiaddr); + +impl Address { + pub fn inner(&self) -> &Multiaddr { + &self.0 + } +} + +impl From for Address { + fn from(multiaddr: Multiaddr) -> Self { + Self(multiaddr) + } +} + +impl FromStr for Address { + type Err = AddressError; + + fn from_str(s: &str) -> Result { + let address: Option = match Multiaddr::from_str(s) { + Ok(multiaddr) => Some(multiaddr), + Err(err) => { + info!("Failed to parse multiaddr: {}", err); + None + } + }; + + let multi_address = address.or_else(|| match std::net::SocketAddr::from_str(s) { + Ok(sa) => { + let mut multiaddr = Multiaddr::empty(); + match sa { + std::net::SocketAddr::V4(v4) => { + multiaddr.push(Protocol::Ip4(*v4.ip())); + multiaddr.push(Protocol::Tcp(v4.port())); + } + std::net::SocketAddr::V6(v6) => { + multiaddr.push(Protocol::Ip6(*v6.ip())); + multiaddr.push(Protocol::Tcp(v6.port())); + } + } + + Some(multiaddr) + } + Err(err) => { + info!("Failed to parse socket addr: {err}"); + None + } + }); + + match multi_address { + Some(multi_address) => Ok(Self(multi_address)), + None => Err(AddressError::ParsingError(s.to_string())), + } + } +} + +impl TryFrom

for (IpAddr, u16) { + type Error = std::io::Error; + + fn try_from(addr: Address) -> Result { + let mut multiaddr = addr.0; + if let Some(Protocol::Tcp(port)) = multiaddr.pop() { + if let Some(Protocol::Ip4(ip)) = multiaddr.pop() { + return Ok((IpAddr::V4(ip), port)); + } + } + Err(std::io::Error::new( + std::io::ErrorKind::Other, + "invalid address", + )) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_parse_multiaddr() { + "/ip4/127.0.0.1/tcp/1234".parse::
().unwrap(); + } + + #[test] + fn test_parse_ip_port() { + "127.0.0.1:1234".parse::
().unwrap(); + } + + #[test] + fn test_fail_parse_multiaddr_without_port() { + let result = "/ip4/127.0.0.1/tcp/".parse::
(); + assert!(matches!(result, Err(AddressError::ParsingError(_)))); + } + + #[test] + fn test_fail_parse_multiaddr_without_ip() { + let result = "/ip4//tcp/1234".parse::
(); + assert!(matches!(result, Err(AddressError::ParsingError(_)))); + } + + #[test] + fn test_fail_parse_ip_port_without_port() { + let result = "127.0.0.1".parse::
(); + assert!(matches!(result, Err(AddressError::ParsingError(_)))); + } + + #[test] + fn test_fail_parse_ip_port_without_ip() { + let result = "1234".parse::
(); + assert!(matches!(result, Err(AddressError::ParsingError(_)))); + } +} diff --git a/ephemera/src/storage/mod.rs b/ephemera/src/storage/mod.rs new file mode 100644 index 0000000000..2342625d19 --- /dev/null +++ b/ephemera/src/storage/mod.rs @@ -0,0 +1,65 @@ +//! # Database +//! +//! It supports `SqlLite` and `RocksDB`. +//! +//! ## `RocksDB` +//! +//! `SqlLite` is used by default. +//! +//! ## `SqlLite` +//! +//! To use `SqlLite`, you need to compile with the `sqlite_storage` feature and with `--no-default-features` flag. + +use std::collections::HashSet; + +use thiserror::Error; + +use crate::block::types::block::Block; +use crate::peer::PeerId; +use crate::utilities::crypto::Certificate; +use crate::utilities::merkle::MerkleTree; + +#[cfg(feature = "rocksdb_storage")] +pub(crate) mod rocksdb; + +#[cfg(feature = "sqlite_storage")] +pub(crate) mod sqlite; + +pub(crate) type Result = std::result::Result; + +#[derive(Error, Debug)] +pub(crate) enum DatabaseError { + //Pessimistically assume that most database errors are fatal and unrecoverable. + #[error("Database failure: {0}")] + DatabaseFailure(#[from] anyhow::Error), +} + +pub(crate) trait EphemeraDatabase: Send { + /// Returns block by its id. Block ids are generated by Ephemera + fn get_block_by_hash(&self, block_hash: &str) -> Result>; + + /// Returns last committed/finalised block. + fn get_last_block(&self) -> Result>; + + /// Returns block by its height + fn get_block_by_height(&self, height: u64) -> Result>; + + /// Returns block certificates. + /// + /// Certificates were created as part of broadcast protocol and signed by peers who participated. + fn get_block_certificates(&self, block_hash: &str) -> Result>>; + + /// Returns peers who participated in block broadcast. + fn get_block_broadcast_group(&self, block_hash: &str) -> Result>>; + + /// Stores block and its signatures + fn store_block( + &mut self, + block: &Block, + certificates: HashSet, + members: HashSet, + ) -> Result<()>; + + /// Returns block merkle tree + fn get_block_merkle_tree(&self, block_hash: &str) -> Result>; +} diff --git a/ephemera/src/storage/rocksdb/mod.rs b/ephemera/src/storage/rocksdb/mod.rs new file mode 100644 index 0000000000..24e5916bc3 --- /dev/null +++ b/ephemera/src/storage/rocksdb/mod.rs @@ -0,0 +1,125 @@ +use std::collections::HashSet; +use std::sync::Arc; + +use log::info; +use rocksdb::{TransactionDB, TransactionDBOptions}; + +use crate::block::types::block::Block; +use crate::config::DatabaseConfiguration; +use crate::peer::PeerId; +use crate::storage::rocksdb::query::Database; +use crate::storage::rocksdb::store::DbStore; +use crate::storage::EphemeraDatabase; +use crate::storage::Result; +use crate::utilities::crypto::Certificate; +use crate::utilities::merkle::MerkleTree; + +pub(crate) mod query; +pub(crate) mod store; + +pub(crate) struct RocksDbStorage { + pub(crate) db_store: DbStore, + pub(crate) db_query: Database, +} + +const PREFIX_LAST_BLOCK_KEY: &str = "last_block"; +const PREFIX_BLOCK_HASH: &str = "block_hash"; +const PREFIX_BLOCK_HEIGHT: &str = "block_height"; +const PREFIX_CERTIFICATES: &str = "block_certificates"; +const PREFIX_MEMBERS: &str = "block_members"; +const MERKLE_TREE: &str = "merkle_tree"; + +impl RocksDbStorage { + pub fn open(db_conf: &DatabaseConfiguration) -> Result { + info!("Opening RocksDB database at {}", db_conf.rocksdb_path); + + let mut options = rocksdb::Options::default(); + options.create_if_missing(db_conf.create_if_not_exists); + + let db = TransactionDB::open( + &options, + &TransactionDBOptions::default(), + db_conf.rocksdb_path.clone(), + ) + .map_err(|err| anyhow::anyhow!(err))?; + + let db = Arc::new(db); + let db_store = DbStore::new(db.clone()); + let db_query = Database::new(db); + let storage = Self { db_store, db_query }; + + info!("Opened RocksDB database at {}", db_conf.rocksdb_path); + Ok(storage) + } +} + +impl EphemeraDatabase for RocksDbStorage { + fn get_block_by_hash(&self, block_id: &str) -> Result> { + self.db_query + .get_block_by_hash(block_id) + .map_err(Into::into) + } + + fn get_last_block(&self) -> Result> { + self.db_query.get_last_block().map_err(Into::into) + } + + fn get_block_by_height(&self, height: u64) -> Result> { + self.db_query + .get_block_by_height(height) + .map_err(Into::into) + } + + fn get_block_certificates(&self, block_id: &str) -> Result>> { + self.db_query + .get_block_certificates(block_id) + .map_err(Into::into) + } + + fn get_block_broadcast_group(&self, block_id: &str) -> Result>> { + self.db_query + .get_block_broadcast_group(block_id) + .map_err(Into::into) + } + + fn store_block( + &mut self, + block: &Block, + certificates: HashSet, + members: HashSet, + ) -> Result<()> { + self.db_store + .store_block(block, certificates, members) + .map_err(Into::into) + } + + fn get_block_merkle_tree(&self, block_hash: &str) -> Result> { + self.db_query + .get_block_merkle_tree(block_hash) + .map_err(Into::into) + } +} + +fn block_hash_key(block_hash: &str) -> String { + format!("{PREFIX_BLOCK_HASH}:{block_hash}") +} + +fn block_height_key(height: u64) -> String { + format!("{PREFIX_BLOCK_HEIGHT}:{height}") +} + +fn last_block_key() -> String { + PREFIX_LAST_BLOCK_KEY.to_string() +} + +fn certificates_key(block_hash: &str) -> String { + format!("{PREFIX_CERTIFICATES}:{block_hash}",) +} + +fn members_key(block_hash: &str) -> String { + format!("{PREFIX_MEMBERS}:{block_hash}",) +} + +fn merkle_tree_key(block_hash: &str) -> String { + format!("{MERKLE_TREE}:{block_hash}",) +} diff --git a/ephemera/src/storage/rocksdb/query.rs b/ephemera/src/storage/rocksdb/query.rs new file mode 100644 index 0000000000..a41bde78e9 --- /dev/null +++ b/ephemera/src/storage/rocksdb/query.rs @@ -0,0 +1,118 @@ +use std::sync::Arc; + +use log::trace; +use rocksdb::TransactionDB; + +use crate::block::types::block::Block; +use crate::network::PeerId; +use crate::storage::rocksdb::{ + block_hash_key, block_height_key, certificates_key, last_block_key, members_key, + merkle_tree_key, +}; +use crate::utilities::crypto::Certificate; +use crate::utilities::merkle::MerkleTree; + +pub struct Database { + database: Arc, +} + +impl Database { + #[allow(dead_code)] + pub fn new(db: Arc) -> Database { + Database { database: db } + } + + pub(crate) fn get_block_by_hash(&self, block_hash: &str) -> anyhow::Result> { + trace!("Getting block by id: {:?}", block_hash); + + let block_hash_key = block_hash_key(block_hash); + + let block = if let Some(block) = self.database.get(block_hash_key)? { + let block = serde_json::from_slice::(&block)?; + trace!("Found block: {}", block.header); + Some(block) + } else { + trace!("Didn't find block"); + None + }; + Ok(block) + } + + pub(crate) fn get_last_block(&self) -> anyhow::Result> { + trace!("Getting last block"); + + if let Some(block_hash) = self.database.get(last_block_key())? { + let block_hash = String::from_utf8(block_hash)?; + self.get_block_by_hash(&block_hash) + } else { + trace!("Unable to get last block"); + Ok(None) + } + } + + pub(crate) fn get_block_by_height(&self, height: u64) -> anyhow::Result> { + trace!("Getting block by height: {}", height); + + if let Some(block_hash) = self.database.get(block_height_key(height))? { + let block_hash = String::from_utf8(block_hash)?; + self.get_block_by_hash(&block_hash) + } else { + trace!("Didn't find block"); + Ok(None) + } + } + + pub(crate) fn get_block_certificates( + &self, + block_hash: &str, + ) -> anyhow::Result>> { + trace!("Getting block signatures: {}", block_hash); + + let certificates_key = certificates_key(block_hash); + + if let Some(certificates) = self.database.get(certificates_key)? { + let certificates: Vec = serde_json::from_slice(&certificates)?; + trace!("Found certificates: {:?}", certificates); + Ok(Some(certificates)) + } else { + trace!("Didn't find signatures"); + Ok(None) + } + } + + pub(crate) fn get_block_broadcast_group( + &self, + block_hash: &str, + ) -> anyhow::Result>> { + trace!("Getting block broadcast group: {}", block_hash); + + let members_key = members_key(block_hash); + + if let Some(members) = self.database.get(members_key)? { + let members: Vec = serde_json::from_slice(&members)?; + trace!("Found members: {:?}", members); + Ok(Some(members)) + } else { + trace!("Didn't find members"); + Ok(None) + } + } + + pub(crate) fn get_block_merkle_tree( + &self, + block_hash: &str, + ) -> anyhow::Result> { + trace!("Getting block merkle tree: {}", block_hash); + + let merkle_tree_key = merkle_tree_key(block_hash); + + if let Some(merkle_tree) = self.database.get(merkle_tree_key)? { + let merkle_tree: MerkleTree = serde_json::from_slice(&merkle_tree)?; + trace!("Found merkle tree: {:?}", merkle_tree); + Ok(Some(merkle_tree)) + } else { + trace!("Didn't find merkle tree"); + Ok(None) + } + } +} diff --git a/ephemera/src/storage/rocksdb/store.rs b/ephemera/src/storage/rocksdb/store.rs new file mode 100644 index 0000000000..9b65137863 --- /dev/null +++ b/ephemera/src/storage/rocksdb/store.rs @@ -0,0 +1,80 @@ +use std::collections::HashSet; +use std::sync::Arc; + +use crate::block::types::block::Block; +use crate::network::PeerId; +use crate::storage::rocksdb::{ + block_hash_key, block_height_key, certificates_key, last_block_key, members_key, + merkle_tree_key, +}; +use log::{debug, trace}; +use rocksdb::{TransactionDB, WriteBatchWithTransaction}; + +use crate::utilities::crypto::Certificate; + +#[allow(clippy::module_name_repetitions)] +pub struct DbStore { + connection: Arc, +} + +impl DbStore { + pub fn new(db: Arc) -> DbStore { + DbStore { connection: db } + } + + pub(crate) fn store_block( + &self, + block: &Block, + certificates: HashSet, + members: HashSet, + ) -> anyhow::Result<()> { + debug!("Storing block: {}", block.header); + trace!("Storing block certificates: {}", certificates.len()); + + let hash_str = block.header.hash.to_string(); + + let block_id_key = block_hash_key(&hash_str); + let certificates_key = certificates_key(&hash_str); + let height_key = block_height_key(block.header.height); + let members_key = members_key(&hash_str); + let merkle_tree_key = merkle_tree_key(&hash_str); + + // Check UNIQUE constraints + let existing_id = self.connection.get(&block_id_key)?; + if existing_id.is_some() { + return Err(anyhow::anyhow!("Block already exists")); + } + + let mut batch = WriteBatchWithTransaction::::default(); + + //Store last block id(without prefix!) + //May want to check that height is incremented by 1 + batch.put(last_block_key(), hash_str.clone()); + + // Store block height + batch.put(height_key.as_bytes(), hash_str); + + // Store block(without signature) + let block_bytes = serde_json::to_vec::(block)?; + batch.put(block_id_key.as_bytes(), block_bytes); + + // Store block certificates + let certificates_bytes = + serde_json::to_vec(&certificates.into_iter().collect::>()) + .map_err(|e| anyhow::anyhow!(e))?; + batch.put(certificates_key.as_bytes(), certificates_bytes); + + // Store block members + let members_bytes = serde_json::to_vec(&members.into_iter().collect::>()) + .map_err(|e| anyhow::anyhow!(e))?; + batch.put(members_key.as_bytes(), members_bytes); + + //Store Merkle Tree + let merkle_tree = block.merkle_tree()?; + let merkle_tree_bytes = serde_json::to_vec(&merkle_tree).map_err(|e| anyhow::anyhow!(e))?; + batch.put(merkle_tree_key.as_bytes(), merkle_tree_bytes); + + self.connection.write(batch)?; + Ok(()) + } +} diff --git a/ephemera/src/storage/sqlite/mod.rs b/ephemera/src/storage/sqlite/mod.rs new file mode 100644 index 0000000000..798e9eb501 --- /dev/null +++ b/ephemera/src/storage/sqlite/mod.rs @@ -0,0 +1,107 @@ +use log::{error, info}; +use rusqlite::Connection; +use std::collections::HashSet; + +use crate::block::types::block::Block; +use crate::config::DatabaseConfiguration; +use crate::peer::PeerId; +use crate::storage::sqlite::query::DbQuery; +use crate::storage::sqlite::store::Database; +use crate::storage::EphemeraDatabase; +use crate::storage::Result; +use crate::utilities::crypto::Certificate; +use crate::utilities::merkle::MerkleTree; + +pub(crate) mod query; +pub(crate) mod store; + +mod migrations { + use refinery::embed_migrations; + + embed_migrations!("migrations"); +} + +pub(crate) struct SqliteStorage { + pub(crate) db_store: Database, + pub(crate) db_query: DbQuery, +} + +impl SqliteStorage { + pub(crate) fn open(db_conf: DatabaseConfiguration) -> Result { + let mut flags = rusqlite::OpenFlags::default(); + if !db_conf.create_if_not_exists { + flags.remove(rusqlite::OpenFlags::SQLITE_OPEN_CREATE); + } + + let mut connection = Connection::open_with_flags(db_conf.sqlite_path.clone(), flags) + .map_err(|err| anyhow::anyhow!(err))?; + Self::run_migrations(&mut connection)?; + + info!("Starting db backend with path: {}", db_conf.sqlite_path); + let db_store = Database::open(db_conf.clone(), flags)?; + let db_query = DbQuery::open(db_conf, flags)?; + let storage = Self { db_store, db_query }; + Ok(storage) + } + + pub(crate) fn run_migrations(connection: &mut Connection) -> Result<()> { + info!("Running database migrations"); + match migrations::migrations::runner().run(connection) { + Ok(ok) => { + info!("Database migrations completed:{:?} ", ok); + Ok(()) + } + Err(err) => { + error!("Database migrations failed: {}", err); + Err(anyhow::anyhow!(err).into()) + } + } + } +} + +impl EphemeraDatabase for SqliteStorage { + fn get_block_by_hash(&self, block_id: &str) -> Result> { + self.db_query + .get_block_by_hash(block_id) + .map_err(Into::into) + } + + fn get_last_block(&self) -> Result> { + self.db_query.get_last_block().map_err(Into::into) + } + + fn get_block_by_height(&self, height: u64) -> Result> { + self.db_query + .get_block_by_height(height) + .map_err(Into::into) + } + + fn get_block_certificates(&self, block_id: &str) -> Result>> { + self.db_query + .get_block_certificates(block_id) + .map_err(Into::into) + } + + fn get_block_broadcast_group(&self, block_id: &str) -> Result>> { + self.db_query + .get_block_broadcast_group(block_id) + .map_err(Into::into) + } + + fn store_block( + &mut self, + block: &Block, + certificates: HashSet, + members: HashSet, + ) -> Result<()> { + self.db_store + .store_block(block, certificates, members) + .map_err(Into::into) + } + + fn get_block_merkle_tree(&self, block_hash: &str) -> Result> { + self.db_query + .get_block_merkle_tree(block_hash) + .map_err(Into::into) + } +} diff --git a/ephemera/src/storage/sqlite/query.rs b/ephemera/src/storage/sqlite/query.rs new file mode 100644 index 0000000000..df3b1cd342 --- /dev/null +++ b/ephemera/src/storage/sqlite/query.rs @@ -0,0 +1,167 @@ +use log::{error, trace}; +use rusqlite::{params, Connection, OpenFlags, OptionalExtension, Row}; + +use crate::block::types::block::Block; +use crate::config::DatabaseConfiguration; +use crate::peer::PeerId; +use crate::utilities::crypto::Certificate; +use crate::utilities::merkle::MerkleTree; + +pub(crate) struct DbQuery { + pub(crate) connection: Connection, +} + +impl DbQuery { + pub(crate) fn open(db_conf: DatabaseConfiguration, flags: OpenFlags) -> anyhow::Result { + let connection = Connection::open_with_flags(db_conf.sqlite_path, flags)?; + let query = Self { connection }; + Ok(query) + } + + pub(crate) fn get_block_by_hash(&self, block_hash: &str) -> anyhow::Result> { + let mut stmt = self + .connection + .prepare_cached("SELECT block FROM blocks WHERE block_hash = ?1")?; + let block = stmt + .query_row(params![block_hash], Self::map_block()) + .optional()?; + + if let Some(block) = &block { + trace!("Found block: {}", block.header); + } else { + trace!("Block not found: {}", block_hash); + }; + + Ok(block) + } + + pub(crate) fn get_last_block(&self) -> anyhow::Result> { + let mut stmt = self + .connection + .prepare_cached("SELECT block FROM blocks where id = (select max(id) from blocks)")?; + + let block = stmt.query_row(params![], Self::map_block()).optional()?; + + if let Some(block) = &block { + trace!("Found last block: {}", block.header); + } else { + trace!("Last block not found"); + }; + + Ok(block) + } + + pub(crate) fn get_block_by_height(&self, height: u64) -> anyhow::Result> { + let mut stmt = self + .connection + .prepare_cached("SELECT block FROM blocks WHERE height = ?1")?; + let block = stmt + .query_row(params![height], Self::map_block()) + .optional()?; + + if let Some(block) = &block { + trace!("Found block: {}", block.header); + } else { + trace!("Block not found: {}", height); + }; + + Ok(block) + } + + pub(crate) fn get_block_certificates( + &self, + block_hash: &str, + ) -> anyhow::Result>> { + let mut stmt = self + .connection + .prepare_cached("SELECT certificates FROM block_certificates where block_hash = ?1")?; + + let signatures = stmt + .query_row(params![block_hash], |row| { + let certificates: Vec = row.get(0)?; + let certificates = serde_json::from_slice::>(&certificates) + .map_err(|e| { + error!("Error deserializing certificates: {}", e); + rusqlite::Error::InvalidQuery {} + })?; + Ok(certificates) + }) + .optional()?; + + if signatures.is_some() { + trace!("Found block {} certificates", block_hash); + } else { + trace!("Certificates not found"); + }; + + Ok(signatures) + } + + pub(crate) fn get_block_broadcast_group( + &self, + block_hash: &str, + ) -> anyhow::Result>> { + let mut stmt = self + .connection + .prepare_cached("SELECT members FROM block_broadcast_group where block_hash = ?1")?; + + let members = stmt + .query_row(params![block_hash], |row| { + let members: Vec = row.get(0)?; + let members = serde_json::from_slice::>(&members).map_err(|e| { + error!("Error deserializing members: {}", e); + rusqlite::Error::InvalidQuery {} + })?; + Ok(members) + }) + .optional()?; + + if members.is_some() { + trace!("Found block {} members", block_hash); + } else { + trace!("Members not found"); + }; + + Ok(members) + } + + pub(crate) fn get_block_merkle_tree( + &self, + block_hash: &str, + ) -> anyhow::Result> { + let mut stmt = self + .connection + .prepare_cached("SELECT merkle_tree FROM block_merkle_tree where block_hash = ?1")?; + + let merkle_tree = stmt + .query_row(params![block_hash], |row| { + let merkle_tree: Vec = row.get(0)?; + let merkle_tree = + serde_json::from_slice::(&merkle_tree).map_err(|e| { + error!("Error deserializing merkle_tree: {}", e); + rusqlite::Error::InvalidQuery {} + })?; + Ok(merkle_tree) + }) + .optional()?; + + if merkle_tree.is_some() { + trace!("Found block {} merkle_tree", block_hash); + } else { + trace!("Merkle_tree not found"); + }; + + Ok(merkle_tree) + } + + fn map_block() -> impl FnOnce(&Row) -> Result { + |row| { + let body: Vec = row.get(0)?; + let block = serde_json::from_slice::(&body).map_err(|e| { + error!("Error deserializing block: {}", e); + rusqlite::Error::InvalidQuery {} + })?; + Ok(block) + } + } +} diff --git a/ephemera/src/storage/sqlite/store.rs b/ephemera/src/storage/sqlite/store.rs new file mode 100644 index 0000000000..29cf181d9f --- /dev/null +++ b/ephemera/src/storage/sqlite/store.rs @@ -0,0 +1,71 @@ +use crate::block::types::block::Block; +use anyhow::Result; +use log::debug; +use rusqlite::{params, Connection, OpenFlags}; +use std::collections::HashSet; + +use crate::config::DatabaseConfiguration; +use crate::network::PeerId; +use crate::utilities::crypto::Certificate; + +pub struct Database { + connection: Connection, +} + +impl Database { + pub fn open(db_conf: DatabaseConfiguration, flags: OpenFlags) -> Result { + let connection = Connection::open_with_flags(db_conf.sqlite_path, flags)?; + Ok(Database { connection }) + } + + pub(crate) fn store_block( + &mut self, + block: &Block, + certificates: HashSet, + members: HashSet, + ) -> Result<()> { + debug!("Storing block: {}", block.header); + + let hash = block.header.hash.to_string(); + let height = block.header.height; + let block_bytes = serde_json::to_vec::(block).map_err(|e| anyhow::anyhow!(e))?; + let certificates_bytes = + serde_json::to_vec(&certificates.into_iter().collect::>()) + .map_err(|e| anyhow::anyhow!(e))?; + let members_bytes = serde_json::to_vec(&members.into_iter().collect::>()) + .map_err(|e| anyhow::anyhow!(e))?; + let merkle_tree = block.merkle_tree()?; + let merkle_tree_bytes = serde_json::to_vec(&merkle_tree).map_err(|e| anyhow::anyhow!(e))?; + + let tx = self.connection.transaction()?; + { + let mut statement = tx.prepare_cached( + "INSERT INTO blocks (block_hash, height, block) VALUES (?1, ?2, ?3)", + )?; + statement.execute(params![&hash, &height, &block_bytes,])?; + + let mut statement = tx.prepare_cached( + "INSERT INTO block_certificates (block_hash, certificates) VALUES (?1, ?2)", + )?; + + statement.execute(params![&hash, &certificates_bytes,])?; + + let mut statement = tx.prepare_cached( + "INSERT INTO block_broadcast_group (block_hash, members) VALUES (?1, ?2)", + )?; + + statement.execute(params![&hash, &members_bytes])?; + + //store Merkle Tree + let mut statement = tx.prepare_cached( + "INSERT INTO block_merkle_tree (block_hash, merkle_tree) VALUES (?1, ?2)", + )?; + + statement.execute(params![&hash, &merkle_tree_bytes])?; + } + + tx.commit()?; + + Ok(()) + } +} diff --git a/ephemera/src/utilities/codec/mod.rs b/ephemera/src/utilities/codec/mod.rs new file mode 100644 index 0000000000..69905b5440 --- /dev/null +++ b/ephemera/src/utilities/codec/mod.rs @@ -0,0 +1,102 @@ +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +pub(crate) mod varint_async; +pub(crate) mod varint_bytes; + +pub(crate) type Codec = SerdeCodec; + +#[derive(Debug, Error)] +pub enum DecodingError { + #[error("Decoding error: {0}")] + DecodingError(#[from] serde_json::Error), +} + +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, Error)] +pub enum EncodingError { + #[error("Encoding error: {0}")] + EncodingError(#[from] serde_json::Error), +} + +/// Simple trait for encoding +pub(crate) trait EphemeraCodec { + /// Encodes a message into a vector of bytes + /// + /// # Arguments + /// + /// * `data` - data to encode + /// + /// # Returns + /// + /// * `Result, EncodingError>` - encoded data or error + /// + /// # Errors + /// + /// * `EncodingError` - if encoding fails + fn encode(data: &M) -> Result, EncodingError>; + + /// Decodes a message from a vector of bytes + fn decode serde::Deserialize<'de>>(bytes: &[u8]) -> Result; +} + +pub(crate) struct SerdeCodec; + +impl EphemeraCodec for SerdeCodec { + fn encode(data: &M) -> Result, EncodingError> { + let bytes = serde_json::to_vec(data)?; + Ok(bytes) + } + + fn decode Deserialize<'de>>(bytes: &[u8]) -> Result { + let decoded = serde_json::from_slice(bytes)?; + Ok(decoded) + } +} + +/// Trait which types can implement to provide their own encoding +pub trait Encode { + /// Encodes itself into a vector of bytes + /// + /// # Returns + /// + /// * `Result, EncodingError>` - encoded data or error + /// + /// # Errors + /// + /// * `EncodingError` - if encoding fails + fn encode(&self) -> Result, EncodingError>; +} + +/// Trait which types can implement to provide their own decoding +pub trait Decode { + type Output: for<'de> serde::Deserialize<'de>; + + /// Decodes itself from a vector of bytes + /// + /// # Arguments + /// + /// * `bytes` - bytes to decode + /// + /// # Returns + /// + /// * `Result` - decoded data or error + /// + /// # Errors + /// + /// * `DecodingError` - if decoding fails + fn decode(bytes: &[u8]) -> Result; +} + +#[cfg(test)] +mod test { + use crate::utilities::codec::EphemeraCodec; + + #[test] + fn test_encode_decode() { + let data = vec![1, 2, 3, 4, 5]; + let encoded = super::SerdeCodec::encode(&data).unwrap(); + let decoded = super::SerdeCodec::decode::>(&encoded).unwrap(); + assert_eq!(data, decoded); + } +} diff --git a/ephemera/src/utilities/codec/varint_async.rs b/ephemera/src/utilities/codec/varint_async.rs new file mode 100644 index 0000000000..d097ce95c9 --- /dev/null +++ b/ephemera/src/utilities/codec/varint_async.rs @@ -0,0 +1,100 @@ +use futures::{AsyncRead, AsyncWrite}; +use futures_util::{AsyncReadExt, AsyncWriteExt}; +use log::error; + +#[allow(clippy::cast_possible_truncation)] + +pub(crate) async fn write_length_prefixed, I: AsyncWrite + Unpin>( + io: &mut I, + data: D, +) -> Result<(), std::io::Error> { + write_varint(io, data.as_ref().len() as u32).await?; + io.write_all(data.as_ref()).await?; + io.flush().await?; + + Ok(()) +} + +async fn write_varint(io: &mut I, len: u32) -> Result<(), std::io::Error> { + let mut len_data = unsigned_varint::encode::u32_buffer(); + let encoded_len = unsigned_varint::encode::u32(len, &mut len_data).len(); + io.write_all(&len_data[..encoded_len]).await?; + + Ok(()) +} + +async fn read_varint(io: &mut I) -> Result { + let mut buffer = unsigned_varint::encode::u32_buffer(); + let mut buffer_len = 0; + + loop { + //read 1 byte at time because we don't know how it compacted 32 bit integer + io.read_exact(&mut buffer[buffer_len..=buffer_len]).await?; + buffer_len += 1; + match unsigned_varint::decode::u32(&buffer[..buffer_len]) { + Ok((len, _)) => { + return Ok(len); + } + Err(unsigned_varint::decode::Error::Overflow) => { + error!("Invalid varint received"); + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid varint", + )); + } + Err(unsigned_varint::decode::Error::Insufficient) => { + continue; + } + Err(_) => { + error!("Varint decoding error: #[non_exhaustive]"); + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid varint", + )); + } + } + } +} + +pub(crate) async fn read_length_prefixed( + io: &mut I, + max_size: u32, +) -> Result, std::io::Error> { + let len = read_varint(io).await?; + if len > max_size { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Message too large", + )); + } + + let mut buf = vec![0; len as usize]; + io.read_exact(&mut buf).await?; + Ok(buf) +} + +#[cfg(test)] +mod test { + + use futures_util::io::Cursor; + + use super::*; + + #[tokio::test] + async fn test_read_write() { + let data = "hello world".to_string(); + + let mut buf = Vec::with_capacity(data.len() + 1); + let mut cursor = Cursor::new(&mut buf); + + write_length_prefixed(&mut cursor, data.as_bytes()) + .await + .unwrap(); + cursor.set_position(0); + + let vec = read_length_prefixed(&mut cursor, 100).await.unwrap(); + let result = String::from_utf8(vec).unwrap(); + + assert_eq!(result, data); + } +} diff --git a/ephemera/src/utilities/codec/varint_bytes.rs b/ephemera/src/utilities/codec/varint_bytes.rs new file mode 100644 index 0000000000..9a7d9d08ea --- /dev/null +++ b/ephemera/src/utilities/codec/varint_bytes.rs @@ -0,0 +1,85 @@ +use bytes::{Buf, BytesMut}; + +use thiserror::Error; +use unsigned_varint::{decode, encode}; + +#[derive(Debug, Error)] +pub(crate) enum VarintError { + #[error("InvalidInput: {0}")] + InvalidInput(String), + #[error("varint error: {0}")] + Varint(#[from] decode::Error), + #[error("TooLarge")] + TooLarge, +} + +#[allow(clippy::cast_possible_truncation)] + +pub(crate) fn write_length_prefixed>(dst: &mut BytesMut, data: D) { + write_varint(dst, data.as_ref().len() as u32); + dst.extend_from_slice(data.as_ref()); +} + +fn write_varint(dst: &mut BytesMut, len: u32) { + let mut len_data = encode::u32_buffer(); + let encoded_len = encode::u32(len, &mut len_data).len(); + dst.extend_from_slice(&len_data[..encoded_len]); +} + +pub(crate) fn read_length_prefixed( + bytes: &mut BytesMut, + max_size: u32, +) -> Result>, VarintError> { + let len = read_varint(bytes)?; + if len > max_size { + return Err(VarintError::TooLarge); + } + + if bytes.remaining() < len as usize { + return Ok(None); + } + + let vec = bytes.to_vec(); + bytes.advance(len as usize); + Ok(Some(vec)) +} + +fn read_varint(bytes: &mut BytesMut) -> Result { + let mut buffer = encode::u32_buffer(); + + for (i, byte) in bytes.iter().enumerate() { + buffer[i] = *byte; + match decode::u32(&buffer[..i]) { + Ok((len, _)) => { + bytes.advance(i); + return Ok(len); + } + Err(decode::Error::Insufficient) => continue, + Err(err) => Err(err)?, + } + } + + Err(VarintError::InvalidInput( + "Unable to read varint".to_string(), + )) +} + +#[cfg(test)] +mod test { + use bytes::BytesMut; + + use super::*; + + #[test] + fn test_read_write() { + let data = "hello world".to_string(); + + let mut encoded = BytesMut::with_capacity(0); + write_length_prefixed(&mut encoded, data.clone()); + let vec = read_length_prefixed(&mut encoded, 100).unwrap().unwrap(); + let result = String::from_utf8(vec).unwrap(); + + assert_eq!(result, data); + assert_eq!(encoded.remaining(), 0); + } +} diff --git a/ephemera/src/utilities/crypto/ed25519.rs b/ephemera/src/utilities/crypto/ed25519.rs new file mode 100644 index 0000000000..9b92304c2d --- /dev/null +++ b/ephemera/src/utilities/crypto/ed25519.rs @@ -0,0 +1,186 @@ +use std::fmt::Display; +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; + +use crate::crypto::EphemeraKeypair; +use crate::peer::{PeerId, ToPeerId}; +use crate::utilities::crypto::keypair::KeyPairError; +use crate::utilities::crypto::{EphemeraPublicKey, Signature}; + +// Internally uses libp2p Keypair for now +pub struct Keypair(pub(crate) libp2p::identity::Keypair); + +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct PublicKey(libp2p::identity::PublicKey); + +impl PublicKey { + pub(crate) fn inner(&self) -> &libp2p::identity::PublicKey { + &self.0 + } + + pub(crate) fn to_bytes(&self) -> Vec { + self.0.encode_protobuf() + } +} + +impl Keypair { + pub(crate) fn inner(&self) -> &libp2p::identity::Keypair { + &self.0 + } +} + +impl Display for Keypair { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Public key: {}, Secret key: .........", + self.public_key().to_base58() + ) + } +} + +impl Serialize for PublicKey { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_base58()) + } +} + +impl<'de> Deserialize<'de> for PublicKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + PublicKey::from_base58(&s).map_err(serde::de::Error::custom) + } +} + +impl Display for PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_base58()) + } +} + +impl FromStr for PublicKey { + type Err = KeyPairError; + + fn from_str(s: &str) -> Result { + PublicKey::from_base58(s) + } +} + +/// A wrapper around the libp2p Keypair type. +/// libp2p internally supports different key types, we only use Ed25519. +impl EphemeraKeypair for Keypair { + type Signature = Signature; + type PublicKey = PublicKey; + + fn generate(_seed: Option>) -> Self { + let keypair = libp2p::identity::Keypair::generate_ed25519(); + Keypair(keypair) + } + + fn sign>(&self, msg: &M) -> Result { + self.inner() + .sign(msg.as_ref()) + .map_err(|err| KeyPairError::Signing(err.to_string())) + .map(Signature) + } + + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { + self.0.public().verify(msg.as_ref(), signature.as_ref()) + } + + fn to_bytes(&self) -> Vec { + self.inner().to_protobuf_encoding().unwrap() + } + + fn from_bytes(raw: &[u8]) -> Result + where + Self: Sized, + { + let keypair = libp2p::identity::Keypair::from_protobuf_encoding(raw) + .map_err(|err| KeyPairError::Decoding(err.to_string()))?; + Ok(Keypair(keypair)) + } + + fn public_key(&self) -> Self::PublicKey { + PublicKey(self.0.public()) + } +} + +impl EphemeraPublicKey for PublicKey { + type Signature = Signature; + + fn to_bytes(&self) -> Vec { + self.0.encode_protobuf() + } + + fn from_bytes(raw: &[u8]) -> Result + where + Self: Sized, + { + let public_key = libp2p::identity::PublicKey::try_decode_protobuf(raw) + .map_err(|err| KeyPairError::Decoding(err.to_string()))?; + Ok(PublicKey(public_key)) + } + + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { + self.0.verify(msg.as_ref(), signature.as_ref()) + } +} + +impl ToPeerId for Keypair { + fn peer_id(&self) -> PeerId { + PeerId(self.0.public().to_peer_id()) + } +} + +impl ToPeerId for PublicKey { + fn peer_id(&self) -> PeerId { + PeerId(self.0.to_peer_id()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::crypto::EphemeraKeypair; + use crate::peer::ToPeerId; + use crate::utilities::crypto::keypair::KeyPairError; + use crate::utilities::crypto::EphemeraPublicKey; + use assert_matches::assert_matches; + use std::str::FromStr; + + #[test] + fn test_keypair() { + let keypair = Keypair::generate(None); + let public_key = keypair.public_key(); + let peer_id = keypair.peer_id(); + let peer_id_from_public_key = public_key.peer_id(); + assert_eq!(peer_id, peer_id_from_public_key); + + let msg = "Message to sign"; + let signature = keypair.sign(&msg).unwrap(); + assert!(public_key.verify(&msg, &signature)); + assert!(keypair.verify(&msg, &signature)); + + let initial = keypair.to_bytes(); + let parsed = Keypair::from_bytes(&initial).unwrap(); + assert_eq!(initial, parsed.to_bytes()); + + let initial = public_key.to_bytes(); + let parsed = PublicKey::from_bytes(&initial).unwrap(); + assert_eq!(initial, parsed.to_bytes()); + + let public_key_from_str = PublicKey::from_str(&public_key.to_base58()).unwrap(); + assert_eq!(public_key, public_key_from_str); + + let public_key_from_str = PublicKey::from_str(&keypair.to_base58()); + assert_matches!(public_key_from_str, Err(KeyPairError::Decoding(_))); + } +} diff --git a/ephemera/src/utilities/crypto/key_manager.rs b/ephemera/src/utilities/crypto/key_manager.rs new file mode 100644 index 0000000000..b27cb9020d --- /dev/null +++ b/ephemera/src/utilities/crypto/key_manager.rs @@ -0,0 +1,14 @@ +use std::sync::Arc; + +use crate::crypto::{EphemeraKeypair, Keypair}; + +pub(crate) struct KeyManager; + +impl KeyManager { + pub(crate) fn read_keypair_from_str(private_key: &str) -> anyhow::Result> { + let keypair: Arc = Keypair::from_base58(private_key) + .map_err(|e| anyhow::anyhow!("Failed to parse private key from config. Error: {e:?}"))? + .into(); + Ok(keypair) + } +} diff --git a/ephemera/src/utilities/crypto/keypair.rs b/ephemera/src/utilities/crypto/keypair.rs new file mode 100644 index 0000000000..fa609ae7e4 --- /dev/null +++ b/ephemera/src/utilities/crypto/keypair.rs @@ -0,0 +1,158 @@ +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum KeyPairError { + #[error("Failed to encode: {0}")] + Encoding(String), + #[error("Failed to decode: {0}")] + Decoding(String), + #[error("Invalid signature")] + Signature(String), + #[error("Signing failed: {0}")] + Signing(String), +} + +pub trait EphemeraPublicKey { + type Signature: AsRef<[u8]>; + + /// Returns raw bytes of the public key + /// + /// # Returns + /// * `Vec` - raw bytes of the public key + fn to_bytes(&self) -> Vec; + + /// Parses public key from raw bytes + /// + /// # Arguments + /// * `raw` - raw bytes of the public key + /// + /// # Returns + /// * `Result` - public key or error + /// + /// # Errors + /// * `KeyPairError::Decoding` - if bytes are not valid + fn from_bytes(bytes: &[u8]) -> Result + where + Self: Sized; + + /// Verifies the signature of the message using the public key + /// + /// # Arguments + /// * `msg` - message to verify + /// * `signature` - signature of the message + /// + /// # Returns + /// * `bool` - true if the signature is valid, false otherwise + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool; + + /// Returns base58 encoded public key + /// + /// # Returns + /// * `String` - base58 encoded public key + fn to_base58(&self) -> String { + bs58::encode(self.to_bytes()).into_string() + } + + /// Parses public key from base58 encoded string + /// + /// # Arguments + /// * `base58` - base58 encoded public key + /// + /// # Returns + /// * `Result` - public key or error + /// + /// # Errors + /// * `KeyPairError::Decoding` - if bytes are not valid + fn from_base58(base58: &str) -> Result + where + Self: Sized, + { + let raw = bs58::decode(base58) + .into_vec() + .map_err(|err| KeyPairError::Decoding(err.to_string()))?; + Self::from_bytes(&raw) + } +} + +#[allow(clippy::module_name_repetitions)] +pub trait EphemeraKeypair { + type Signature; + type PublicKey; + + /// Generates a new keypair. Depending on the implementation, the seed may be used to + /// add entropy to the key generation process. + fn generate(seed: Option>) -> Self; + + /// Signs a message with the private key + /// + /// # Arguments + /// * `msg` - message to sign + /// + /// # Returns + /// * `Result` - signature or error + /// + /// # Errors + /// * `KeyPairError::Signing` - if the message cannot be encoded + fn sign>(&self, msg: &M) -> Result; + + /// Verifies the signature of the message using the related public key + /// + /// # Arguments + /// * `msg` - message to verify + /// * `signature` - signature of the message + /// + /// # Returns + /// * `bool` - true if the signature is valid, false otherwise + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool; + + /// Returns raw bytes of the keypair + /// + /// # Returns + /// * `Vec` - raw bytes of the keypair + fn to_bytes(&self) -> Vec; + + /// Parses keypair from bytes + /// + /// # Arguments + /// * `raw` - raw bytes of the keypair + /// + /// # Returns + /// * `Result` - keypair or error + /// + /// # Errors + /// * `KeyPairError::Decoding` - if bytes are not valid + fn from_bytes(raw: &[u8]) -> Result + where + Self: Sized; + + /// Returns related public key of the keypair + fn public_key(&self) -> Self::PublicKey; + + /// Returns base58 encoded keypair + /// + /// # Returns + /// * `String` - base58 encoded keypair + fn to_base58(&self) -> String { + bs58::encode(self.to_bytes()).into_string() + } + + /// Parses keypair from base58 encoded string + /// + /// # Arguments + /// * `base58` - base58 encoded keypair + /// + /// # Returns + /// * `Result` - keypair or error + /// + /// # Errors + /// * `KeyPairError::Decoding` - if bytes are not valid base58 encoded string + fn from_base58(base58: &str) -> Result + where + Self: Sized, + { + let raw = bs58::decode(base58) + .into_vec() + .map_err(|err| KeyPairError::Decoding(err.to_string()))?; + Self::from_bytes(&raw) + } +} diff --git a/ephemera/src/utilities/crypto/mod.rs b/ephemera/src/utilities/crypto/mod.rs new file mode 100644 index 0000000000..f95f26342b --- /dev/null +++ b/ephemera/src/utilities/crypto/mod.rs @@ -0,0 +1,137 @@ +use std::fmt::{Debug, Display}; +use std::hash::Hash; + +use serde::{Deserialize, Serialize}; + +pub use ed25519::{Keypair as Ed25519Keypair, PublicKey as Ed25519PublicKey}; +pub use keypair::{EphemeraKeypair, EphemeraPublicKey, KeyPairError}; + +use crate::codec::Encode; +use crate::utilities::codec::{Codec, EncodingError, EphemeraCodec}; + +pub mod ed25519; +pub mod key_manager; +mod keypair; + +pub type Keypair = Ed25519Keypair; +pub type PublicKey = Ed25519PublicKey; + +#[derive(Clone, PartialEq, Hash, Eq, PartialOrd, Ord, Deserialize, Serialize)] +pub struct Signature(Vec); + +impl Signature { + pub fn new(signature: Vec) -> Self { + Self(signature) + } + + pub fn to_bytes(&self) -> Vec { + self.0.clone() + } + + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + + pub fn inner(&self) -> &[u8] { + &self.0 + } + + pub fn to_base58(&self) -> String { + bs58::encode(self.0.clone()).into_string() + } +} + +impl Debug for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_base58()) + } +} + +impl Display for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_base58()) + } +} + +#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord, Deserialize, Serialize)] +pub(crate) struct Certificate { + pub(crate) signature: Signature, + pub(crate) public_key: PublicKey, +} + +impl Certificate { + pub fn prepare(key_pair: &Keypair, data: &D) -> anyhow::Result { + let data_bytes = data.encode()?; + let signature = key_pair.sign(&data_bytes)?; + let public_key = key_pair.public_key(); + Ok(Self::new(signature, public_key)) + } +} + +impl AsRef<[u8]> for Signature { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl From> for Signature { + fn from(signature: Vec) -> Self { + Self::new(signature) + } +} + +impl Certificate { + pub(crate) fn new(signature: Signature, public_key: PublicKey) -> Self { + Self { + signature, + public_key, + } + } + + pub(crate) fn verify(&self, data: &D) -> anyhow::Result { + let data_bytes = data.encode()?; + let valid = self.public_key.verify(&data_bytes, &self.signature); + Ok(valid) + } +} + +impl Encode for Certificate { + fn encode(&self) -> Result, EncodingError> { + let mut result = Codec::encode(&self.signature)?; + result.extend_from_slice(&self.public_key.encode()?); + Ok(result) + } +} + +impl Encode for PublicKey { + fn encode(&self) -> Result, EncodingError> { + Ok(self.to_bytes()) + } +} + +impl Display for Certificate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Signature {}, PublicKey {}", + self.signature.to_base58(), + self.public_key.to_base58() + ) + } +} + +#[cfg(test)] +mod test { + + use crate::crypto::{EphemeraKeypair, EphemeraPublicKey}; + + #[test] + fn test_sign_and_verify() { + let keypair = super::Keypair::generate(None); + let data = "Secret data"; + let signature = keypair.sign(&data.as_bytes()).unwrap(); + let public_key = keypair.public_key(); + let valid = public_key.verify(&data.as_bytes(), &signature); + assert!(valid); + } +} diff --git a/ephemera/src/utilities/hash/mod.rs b/ephemera/src/utilities/hash/mod.rs new file mode 100644 index 0000000000..e95a975da4 --- /dev/null +++ b/ephemera/src/utilities/hash/mod.rs @@ -0,0 +1,109 @@ +use std::fmt::{Debug, Display}; + +use std::str::FromStr; + +use blake2::{Blake2b, Digest}; +use digest::consts::U32; +use serde::{Deserialize, Serialize}; + +pub type Hasher = Blake2bHasher; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Deserialize, Serialize)] +pub struct Hash([u8; 32]); + +impl Hash { + pub fn new(hash: [u8; 32]) -> Self { + Self(hash) + } + + pub fn inner(&self) -> [u8; 32] { + self.0 + } + + pub(crate) fn base58(&self) -> String { + bs58::encode(self.0).into_string() + } +} + +impl FromStr for Hash { + type Err = bs58::decode::Error; + + fn from_str(s: &str) -> Result { + let bytes = bs58::decode(s).into_vec()?; + let mut hash = [0u8; 32]; + hash.copy_from_slice(&bytes); + Ok(Self(hash)) + } +} + +impl Debug for Hash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.base58()) + } +} + +impl Display for Hash { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.base58()) + } +} + +impl From<[u8; 32]> for Hash { + fn from(hash: [u8; 32]) -> Self { + Self(hash) + } +} + +/// A trait for hashing data. +pub(crate) trait EphemeraHasher: Default { + /// Hashes the given data. + fn digest(data: &[u8]) -> [u8; 32]; + + /// Updates the hasher with the given data. + fn update(&mut self, bytes: &[u8]); + + /// Finalizes the hasher and returns the hash. + fn finish(&mut self) -> [u8; 32]; +} + +#[derive(Default)] +pub struct Blake2bHasher { + hasher: Blake2b, +} + +impl EphemeraHasher for Blake2bHasher { + fn digest(data: &[u8]) -> [u8; 32] { + type Blake2b256 = blake2::Blake2b; + let mut dest = [0; 32]; + dest.copy_from_slice(Blake2b256::digest(data).as_slice()); + dest + } + + fn update(&mut self, bytes: &[u8]) { + self.hasher.update(bytes); + } + + fn finish(&mut self) -> [u8; 32] { + self.hasher.finalize_reset().into() + } +} + +pub(crate) trait EphemeraHash { + fn hash(&self, state: &mut H) -> anyhow::Result<()>; +} + +#[cfg(test)] +mod test { + use super::*; + use rand::RngCore; + + #[test] + fn to_base58_parse() { + let mut bytes = [0u8; 32]; + rand::thread_rng().fill_bytes(&mut bytes); + let hash: Hash = bytes.into(); + let base58 = hash.base58(); + let hash2 = base58.parse::().unwrap(); + assert_eq!(hash, hash2); + } +} diff --git a/ephemera/src/utilities/id/mod.rs b/ephemera/src/utilities/id/mod.rs new file mode 100644 index 0000000000..5548f6070a --- /dev/null +++ b/ephemera/src/utilities/id/mod.rs @@ -0,0 +1,94 @@ +use std::fmt::{Debug, Display}; +use std::str::FromStr; + +use crate::utilities::hash::{EphemeraHash, EphemeraHasher}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +pub(crate) type EphemeraId = UuidEphemeraIdentifier; + +pub(crate) trait EphemeraIdentifier: ToString { + type Identifier; + + fn generate() -> Self; + + fn inner(&self) -> &Self::Identifier; + + fn into_inner(self) -> Self::Identifier; + + fn as_bytes(&self) -> &[u8]; +} + +#[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Deserialize, Serialize)] +pub struct UuidEphemeraIdentifier { + identifier: String, +} + +impl FromStr for UuidEphemeraIdentifier { + type Err = uuid::Error; + + fn from_str(s: &str) -> Result { + Ok(UuidEphemeraIdentifier { + identifier: Uuid::parse_str(s)?.to_string(), + }) + } +} + +impl Display for UuidEphemeraIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.identifier) + } +} + +impl Debug for UuidEphemeraIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.identifier) + } +} + +impl Default for UuidEphemeraIdentifier { + fn default() -> Self { + UuidEphemeraIdentifier { + identifier: Uuid::new_v4().to_string(), + } + } +} + +impl EphemeraHash for UuidEphemeraIdentifier { + fn hash(&self, state: &mut H) -> anyhow::Result<()> { + state.update(self.as_bytes()); + Ok(()) + } +} + +impl EphemeraIdentifier for UuidEphemeraIdentifier { + type Identifier = String; + + fn generate() -> Self { + UuidEphemeraIdentifier::default() + } + + fn inner(&self) -> &Self::Identifier { + &self.identifier + } + + fn into_inner(self) -> Self::Identifier { + self.identifier + } + + fn as_bytes(&self) -> &[u8] { + self.identifier.as_bytes() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_generate_parse() { + let id = UuidEphemeraIdentifier::generate(); + let id2 = UuidEphemeraIdentifier::from_str(&id.to_string()).unwrap(); + assert_eq!(id, id2); + } +} diff --git a/ephemera/src/utilities/merkle/mod.rs b/ephemera/src/utilities/merkle/mod.rs new file mode 100644 index 0000000000..1f0f866539 --- /dev/null +++ b/ephemera/src/utilities/merkle/mod.rs @@ -0,0 +1,162 @@ +use serde::{Deserialize, Serialize}; + +use crate::utilities::hash::{EphemeraHasher, Hash, Hasher}; + +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, Serialize, Deserialize)] +pub struct MerkleTree { + leaf_count: usize, + nodes: Vec, +} + +impl MerkleTree { + pub(crate) fn build_tree(leaves: &[Hash]) -> Self { + if leaves.is_empty() { + return Self { + leaf_count: 1, + //So every message which matches this hash will be accepted + //Not sure if it's a problem + nodes: vec![Hash::new([0; 32])], + }; + } + + let leaf_count = leaves.len(); + let mut nodes = Vec::with_capacity(leaf_count * 2); + nodes.extend_from_slice(leaves); + + let mut prev_level_len = leaf_count; + let mut prev_offset = 0; + let mut current_offset = leaf_count; + + while prev_level_len > 1 { + let current_level_len = (prev_level_len + 1) / 2; + + for i in 0..current_level_len { + let prev_index = i * 2; + let left = nodes[prev_offset + prev_index]; + let right = if prev_index + 1 < prev_level_len { + nodes[prev_offset + prev_index + 1] + } else { + nodes[prev_offset + prev_index] + }; + let hash = Hasher::digest(&[left.inner(), right.inner()].concat()).into(); + nodes.push(hash); + } + prev_level_len = current_level_len; + prev_offset = current_offset; + current_offset += current_level_len; + } + Self { leaf_count, nodes } + } + + pub(crate) fn root_hash(&self) -> Hash { + self.nodes[self.nodes.len() - 1] + } + + pub(crate) fn verify_leaf_at_index(&self, hash: Hash, leaf_index: usize) -> bool { + let mut level_offset = 0; + let mut level_len = self.leaf_count; + let mut leaf_index = leaf_index; + let mut current_hash = hash; + while level_offset + level_len < self.nodes.len() { + let level = &self.nodes[level_offset..(level_offset + level_len)]; + if leaf_index % 2 == 0 { + let right_index = if leaf_index + 1 < level_len { + leaf_index + 1 + } else { + leaf_index + }; + current_hash = + Hasher::digest(&[current_hash.inner(), level[right_index].inner()].concat()) + .into(); + } else { + current_hash = + Hasher::digest(&[level[leaf_index - 1].inner(), current_hash.inner()].concat()) + .into(); + } + leaf_index /= 2; + level_offset += level_len; + level_len = (level_len + 1) / 2; + } + current_hash == self.root_hash() + } +} + +#[cfg(test)] +mod tests { + use std::iter; + + use rand::RngCore; + + use crate::utilities::hash::Hash; + + use super::*; + + #[test] + fn test_merkle() { + //Technically testing one implementation against another... + for i in 1..10 { + let mut rnd = rand::thread_rng(); + let leaves = iter::repeat_with(|| { + let mut bytes = [0u8; 32]; + rnd.fill_bytes(&mut bytes); + Hash::new(bytes) + }) + .take(i) + .collect::>(); + + let mut level: Vec = leaves.clone(); + + while level.len() > 1 { + level = level + .chunks(2) + .map(|chunk| { + if chunk.len() == 1 { + Hasher::digest(&[chunk[0].inner(), chunk[0].inner()].concat()).into() + } else { + Hasher::digest(&[chunk[0].inner(), chunk[1].inner()].concat()).into() + } + }) + .collect(); + } + + let root: Hash = level[0]; + + let tree = MerkleTree::build_tree(&leaves); + assert_eq!(tree.root_hash(), root); + } + } + + #[test] + fn test_verify_leaf() { + for i in 0..10 { + let mut rnd = rand::thread_rng(); + let right_leaves = iter::repeat_with(|| { + let mut bytes = [0u8; 32]; + rnd.fill_bytes(&mut bytes); + Hash::new(bytes) + }) + .take(i) + .collect::>(); + + let wrong_leaves = iter::repeat_with(|| { + let mut bytes = [0u8; 32]; + rnd.fill_bytes(&mut bytes); + Hash::new(bytes) + }) + .take(i) + .collect::>(); + + let tree = MerkleTree::build_tree(&right_leaves); + + for (i, (correct, wrong)) in right_leaves + .into_iter() + .zip(wrong_leaves.into_iter()) + .enumerate() + { + assert!(tree.verify_leaf_at_index(correct, i)); + assert!(!tree.verify_leaf_at_index(wrong, i)); + } + } + } +} diff --git a/ephemera/src/utilities/mod.rs b/ephemera/src/utilities/mod.rs new file mode 100644 index 0000000000..724cdba514 --- /dev/null +++ b/ephemera/src/utilities/mod.rs @@ -0,0 +1,6 @@ +pub(crate) mod codec; +pub(crate) mod crypto; +pub(crate) mod hash; +pub(crate) mod id; +pub(crate) mod merkle; +pub(crate) mod time; diff --git a/ephemera/src/utilities/time/mod.rs b/ephemera/src/utilities/time/mod.rs new file mode 100644 index 0000000000..0b3f2091c0 --- /dev/null +++ b/ephemera/src/utilities/time/mod.rs @@ -0,0 +1,11 @@ +use chrono::Utc; + +#[allow(clippy::module_name_repetitions)] +pub struct EphemeraTime; + +impl EphemeraTime { + #[allow(clippy::cast_sign_loss)] + pub fn now() -> u64 { + Utc::now().timestamp_millis() as u64 + } +} diff --git a/ephemera/src/websocket/mod.rs b/ephemera/src/websocket/mod.rs new file mode 100644 index 0000000000..73a138f3cb --- /dev/null +++ b/ephemera/src/websocket/mod.rs @@ -0,0 +1 @@ +pub(crate) mod ws_manager; diff --git a/ephemera/src/websocket/ws_manager.rs b/ephemera/src/websocket/ws_manager.rs new file mode 100644 index 0000000000..a7e837a2d4 --- /dev/null +++ b/ephemera/src/websocket/ws_manager.rs @@ -0,0 +1,138 @@ +use std::net::SocketAddr; + +use anyhow::Result; +use futures_util::SinkExt; +use log::{debug, error, info}; +use tokio::sync::broadcast::error::RecvError; +use tokio::{ + net::{TcpListener, TcpStream}, + sync::broadcast, +}; +use tokio_tungstenite::{tungstenite::Message, WebSocketStream}; + +use crate::api::types::ApiBlock; +use crate::block::types::block::Block; + +pub struct WsConnection { + socket: WebSocketStream, + pending_messages_rx: broadcast::Receiver, + address: SocketAddr, +} + +impl WsConnection { + pub fn new( + socket: WebSocketStream, + pending_messages_rx: broadcast::Receiver, + address: SocketAddr, + ) -> WsConnection { + WsConnection { + socket, + pending_messages_rx, + address, + } + } + + pub async fn accept_messages(mut self) { + loop { + match self.pending_messages_rx.recv().await { + Ok(msg) => { + debug!("Sending message to {}", self.address); + if let Err(err) = self.socket.send(msg).await { + error!("Error sending message to websocket client: {:?}", err); + } + } + Err(e) => { + if RecvError::Closed == e { + error!("Error while receiving message from broadcast channel: {:?}, closing connection", e); + break; + } + } + } + } + } +} + +#[derive(Clone)] +pub(crate) struct WsMessageBroadcaster { + pub(crate) pending_messages_tx: broadcast::Sender, +} + +impl WsMessageBroadcaster { + pub(crate) fn new(pending_messages_tx: broadcast::Sender) -> WsMessageBroadcaster { + WsMessageBroadcaster { + pending_messages_tx, + } + } + + pub(crate) fn send_block(&self, block: &Block) -> Result<()> { + debug!("Sending block {} to websocket clients", block.header.hash); + let json = serde_json::to_string::(block.into())?; + let msg = Message::Text(json); + self.pending_messages_tx.send(msg)?; + Ok(()) + } +} + +pub(crate) struct WsManager { + pub(crate) listener: Option, + pub(crate) ws_address: String, + pub(crate) pending_messages_tx: broadcast::Sender, + _pending_messages_rcv: broadcast::Receiver, +} + +impl WsManager { + #[allow(clippy::used_underscore_binding)] + pub(crate) fn new(address: String) -> (WsManager, WsMessageBroadcaster) { + let (pending_messages_tx, _pending_messages_rcv) = broadcast::channel(1000); + let ws_message_broadcast = WsMessageBroadcaster::new(pending_messages_tx.clone()); + let manager = WsManager { + listener: None, + ws_address: address, + pending_messages_tx, + _pending_messages_rcv, + }; + (manager, ws_message_broadcast) + } + + pub(crate) async fn listen(&mut self) -> Result<()> { + let listener = TcpListener::bind(&self.ws_address).await?; + info!("Listening for websocket connections on {}", self.ws_address); + self.listener = Some(listener); + Ok(()) + } + + pub async fn run(mut self) -> Result<()> { + let listener = self.listener.take().expect("Listener not set"); + loop { + tokio::select! { + res = listener.accept() => { + match res { + Ok((stream, addr)) => { + debug!("Accepted websocket connection from: {}", addr); + self.handle_connection(stream, addr); + } + Err(err) => { + return Err(err.into()); + } + } + } + } + } + } + + pub fn handle_connection(&self, stream: TcpStream, addr: SocketAddr) { + let pending_messages_rx = self.pending_messages_tx.subscribe(); + tokio::spawn(async move { + match tokio_tungstenite::accept_async(stream).await { + Ok(ws_stream) => { + let connection = WsConnection::new(ws_stream, pending_messages_rx, addr); + connection.accept_messages().await; + } + Err(err) => { + error!("Error accepting websocket connection: {:?}", err); + } + } + debug!("Websocket connection closed"); + }); + } +} diff --git a/explorer-api/.env.dev b/explorer-api/.env.dev new file mode 100644 index 0000000000..0020aba233 --- /dev/null +++ b/explorer-api/.env.dev @@ -0,0 +1,44 @@ +CONFIGURED=true + +RUST_LOG=info +RUST_BACKTRACE=1 + +BECH32_PREFIX=n +MIX_DENOM=unym +MIX_DENOM_DISPLAY=nym +STAKE_DENOM=unyx +STAKE_DENOM_DISPLAY=nyx +DENOMS_EXPONENT=6 + +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 +NAME_SERVICE_CONTRACT_ADDRESS=n12ne7qtmdwd0j03t9t5es8md66wq4e5xg9neladrsag8fx3y89rcs36asfp +SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS=n1ps5yutd7sufwg058qd7ac7ldnlazsvmhzqwucsfxmm445d70u8asqxpur4 + +STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0" +NYXD="https://sandbox-validator1.nymtech.net" +NYM_API="https://sandbox-nym-api1.nymtech.net/api" + +GEOIP_DB_PATH=geo_ip/GeoLite2-City.mmdb +# MaxMind account ID +# TODO replace with your own account ID +GEOIPUPDATE_ACCOUNT_ID=xxx +# MaxMind license key +# TODO replace with your own license key +GEOIPUPDATE_LICENSE_KEY=xxx +# List of space-separated database edition IDs. Edition IDs may +# consist of letters, digits, and dashes. For example, GeoIP2-City +# would download the GeoIP2 City database (GeoIP2-City). +GEOIPUPDATE_EDITION_IDS=GeoLite2-City +# The number of hours between geoipupdate runs. If this is not set +# or is set to 0, geoipupdate will run once and exit. +GEOIPUPDATE_FREQUENCY=72 +# The path to the directory where geoipupdate will download the +# database. +GEOIP_DB_DIRECTORY=./geo_ip diff --git a/explorer-api/.gitignore b/explorer-api/.gitignore index 66ab76436b..fd6f795b06 100644 --- a/explorer-api/.gitignore +++ b/explorer-api/.gitignore @@ -1,3 +1,4 @@ target explorer-api-state.json /geo_ip +!.env.dev diff --git a/explorer-api/Cargo.toml b/explorer-api/Cargo.toml index 5e1dec9f48..10ca849b3d 100644 --- a/explorer-api/Cargo.toml +++ b/explorer-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "explorer-api" -version = "1.1.23" +version = "1.1.27" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -8,13 +8,19 @@ edition = "2021" [dependencies] chrono = { version = "0.4.19", features = ["serde"] } clap = { version = "4.0", features = ["cargo", "derive"] } +dotenvy = "0.15.6" humantime-serde = "1.0" isocountry = "0.3.2" itertools = "0.10.3" +lazy_static = "1.4.0" log = { workspace = true } +maxminddb = "0.23.0" okapi = { version = "0.7.0-rc.1", features = ["impl_json_schema"] } pretty_env_logger = "0.4.0" -reqwest = "0.11.4" +rand = "0.8.5" +rand_pcg = "0.3.1" +rand_seeder = "0.2.3" +reqwest = { workspace = true } rocket = { version = "0.5.0-rc.2", features = ["json"] } rocket_cors = { git="https://github.com/lawliet89/rocket_cors", rev="dfd3662c49e2f6fc37df35091cb94d82f7fb5915" } rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] } @@ -23,15 +29,11 @@ serde = "1.0.126" serde_json = "1.0.66" thiserror = "1.0.29" tokio = {version = "1.21.2", features = ["full"] } -maxminddb = "0.23.0" -dotenvy = "0.15.6" -rand = "0.8.5" -rand_seeder = "0.2.3" -rand_pcg = "0.3.1" -nym-mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" } -nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" } -nym-network-defaults = { path = "../common/network-defaults" } nym-bin-common = { path = "../common/bin-common"} +nym-contracts-common = { path = "../common/cosmwasm-smart-contracts/contracts-common" } +nym-explorer-api-requests = { path = "explorer-api-requests" } +nym-mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" } +nym-network-defaults = { path = "../common/network-defaults" } nym-task = { path = "../common/task" } nym-validator-client = { path = "../common/client-libs/validator-client", features=["http-client"] } diff --git a/explorer-api/README.md b/explorer-api/README.md index 86e64e4c12..8ab514d616 100644 --- a/explorer-api/README.md +++ b/explorer-api/README.md @@ -8,9 +8,17 @@ Features: - calculates how many nodes are in each country - proxies mixnode API requests to add HTTPS +## Development + +Several environment variables are required. They can be +provisioned via a `.env` file. For convenience a `.env.dev` is +provided, just copy its content into `.env`. + +Follow the steps to setup the geoip database. + ## GeoIP db install/update -First we need to install the geoip database. +A geoip database needs to be installed. We use https://github.com/maxmind/geoipupdate to automatically download and update GeoLite2 binary database. For convenience we @@ -37,13 +45,7 @@ When starting the explorer-api, supply the environment variable `GEOIP_DB_PATH`, pointing to the GeoLite2 binary database file. It should be previously installed thanks to `geoipupdate` service. -For example: - -```shell -GEOIP_DB_PATH=./geo_ip/GeoLite2-City.mmdb cargo run -``` - -Note: explorer-api binary reads the provided `.env` file. +Note: As mentioned above the explorer-api binary reads the provided `.env` file. Run as a service and reverse proxy with `nginx` to add `https` with Lets Encrypt. diff --git a/explorer-api/explorer-api-requests/Cargo.toml b/explorer-api/explorer-api-requests/Cargo.toml new file mode 100644 index 0000000000..d7139b9f78 --- /dev/null +++ b/explorer-api/explorer-api-requests/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "nym-explorer-api-requests" +version = "0.1.0" +edition = "2021" + +[dependencies] +nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" } +nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" } +nym-api-requests = { path = "../../nym-api/nym-api-requests" } +schemars = { version = "0.8", features = ["preserve_order"] } +serde = { version = "1.0", features = ["derive"] } +ts-rs = { workspace = true, optional = true } diff --git a/explorer-api/explorer-api-requests/src/lib.rs b/explorer-api/explorer-api-requests/src/lib.rs new file mode 100644 index 0000000000..2109348899 --- /dev/null +++ b/explorer-api/explorer-api-requests/src/lib.rs @@ -0,0 +1,54 @@ +use nym_api_requests::models::NodePerformance; +use nym_contracts_common::Percent; +use nym_mixnet_contract_common::{Addr, Coin, Gateway, Layer, MixId, MixNode}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum MixnodeStatus { + Active, // in both the active set and the rewarded set + Standby, // only in the rewarded set + Inactive, // in neither the rewarded set nor the active set +} + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct PrettyDetailedMixNodeBond { + pub mix_id: MixId, + pub location: Option, + pub status: MixnodeStatus, + pub pledge_amount: Coin, + pub total_delegation: Coin, + pub owner: Addr, + pub layer: Layer, + pub mix_node: MixNode, + pub stake_saturation: f32, + pub uncapped_saturation: f32, + pub avg_uptime: u8, + pub node_performance: NodePerformance, + pub estimated_operator_apy: f64, + pub estimated_delegators_apy: f64, + pub operating_cost: Coin, + pub profit_margin_percent: Percent, + pub family_id: Option, + pub blacklisted: bool, +} + +#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)] +pub struct Location { + pub two_letter_iso_country_code: String, + pub three_letter_iso_country_code: String, + pub country_name: String, + pub latitude: Option, + pub longitude: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct PrettyDetailedGatewayBond { + pub pledge_amount: Coin, + pub owner: Addr, + pub block_height: u64, + pub gateway: Gateway, + pub proxy: Option, + pub location: Option, +} diff --git a/explorer-api/src/client.rs b/explorer-api/src/client.rs index 9b345e4cf6..1b565fcd60 100644 --- a/explorer-api/src/client.rs +++ b/explorer-api/src/client.rs @@ -2,7 +2,7 @@ use nym_network_defaults::{ var_names::{NYM_API, NYXD}, NymNetworkDetails, }; -use nym_validator_client::nyxd::QueryNyxdClient; +use nym_validator_client::QueryHttpRpcValidatorClient; use reqwest::Url; use std::{str::FromStr, sync::Arc}; @@ -11,9 +11,7 @@ use std::{str::FromStr, sync::Arc}; // when that becomes a requirement, we would simply put an extra RwLock (or Mutex) in here #[derive(Clone)] -pub(crate) struct ThreadsafeValidatorClient( - pub(crate) Arc>, -); +pub(crate) struct ThreadsafeValidatorClient(pub(crate) Arc); impl ThreadsafeValidatorClient { pub(crate) fn new() -> Self { diff --git a/explorer-api/src/commands/mod.rs b/explorer-api/src/commands/mod.rs index ee20748fd0..1e19d2efce 100644 --- a/explorer-api/src/commands/mod.rs +++ b/explorer-api/src/commands/mod.rs @@ -1,10 +1,21 @@ -// Copyright 2022 - Nym Technologies SA +// Copyright 2022-2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 use clap::Parser; +use lazy_static::lazy_static; +use nym_bin_common::bin_info; + +lazy_static! { + pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print(); +} + +// Helper for passing LONG_VERSION to clap +fn pretty_build_info_static() -> &'static str { + &PRETTY_BUILD_INFORMATION +} #[derive(Parser)] -#[clap(author = "Nymtech", version, about)] +#[clap(author = "Nymtech", version, about, long_version = pretty_build_info_static())] pub(crate) struct Cli { /// Path pointing to an env file that configures the explorer api. #[clap(short, long)] diff --git a/explorer-api/src/country_statistics/geolocate.rs b/explorer-api/src/country_statistics/geolocate.rs index 8158c6e65e..858f593986 100644 --- a/explorer-api/src/country_statistics/geolocate.rs +++ b/explorer-api/src/country_statistics/geolocate.rs @@ -1,9 +1,9 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::mix_nodes::location::Location; use crate::state::ExplorerApiStateContext; use log::{info, warn}; +use nym_explorer_api_requests::Location; use nym_task::TaskClient; pub(crate) struct GeoLocateTask { @@ -17,13 +17,14 @@ impl GeoLocateTask { } pub(crate) fn start(mut self) { - info!("Spawning mix node locator task runner..."); + info!("Spawning locator task runner..."); tokio::spawn(async move { let mut interval_timer = tokio::time::interval(std::time::Duration::from_millis(50)); while !self.shutdown.is_shutdown() { tokio::select! { _ = interval_timer.tick() => { self.locate_mix_nodes().await; + self.locate_gateways().await; } _ = self.shutdown.recv() => { trace!("Listener: Received shutdown"); @@ -64,7 +65,7 @@ impl GeoLocateTask { ) { Ok(opt) => match opt { Some(location) => { - let location = Location::new(location); + let location: Location = location.into(); trace!( "{} mix nodes already located. Ip {} is located in {:#?}", @@ -107,4 +108,68 @@ impl GeoLocateTask { trace!("All mix nodes located"); } + + async fn locate_gateways(&mut self) { + let gateways = self.state.inner.gateways.get_gateways().await; + + let geo_ip = self.state.inner.geo_ip.0.clone(); + + for (i, cache_item) in gateways.iter().enumerate() { + if self + .state + .inner + .gateways + .is_location_valid(cache_item.identity().to_owned()) + .await + { + // when the cached location is valid, don't locate and continue to next gateway + continue; + } + + match geo_ip.query(&cache_item.gateway.host, Some(cache_item.gateway.mix_port)) { + Ok(opt) => match opt { + Some(location) => { + let location: Location = location.into(); + + trace!( + "{} gateways already located. Ip {} is located in {:#?}", + i, + cache_item.gateway.host, + location.three_letter_iso_country_code, + ); + + if i > 0 && (i % 100) == 0 { + info!("Located {} gateways...", i + 1,); + } + + self.state + .inner + .gateways + .set_location(cache_item.identity().to_owned(), Some(location)) + .await; + + // one node has been located, so return out of the loop + return; + } + None => { + warn!("❌ Location for {} not found.", cache_item.gateway.host); + self.state + .inner + .gateways + .set_location(cache_item.identity().to_owned(), None) + .await; + } + }, + Err(_e) => { + // warn!( + // "❌ Oh no! Location for {} failed. Error: {:#?}", + // cache_item.gateway.host, + // e + // ); + } + }; + } + + trace!("All gateways located"); + } } diff --git a/explorer-api/src/gateways/http.rs b/explorer-api/src/gateways/http.rs index 1f494d0edd..ac0d85c0b1 100644 --- a/explorer-api/src/gateways/http.rs +++ b/explorer-api/src/gateways/http.rs @@ -1,6 +1,7 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use nym_explorer_api_requests::PrettyDetailedGatewayBond; use rocket::response::status::NotFound; use rocket::serde::json::Json; use rocket::{Route, State}; @@ -9,7 +10,6 @@ use rocket_okapi::openapi_get_routes_spec; use rocket_okapi::settings::OpenApiSettings; use crate::state::ExplorerApiStateContext; -use nym_mixnet_contract_common::GatewayBond; pub fn gateways_make_default_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { openapi_get_routes_spec![settings: list] @@ -19,6 +19,6 @@ pub fn gateways_make_default_routes(settings: &OpenApiSettings) -> (Vec, #[get("/")] pub(crate) async fn list( state: &State, -) -> Result>, NotFound> { - Ok(Json(state.inner.gateways.get_gateways().await)) +) -> Result>, NotFound> { + Ok(Json(state.inner.gateways.get_detailed_gateways().await)) } diff --git a/explorer-api/src/gateways/location.rs b/explorer-api/src/gateways/location.rs new file mode 100644 index 0000000000..68420720b3 --- /dev/null +++ b/explorer-api/src/gateways/location.rs @@ -0,0 +1,8 @@ +// Copyright 2021 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use nym_contracts_common::IdentityKey; + +use crate::location::LocationCache; + +pub(crate) type GatewayLocationCache = LocationCache; diff --git a/explorer-api/src/gateways/mod.rs b/explorer-api/src/gateways/mod.rs index 5df938f83c..cbce7ef930 100644 --- a/explorer-api/src/gateways/mod.rs +++ b/explorer-api/src/gateways/mod.rs @@ -1,2 +1,3 @@ pub(crate) mod http; +pub(crate) mod location; pub(crate) mod models; diff --git a/explorer-api/src/gateways/models.rs b/explorer-api/src/gateways/models.rs index ab2712c3fc..a3ed406067 100644 --- a/explorer-api/src/gateways/models.rs +++ b/explorer-api/src/gateways/models.rs @@ -1,12 +1,15 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::cache::Cache; +use crate::{cache::Cache, location::LocationCacheItem}; +use nym_explorer_api_requests::{Location, PrettyDetailedGatewayBond}; use nym_mixnet_contract_common::{GatewayBond, IdentityKey}; use serde::Serialize; -use std::sync::Arc; +use std::{sync::Arc, time::SystemTime}; use tokio::sync::RwLock; +use super::location::GatewayLocationCache; + pub(crate) struct GatewayCache { pub(crate) gateways: Cache, } @@ -18,30 +21,93 @@ pub(crate) struct GatewaySummary { #[derive(Clone)] pub(crate) struct ThreadsafeGatewayCache { - inner: Arc>, + gateways: Arc>, + locations: Arc>, } impl ThreadsafeGatewayCache { pub(crate) fn new() -> Self { ThreadsafeGatewayCache { - inner: Arc::new(RwLock::new(GatewayCache { + gateways: Arc::new(RwLock::new(GatewayCache { gateways: Cache::new(), })), + locations: Arc::new(RwLock::new(GatewayLocationCache::new())), + } + } + + fn create_detailed_gateway( + &self, + bond: GatewayBond, + location: Option<&LocationCacheItem>, + ) -> PrettyDetailedGatewayBond { + PrettyDetailedGatewayBond { + pledge_amount: bond.pledge_amount, + owner: bond.owner, + block_height: bond.block_height, + gateway: bond.gateway, + proxy: bond.proxy, + location: location.and_then(|l| l.location.clone()), } } pub(crate) async fn get_gateways(&self) -> Vec { - self.inner.read().await.gateways.get_all() + self.gateways.read().await.gateways.get_all() + } + + pub(crate) async fn get_detailed_gateways(&self) -> Vec { + let gateways_guard = self.gateways.read().await; + let location_guard = self.locations.read().await; + + gateways_guard + .gateways + .get_all() + .iter() + .map(|bond| { + let location = location_guard.get(bond.identity()); + self.create_detailed_gateway(bond.to_owned(), location) + }) + .collect() } pub(crate) async fn get_gateway_summary(&self) -> GatewaySummary { GatewaySummary { - count: self.inner.read().await.gateways.len(), + count: self.gateways.read().await.gateways.len(), } } + pub(crate) fn new_with_location_cache(locations: GatewayLocationCache) -> Self { + ThreadsafeGatewayCache { + gateways: Arc::new(RwLock::new(GatewayCache { + gateways: Cache::new(), + })), + locations: Arc::new(RwLock::new(locations)), + } + } + + pub(crate) async fn is_location_valid(&self, identity_key: IdentityKey) -> bool { + self.locations + .read() + .await + .get(&identity_key) + .map_or(false, |cache_item| { + cache_item.valid_until > SystemTime::now() + }) + } + + pub(crate) async fn get_locations(&self) -> GatewayLocationCache { + self.locations.read().await.clone() + } + + pub(crate) async fn set_location(&self, identy_key: IdentityKey, location: Option) { + // cache the location for this mix node so that it can be used when the mix node list is refreshed + self.locations + .write() + .await + .insert(identy_key, LocationCacheItem::new_from_location(location)); + } + pub(crate) async fn update_cache(&self, gateways: Vec) { - let mut guard = self.inner.write().await; + let mut guard = self.gateways.write().await; for gateway in gateways { guard diff --git a/explorer-api/src/geo_ip/location.rs b/explorer-api/src/geo_ip/location.rs index adbdb6cef3..fdb8584938 100644 --- a/explorer-api/src/geo_ip/location.rs +++ b/explorer-api/src/geo_ip/location.rs @@ -42,6 +42,18 @@ pub(crate) struct Location { pub(crate) longitude: Option, } +impl From for nym_explorer_api_requests::Location { + fn from(location: Location) -> Self { + nym_explorer_api_requests::Location { + country_name: location.name, + two_letter_iso_country_code: location.iso_alpha2, + three_letter_iso_country_code: location.iso_alpha3, + latitude: location.latitude, + longitude: location.longitude, + } + } +} + impl GeoIp { pub fn new() -> Self { let db_path = std::env::var("GEOIP_DB_PATH").unwrap_or_else(|e| { diff --git a/explorer-api/src/location.rs b/explorer-api/src/location.rs new file mode 100644 index 0000000000..d11572e631 --- /dev/null +++ b/explorer-api/src/location.rs @@ -0,0 +1,40 @@ +// Copyright 2021 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use nym_explorer_api_requests::Location; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::time::{Duration, SystemTime}; + +pub(crate) type LocationCache = HashMap; + +#[allow(dead_code)] +#[derive(Debug, Deserialize)] +pub(crate) struct GeoLocation { + pub(crate) ip: String, + pub(crate) country_code: String, + pub(crate) country_name: String, + pub(crate) region_code: String, + pub(crate) region_name: String, + pub(crate) city: String, + pub(crate) zip_code: String, + pub(crate) time_zone: String, + pub(crate) latitude: f32, + pub(crate) longitude: f32, + pub(crate) metro_code: u32, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub(crate) struct LocationCacheItem { + pub(crate) location: Option, + pub(crate) valid_until: SystemTime, +} + +impl LocationCacheItem { + pub(crate) fn new_from_location(location: Option) -> Self { + LocationCacheItem { + location, + valid_until: SystemTime::now() + Duration::from_secs(60 * 60 * 24), // valid for 1 day + } + } +} diff --git a/explorer-api/src/main.rs b/explorer-api/src/main.rs index 676f1c5cea..1c00c1548f 100644 --- a/explorer-api/src/main.rs +++ b/explorer-api/src/main.rs @@ -19,6 +19,7 @@ mod geo_ip; mod guards; mod helpers; mod http; +mod location; mod mix_node; pub(crate) mod mix_nodes; mod overview; @@ -35,7 +36,7 @@ async fn main() { dotenv().ok(); setup_logging(); let args = commands::Cli::parse(); - setup_env(args.config_env_file.as_ref()); + setup_env(args.config_env_file); let mut explorer_api = ExplorerApi::new(); explorer_api.run().await; } diff --git a/explorer-api/src/mix_node/delegations.rs b/explorer-api/src/mix_node/delegations.rs index d9c151214d..feaefcf5c0 100644 --- a/explorer-api/src/mix_node/delegations.rs +++ b/explorer-api/src/mix_node/delegations.rs @@ -5,6 +5,7 @@ use super::models::SummedDelegations; use crate::client::ThreadsafeValidatorClient; use itertools::Itertools; use nym_mixnet_contract_common::{Delegation, MixId}; +use nym_validator_client::nyxd::contract_traits::PagedMixnetQueryClient; pub(crate) async fn get_single_mixnode_delegations( client: &ThreadsafeValidatorClient, @@ -12,7 +13,8 @@ pub(crate) async fn get_single_mixnode_delegations( ) -> Vec { match client .0 - .get_all_nyxd_single_mixnode_delegations(mix_id) + .nyxd + .get_all_single_mixnode_delegations(mix_id) .await { Ok(result) => result, diff --git a/explorer-api/src/mix_node/http.rs b/explorer-api/src/mix_node/http.rs index 137c6250ed..e00ed8ff82 100644 --- a/explorer-api/src/mix_node/http.rs +++ b/explorer-api/src/mix_node/http.rs @@ -6,9 +6,10 @@ use crate::mix_node::delegations::{ }; use crate::mix_node::econ_stats::retrieve_mixnode_econ_stats; use crate::mix_node::models::{ - EconomicDynamicsStats, NodeDescription, NodeStats, PrettyDetailedMixNodeBond, SummedDelegations, + EconomicDynamicsStats, NodeDescription, NodeStats, SummedDelegations, }; use crate::state::ExplorerApiStateContext; +use nym_explorer_api_requests::PrettyDetailedMixNodeBond; use nym_mixnet_contract_common::{Delegation, MixId}; use reqwest::Error as ReqwestError; use rocket::response::status::NotFound; diff --git a/explorer-api/src/mix_node/mod.rs b/explorer-api/src/mix_node/mod.rs index 88c3b1cd71..dac365c71d 100644 --- a/explorer-api/src/mix_node/mod.rs +++ b/explorer-api/src/mix_node/mod.rs @@ -1,4 +1,4 @@ pub(crate) mod delegations; pub(crate) mod econ_stats; pub(crate) mod http; -pub(crate) mod models; +pub mod models; diff --git a/explorer-api/src/mix_node/models.rs b/explorer-api/src/mix_node/models.rs index 94ae6558d0..4adfa6dd86 100644 --- a/explorer-api/src/mix_node/models.rs +++ b/explorer-api/src/mix_node/models.rs @@ -2,49 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 use crate::cache::Cache; -use crate::mix_nodes::location::Location; -use nym_contracts_common::Percent; use nym_mixnet_contract_common::Delegation; -use nym_mixnet_contract_common::{Addr, Coin, Layer, MixId, MixNode}; -use nym_validator_client::models::{NodePerformance, SelectionChance}; +use nym_mixnet_contract_common::{Addr, Coin, MixId}; +use nym_validator_client::models::SelectionChance; use serde::Deserialize; use serde::Serialize; use std::sync::Arc; use std::time::SystemTime; use tokio::sync::RwLock; -#[derive(Clone, Debug, Serialize, JsonSchema, PartialEq)] -#[serde(rename_all = "snake_case")] -pub(crate) enum MixnodeStatus { - Active, // in both the active set and the rewarded set - Standby, // only in the rewarded set - Inactive, // in neither the rewarded set nor the active set -} - -#[derive(Clone, Debug, Serialize, JsonSchema)] -pub(crate) struct PrettyDetailedMixNodeBond { - // I leave this to @MS to refactor this type as a lot of things here are redundant thanks to - // the existence of `MixNodeDetails` - pub mix_id: MixId, - pub location: Option, - pub status: MixnodeStatus, - pub pledge_amount: Coin, - pub total_delegation: Coin, - pub owner: Addr, - pub layer: Layer, - pub mix_node: MixNode, - pub stake_saturation: f32, - pub uncapped_saturation: f32, - pub avg_uptime: u8, - pub node_performance: NodePerformance, - pub estimated_operator_apy: f64, - pub estimated_delegators_apy: f64, - pub operating_cost: Coin, - pub profit_margin_percent: Percent, - pub family_id: Option, - pub blacklisted: bool, -} - #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, JsonSchema)] pub struct SummedDelegations { pub owner: Addr, diff --git a/explorer-api/src/mix_nodes/http.rs b/explorer-api/src/mix_nodes/http.rs index ef5570e455..a36843b66f 100644 --- a/explorer-api/src/mix_nodes/http.rs +++ b/explorer-api/src/mix_nodes/http.rs @@ -1,6 +1,6 @@ -use crate::mix_node::models::{MixnodeStatus, PrettyDetailedMixNodeBond}; use crate::mix_nodes::models::{MixNodeActiveSetSummary, MixNodeSummary}; use crate::state::ExplorerApiStateContext; +use nym_explorer_api_requests::{MixnodeStatus, PrettyDetailedMixNodeBond}; use rocket::serde::json::Json; use rocket::{Route, State}; use rocket_okapi::okapi::openapi3::OpenApi; diff --git a/explorer-api/src/mix_nodes/location.rs b/explorer-api/src/mix_nodes/location.rs index b876cdc792..de3c2e2fd6 100644 --- a/explorer-api/src/mix_nodes/location.rs +++ b/explorer-api/src/mix_nodes/location.rs @@ -1,62 +1,8 @@ // Copyright 2021 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::geo_ip::location; use nym_mixnet_contract_common::MixId; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::time::{Duration, SystemTime}; -pub(crate) type LocationCache = HashMap; +use crate::location::LocationCache; -#[allow(dead_code)] -#[derive(Debug, Deserialize)] -pub(crate) struct GeoLocation { - pub(crate) ip: String, - pub(crate) country_code: String, - pub(crate) country_name: String, - pub(crate) region_code: String, - pub(crate) region_name: String, - pub(crate) city: String, - pub(crate) zip_code: String, - pub(crate) time_zone: String, - pub(crate) latitude: f32, - pub(crate) longitude: f32, - pub(crate) metro_code: u32, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub(crate) struct LocationCacheItem { - pub(crate) location: Option, - pub(crate) valid_until: SystemTime, -} - -#[derive(Clone, Debug, JsonSchema, Serialize, Deserialize)] -pub(crate) struct Location { - pub(crate) two_letter_iso_country_code: String, - pub(crate) three_letter_iso_country_code: String, - pub(crate) country_name: String, - pub(crate) latitude: Option, - pub(crate) longitude: Option, -} - -impl Location { - pub(crate) fn new(location: location::Location) -> Self { - Location { - country_name: location.name, - two_letter_iso_country_code: location.iso_alpha2, - three_letter_iso_country_code: location.iso_alpha3, - latitude: location.latitude, - longitude: location.longitude, - } - } -} - -impl LocationCacheItem { - pub(crate) fn new_from_location(location: Option) -> Self { - LocationCacheItem { - location, - valid_until: SystemTime::now() + Duration::from_secs(60 * 60 * 24), // valid for 1 day - } - } -} +pub(crate) type MixnodeLocationCache = LocationCache; diff --git a/explorer-api/src/mix_nodes/models.rs b/explorer-api/src/mix_nodes/models.rs index bfead42c8c..fe5ae7fde8 100644 --- a/explorer-api/src/mix_nodes/models.rs +++ b/explorer-api/src/mix_nodes/models.rs @@ -5,17 +5,18 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use std::time::{Duration, SystemTime}; +use nym_explorer_api_requests::{Location, MixnodeStatus, PrettyDetailedMixNodeBond}; use nym_mixnet_contract_common::rewarding::helpers::truncate_reward; use nym_mixnet_contract_common::MixId; use serde::Serialize; use tokio::sync::{RwLock, RwLockReadGuard}; use crate::helpers::best_effort_small_dec_to_f64; +use crate::location::LocationCacheItem; use nym_validator_client::models::MixNodeBondAnnotated; +use super::location::MixnodeLocationCache; use super::utils::family_numerical_id; -use crate::mix_node::models::{MixnodeStatus, PrettyDetailedMixNodeBond}; -use crate::mix_nodes::location::{Location, LocationCache, LocationCacheItem}; use crate::mix_nodes::CACHE_ENTRY_TTL; #[derive(Clone, Debug, Serialize, JsonSchema)] @@ -83,18 +84,18 @@ impl MixNodesResult { #[derive(Clone)] pub(crate) struct ThreadsafeMixNodesCache { mixnodes: Arc>, - locations: Arc>, + locations: Arc>, } impl ThreadsafeMixNodesCache { pub(crate) fn new() -> Self { ThreadsafeMixNodesCache { mixnodes: Arc::new(RwLock::new(MixNodesResult::new())), - locations: Arc::new(RwLock::new(LocationCache::new())), + locations: Arc::new(RwLock::new(MixnodeLocationCache::new())), } } - pub(crate) fn new_with_location_cache(locations: LocationCache) -> Self { + pub(crate) fn new_with_location_cache(locations: MixnodeLocationCache) -> Self { ThreadsafeMixNodesCache { mixnodes: Arc::new(RwLock::new(MixNodesResult::new())), locations: Arc::new(RwLock::new(locations)), @@ -111,7 +112,7 @@ impl ThreadsafeMixNodesCache { }) } - pub(crate) async fn get_locations(&self) -> LocationCache { + pub(crate) async fn get_locations(&self) -> MixnodeLocationCache { self.locations.read().await.clone() } diff --git a/explorer-api/src/mix_nodes/utils.rs b/explorer-api/src/mix_nodes/utils.rs index 0f7eb6b991..5273d66f55 100644 --- a/explorer-api/src/mix_nodes/utils.rs +++ b/explorer-api/src/mix_nodes/utils.rs @@ -1,13 +1,13 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +use isocountry::CountryCode; use nym_mixnet_contract_common::families::FamilyHead; use rand::Rng; use rand_pcg::Pcg64; use rand_seeder::Seeder; -use crate::mix_nodes::location::GeoLocation; -use isocountry::CountryCode; +use crate::location::GeoLocation; #[allow(dead_code)] pub(crate) fn map_2_letter_to_3_letter_country_code(geo: &GeoLocation) -> String { diff --git a/explorer-api/src/state.rs b/explorer-api/src/state.rs index 0011a924d3..8ecee92ae2 100644 --- a/explorer-api/src/state.rs +++ b/explorer-api/src/state.rs @@ -13,9 +13,10 @@ use nym_validator_client::models::MixNodeBondAnnotated; use crate::country_statistics::country_nodes_distribution::{ CountryNodesDistribution, ThreadsafeCountryNodesDistribution, }; +use crate::gateways::location::GatewayLocationCache; use crate::gateways::models::ThreadsafeGatewayCache; use crate::mix_node::models::ThreadsafeMixNodeCache; -use crate::mix_nodes::location::LocationCache; +use crate::mix_nodes::location::MixnodeLocationCache; use crate::mix_nodes::models::ThreadsafeMixNodesCache; use crate::ping::models::ThreadsafePingCache; use crate::validators::models::ThreadsafeValidatorCache; @@ -46,7 +47,8 @@ impl ExplorerApiState { #[derive(Debug, Serialize, Deserialize)] pub struct ExplorerApiStateOnDisk { pub(crate) country_node_distribution: CountryNodesDistribution, - pub(crate) location_cache: LocationCache, + pub(crate) mixnode_location_cache: MixnodeLocationCache, + pub(crate) gateway_location_cache: GatewayLocationCache, pub(crate) as_at: DateTime, } @@ -76,9 +78,13 @@ impl ExplorerApiStateContext { ThreadsafeCountryNodesDistribution::new_from_distribution( state.country_node_distribution, ), - gateways: ThreadsafeGatewayCache::new(), + gateways: ThreadsafeGatewayCache::new_with_location_cache( + state.gateway_location_cache, + ), mixnode: ThreadsafeMixNodeCache::new(), - mixnodes: ThreadsafeMixNodesCache::new_with_location_cache(state.location_cache), + mixnodes: ThreadsafeMixNodesCache::new_with_location_cache( + state.mixnode_location_cache, + ), ping: ThreadsafePingCache::new(), validators: ThreadsafeValidatorCache::new(), validator_client: ThreadsafeValidatorClient::new(), @@ -109,7 +115,8 @@ impl ExplorerApiStateContext { let file = File::create(json_file_path).expect("unable to create state json file"); let state = ExplorerApiStateOnDisk { country_node_distribution: self.inner.country_node_distribution.get_all().await, - location_cache: self.inner.mixnodes.get_locations().await, + mixnode_location_cache: self.inner.mixnodes.get_locations().await, + gateway_location_cache: self.inner.gateways.get_locations().await, as_at: Utc::now(), }; serde_json::to_writer(file, &state).expect("error writing state to disk"); diff --git a/explorer-api/src/tasks.rs b/explorer-api/src/tasks.rs index 548d1f1a0a..b0b9a9701b 100644 --- a/explorer-api/src/tasks.rs +++ b/explorer-api/src/tasks.rs @@ -1,14 +1,13 @@ // Copyright 2022 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use std::future::Future; - use nym_mixnet_contract_common::GatewayBond; use nym_task::TaskClient; use nym_validator_client::models::MixNodeBondAnnotated; use nym_validator_client::nyxd::error::NyxdError; -use nym_validator_client::nyxd::{Paging, QueryNyxdClient, ValidatorResponse}; -use nym_validator_client::ValidatorClientError; +use nym_validator_client::nyxd::{Paging, TendermintRpcClient, ValidatorResponse}; +use nym_validator_client::{QueryHttpRpcValidatorClient, ValidatorClientError}; +use std::future::Future; use crate::mix_nodes::CACHE_REFRESH_RATE; use crate::state::ExplorerApiStateContext; @@ -26,7 +25,7 @@ impl ExplorerApiTasks { // a helper to remove duplicate code when grabbing active/rewarded/all mixnodes async fn retrieve_mixnodes<'a, F, Fut>(&'a self, f: F) -> Vec where - F: FnOnce(&'a nym_validator_client::Client) -> Fut, + F: FnOnce(&'a QueryHttpRpcValidatorClient) -> Fut, Fut: Future, ValidatorClientError>>, { let bonds = match f(&self.state.inner.validator_client.0).await { @@ -75,7 +74,7 @@ impl ExplorerApiTasks { .validator_client .0 .nyxd - .get_validators(height.value(), Paging::All) + .validators(height, Paging::All) .await?; info!("Fetched {} validators", response.validators.len()); Ok(response) diff --git a/explorer/.env.dev b/explorer/.env.dev new file mode 100644 index 0000000000..fe4119f671 --- /dev/null +++ b/explorer/.env.dev @@ -0,0 +1,9 @@ +# When running the explorer API locally +#EXPLORER_API_URL=http://localhost:8000/v1 + +EXPLORER_API_URL=https://sandbox-explorer.nymtech.net/api/v1 +NYM_API_URL=https://sandbox-nym-api1.nymtech.net +VALIDATOR_URL=https://sandbox-validator1.nymtech.net +BIG_DIPPER_URL=https://sandbox-blocks.nymtech.net +CURRENCY_DENOM=unym +CURRENCY_STAKING_DENOM=unyx diff --git a/explorer/.gitignore b/explorer/.gitignore index b9a105e264..b8b1827577 100644 --- a/explorer/.gitignore +++ b/explorer/.gitignore @@ -1,3 +1,4 @@ dist .DS_Store -detailAPI.md \ No newline at end of file +detailAPI.md +!.env.dev diff --git a/explorer/README.md b/explorer/README.md index cc8c7f0363..a7bbc2346a 100644 --- a/explorer/README.md +++ b/explorer/README.md @@ -32,6 +32,21 @@ You can then open a browser to http://localhost:3000 and start development. Documentation for developers [can be found here](./docs). +Several environment variables are required. They can be +provisioned via a `.env` file. For convenience a `.env.dev` is +provided, just copy its content into `.env`. + +#### Required env vars + +``` +EXPLORER_API_URL +NYM_API_URL +VALIDATOR_URL +BIG_DIPPER_URL +CURRENCY_DENOM +CURRENCY_STAKING_DENOM +``` + ## Deployment Build the UI with (starting in the repository root): diff --git a/explorer/package.json b/explorer/package.json index 094e705132..885f378254 100644 --- a/explorer/package.json +++ b/explorer/package.json @@ -3,6 +3,20 @@ "version": "1.0.7", "private": true, "license": "Apache-2.0", + "scripts": { + "start": "webpack serve --progress --port 3000", + "build": "webpack build --progress --config webpack.prod.js", + "build:serve": "npx serve dist", + "test": "jest", + "test:watch": "jest --watch", + "tsc": "tsc --noEmit true", + "tsc:watch": "tsc --watch --noEmit true", + "lint": "eslint src", + "lint:fix": "eslint src --fix", + "prestorybook": "yarn --cwd .. build", + "storybook": "start-storybook -p 6006", + "storybook:build": "build-storybook" + }, "dependencies": { "@cosmjs/math": "^0.26.2", "@emotion/react": "^11.4.1", @@ -18,6 +32,7 @@ "big.js": "^6.2.1", "d3-scale": "^4.0.0", "date-fns": "^2.24.0", + "lodash": "^4.17.21", "i18n-iso-countries": "^6.8.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -95,20 +110,6 @@ "webpack-favicons": "^1.3.8", "webpack-merge": "^5.8.0" }, - "scripts": { - "start": "webpack serve --progress --port 3000", - "build": "webpack build --progress --config webpack.prod.js", - "build:serve": "npx serve dist", - "test": "jest", - "test:watch": "jest --watch", - "tsc": "tsc --noEmit true", - "tsc:watch": "tsc --watch --noEmit true", - "lint": "eslint src", - "lint:fix": "eslint src --fix", - "prestorybook": "yarn --cwd .. build", - "storybook": "start-storybook -p 6006", - "storybook:build": "build-storybook" - }, "browserslist": { "production": [ ">0.2%", diff --git a/explorer/src/api/constants.ts b/explorer/src/api/constants.ts index e3f39a3cae..f00c9ec280 100644 --- a/explorer/src/api/constants.ts +++ b/explorer/src/api/constants.ts @@ -9,6 +9,7 @@ export const OVERVIEW_API = `${API_BASE_URL}/overview`; export const MIXNODE_PING = `${API_BASE_URL}/ping`; export const MIXNODES_API = `${API_BASE_URL}/mix-nodes`; export const MIXNODE_API = `${API_BASE_URL}/mix-node`; +export const GATEWAYS_EXPLORER_API = `${API_BASE_URL}/gateways`; export const GATEWAYS_API = `${NYM_API_BASE_URL}/api/v1/status/gateways/detailed`; export const VALIDATORS_API = `${VALIDATOR_BASE_URL}/validators`; export const BLOCK_API = `${NYM_API_BASE_URL}/block`; diff --git a/explorer/src/api/index.ts b/explorer/src/api/index.ts index ceb5878e97..a2b134d4ae 100644 --- a/explorer/src/api/index.ts +++ b/explorer/src/api/index.ts @@ -1,3 +1,4 @@ +import keyBy from 'lodash/keyBy'; import { API_BASE_URL, BLOCK_API, @@ -11,6 +12,7 @@ import { UPTIME_STORY_API, VALIDATORS_API, SERVICE_PROVIDERS, + GATEWAYS_EXPLORER_API, } from './constants'; import { @@ -32,6 +34,7 @@ import { GatewayBondAnnotated, GatewayBond, DirectoryServiceProvider, + LocatedGateway, } from '../typeDefs/explorer-api'; function getFromCache(key: string) { @@ -98,9 +101,13 @@ export class Api { static fetchGateways = async (): Promise => { const res = await fetch(GATEWAYS_API); const gatewaysAnnotated: GatewayBondAnnotated[] = await res.json(); + const res2 = await fetch(GATEWAYS_EXPLORER_API); + const locatedGateways: LocatedGateway[] = await res2.json(); + const locatedGatewaysByOwner = keyBy(locatedGateways, 'owner'); return gatewaysAnnotated.map(({ gateway_bond, node_performance }) => ({ ...gateway_bond, node_performance, + location: locatedGatewaysByOwner[gateway_bond.owner]?.location, })); }; diff --git a/explorer/src/components/Gateways.ts b/explorer/src/components/Gateways.ts index b78b1320da..4072a33db7 100644 --- a/explorer/src/components/Gateways.ts +++ b/explorer/src/components/Gateways.ts @@ -26,7 +26,7 @@ export function gatewayToGridRow(arrayOfGateways: GatewayResponse): GatewayRowTy id: gw.owner, owner: gw.owner, identity_key: gw.gateway.identity_key || '', - location: gw?.gateway?.location || '', + location: gw.location?.country_name.toUpperCase() || '', bond: gw.pledge_amount.amount || 0, host: gw.gateway.host || '', version: gw.gateway.version || '', @@ -39,7 +39,7 @@ export function gatewayEnrichedToGridRow(gateway: GatewayBond, report: GatewayRe id: gateway.owner, owner: gateway.owner, identity_key: gateway.gateway.identity_key || '', - location: gateway?.gateway?.location || '', + location: gateway.location?.country_name.toUpperCase() || '', bond: gateway.pledge_amount.amount || 0, host: gateway.gateway.host || '', version: gateway.gateway.version || '', diff --git a/explorer/src/typeDefs/explorer-api.ts b/explorer/src/typeDefs/explorer-api.ts index 736c6e8ffa..9319c33952 100644 --- a/explorer/src/typeDefs/explorer-api.ts +++ b/explorer/src/typeDefs/explorer-api.ts @@ -131,6 +131,7 @@ export interface GatewayBond { owner: string; gateway: Gateway; node_performance: NodePerformance; + location?: Location; } export interface GatewayBondAnnotated { @@ -138,6 +139,23 @@ export interface GatewayBondAnnotated { node_performance: NodePerformance; } +export interface Location { + two_letter_iso_country_code: string; + three_letter_iso_country_code: string; + country_name: string; + latitude?: number; + longitude?: number; +} + +export interface LocatedGateway { + pledge_amount: Amount; + owner: string; + block_height: number; + gateway: Gateway; + proxy?: string; + location?: Location; +} + export type GatewayResponse = GatewayBond[]; export interface GatewayReportResponse { diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index 763b3ab676..8e907a9eee 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-gateway" -version = "1.1.23" +version = "1.1.27" authors = [ "Dave Hrycyszyn ", "Jędrzej Stuczyński ", @@ -25,7 +25,7 @@ colored = "2.0" dashmap = "4.0" dirs = "4.0" dotenvy = { workspace = true } -futures = "0.3" +futures = { workspace = true } humantime-serde = "1.0.1" lazy_static = "1.4.0" log = { workspace = true } diff --git a/gateway/gateway-requests/Cargo.toml b/gateway/gateway-requests/Cargo.toml index 92b2a7c8e4..dcd8ad3743 100644 --- a/gateway/gateway-requests/Cargo.toml +++ b/gateway/gateway-requests/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] bs58 = "0.4.0" -futures = "0.3.15" +futures = { workspace = true } generic-array = { workspace = true, features = ["serde"] } log = { workspace = true } rand = { version = "0.7.3", features = ["wasm-bindgen"] } diff --git a/gateway/gateway-requests/src/registration/handshake/gateway.rs b/gateway/gateway-requests/src/registration/handshake/gateway.rs index 2afebbc115..69381dc5d4 100644 --- a/gateway/gateway-requests/src/registration/handshake/gateway.rs +++ b/gateway/gateway-requests/src/registration/handshake/gateway.rs @@ -94,7 +94,7 @@ impl<'a> GatewayHandshake<'a> { .to_bytes() .iter() .cloned() - .chain(material.into_iter()) + .chain(material) .collect() } diff --git a/gateway/gateway-requests/src/registration/handshake/shared_key.rs b/gateway/gateway-requests/src/registration/handshake/shared_key.rs index abe1754e31..d3101017a4 100644 --- a/gateway/gateway-requests/src/registration/handshake/shared_key.rs +++ b/gateway/gateway-requests/src/registration/handshake/shared_key.rs @@ -85,10 +85,7 @@ impl SharedKeys { let mac = compute_keyed_hmac::(self.mac_key(), &encrypted_data); - mac.into_bytes() - .into_iter() - .chain(encrypted_data.into_iter()) - .collect() + mac.into_bytes().into_iter().chain(encrypted_data).collect() } pub fn decrypt_tagged( diff --git a/gateway/src/commands/build_info.rs b/gateway/src/commands/build_info.rs new file mode 100644 index 0000000000..e3385bb933 --- /dev/null +++ b/gateway/src/commands/build_info.rs @@ -0,0 +1,16 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::Args; +use nym_bin_common::bin_info_owned; +use nym_bin_common::output_format::OutputFormat; + +#[derive(Args)] +pub(crate) struct BuildInfo { + #[clap(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +pub(crate) fn execute(args: BuildInfo) { + println!("{}", args.output.format(&bin_info_owned!())) +} diff --git a/gateway/src/commands/mod.rs b/gateway/src/commands/mod.rs index 15775ec757..ed70c8b203 100644 --- a/gateway/src/commands/mod.rs +++ b/gateway/src/commands/mod.rs @@ -18,6 +18,7 @@ use std::error::Error; use std::net::IpAddr; use std::path::PathBuf; +pub(crate) mod build_info; pub(crate) mod init; pub(crate) mod node_details; pub(crate) mod run; @@ -37,6 +38,9 @@ pub(crate) enum Commands { /// Sign text to prove ownership of this mixnode Sign(sign::Sign), + /// Show build information of this binary + BuildInfo(build_info::BuildInfo), + /// Generate shell completions Completions(ArgShell), @@ -67,6 +71,7 @@ pub(crate) async fn execute(args: Cli) -> Result<(), Box node_details::execute(m).await?, Commands::Run(m) => run::execute(m).await?, Commands::Sign(m) => sign::execute(m)?, + Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut crate::Cli::command(), bin_name), Commands::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name), } diff --git a/gateway/src/main.rs b/gateway/src/main.rs index 9ea8eb679d..5290037266 100644 --- a/gateway/src/main.rs +++ b/gateway/src/main.rs @@ -5,7 +5,7 @@ use clap::{crate_name, crate_version, Parser}; use colored::Colorize; use lazy_static::lazy_static; use log::error; -use nym_bin_common::build_information::BinaryBuildInformation; +use nym_bin_common::bin_info; use nym_bin_common::logging::{maybe_print_banner, setup_logging}; use nym_bin_common::output_format::OutputFormat; use nym_network_defaults::setup_env; @@ -18,8 +18,7 @@ mod node; pub(crate) mod support; lazy_static! { - pub static ref PRETTY_BUILD_INFORMATION: String = - BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print(); + pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print(); } // Helper for passing LONG_VERSION to clap @@ -34,18 +33,24 @@ struct Cli { #[clap(short, long)] pub(crate) config_env_file: Option, + /// Flag used for disabling the printed banner in tty. + #[clap(long)] + pub(crate) no_banner: bool, + #[clap(subcommand)] command: commands::Commands, } #[tokio::main] async fn main() -> Result<(), Box> { - setup_logging(); - maybe_print_banner(crate_name!(), crate_version!()); - let args = Cli::parse(); setup_env(args.config_env_file.as_ref()); + if !args.no_banner { + maybe_print_banner(crate_name!(), crate_version!()); + } + setup_logging(); + commands::execute(args).await.map_err(|err| { if atty::is(atty::Stream::Stdout) { let error_message = format!("{err}").red(); diff --git a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs index 1a41937c4b..0fe930a96d 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/authenticated.rs @@ -22,6 +22,7 @@ use crate::node::client_handling::bandwidth::Bandwidth; use crate::node::client_handling::FREE_TESTNET_BANDWIDTH_VALUE; use nym_gateway_requests::iv::IV; use nym_task::TaskClient; +use nym_validator_client::coconut::CoconutApiError; #[derive(Debug, Error)] pub(crate) enum RequestHandlingError { @@ -64,6 +65,9 @@ pub(crate) enum RequestHandlingError { #[error("Coconut interface error - {0}")] CoconutInterfaceError(#[from] nym_coconut_interface::error::CoconutInterfaceError), + #[error("coconut api query failure: {0}")] + CoconutApiError(#[from] CoconutApiError), + #[error("Credential error - {0}")] CredentialError(#[from] nym_credentials::error::Error), } diff --git a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs index cc8c552b07..356a884161 100644 --- a/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs +++ b/gateway/src/node/client_handling/websocket/connection_handler/coconut.rs @@ -4,14 +4,17 @@ use super::authenticated::RequestHandlingError; use log::*; use nym_coconut_interface::Credential; -use nym_validator_client::nyxd::traits::DkgQueryClient; +use nym_validator_client::coconut::all_coconut_api_clients; use nym_validator_client::{ nyxd::{ + contract_traits::{ + CoconutBandwidthSigningClient, DkgQueryClient, MultisigQueryClient, + MultisigSigningClient, + }, cosmwasm_client::logs::{find_attribute, BANDWIDTH_PROPOSAL_ID}, - traits::{CoconutBandwidthSigningClient, MultisigQueryClient, MultisigSigningClient}, - Coin, DirectSigningNyxdClient, Fee, + Coin, Fee, }, - Client, CoconutApiClient, + CoconutApiClient, DirectSigningHttpRpcNyxdClient, }; use std::time::{Duration, SystemTime}; @@ -19,18 +22,13 @@ const ONE_HOUR_SEC: u64 = 3600; const MAX_FEEGRANT_UNYM: u128 = 10000; pub(crate) struct CoconutVerifier { - nyxd_client: Client, + nyxd_client: DirectSigningHttpRpcNyxdClient, mix_denom_base: String, } impl CoconutVerifier { - pub fn new(nyxd_client: Client) -> Self { - let mix_denom_base = nyxd_client - .nyxd - .current_chain_details() - .mix_denom - .base - .clone(); + pub fn new(nyxd_client: DirectSigningHttpRpcNyxdClient) -> Self { + let mix_denom_base = nyxd_client.current_chain_details().mix_denom.base.clone(); CoconutVerifier { nyxd_client, @@ -41,7 +39,7 @@ impl CoconutVerifier { pub async fn all_current_coconut_api_clients( &self, ) -> Result, RequestHandlingError> { - let epoch_id = self.nyxd_client.nyxd.get_current_epoch().await?.epoch_id; + let epoch_id = self.nyxd_client.get_current_epoch().await?.epoch_id; self.all_coconut_api_clients(epoch_id).await } @@ -49,7 +47,7 @@ impl CoconutVerifier { &self, epoch_id: u64, ) -> Result, RequestHandlingError> { - Ok(CoconutApiClient::all_coconut_api_clients(&self.nyxd_client, epoch_id).await?) + Ok(all_coconut_api_clients(&self.nyxd_client, epoch_id).await?) } pub async fn release_funds( @@ -63,14 +61,13 @@ impl CoconutVerifier { let res = self .nyxd_client - .nyxd .spend_credential( Coin::new( credential.voucher_value().into(), self.mix_denom_base.clone(), ), credential.blinded_serial_number(), - self.nyxd_client.nyxd.address().to_string(), + self.nyxd_client.address().to_string(), None, ) .await?; @@ -84,7 +81,7 @@ impl CoconutVerifier { reason: String::from("proposal id could not be parsed to u64"), })?; - let proposal = self.nyxd_client.nyxd.get_proposal(proposal_id).await?; + let proposal = self.nyxd_client.query_proposal(proposal_id).await?; if !credential.has_blinded_serial_number(&proposal.description)? { return Err(RequestHandlingError::ProposalIdError { reason: String::from("proposal has different serial number"), @@ -94,11 +91,10 @@ impl CoconutVerifier { let req = nym_api_requests::coconut::VerifyCredentialBody::new( credential.clone(), proposal_id, - self.nyxd_client.nyxd.address().clone(), + self.nyxd_client.address().clone(), ); for client in api_clients { self.nyxd_client - .nyxd .grant_allowance( &client.cosmos_address, vec![Coin::new(MAX_FEEGRANT_UNYM, self.mix_denom_base.clone())], @@ -111,22 +107,25 @@ impl CoconutVerifier { .await?; let ret = client.api_client.verify_bandwidth_credential(&req).await; self.nyxd_client - .nyxd .revoke_allowance( &client.cosmos_address, "Cleanup the previous allowance for releasing funds".to_string(), revoke_fee.clone(), ) .await?; - if !ret?.verification_result { - debug!("Validator {} didn't accept the credential. It will probably vote No on the spending proposal", client.api_client.nym_api_client.current_url()); + match ret { + Ok(res) => { + if !res.verification_result { + debug!("Validator {} didn't accept the credential. It will probably vote No on the spending proposal", client.api_client.nym_api.current_url()); + } + } + Err(e) => { + warn!("Validator {} could not be reached. There might be a problem with the coconut endpoint - {:?}", client.api_client.nym_api.current_url(), e); + } } } - self.nyxd_client - .nyxd - .execute_proposal(proposal_id, None) - .await?; + self.nyxd_client.execute_proposal(proposal_id, None).await?; Ok(()) } diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index 8487e6c99f..93bce3a6fd 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -17,7 +17,7 @@ use nym_mixnet_client::forwarder::{MixForwardingSender, PacketForwarder}; use nym_network_defaults::NymNetworkDetails; use nym_statistics_common::collector::StatisticsSender; use nym_task::{TaskClient, TaskManager}; -use nym_validator_client::Client; +use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient}; use rand::seq::SliceRandom; use rand::thread_rng; use std::error::Error; @@ -212,26 +212,22 @@ impl Gateway { nym_validator_client::NymApiClient::new(nym_api.clone()) } - fn random_nyxd_client( - &self, - ) -> nym_validator_client::Client { + fn random_nyxd_client(&self) -> DirectSigningHttpRpcNyxdClient { let endpoints = self.config.get_nyxd_urls(); let validator_nyxd = endpoints .choose(&mut thread_rng()) .expect("The list of validators is empty"); let network_details = NymNetworkDetails::new_from_env(); - let client_config = nym_validator_client::Config::try_from_nym_network_details( - &network_details, - ) - .expect("failed to construct valid validator client config with the provided network"); + let client_config = nyxd::Config::try_from_nym_network_details(&network_details) + .expect("failed to construct valid validator client config with the provided network"); - let mut client = Client::new_signing(client_config, self.config.get_cosmos_mnemonic()) - .expect("Could not connect with mnemonic"); - client - .change_nyxd(validator_nyxd.clone()) - .expect("Could not use the random nyxd URL"); - client + DirectSigningHttpRpcNyxdClient::connect_with_mnemonic( + client_config, + validator_nyxd.as_ref(), + self.config.get_cosmos_mnemonic(), + ) + .expect("Could not connect with mnemonic") } async fn check_if_bonded(&self) -> Result { diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index 9f512f0d85..0a9edd82b9 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-mixnode" -version = "1.1.24" +version = "1.1.28" authors = [ "Dave Hrycyszyn ", "Jędrzej Stuczyński ", @@ -22,7 +22,7 @@ clap = { version = "4.0", features = ["cargo", "derive"] } colored = "2.0" cupid = "0.6.1" dirs = "4.0" -futures = "0.3.0" +futures = { workspace = true } humantime-serde = "1.0" lazy_static = "1.4.0" log = { workspace = true } diff --git a/mixnode/src/commands/build_info.rs b/mixnode/src/commands/build_info.rs new file mode 100644 index 0000000000..e3385bb933 --- /dev/null +++ b/mixnode/src/commands/build_info.rs @@ -0,0 +1,16 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::Args; +use nym_bin_common::bin_info_owned; +use nym_bin_common::output_format::OutputFormat; + +#[derive(Args)] +pub(crate) struct BuildInfo { + #[clap(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +pub(crate) fn execute(args: BuildInfo) { + println!("{}", args.output.format(&bin_info_owned!())) +} diff --git a/mixnode/src/commands/mod.rs b/mixnode/src/commands/mod.rs index b8c8f84894..e79df8e2ab 100644 --- a/mixnode/src/commands/mod.rs +++ b/mixnode/src/commands/mod.rs @@ -15,6 +15,7 @@ use nym_crypto::bech32_address_validation; use std::net::IpAddr; use std::process; +mod build_info; mod describe; mod init; mod node_details; @@ -38,6 +39,9 @@ pub(crate) enum Commands { /// Show details of this mixnode NodeDetails(node_details::NodeDetails), + /// Show build information of this binary + BuildInfo(build_info::BuildInfo), + /// Generate shell completions Completions(ArgShell), @@ -64,6 +68,7 @@ pub(crate) async fn execute(args: Cli) -> anyhow::Result<()> { Commands::Run(m) => run::execute(&m).await?, Commands::Sign(m) => sign::execute(&m)?, Commands::NodeDetails(m) => node_details::execute(&m)?, + Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut crate::Cli::command(), bin_name), Commands::GenerateFigSpec => fig_generate(&mut crate::Cli::command(), bin_name), } diff --git a/mixnode/src/main.rs b/mixnode/src/main.rs index 5681e02cc7..bdda653edf 100644 --- a/mixnode/src/main.rs +++ b/mixnode/src/main.rs @@ -7,7 +7,8 @@ extern crate rocket; use ::nym_config::defaults::setup_env; use clap::{crate_name, crate_version, Parser}; use lazy_static::lazy_static; -use nym_bin_common::build_information::BinaryBuildInformation; +use nym_bin_common::bin_info; + #[allow(unused_imports)] use nym_bin_common::logging::{maybe_print_banner, setup_logging}; #[cfg(feature = "cpucycles")] @@ -16,13 +17,13 @@ use nym_bin_common::setup_tracing; use nym_mixnode_common::measure; #[cfg(feature = "cpucycles")] use tracing::instrument; + mod commands; mod config; mod node; lazy_static! { - pub static ref PRETTY_BUILD_INFORMATION: String = - BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print(); + pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print(); } // Helper for passing LONG_VERSION to clap @@ -37,6 +38,10 @@ struct Cli { #[clap(short, long)] pub(crate) config_env_file: Option, + /// Flag used for disabling the printed banner in tty. + #[clap(long)] + pub(crate) no_banner: bool, + #[clap(subcommand)] command: commands::Commands, } @@ -49,6 +54,13 @@ fn test_function() { #[tokio::main] async fn main() -> anyhow::Result<()> { + let args = Cli::parse(); + setup_env(args.config_env_file.as_ref()); + + if !args.no_banner { + maybe_print_banner(crate_name!(), crate_version!()); + } + cfg_if::cfg_if! { if #[cfg(feature = "cpucycles")] { setup_tracing!("mixnode"); @@ -59,10 +71,6 @@ async fn main() -> anyhow::Result<()> { } } - maybe_print_banner(crate_name!(), crate_version!()); - - let args = Cli::parse(); - setup_env(args.config_env_file.as_ref()); commands::execute(args).await?; cfg_if::cfg_if! { diff --git a/nym-api/Cargo.toml b/nym-api/Cargo.toml index f9b378f59d..29be65e7bb 100644 --- a/nym-api/Cargo.toml +++ b/nym-api/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-api" -version = "1.1.24" +version = "1.1.28" authors = [ "Dave Hrycyszyn ", "Jędrzej Stuczyński ", @@ -22,7 +22,7 @@ cfg-if = "1.0" clap = { version = "4.0", features = ["cargo", "derive"] } console-subscriber = { version = "0.1.1", optional = true } # validator-api needs to be built with RUSTFLAGS="--cfg tokio_unstable" dirs = "4.0" -futures = "0.3.24" +futures = { workspace = true } humantime-serde = "1.0" lazy_static = "1.4.0" log = { workspace = true } @@ -30,13 +30,13 @@ pin-project = "1.0" pretty_env_logger = "0.4.0" rand = "0.8.5" rand-07 = { package = "rand", version = "0.7.3" } # required for compatibility -reqwest = { version = "0.11.11", features = ["json"] } +reqwest = { workspace = true, features = ["json"] } rocket = { version = "0.5.0-rc.2", features = ["json"] } rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", rev = "dfd3662c49e2f6fc37df35091cb94d82f7fb5915" } -serde = "1.0" +serde = { workspace = true } serde_json = { workspace = true } tap = "1.0" -thiserror = "1.0" +thiserror = { workspace = true } time = { version = "0.3.14", features = ["serde-human-readable", "parsing"] } tokio = { version = "1.24.1", features = [ "rt-multi-thread", @@ -45,9 +45,9 @@ tokio = { version = "1.24.1", features = [ "time", ] } tokio-stream = "0.1.11" -url = "2.2" +url = { workspace = true } -ts-rs = {version = "6.1", optional = true} +ts-rs = { workspace = true, optional = true} anyhow = "1.0" getset = "0.1.1" @@ -64,16 +64,28 @@ rocket_okapi = { version = "0.8.0-rc.2", features = ["swagger"] } schemars = { version = "0.8", features = ["preserve_order"] } zeroize = { workspace = true } +## ephemera-specific +actix-web = "4" +array-bytes = "6.0.0" +chrono = { version = "0.4.24", default-features = false, features = ["clock"] } +futures-util = "0.3.25" +serde_derive = "1.0.149" +tempfile = "3.3.0" +uuid = { version = "1.3.0", features = ["serde", "v4"] } + ## internal +ephemera = { path = "../ephemera" } nym-bandwidth-controller = { path = "../common/bandwidth-controller" } nym-coconut-bandwidth-contract-common = { path = "../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" } nym-coconut-dkg-common = { path = "../common/cosmwasm-smart-contracts/coconut-dkg" } nym-coconut-interface = { path = "../common/coconut-interface" } +nym-ephemera-common = { path = "../common/cosmwasm-smart-contracts/ephemera" } nym-config = { path = "../common/config" } cosmwasm-std = { workspace = true } nym-credential-storage = { path = "../common/credential-storage" } nym-credentials = { path = "../common/credentials" } nym-crypto = { path = "../common/crypto" } +cw2 = { workspace = true } cw3 = { workspace = true } cw4 = { workspace = true } nym-dkg = { path = "../common/dkg", features = ["cw-types"] } @@ -91,9 +103,7 @@ nym-pemstore = { path = "../common/pemstore" } 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 = [ - "http-client", "signing" -] } +nym-validator-client = { path = "../common/client-libs/validator-client" } nym-bin-common = { path = "../common/bin-common" } nym-node-tester-utils = { path = "../common/node-tester-utils" } diff --git a/nym-api/ephemera_migrations/contract/V1__contract.sql b/nym-api/ephemera_migrations/contract/V1__contract.sql new file mode 100644 index 0000000000..947c316940 --- /dev/null +++ b/nym-api/ephemera_migrations/contract/V1__contract.sql @@ -0,0 +1,19 @@ +CREATE TABLE contract_mixnode_reward +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + mix_id INTEGER NOT NULL, + epoch INTEGER NOT NULL, + nym_api_id INTEGER NOT NULL, + reliability INTEGER NOT NULL, + timestamp INTEGER NOT NULL, + UNIQUE (mix_id, epoch) +); + +CREATE TABLE epoch_info +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + epoch_id INTEGER NOT NULL, + start_time INTEGER NOT NULL, + duration INTEGER NOT NULL, + UNIQUE (epoch_id) +); \ No newline at end of file diff --git a/nym-api/ephemera_migrations/rewardsnew/V1__metrics.sql b/nym-api/ephemera_migrations/rewardsnew/V1__metrics.sql new file mode 100644 index 0000000000..9f5e0e2f04 --- /dev/null +++ b/nym-api/ephemera_migrations/rewardsnew/V1__metrics.sql @@ -0,0 +1,23 @@ +CREATE TABLE mixnode_status +( + mix_id INTEGER NOT NULL, + reliability INTEGER NOT NULL, + timestamp INTEGER NOT NULL, + UNIQUE (mix_id, timestamp) +); + +CREATE TABLE rewarding_report +( + epoch_id INTEGER NOT NULL, + eligible_mixnodes INTEGER NOT NULL, + timestamp INTEGER NOT NULL, + UNIQUE (epoch_id) +); + +CREATE TABLE epoch_blocks +( + epoch_id INTEGER NOT NULL, + block_id INTEGER NOT NULL, + timestamp INTEGER NOT NULL, + UNIQUE (epoch_id, block_id) +); \ No newline at end of file diff --git a/nym-api/ephemera_migrations/rewardsold/V1__metrics.sql b/nym-api/ephemera_migrations/rewardsold/V1__metrics.sql new file mode 100644 index 0000000000..3d5a66c4b8 --- /dev/null +++ b/nym-api/ephemera_migrations/rewardsold/V1__metrics.sql @@ -0,0 +1,6 @@ +CREATE TABLE mixnode_status +( + mix_id INTEGER NOT NULL, + reliability INTEGER NOT NULL, + timestamp INTEGER NOT NULL +); \ No newline at end of file diff --git a/nym-api/nym-api-requests/Cargo.toml b/nym-api/nym-api-requests/Cargo.toml index 37aded5efa..e890aa8e44 100644 --- a/nym-api/nym-api-requests/Cargo.toml +++ b/nym-api/nym-api-requests/Cargo.toml @@ -12,7 +12,7 @@ cosmwasm-std = { workspace = true, default-features = false } getset = "0.1.1" schemars = { version = "0.8", features = ["preserve_order"] } serde = { version = "1.0", features = ["derive"] } -ts-rs = { version = "6.1.2", optional = true } +ts-rs = { workspace = true, optional = true } nym-coconut-interface = { path = "../../common/coconut-interface" } nym-mixnet-contract-common = { path= "../../common/cosmwasm-smart-contracts/mixnet-contract" } diff --git a/nym-api/src/circulating_supply_api/cache/mod.rs b/nym-api/src/circulating_supply_api/cache/mod.rs index 003d25feb4..70794bc72e 100644 --- a/nym-api/src/circulating_supply_api/cache/mod.rs +++ b/nym-api/src/circulating_supply_api/cache/mod.rs @@ -4,8 +4,8 @@ use self::data::CirculatingSupplyCacheData; use cosmwasm_std::Addr; use nym_api_requests::models::CirculatingSupplyResponse; +use nym_validator_client::nyxd::error::NyxdError; use nym_validator_client::nyxd::Coin; -use nym_validator_client::ValidatorClientError; use rocket::fairing::AdHoc; use std::ops::Deref; use std::{ @@ -34,7 +34,7 @@ enum CirculatingSupplyCacheError { #[error(transparent)] ClientError { #[from] - source: ValidatorClientError, + source: NyxdError, }, } diff --git a/nym-api/src/coconut/comm.rs b/nym-api/src/coconut/comm.rs index 04e53c2c02..cf671defa1 100644 --- a/nym-api/src/coconut/comm.rs +++ b/nym-api/src/coconut/comm.rs @@ -6,7 +6,8 @@ use crate::nyxd; use nym_coconut_dkg_common::types::EpochId; use nym_coconut_interface::VerificationKey; use nym_credentials::coconut::utils::obtain_aggregate_verification_key; -use nym_validator_client::CoconutApiClient; +use nym_validator_client::coconut::all_coconut_api_clients; +use std::ops::Deref; #[async_trait] pub trait APICommunicationChannel { @@ -27,8 +28,7 @@ impl QueryCommunicationChannel { impl APICommunicationChannel for QueryCommunicationChannel { async fn aggregated_verification_key(&self, epoch_id: EpochId) -> Result { let client = self.nyxd_client.0.read().await; - let coconut_api_clients = - CoconutApiClient::all_coconut_api_clients(&client.nyxd, epoch_id).await?; + let coconut_api_clients = all_coconut_api_clients(client.deref(), epoch_id).await?; let vk = obtain_aggregate_verification_key(&coconut_api_clients).await?; Ok(vk) } diff --git a/nym-api/src/coconut/error.rs b/nym-api/src/coconut/error.rs index c540bc62d1..e038e74723 100644 --- a/nym-api/src/coconut/error.rs +++ b/nym-api/src/coconut/error.rs @@ -12,6 +12,7 @@ use nym_crypto::asymmetric::{ identity::{Ed25519RecoveryError, SignatureError}, }; use nym_dkg::error::DkgError; +use nym_validator_client::coconut::CoconutApiError; use nym_validator_client::nyxd::error::NyxdError; use crate::node_status_api::models::NymApiStorageError; @@ -23,6 +24,9 @@ pub enum CoconutError { #[error(transparent)] IOError(#[from] std::io::Error), + #[error("coconut api query failure: {0}")] + CoconutApiError(#[from] CoconutApiError), + #[error(transparent)] SerdeJsonError(#[from] serde_json::Error), diff --git a/nym-api/src/ephemera/application.rs b/nym-api/src/ephemera/application.rs new file mode 100644 index 0000000000..06c1c016d8 --- /dev/null +++ b/nym-api/src/ephemera/application.rs @@ -0,0 +1,209 @@ +use ephemera::{ + configuration::Configuration, + ephemera_api::{ + ApiBlock, ApiEphemeraMessage, Application, ApplicationResult, CheckBlockResult, + RemoveMessages, + }, +}; +use log::{debug, error, info}; + +use crate::ephemera::client::Client; +use crate::ephemera::epoch::Epoch; +use crate::ephemera::peers::members::MembersProvider; +use crate::ephemera::peers::{NymApiEphemeraPeerInfo, NymPeer}; +use crate::ephemera::reward::aggregator::RewardsAggregator; +use crate::ephemera::reward::{EphemeraAccess, RewardManager}; +use crate::ephemera::Args; +use crate::epoch_operations::MixnodeWithPerformance; +use crate::support::nyxd; +use ephemera::crypto::{EphemeraKeypair, EphemeraPublicKey, Keypair}; +use ephemera::ephemera_api::CommandExecutor; +use ephemera::{Ephemera, EphemeraStarterInit}; +use nym_task::TaskManager; + +pub struct NymApi; + +impl NymApi { + pub(crate) async fn run( + args: Args, + ephemera_config: Configuration, + nyxd_client: nyxd::Client, + shutdown: &TaskManager, + ) -> anyhow::Result { + //KEYPAIR - Ephemera keypair or Validator keypair + //Can be a file, keystore etc + let key_pair = Self::read_nym_api_keypair(&ephemera_config)?; + + //EPHEMERA + let ephemera = Self::init_ephemera(ephemera_config, nyxd_client.clone()).await?; + let ephemera_handle = ephemera.handle(); + + //REWARDS + let rewards = + Self::create_rewards_manager(args, nyxd_client, key_pair, ephemera_handle.api.clone()) + .await?; + + //STARTING + let shutdown_listener = shutdown.subscribe(); + tokio::spawn(ephemera.run(shutdown_listener)); + + Ok(rewards) + } + + pub(crate) async fn init_ephemera( + ephemera_config: Configuration, + nyxd_client: nyxd::Client, + ) -> anyhow::Result> { + info!("Initializing ephemera ..."); + + let node_info = ephemera_config.node.clone(); + + let keypair = bs58::decode(&node_info.private_key).into_vec().unwrap(); + let keypair = Keypair::from_bytes(&keypair).unwrap(); + let local_peer_id = keypair.public_key().to_base58(); + + //Members provider for Ephemera + let members_provider = MembersProvider::new(nyxd_client.clone()); + + let cosmos_address = nyxd_client.client_address().await.to_string(); + let ip_address = format!( + "/ip4/{}/tcp/{}", + ephemera_config.node.ip, ephemera_config.libp2p.port + ); + + if !nyxd_client + .get_ephemera_peers() + .await? + .iter() + .any(|peer_info| peer_info.cosmos_address == cosmos_address) + { + let local_peer = NymPeer::new( + cosmos_address, + ip_address, + keypair.public_key(), + local_peer_id.clone(), + ); + members_provider.register_peer(local_peer).await?; + } + + //Application for Ephemera + let rewards_ephemera_application = + RewardsEphemeraApplication::init(local_peer_id, nyxd_client.clone()).await?; + + //EPHEMERA + let ephemera_builder = EphemeraStarterInit::new(ephemera_config)?; + let ephemera_builder = ephemera_builder.with_application(rewards_ephemera_application); + let ephemera_builder = ephemera_builder.with_members_provider(members_provider)?; + let ephemera = ephemera_builder.build(); + Ok(ephemera) + } + + async fn create_rewards_manager( + args: Args, + nyxd_client: nyxd::Client, + key_pair: Keypair, + ephemera_api: CommandExecutor, + ) -> anyhow::Result { + let epoch = Epoch::request_epoch(nyxd_client).await?; + Ok(RewardManager::new( + args.clone(), + EphemeraAccess::new(ephemera_api, key_pair).into(), + Some(RewardsAggregator), + epoch, + )) + } + + fn read_nym_api_keypair(ephemera_config: &Configuration) -> anyhow::Result { + let key_pair = bs58::decode(&ephemera_config.node.private_key).into_vec()?; + let key_pair = Keypair::from_bytes(&key_pair)?; + Ok(key_pair) + } +} + +pub(crate) struct RewardsEphemeraApplicationConfig { + /// Percentage of messages relative to total number of peers + pub(crate) peers_rewards_threshold: u64, +} + +pub(crate) struct RewardsEphemeraApplication { + peer_info: NymApiEphemeraPeerInfo, + app_config: RewardsEphemeraApplicationConfig, +} + +impl RewardsEphemeraApplication { + pub(crate) async fn init( + local_peer_id: String, + nyxd_client: nyxd::Client, + ) -> anyhow::Result { + let peer_info = match NymApiEphemeraPeerInfo::from_ephemera_dev_cluster_conf( + local_peer_id, + nyxd_client, + ) + .await + { + Ok(info) => info, + Err(err) => { + error!("Failed to load peers info: {}", err); + return Err(err); + } + }; + let app_config = RewardsEphemeraApplicationConfig { + peers_rewards_threshold: peer_info.get_peers_count() as u64, + }; + Ok(Self { + peer_info, + app_config, + }) + } +} + +/// - TODO: We should also check that the messages has expected label(like epoch 100) +/// because next block should have only reward info for correct epoch. +impl Application for RewardsEphemeraApplication { + /// Perform validation checks: + /// - Check that the transaction has a valid signature, we don't want to accept garbage messages + /// or messages from unknown peers + fn check_tx(&self, tx: ApiEphemeraMessage) -> ApplicationResult { + if serde_json::from_slice::>(&tx.data).is_err() { + error!("Message is not a valid Reward message"); + return Ok(false); + } + Ok(true) + + //TODO + //PS! message label should also be part of the message hash to prevent replay attacks + } + + /// Agree to accept the block if it contains threshold number of transactions + /// We trust that transactions are valid(checked by check_tx) + fn check_block(&self, block: &ApiBlock) -> ApplicationResult { + info!("Block message count: {}", block.message_count()); + + let block_threshold = ((block.message_count() as f64 + / self.peer_info.get_peers_count() as f64) + * 100.0) as u64; + + if block_threshold > 100 { + error!("Block threshold is greater than 100%!. We expected only single message from each peer"); + return Ok(CheckBlockResult::RejectAndRemoveMessages( + RemoveMessages::All, + )); + } + + if block_threshold >= self.app_config.peers_rewards_threshold { + info!( + "Block accepted {}:{}", + block.header.height, block.header.hash + ); + Ok(CheckBlockResult::Accept) + } else { + debug!("Block rejected: not enough messages"); + Ok(CheckBlockResult::Reject) + } + } + + /// It is possible to use this method as a callback to get notified when block is committed + fn deliver_block(&self, _block: ApiBlock) -> ApplicationResult<()> { + Ok(()) + } +} diff --git a/nym-api/src/ephemera/client.rs b/nym-api/src/ephemera/client.rs new file mode 100644 index 0000000000..146153759a --- /dev/null +++ b/nym-api/src/ephemera/client.rs @@ -0,0 +1,12 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::ephemera::error::Result; +use nym_ephemera_common::types::JsonPeerInfo; +use nym_validator_client::nyxd::cosmwasm_client::types::ExecuteResult; + +#[async_trait] +pub trait Client { + async fn get_ephemera_peers(&self) -> Result>; + async fn register_ephemera_peer(&self, peer_info: JsonPeerInfo) -> Result; +} diff --git a/nym-api/src/ephemera/epoch.rs b/nym-api/src/ephemera/epoch.rs new file mode 100644 index 0000000000..646dd451eb --- /dev/null +++ b/nym-api/src/ephemera/epoch.rs @@ -0,0 +1,82 @@ +use crate::support::nyxd; +use chrono::{DateTime, NaiveDateTime, Timelike, Utc}; +use log::info; +use nym_mixnet_contract_common::Interval as RewardsInterval; +use serde::{Deserialize, Serialize}; +use std::fmt::Display; +use tokio::time::Interval; + +//It synchronizes rewarding across simulated Nym-APIs and the "Smart Contract". +//Maintained by the "Smart Contract" +#[derive(Debug)] +pub struct Epoch { + pub start_time: DateTime, + pub interval: Interval, + pub epoch_start_id: u64, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct EpochInfo { + pub epoch_id: u64, + pub start_time: i64, + pub duration: u64, +} + +impl EpochInfo { + pub(crate) fn new(epoch_id: u64, start_time: i64, duration: u64) -> Self { + Self { + epoch_id, + start_time, + duration, + } + } +} + +impl From for Epoch { + fn from(value: RewardsInterval) -> Self { + Epoch::new(EpochInfo::new( + value.current_epoch_id() as u64, + value.current_epoch_start().unix_timestamp(), + value.epoch_length_secs(), + )) + } +} + +impl Epoch { + pub fn new(info: EpochInfo) -> Self { + info!("New epoch duration"); + let start_time = NaiveDateTime::from_timestamp_opt(info.start_time, 0) + .ok_or("Invalid start time") + .unwrap(); + let start_time: DateTime = DateTime::from_utc(start_time, Utc); + let interval = tokio::time::interval(std::time::Duration::from_secs(info.duration)); + Self { + start_time, + interval, + epoch_start_id: start_time.minute().into(), + } + } + + pub fn current_epoch_numer(&self) -> u64 { + let since_start = (Utc::now().timestamp() - self.start_time.timestamp()) as u64; + let nr = since_start / self.interval.period().as_secs(); + nr + self.epoch_start_id + } + + pub(crate) async fn request_epoch(nyxd_client: nyxd::Client) -> anyhow::Result { + let interval = nyxd_client.get_current_interval().await?.interval; + Ok(Epoch::from(interval)) + } +} + +impl Display for Epoch { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Epoch {{ start_time: {}, interval: {:?}, epoch_start_id: {} }}", + self.start_time, + self.interval.period(), + self.epoch_start_id + ) + } +} diff --git a/nym-api/src/ephemera/error.rs b/nym-api/src/ephemera/error.rs new file mode 100644 index 0000000000..e8dd0b6ace --- /dev/null +++ b/nym-api/src/ephemera/error.rs @@ -0,0 +1,15 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Debug, Error)] +pub enum EphemeraError { + #[error("Validator client error - {0}")] + ValidatorClientError(#[from] nym_validator_client::ValidatorClientError), + + #[error("Nyxd error - {0}")] + NyxdError(#[from] nym_validator_client::nyxd::error::NyxdError), +} diff --git a/nym-api/src/ephemera/metrics/mod.rs b/nym-api/src/ephemera/metrics/mod.rs new file mode 100644 index 0000000000..cd408564ea --- /dev/null +++ b/nym-api/src/ephemera/metrics/mod.rs @@ -0,0 +1 @@ +pub mod types; diff --git a/nym-api/src/ephemera/metrics/types.rs b/nym-api/src/ephemera/metrics/types.rs new file mode 100644 index 0000000000..eb549a2360 --- /dev/null +++ b/nym-api/src/ephemera/metrics/types.rs @@ -0,0 +1,9 @@ +#[derive(Debug)] +pub struct MixnodeResult { + pub mix_id: u32, + pub reliability: u8, +} + +// value in range 0-100 +#[derive(Clone, Copy, Debug, Default)] +pub struct Uptime(u8); diff --git a/nym-api/src/ephemera/mod.rs b/nym-api/src/ephemera/mod.rs new file mode 100644 index 0000000000..d6960fcba5 --- /dev/null +++ b/nym-api/src/ephemera/mod.rs @@ -0,0 +1,51 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +extern crate core; + +use clap::Parser; +use ephemera::cli::init::Cmd; +use serde_derive::{Deserialize, Serialize}; +use std::path::PathBuf; + +pub(crate) mod application; +pub(crate) mod client; +pub(crate) mod epoch; +pub(crate) mod error; +pub(crate) mod metrics; +pub(crate) mod peers; +pub(crate) mod reward; + +#[derive(Parser, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct Args { + #[clap(skip)] + pub ephemera_config: PathBuf, + #[command(flatten)] + #[serde(skip)] + pub cmd: Cmd, + #[clap(skip)] + #[serde(skip, default = "default_block_polling_interval_seconds")] + pub block_polling_interval_seconds: u64, + #[clap(skip)] + #[serde(skip, default = "default_block_polling_max_attempts")] + pub block_polling_max_attempts: u64, +} + +fn default_block_polling_interval_seconds() -> u64 { + 1 +} + +fn default_block_polling_max_attempts() -> u64 { + 60 +} + +impl Default for Args { + fn default() -> Self { + Args { + ephemera_config: Default::default(), + cmd: Default::default(), + block_polling_interval_seconds: default_block_polling_interval_seconds(), + block_polling_max_attempts: default_block_polling_max_attempts(), + } + } +} diff --git a/nym-api/src/ephemera/peers/members.rs b/nym-api/src/ephemera/peers/members.rs new file mode 100644 index 0000000000..c0ff8e286e --- /dev/null +++ b/nym-api/src/ephemera/peers/members.rs @@ -0,0 +1,108 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::ephemera::client::Client; +use crate::ephemera::peers::NymPeer; +use crate::support::nyxd; +use cosmwasm_std::Addr; +use ephemera::membership::{PeerInfo, ProviderError}; +use futures_util::future::BoxFuture; +use futures_util::FutureExt; +use nym_ephemera_common::types::JsonPeerInfo; +use std::future::Future; +use std::pin::Pin; +use std::task::Poll::Pending; +use std::task::{Context, Poll}; + +/// Future type which allows user to implement their own peers membership source mechanism. +pub type ProviderFut = BoxFuture<'static, Result, ProviderError>>; + +///[`ProviderFut`] that reads peers from a http endpoint. +/// +/// The endpoint must return a json array of [`JsonPeerInfo`]. +/// # Configuration example +/// ```json +/// [ +/// { +/// "name": "node1", +/// "address": "/ip4/", +/// "public_key": "4XTTMEghav9LZThm6opUaHrdGEEYUkrfkakVg4VAetetBZDWJ" +/// }, +/// { +/// "name": "node2", +/// "address": "/ip4/", +/// "public_key": "4XTTMFQt2tgNRmwRgEAaGQe2NXygsK6Vr3pkuBfYezhDfoVty" +/// } +/// ] +/// ``` +pub struct MembersProvider { + /// The url of the http endpoint. + nyxd_client: nyxd::Client, + fut: Option, +} + +impl MembersProvider { + #[must_use] + pub(crate) fn new(nyxd_client: nyxd::Client) -> Self { + Self { + nyxd_client, + fut: None, + } + } + + pub(crate) async fn register_peer( + &self, + peer_info: NymPeer, + ) -> crate::ephemera::error::Result<()> { + let json_peer_info = JsonPeerInfo::new( + Addr::unchecked(peer_info.cosmos_address), + peer_info.ip_address, + peer_info.public_key.to_string(), + ); + self.nyxd_client + .register_ephemera_peer(json_peer_info) + .await?; + Ok(()) + } + + async fn request_peers(nyxd_client: nyxd::Client) -> Result, ProviderError> { + let peers = nyxd_client + .get_ephemera_peers() + .await + .map_err(|e| ProviderError::GetPeers(e.to_string()))? + .into_iter() + .filter_map(|peer| PeerInfo::try_from(peer).ok()) + .collect(); + Ok(peers) + } +} + +impl Future for MembersProvider { + type Output = Result, ProviderError>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.fut.take() { + None => { + self.fut = Some(Box::pin(MembersProvider::request_peers( + self.nyxd_client.clone(), + ))); + } + Some(mut fut) => { + let peers = match fut.poll_unpin(cx) { + Poll::Ready(Ok(peers)) => peers, + Poll::Ready(Err(err)) => { + error!("Failed to get peers: {err}"); + return Poll::Ready(Err(err)); + } + Pending => { + self.fut = Some(fut); + return Pending; + } + }; + + return Poll::Ready(Ok(peers)); + } + } + Pending + } +} diff --git a/nym-api/src/ephemera/peers/mod.rs b/nym-api/src/ephemera/peers/mod.rs new file mode 100644 index 0000000000..0706c51fde --- /dev/null +++ b/nym-api/src/ephemera/peers/mod.rs @@ -0,0 +1,78 @@ +use std::collections::HashMap; +use std::str::FromStr; + +use anyhow::anyhow; + +use crate::ephemera::client::Client; +use crate::support::nyxd; +use ephemera::crypto::PublicKey; + +pub(crate) type PeerId = String; + +pub mod members; + +#[derive(Debug, Clone)] +pub struct NymPeer { + pub cosmos_address: String, + pub ip_address: String, + pub public_key: PublicKey, + pub peer_id: PeerId, +} + +impl NymPeer { + pub(crate) fn new( + cosmos_address: String, + ip_address: String, + public_key: PublicKey, + peer_id: PeerId, + ) -> Self { + Self { + cosmos_address, + ip_address, + public_key, + peer_id, + } + } +} + +// Information about other Nym-Apis. +pub(crate) struct NymApiEphemeraPeerInfo { + pub(crate) _local_peer: NymPeer, + pub(crate) peers: HashMap, +} + +impl NymApiEphemeraPeerInfo { + fn new(_local_peer: NymPeer, peers: HashMap) -> Self { + Self { _local_peer, peers } + } + + pub(crate) fn get_peers_count(&self) -> usize { + self.peers.len() + } + + pub(crate) async fn from_ephemera_dev_cluster_conf( + local_peer_id: String, + nyxd_client: nyxd::Client, + ) -> anyhow::Result { + let mut peers = HashMap::new(); + for peer_info in nyxd_client.get_ephemera_peers().await? { + let public_key = PublicKey::from_str(&peer_info.public_key)?; + + let peer = NymPeer::new( + peer_info.cosmos_address.to_string(), + peer_info.ip_address, + public_key, + peer_info.public_key.clone(), + ); + + peers.insert(peer_info.public_key, peer); + } + + let local_peer = peers + .get(&local_peer_id) + .ok_or(anyhow!("Failed to get local peer"))? + .clone(); + let peer_info = NymApiEphemeraPeerInfo::new(local_peer, peers); + Ok(peer_info) + } +} diff --git a/nym-api/src/ephemera/reward/aggregator.rs b/nym-api/src/ephemera/reward/aggregator.rs new file mode 100644 index 0000000000..e83119c4f3 --- /dev/null +++ b/nym-api/src/ephemera/reward/aggregator.rs @@ -0,0 +1,42 @@ +use cosmwasm_std::Decimal; +use log::{info, trace}; +use nym_mixnet_contract_common::reward_params::Performance; +use std::collections::HashMap; + +use crate::epoch_operations::MixnodeWithPerformance; + +pub(crate) struct RewardsAggregator; + +impl RewardsAggregator { + //Simple mean average + pub(crate) fn aggregate( + &self, + all_rewards: Vec>, + ) -> anyhow::Result> { + let mut mix_rewards = HashMap::new(); + for api_rewards in all_rewards { + for mixnode in api_rewards { + mix_rewards + .entry(mixnode.mix_id) + .or_insert(vec![]) + .push(mixnode.performance); + } + } + trace!("Mix rewards by node: {:?}", mix_rewards); + + trace!("Calculating mean average for each node"); + let mut mean_avg = vec![]; + for (mix_id, rewards) in mix_rewards { + let sum: Decimal = rewards.iter().map(|r| r.value()).sum(); + let avg = sum / Decimal::from_ratio(rewards.len() as u128, 1u128); + let performance = Performance::new(avg)?; + mean_avg.push(MixnodeWithPerformance { + mix_id, + performance, + }); + } + info!("Mean average rewards: {:?}", mean_avg); + + Ok(mean_avg) + } +} diff --git a/nym-api/src/ephemera/reward/mod.rs b/nym-api/src/ephemera/reward/mod.rs new file mode 100644 index 0000000000..e4e815a9e4 --- /dev/null +++ b/nym-api/src/ephemera/reward/mod.rs @@ -0,0 +1,263 @@ +use async_trait::async_trait; +use log::{debug, info, trace}; +use std::time::Duration; + +use crate::epoch_operations::MixnodeWithPerformance; +use ephemera::{ + crypto::Keypair, + ephemera_api::{self, ApiBlock, ApiEphemeraMessage, ApiError, CommandExecutor}, +}; + +use super::epoch::Epoch; +use super::reward::aggregator::RewardsAggregator; +use super::Args; + +pub(crate) mod aggregator; + +pub struct EphemeraAccess { + pub(crate) api: CommandExecutor, + pub(crate) key_pair: Keypair, +} + +#[async_trait] +impl EpochOperations for RewardManager { + async fn perform_epoch_operations( + &mut self, + rewards: Vec, + ) -> anyhow::Result> { + //Submit our own rewards message. + //It will be included in the next block(ours and/or others) + self.send_rewards_to_ephemera(rewards).await?; + + //Assuming that next block includes our rewards message + //This assumptions need to be "configured" by the application. + let prev_block = self.get_last_block().await?; + let next_height = prev_block.header.height + 1; + + //Poll next block which should include all messages from the previous epoch from almost all Nym-Api nodes + let mut counter = 0; + info!( + "Waiting for block with height {next_height} maximum {} seconds", + self.args.block_polling_max_attempts * self.args.block_polling_interval_seconds + ); + loop { + if counter > self.args.block_polling_max_attempts { + error!("Block for height {next_height} is not available after {counter} attempts"); + break; + } + tokio::select! { + Ok(Some(block)) = self.get_block_by_height(next_height) => { + info!("Received local block with height {next_height}, hash:{:?}", block.header.hash); + if let Ok(agg_rewards) = self.try_aggregate_rewards(block.clone()).await{ + info!("Submitted rewards to smart contract"); + let epoch_id = self.epoch.current_epoch_numer(); + self.store_in_dht(epoch_id, &block).await?; + info!("Stored rewards in DHT"); + return Ok(agg_rewards); + } + break; + } + _ = tokio::time::sleep(Duration::from_secs(self.args.block_polling_interval_seconds)) => { + trace!("Block for height {next_height} is not available yet, waiting..."); + } + } + counter += 1; + } + + info!("Querying for block with height {next_height} from the DHT"); + counter = 0; + let epoch_id = self.epoch.current_epoch_numer(); + loop { + if counter > self.args.block_polling_max_attempts { + error!( + "DHT: Block for height {next_height} is not available after {counter} attempts" + ); + break; + } + tokio::select! { + Ok(Some(block)) = self.query_dht(epoch_id) => { + info!("DHT: Received block {block}"); + break; + } + _= tokio::time::sleep(Duration::from_secs(self.args.block_polling_interval_seconds)) => { + trace!("DHT: Block for height {next_height} is not available in yet, waiting..."); + } + } + counter += 1; + } + + // TODO: query smart contract for the nym-api which was able to submit rewards and query its block. + // TODO: Because each Ephemera "sees" all blocks during RB then it might be safe to save them locally + // TODO: already during RB. In case of failure of that node. + + info!("Finished reward calculation for previous epoch"); + Ok(vec![]) + } +} + +impl EphemeraAccess { + pub(crate) fn new(api: CommandExecutor, key_pair: Keypair) -> Self { + Self { api, key_pair } + } +} + +#[async_trait] +pub(crate) trait EpochOperations { + async fn perform_epoch_operations( + &mut self, + rewards: Vec, + ) -> anyhow::Result>; +} + +pub(crate) struct RewardManager { + pub epoch: Epoch, + pub args: Args, + pub ephemera_access: Option, + aggregator: Option, +} + +impl RewardManager +where + Self: EpochOperations, +{ + pub(crate) fn new( + args: Args, + ephemera_access: Option, + aggregator: Option, + epoch: Epoch, + ) -> Self { + info!( + "Starting RewardManager with epoch nr {}", + epoch.current_epoch_numer() + ); + Self { + epoch, + args, + ephemera_access, + aggregator, + } + } + + pub(crate) async fn get_last_block(&self) -> Result { + let access = self + .ephemera_access + .as_ref() + .expect("Ephemera access not set"); + let block = access.api.get_last_block().await?; + Ok(block) + } + + pub(crate) async fn get_block_by_height( + &self, + height: u64, + ) -> Result, ApiError> { + let access = self + .ephemera_access + .as_ref() + .expect("Ephemera access not set"); + let block = access.api.get_block_by_height(height).await?; + Ok(block) + } + + pub(crate) async fn store_in_dht(&self, epoch_id: u64, block: &ApiBlock) -> anyhow::Result<()> { + info!("Storing ourselves as 'winner' in DHT for epoch id: {epoch_id:?}"); + + let access = self + .ephemera_access + .as_ref() + .expect("Ephemera access not set"); + + let key = format!("epoch_id_{epoch_id}").into_bytes(); + let value = serde_json::to_vec(&block).expect("Failed to serialize block"); + + access.api.store_in_dht(key, value).await?; + info!("Sent store request to DHT"); + Ok(()) + } + + pub(crate) async fn query_dht(&self, epoch_id: u64) -> anyhow::Result> { + let access = self + .ephemera_access + .as_ref() + .expect("Ephemera access not set"); + + let key = format!("epoch_id_{epoch_id}").into_bytes(); + + match access.api.query_dht(key).await? { + None => { + info!("No 'winner' found for epoch id from DHT: {epoch_id:?}"); + Ok(None) + } + Some((_, block)) => { + let block = serde_json::from_slice(block.as_slice())?; + info!("'Winner' found for epoch id from DHT: {epoch_id:?} - {block:?}"); + Ok(Some(block)) + } + } + } + + pub(crate) async fn send_rewards_to_ephemera( + &self, + rewards: Vec, + ) -> anyhow::Result<()> { + let ephemera_msg = self.create_ephemera_message(rewards)?; + debug!("Sending rewards to ephemera: {:?}", ephemera_msg); + + let access = self + .ephemera_access + .as_ref() + .expect("Ephemera access not set"); + + access.api.send_ephemera_message(ephemera_msg).await?; + Ok(()) + } + + fn create_ephemera_message( + &self, + rewards: Vec, + ) -> anyhow::Result { + let keypair = &self + .ephemera_access + .as_ref() + .expect("Ephemera access not set") + .key_pair; + + let label = self.epoch.current_epoch_numer().to_string(); + let data = serde_json::to_vec(&rewards)?; + let raw_message = ephemera_api::RawApiEphemeraMessage::new(label, data); + + let certificate = ephemera_api::ApiCertificate::prepare(keypair, &raw_message)?; + let signed_message = ApiEphemeraMessage::new(raw_message, certificate); + + Ok(signed_message) + } + + //By current assumption, all nodes will try submit their aggregated rewards + //and contract will reject all but first one. + async fn try_aggregate_rewards( + &mut self, + block: ApiBlock, + ) -> anyhow::Result> { + info!( + "Calculating aggregated rewards from block with height: {:?}", + block.header.height + ); + let mut mix_node_rewards = vec![]; + + for message in block.messages { + trace!("Message: {}", message); + let mix_node_reward: Vec = + serde_json::from_slice(&message.data)?; + mix_node_rewards.push(mix_node_reward); + } + + let aggregated_rewards = self.aggregator().aggregate(mix_node_rewards)?; + debug!("Aggregated rewards: {:?}", aggregated_rewards); + + Ok(aggregated_rewards) + } + + fn aggregator(&self) -> &RewardsAggregator { + self.aggregator.as_ref().expect("Aggregator not set") + } +} diff --git a/nym-api/src/epoch_operations/error.rs b/nym-api/src/epoch_operations/error.rs index ab354701dc..e92d28700f 100644 --- a/nym-api/src/epoch_operations/error.rs +++ b/nym-api/src/epoch_operations/error.rs @@ -47,6 +47,9 @@ pub enum RewardingError { #[from] source: rand::distributions::WeightedError, }, + + #[error("{0}")] + GenericError(#[from] anyhow::Error), } impl From for RewardingError { diff --git a/nym-api/src/epoch_operations/helpers.rs b/nym-api/src/epoch_operations/helpers.rs index 2f82df8248..929cbac770 100644 --- a/nym-api/src/epoch_operations/helpers.rs +++ b/nym-api/src/epoch_operations/helpers.rs @@ -5,8 +5,9 @@ use crate::epoch_operations::RewardedSetUpdater; use cosmwasm_std::{Decimal, Fraction}; use nym_mixnet_contract_common::reward_params::Performance; use nym_mixnet_contract_common::{ExecuteMsg, Interval, MixId}; +use serde_derive::{Deserialize, Serialize}; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub(crate) struct MixnodeWithPerformance { pub(crate) mix_id: MixId, diff --git a/nym-api/src/epoch_operations/mod.rs b/nym-api/src/epoch_operations/mod.rs index d1d58998cc..aa88d0aa66 100644 --- a/nym-api/src/epoch_operations/mod.rs +++ b/nym-api/src/epoch_operations/mod.rs @@ -12,6 +12,7 @@ // 3. Eventually this whole procedure is going to get expanded to allow for distribution of rewarded set generation // and hence this might be a good place for it. +use crate::ephemera::reward::{EpochOperations, RewardManager}; use crate::node_status_api::ONE_DAY; use crate::nym_contract_cache::cache::NymContractCache; use crate::support::nyxd::Client; @@ -32,6 +33,7 @@ mod rewarding; mod transition_beginning; pub struct RewardedSetUpdater { + ephemera_reward_manager: Option, nyxd_client: Client, nym_contract_cache: NymContractCache, storage: NymApiStorage, @@ -45,11 +47,13 @@ impl RewardedSetUpdater { } pub(crate) fn new( + ephemera_reward_manager: Option, nyxd_client: Client, nym_contract_cache: NymContractCache, storage: NymApiStorage, ) -> Self { RewardedSetUpdater { + ephemera_reward_manager, nyxd_client, nym_contract_cache, storage, @@ -58,9 +62,11 @@ impl RewardedSetUpdater { // This is where the epoch gets advanced, and all epoch related transactions originate /// Upon each epoch having finished the following actions are executed by this nym-api: - /// 1. it queries the mixnet contract to check the current `EpochState` in order to figure out whether + /// 1. it computes the rewards for each node using the ephemera channel for the epoch that + /// ended + /// 2. it queries the mixnet contract to check the current `EpochState` in order to figure out whether /// a different nym-api has already started epoch transition (not yet applicable) - /// 2. it sends a `BeginEpochTransition` message to the mixnet contract causing the following to happen: + /// 3. it sends a `BeginEpochTransition` message to the mixnet contract causing the following to happen: /// - if successful, the address of the this validator is going to be saved as being responsible for progressing this epoch. /// What it means in practice is that once we have multiple instances of nym-api running, /// only this one will try to perform the rest of the actions. It will also allow it to @@ -70,21 +76,29 @@ impl RewardedSetUpdater { /// until that is done. /// - ability to send transactions (by other users) that get resolved once given epoch/interval rolls over, /// such as `BondMixnode` or `DelegateToMixnode` will temporarily be frozen until the entire procedure is finished. - /// 3. it obtains the current rewarded set and for each node in there (**SORTED BY MIX_ID!!**), + /// 4. it obtains the current rewarded set and for each node in there (**SORTED BY MIX_ID!!**), /// it sends (in a single batch) `RewardMixnode` message with the measured performance. /// Once the final message gets executed, the mixnet contract automatically transitions /// the state to `ReconcilingEvents`. - /// 4. it obtains the number of pending epoch and interval events and repeatedly sends + /// 5. it obtains the number of pending epoch and interval events and repeatedly sends /// `ReconcileEpochEvents` transaction until all of them are resolved. /// At this point the mixnet contract automatically transitions the state to `AdvancingEpoch`. - /// 5. it obtains the list of all nodes on the network and pseudorandomly (but weighted by total stake) + /// 6. it obtains the list of all nodes on the network and pseudorandomly (but weighted by total stake) /// determines the new rewarded set. It then assigns layers to the provided nodes taking /// family information into consideration. Finally it sends `AdvanceCurrentEpoch` message /// containing the set and layer information thus rolling over the epoch and changing the state /// to `InProgress`. - /// 6. it purges old (older than 48h) measurement data - /// 7. the whole process repeats once the new epoch finishes - async fn perform_epoch_operations(&self, interval: Interval) -> Result<(), RewardingError> { + /// 7. it purges old (older than 48h) measurement data + /// 8. the whole process repeats once the new epoch finishes + async fn perform_epoch_operations(&mut self, interval: Interval) -> Result<(), RewardingError> { + let mut rewards = self.nodes_to_reward(interval).await; + if let Some(ephemera_reward_manager) = self.ephemera_reward_manager.as_mut() { + rewards = ephemera_reward_manager + .perform_epoch_operations(rewards) + .await?; + } + rewards.sort_by_key(|a| a.mix_id); + log::info!("The current epoch has finished."); log::info!( "Interval id: {}, epoch id: {} (absolute epoch id: {})", @@ -128,7 +142,7 @@ impl RewardedSetUpdater { // Reward all the nodes in the still current, soon to be previous rewarded set log::info!("Rewarding the current rewarded set..."); - self.reward_current_rewarded_set(interval).await?; + self.reward_current_rewarded_set(&rewards, interval).await?; // note: those operations don't really have to be atomic, so it's fine to send them // as separate transactions @@ -260,12 +274,14 @@ impl RewardedSetUpdater { } pub(crate) fn start( + ephemera_reward_manager: Option, nyxd_client: Client, nym_contract_cache: &NymContractCache, storage: &NymApiStorage, shutdown: &TaskManager, ) { let mut rewarded_set_updater = RewardedSetUpdater::new( + ephemera_reward_manager, nyxd_client, nym_contract_cache.to_owned(), storage.to_owned(), diff --git a/nym-api/src/epoch_operations/rewarding.rs b/nym-api/src/epoch_operations/rewarding.rs index d050f7efd6..56084a3dfd 100644 --- a/nym-api/src/epoch_operations/rewarding.rs +++ b/nym-api/src/epoch_operations/rewarding.rs @@ -9,6 +9,7 @@ use nym_mixnet_contract_common::{EpochState, Interval, MixId}; impl RewardedSetUpdater { pub(super) async fn reward_current_rewarded_set( &self, + to_reward: &[MixnodeWithPerformance], current_interval: Interval, ) -> Result<(), RewardingError> { let epoch_status = self.nyxd_client.get_current_epoch_status().await?; @@ -34,7 +35,10 @@ impl RewardedSetUpdater { return Err(RewardingError::MidMixRewarding { last_rewarded }); } - if let Err(err) = self._reward_current_rewarded_set(current_interval).await { + if let Err(err) = self + ._reward_current_rewarded_set(to_reward, current_interval) + .await + { log::error!("FAILED to reward rewarded set - {err}"); Err(err) } else { @@ -47,14 +51,12 @@ impl RewardedSetUpdater { async fn _reward_current_rewarded_set( &self, + to_reward: &[MixnodeWithPerformance], current_interval: Interval, ) -> Result<(), RewardingError> { - let mut to_reward = self.nodes_to_reward(current_interval).await; - to_reward.sort_by_key(|a| a.mix_id); - if to_reward.is_empty() { error!("There are no nodes to reward in this epoch - we shouldn't have been in the 'Rewarding' state!"); - } else if let Err(err) = self.nyxd_client.send_rewarding_messages(&to_reward).await { + } else if let Err(err) = self.nyxd_client.send_rewarding_messages(to_reward).await { error!( "failed to perform mixnode rewarding for epoch {}! Error encountered: {err}", current_interval.current_epoch_absolute_id(), @@ -67,7 +69,7 @@ impl RewardedSetUpdater { Ok(()) } - async fn nodes_to_reward(&self, interval: Interval) -> Vec { + pub(crate) async fn nodes_to_reward(&self, interval: Interval) -> Vec { // try to get current up to date view of the network bypassing the cache // in case the epochs were significantly shortened for the purposes of testing let rewarded_set: Vec = match self.nyxd_client.get_rewarded_set_mixnodes().await { diff --git a/nym-api/src/main.rs b/nym-api/src/main.rs index 39719ba0fe..a259879ace 100644 --- a/nym-api/src/main.rs +++ b/nym-api/src/main.rs @@ -5,12 +5,14 @@ extern crate rocket; use crate::epoch_operations::RewardedSetUpdater; +use crate::network::models::NetworkDetails; use crate::node_status_api::uptime_updater::HistoricalUptimeUpdater; use crate::support::cli; use crate::support::cli::CliArgs; use crate::support::config::Config; use crate::support::storage; use crate::support::storage::NymApiStorage; +use ::ephemera::configuration::Configuration as EphemeraConfiguration; use ::nym_config::defaults::setup_env; use anyhow::Result; use circulating_supply_api::cache::CirculatingSupplyCache; @@ -28,7 +30,9 @@ use support::{http, nyxd}; mod circulating_supply_api; mod coconut; +mod ephemera; mod epoch_operations; +pub(crate) mod network; mod network_monitor; pub(crate) mod node_status_api; pub(crate) mod nym_contract_cache; @@ -58,14 +62,16 @@ async fn start_nym_api_tasks( config: Config, ) -> Result> { let nyxd_client = nyxd::Client::new(&config); - let mix_denom = nyxd_client.chain_details().await.mix_denom.base; + let connected_nyxd = config.get_nyxd_url(); + let nym_network_details = config.get_network_details(); + let network_details = NetworkDetails::new(connected_nyxd.to_string(), nym_network_details); let coconut_keypair = coconut::keypair::KeyPair::new(); // let's build our rocket! let rocket = http::setup_rocket( &config, - mix_denom, + network_details, nyxd_client.clone(), coconut_keypair.clone(), ) @@ -122,6 +128,33 @@ async fn start_nym_api_tasks( // and then only start the uptime updater (and the monitor itself, duh) // if the monitoring if it's enabled if config.network_monitor.enabled { + let ephemera_config = + match EphemeraConfiguration::try_load(config.get_ephemera_config_path()) { + Ok(c) => c, + Err(_) => { + config + .get_ephemera_args() + .cmd + .clone() + .execute(Some(&config.get_id())); + EphemeraConfiguration::try_load(config.get_ephemera_config_path()) + .expect("Config file should be created now") + } + }; + let ephemera_reward_manager = if config.ephemera.enabled { + Some( + ephemera::application::NymApi::run( + config.get_ephemera_args().clone(), + ephemera_config, + nyxd_client.clone(), + &shutdown, + ) + .await?, + ) + } else { + None + }; + // if network monitor is enabled, the storage MUST BE available let storage = maybe_storage.unwrap(); @@ -139,7 +172,13 @@ async fn start_nym_api_tasks( // start 'rewarding' if its enabled if config.rewarding.enabled { epoch_operations::ensure_rewarding_permission(&nyxd_client).await?; - RewardedSetUpdater::start(nyxd_client, nym_contract_cache_state, storage, &shutdown); + RewardedSetUpdater::start( + ephemera_reward_manager, + nyxd_client, + nym_contract_cache_state, + storage, + &shutdown, + ); } } @@ -160,6 +199,11 @@ async fn run_nym_api(cli_args: CliArgs) -> Result<(), Box +// SPDX-License-Identifier: Apache-2.0 + +use okapi::openapi3::OpenApi; +use rocket::Route; +use rocket_okapi::openapi_get_routes_spec; +use rocket_okapi::settings::OpenApiSettings; + +pub(crate) mod models; +mod routes; + +pub(crate) fn network_routes(settings: &OpenApiSettings) -> (Vec, OpenApi) { + openapi_get_routes_spec![ + settings: routes::network_details, + routes::nym_contracts, + routes::nym_contracts_detailed + ] +} diff --git a/nym-api/src/network/models.rs b/nym-api/src/network/models.rs new file mode 100644 index 0000000000..518b4352f1 --- /dev/null +++ b/nym-api/src/network/models.rs @@ -0,0 +1,28 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use nym_config::defaults::NymNetworkDetails; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Serialize, Deserialize, JsonSchema)] +pub struct NetworkDetails { + pub(crate) connected_nyxd: String, + pub(crate) network: NymNetworkDetails, +} + +impl NetworkDetails { + pub fn new(connected_nyxd: String, network: NymNetworkDetails) -> Self { + Self { + connected_nyxd, + network, + } + } +} + +#[derive(Serialize, Deserialize, Clone, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub struct ContractInformation { + pub(crate) address: Option, + pub(crate) details: Option, +} diff --git a/nym-api/src/network/routes.rs b/nym-api/src/network/routes.rs new file mode 100644 index 0000000000..77f60f0fa9 --- /dev/null +++ b/nym-api/src/network/routes.rs @@ -0,0 +1,63 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::network::models::{ContractInformation, NetworkDetails}; +use crate::nym_contract_cache::cache::NymContractCache; +use nym_contracts_common::ContractBuildInformation; +use rocket::serde::json::Json; +use rocket::State; +use rocket_okapi::openapi; +use std::collections::HashMap; +use std::ops::Deref; + +#[openapi(tag = "network")] +#[get("/details")] +pub(crate) fn network_details(details: &State) -> Json { + Json(details.deref().clone()) +} + +// I agree, it feels weird to be pulling contract cache here, but I feel like it makes +// more sense to return this information here rather than in the generic cache route +#[openapi(tag = "network")] +#[get("/nym-contracts")] +pub(crate) async fn nym_contracts( + cache: &State, +) -> Json>> { + let info = cache.contract_details().await; + Json( + info.value + .into_iter() + .map(|(contract, info)| { + ( + contract, + ContractInformation { + address: info.address.map(|a| a.to_string()), + details: info.base, + }, + ) + }) + .collect(), + ) +} + +#[openapi(tag = "network")] +#[get("/nym-contracts-detailed")] +pub(crate) async fn nym_contracts_detailed( + cache: &State, +) -> Json>> { + let info = cache.contract_details().await; + Json( + info.value + .into_iter() + .map(|(contract, info)| { + ( + contract, + ContractInformation { + address: info.address.map(|a| a.to_string()), + details: info.detailed, + }, + ) + }) + .collect(), + ) +} diff --git a/nym-api/src/nym_contract_cache/cache/data.rs b/nym-api/src/nym_contract_cache/cache/data.rs index 27285db41a..ede4bc6838 100644 --- a/nym-api/src/nym_contract_cache/cache/data.rs +++ b/nym-api/src/nym_contract_cache/cache/data.rs @@ -2,13 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 use crate::support::caching::Cache; +use nym_contracts_common::ContractBuildInformation; use nym_mixnet_contract_common::{ families::FamilyHead, GatewayBond, IdentityKey, Interval, MixId, MixNodeDetails, RewardingParams, }; use nym_name_service_common::RegisteredName; use nym_service_provider_directory_common::Service; -use std::collections::HashSet; +use nym_validator_client::nyxd::AccountId; +use std::collections::{HashMap, HashSet}; pub(crate) struct ValidatorCacheData { pub(crate) mixnodes: Cache>, @@ -27,6 +29,8 @@ pub(crate) struct ValidatorCacheData { pub(crate) service_providers: Cache>, pub(crate) registered_names: Cache>, + + pub(crate) contracts_info: Cache, } impl ValidatorCacheData { @@ -43,6 +47,31 @@ impl ValidatorCacheData { mix_to_family: Cache::default(), service_providers: Cache::default(), registered_names: Cache::default(), + contracts_info: Cache::default(), + } + } +} + +type ContractAddress = String; +pub type CachedContractsInfo = HashMap; + +#[derive(Clone)] +pub struct CachedContractInfo { + pub(crate) address: Option, + pub(crate) base: Option, + pub(crate) detailed: Option, +} + +impl CachedContractInfo { + pub fn new( + address: Option<&AccountId>, + base: Option, + detailed: Option, + ) -> Self { + Self { + address: address.cloned(), + base, + detailed, } } } diff --git a/nym-api/src/nym_contract_cache/cache/mod.rs b/nym-api/src/nym_contract_cache/cache/mod.rs index 2eac4f0c61..ec0ce7f8e5 100644 --- a/nym-api/src/nym_contract_cache/cache/mod.rs +++ b/nym-api/src/nym_contract_cache/cache/mod.rs @@ -1,3 +1,4 @@ +use crate::nym_contract_cache::cache::data::CachedContractsInfo; use crate::support::caching::Cache; use data::ValidatorCacheData; use nym_api_requests::models::MixnodeStatus; @@ -54,6 +55,7 @@ impl NymContractCache { mix_to_family: Vec<(IdentityKey, FamilyHead)>, services: Option>, names: Option>, + nym_contracts_info: CachedContractsInfo, ) { match time::timeout(Duration::from_millis(100), self.inner.write()).await { Ok(mut cache) => { @@ -67,6 +69,7 @@ impl NymContractCache { // Just return empty lists when these are not available cache.service_providers.update(services.unwrap_or_default()); cache.registered_names.update(names.unwrap_or_default()); + cache.contracts_info.update(nym_contracts_info) } Err(err) => { error!("{err}"); @@ -267,6 +270,16 @@ impl NymContractCache { } } + pub(crate) async fn contract_details(&self) -> Cache { + match time::timeout(Duration::from_millis(100), self.inner.read()).await { + Ok(cache) => cache.contracts_info.clone(), + Err(err) => { + error!("{err}"); + Cache::default() + } + } + } + pub async fn mixnode_details(&self, mix_id: MixId) -> (Option, MixnodeStatus) { // it might not be the most optimal to possibly iterate the entire vector to find (or not) // the relevant value. However, the vectors are relatively small (< 10_000 elements, < 1000 for active set) diff --git a/nym-api/src/nym_contract_cache/cache/refresher.rs b/nym-api/src/nym_contract_cache/cache/refresher.rs index 93ddabf3a1..e1fa5cf18f 100644 --- a/nym-api/src/nym_contract_cache/cache/refresher.rs +++ b/nym-api/src/nym_contract_cache/cache/refresher.rs @@ -1,10 +1,16 @@ use super::NymContractCache; +use crate::nym_contract_cache::cache::data::{CachedContractInfo, CachedContractsInfo}; use crate::nyxd::Client; use crate::support::caching::CacheNotification; use anyhow::Result; +use futures::future::join_all; use nym_mixnet_contract_common::{MixId, MixNodeDetails, RewardedSetNodeStatus}; use nym_task::TaskClient; -use nym_validator_client::nyxd::traits::{NameServiceQueryClient, SpDirectoryQueryClient}; +use nym_validator_client::nyxd::contract_traits::{ + MixnetQueryClient, NameServiceQueryClient, NymContractsProvider, PagedNameServiceQueryClient, + PagedSpDirectoryQueryClient, SpDirectoryQueryClient, VestingQueryClient, +}; +use nym_validator_client::nyxd::CosmWasmClient; use std::{collections::HashMap, sync::atomic::Ordering, time::Duration}; use tokio::sync::watch; use tokio::time; @@ -37,6 +43,129 @@ impl NymContractCacheRefresher { self.update_notifier.subscribe() } + async fn get_nym_contracts_info(&self) -> Result { + let mut updated = HashMap::new(); + + let client_guard = self.nyxd_client.read().await; + + let mixnet = client_guard.mixnet_contract_address(); + let vesting = client_guard.vesting_contract_address(); + let name_service = client_guard.name_service_contract_address(); + let service_provider = client_guard.service_provider_contract_address(); + let coconut_bandwidth = client_guard.coconut_bandwidth_contract_address(); + let coconut_dkg = client_guard.dkg_contract_address(); + let group = client_guard.group_contract_address(); + let multisig = client_guard.multisig_contract_address(); + + // get cw2 versions + let mixnet_cw2_future = client_guard.get_mixnet_contract_cw2_version(); + let vesting_cw2_future = client_guard.get_vesting_contract_cw2_version(); + let service_provider_cw2_future = client_guard.get_name_service_contract_cw2_version(); + let name_service_cw2_future = client_guard.get_name_service_contract_cw2_version(); + + // group and multisig contract save that information in their storage but don't expose it via queries + // so a temporary workaround... + let multisig_cw2 = if let Some(multisig_contract) = multisig { + client_guard + .query_contract_raw(multisig_contract, b"contract_info".to_vec()) + .await + .map(|r| serde_json::from_slice(&r).ok()) + .ok() + .flatten() + } else { + None + }; + let group_cw2 = if let Some(group_contract) = group { + client_guard + .query_contract_raw(group_contract, b"contract_info".to_vec()) + .await + .map(|r| serde_json::from_slice(&r).ok()) + .ok() + .flatten() + } else { + None + }; + + let mut cw2_info = join_all(vec![ + mixnet_cw2_future, + vesting_cw2_future, + service_provider_cw2_future, + name_service_cw2_future, + ]) + .await; + + // get detailed build info + let mixnet_detailed_future = client_guard.get_mixnet_contract_version(); + let vesting_detailed_future = client_guard.get_vesting_contract_version(); + let service_provider_detailed_future = client_guard.get_sp_contract_version(); + let name_service_detailed_future = client_guard.get_name_service_contract_version(); + + let mut build_info = join_all(vec![ + mixnet_detailed_future, + vesting_detailed_future, + service_provider_detailed_future, + name_service_detailed_future, + ]) + .await; + + // the below unwraps are fine as we definitely have the specified number of entries + // Note to whoever updates this code in the future: `pop` removes **LAST** element, + // so make sure you call them in correct order, depending on what's specified in the `join_all` + updated.insert( + "nym-name-service-contract".to_string(), + CachedContractInfo::new( + name_service, + cw2_info.pop().unwrap().ok(), + build_info.pop().unwrap().ok(), + ), + ); + + updated.insert( + "nym-service-provider-directory-contract".to_string(), + CachedContractInfo::new( + service_provider, + cw2_info.pop().unwrap().ok(), + build_info.pop().unwrap().ok(), + ), + ); + + updated.insert( + "nym-vesting-contract".to_string(), + CachedContractInfo::new( + vesting, + cw2_info.pop().unwrap().ok(), + build_info.pop().unwrap().ok(), + ), + ); + updated.insert( + "nym-mixnet-contract".to_string(), + CachedContractInfo::new( + mixnet, + cw2_info.pop().unwrap().ok(), + build_info.pop().unwrap().ok(), + ), + ); + + updated.insert( + "nym-coconut-bandwidth-contract".to_string(), + CachedContractInfo::new(coconut_bandwidth, None, None), + ); + updated.insert( + "nym-coconut-dkg-contract".to_string(), + CachedContractInfo::new(coconut_dkg, None, None), + ); + updated.insert( + "nym-cw3-multisig-contract".to_string(), + CachedContractInfo::new(multisig, multisig_cw2, None), + ); + updated.insert( + "nym-cw4-group-contract".to_string(), + CachedContractInfo::new(group, group_cw2, None), + ); + + Ok(updated) + } + async fn refresh(&self) -> Result<()> { let rewarding_params = self.nyxd_client.get_current_rewarding_parameters().await?; let current_interval = self.nyxd_client.get_current_interval().await?.interval; @@ -54,6 +183,7 @@ impl NymContractCacheRefresher { // The service providers and names are optional let services = self.nyxd_client.get_all_services().await.ok(); let names = self.nyxd_client.get_all_names().await.ok(); + let contract_info = self.get_nym_contracts_info().await?; info!( "Updating validator cache. There are {} mixnodes and {} gateways", @@ -72,6 +202,7 @@ impl NymContractCacheRefresher { mix_to_family, services, names, + contract_info, ) .await; diff --git a/nym-api/src/support/caching/mod.rs b/nym-api/src/support/caching/mod.rs index ad2e40e80d..0d2615d327 100644 --- a/nym-api/src/support/caching/mod.rs +++ b/nym-api/src/support/caching/mod.rs @@ -2,13 +2,13 @@ use serde::Serialize; use std::ops::Deref; use time::OffsetDateTime; -#[derive(Default, Serialize, Clone)] +#[derive(Serialize, Clone)] pub struct Cache { pub value: T, as_at: i64, } -impl Cache { +impl Cache { pub fn new(value: T) -> Self { Cache { value, @@ -38,6 +38,18 @@ impl Deref for Cache { } } +impl Default for Cache +where + T: Default, +{ + fn default() -> Self { + Cache { + value: T::default(), + as_at: 0, + } + } +} + fn current_unix_timestamp() -> i64 { let now = OffsetDateTime::now_utc(); now.unix_timestamp() diff --git a/nym-api/src/support/cli/mod.rs b/nym-api/src/support/cli/mod.rs index 1bcc518ad4..4de111a1b8 100644 --- a/nym-api/src/support/cli/mod.rs +++ b/nym-api/src/support/cli/mod.rs @@ -8,14 +8,13 @@ use ::nym_config::defaults::var_names::{MIXNET_CONTRACT_ADDRESS, VESTING_CONTRAC use anyhow::Result; use clap::Parser; use lazy_static::lazy_static; -use nym_bin_common::build_information::BinaryBuildInformation; +use nym_bin_common::bin_info; use nym_config::defaults::var_names::NYXD; use nym_config::OptionalSet; use nym_validator_client::nyxd; lazy_static! { - pub static ref PRETTY_BUILD_INFORMATION: String = - BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print(); + pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print(); } // Helper for passing LONG_VERSION to clap @@ -56,6 +55,10 @@ pub(crate) struct CliArgs { #[clap(short = 'r', long, requires = "enable_monitor", requires = "mnemonic")] pub(crate) enable_rewarding: Option, + /// Specifies whether ephemera is used to aggregate monitor data on this API + #[clap(short = 'e', long, requires = "enable_monitor")] + pub(crate) enable_ephemera: Option, + /// Endpoint to nyxd instance from which the monitor will grab nodes to test #[clap(long)] pub(crate) nyxd_validator: Option, @@ -106,6 +109,10 @@ pub(crate) struct CliArgs { hide = true )] pub(crate) enable_coconut: Option, + + /// Ephemera configuration arguments. + #[command(flatten)] + pub(crate) ephemera_args: ephemera::cli::init::Cmd, } pub(crate) fn override_config(config: Config, args: CliArgs) -> Config { @@ -140,12 +147,26 @@ pub(crate) fn override_config(config: Config, args: CliArgs) -> Config { ) .with_optional(Config::with_network_monitor_enabled, args.enable_monitor) .with_optional(Config::with_rewarding_enabled, args.enable_rewarding) + .with_optional(Config::with_ephemera_enabled, args.enable_ephemera) .with_optional( Config::with_disabled_credentials_mode, args.enabled_credentials_mode.map(|b| !b), ) .with_optional(Config::with_announce_address, args.announce_address) .with_optional(Config::with_coconut_signer_enabled, args.enable_coconut) + .with_optional(Config::with_ephemera_ip, args.ephemera_args.ephemera_ip) + .with_optional( + Config::with_ephemera_protocol_port, + args.ephemera_args.ephemera_protocol_port, + ) + .with_optional( + Config::with_ephemera_websocket_port, + args.ephemera_args.ephemera_websocket_port, + ) + .with_optional( + Config::with_ephemera_http_api_port, + args.ephemera_args.ephemera_http_api_port, + ) } pub(crate) fn build_config(args: CliArgs) -> Result { diff --git a/nym-api/src/support/config/helpers.rs b/nym-api/src/support/config/helpers.rs index 4c20088ad0..1c84a5e837 100644 --- a/nym-api/src/support/config/helpers.rs +++ b/nym-api/src/support/config/helpers.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::support::config::old_config_v1_1_21::ConfigV1_1_21; +use crate::support::config::old_config_v1_1_27::ConfigV1_1_27; use crate::support::config::{default_config_directory, default_data_directory, Config}; use anyhow::Result; use std::{fs, io}; @@ -22,6 +23,22 @@ fn try_upgrade_v1_1_21_config(id: &str) -> Result<()> { Ok(updated.save_to_default_location()?) } +fn try_upgrade_v1_1_27_config(id: &str) -> Result<()> { + use nym_config::legacy_helpers::nym_config::MigrationNymConfig; + + // explicitly load it as v1.1.27 (which is incompatible with the current, i.e. 1.1.28+) + let Ok(old_config) = ConfigV1_1_27::load_from_file(id) else { + // if we failed to load it, there might have been nothing to upgrade + // or maybe it was an even older file. in either way. just ignore it and carry on with our day + return Ok(()); + }; + info!("It seems the nym-api is using <= v1.1.27 config template."); + info!("It is going to get updated to the current specification."); + + let updated: Config = old_config.into(); + Ok(updated.save_to_default_location()?) +} + fn init_paths(id: &str) -> io::Result<()> { fs::create_dir_all(default_data_directory(id))?; fs::create_dir_all(default_config_directory(id)) @@ -36,6 +53,7 @@ pub(crate) fn initialise_new(id: &str) -> Result { pub(crate) fn try_load_current_config(id: &str) -> Result { try_upgrade_v1_1_21_config(id)?; + try_upgrade_v1_1_27_config(id)?; Ok(Config::read_from_default_path(id)?) } diff --git a/nym-api/src/support/config/mod.rs b/nym-api/src/support/config/mod.rs index 1e74708bda..a66c5ce8a7 100644 --- a/nym-api/src/support/config/mod.rs +++ b/nym-api/src/support/config/mod.rs @@ -5,10 +5,10 @@ use crate::support::config::persistence::{ CoconutSignerPaths, NetworkMonitorPaths, NodeStatusAPIPaths, }; use crate::support::config::template::CONFIG_TEMPLATE; -use nym_config::defaults::mainnet; +use nym_config::defaults::{mainnet, NymNetworkDetails}; use nym_config::{ must_get_home, read_config_from_toml_file, save_formatted_config_to_file, NymConfigTemplate, - DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, NYM_DIR, + DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILENAME, DEFAULT_DATA_DIR, DEFAULT_NYM_APIS_DIR, NYM_DIR, }; use nym_validator_client::nyxd; use serde::{Deserialize, Serialize}; @@ -20,11 +20,10 @@ use zeroize::{Zeroize, ZeroizeOnDrop}; pub(crate) mod helpers; pub(crate) mod old_config_v1_1_21; +pub(crate) mod old_config_v1_1_27; mod persistence; mod template; -const DEFAULT_NYM_APIS_DIR: &str = "nym-api"; - pub const DEFAULT_LOCAL_VALIDATOR: &str = "http://localhost:26657"; pub const DEFAULT_NYM_API_PORT: u16 = 8080; @@ -96,6 +95,18 @@ pub struct Config { pub rewarding: Rewarding, pub coconut_signer: CoconutSigner, + + pub ephemera: Ephemera, +} + +impl<'a> From<&'a Config> for NymNetworkDetails { + fn from(value: &'a Config) -> Self { + // we get the current environmental details and then overwrite whatever is appropriate with + // the values from the config + NymNetworkDetails::new_from_env() + .with_mixnet_contract(Some(value.get_mixnet_contract_address().as_ref())) + .with_vesting_contract(Some(value.get_vesting_contract_address().as_ref())) + } } impl NymConfigTemplate for Config { @@ -106,15 +117,15 @@ impl NymConfigTemplate for Config { impl Config { pub fn new>(id: S) -> Self { - let base_data_dir = default_data_directory(id.as_ref()); Config { base: Base::new_default(id.as_ref()), - network_monitor: NetworkMonitor::new_default(&base_data_dir), - node_status_api: NodeStatusAPI::new_default(&base_data_dir), + network_monitor: NetworkMonitor::new_default(id.as_ref()), + node_status_api: NodeStatusAPI::new_default(id.as_ref()), topology_cacher: Default::default(), circulating_supply_cacher: Default::default(), rewarding: Default::default(), - coconut_signer: CoconutSigner::new_default(base_data_dir), + coconut_signer: CoconutSigner::new_default(id.as_ref()), + ephemera: Ephemera::new_default(id.as_ref()), } } @@ -135,6 +146,10 @@ impl Config { save_formatted_config_to_file(self, config_save_location) } + pub fn get_network_details(&self) -> NymNetworkDetails { + self.into() + } + pub fn with_network_monitor_enabled(mut self, enabled: bool) -> Self { self.network_monitor.enabled = enabled; self @@ -155,6 +170,11 @@ impl Config { self } + pub fn with_ephemera_enabled(mut self, enabled: bool) -> Self { + self.ephemera.enabled = enabled; + self + } + pub fn with_custom_nyxd_validator(mut self, validator: Url) -> Self { self.base.local_validator = validator; self @@ -195,6 +215,30 @@ impl Config { self } + pub fn with_ephemera_ip(mut self, ip: String) -> Self { + self.ephemera.args.cmd.ephemera_ip = Some(ip); + self + } + + pub fn with_ephemera_protocol_port(mut self, port: u16) -> Self { + self.ephemera.args.cmd.ephemera_protocol_port = Some(port); + self + } + + pub fn with_ephemera_websocket_port(mut self, port: u16) -> Self { + self.ephemera.args.cmd.ephemera_websocket_port = Some(port); + self + } + + pub fn with_ephemera_http_api_port(mut self, port: u16) -> Self { + self.ephemera.args.cmd.ephemera_http_api_port = Some(port); + self + } + + pub fn get_id(&self) -> String { + self.base.id.clone() + } + pub fn get_nyxd_url(&self) -> Url { self.base.local_validator.clone() } @@ -210,6 +254,14 @@ impl Config { pub fn get_mnemonic(&self) -> bip39::Mnemonic { self.base.mnemonic.clone() } + + pub fn get_ephemera_args(&self) -> &crate::ephemera::Args { + &self.ephemera.args + } + + pub fn get_ephemera_config_path(&self) -> PathBuf { + self.ephemera.args.ephemera_config.clone() + } } // we only really care about the mnemonic being zeroized @@ -529,3 +581,25 @@ impl Default for CoconutSignerDebug { } } } + +#[derive(Debug, Default, Deserialize, PartialEq, Eq, Serialize)] +#[serde(default)] +pub struct Ephemera { + pub enabled: bool, + args: crate::ephemera::Args, +} + +impl Ephemera { + fn new_default(id: &str) -> Self { + Ephemera { + enabled: false, + args: crate::ephemera::Args { + ephemera_config: ephemera::configuration::Configuration::ephemera_config_file_home( + Some(id), + ) + .unwrap(), + ..Default::default() + }, + } + } +} diff --git a/nym-api/src/support/config/old_config_v1_1_21.rs b/nym-api/src/support/config/old_config_v1_1_21.rs index 3e5ded9964..2f13baaad0 100644 --- a/nym-api/src/support/config/old_config_v1_1_21.rs +++ b/nym-api/src/support/config/old_config_v1_1_21.rs @@ -156,6 +156,7 @@ impl From for Config { dkg_contract_polling_rate: value.coconut_signer.dkg_contract_polling_rate, }, }, + ephemera: Default::default(), } } } diff --git a/nym-api/src/support/config/old_config_v1_1_27.rs b/nym-api/src/support/config/old_config_v1_1_27.rs new file mode 100644 index 0000000000..3bfb4b8581 --- /dev/null +++ b/nym-api/src/support/config/old_config_v1_1_27.rs @@ -0,0 +1,268 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::support::config::persistence::{ + CoconutSignerPaths, NetworkMonitorPaths, NodeStatusAPIPaths, +}; +use crate::support::config::{ + Base, CirculatingSupplyCacher, CoconutSigner, CoconutSignerDebug, Config, Ephemera, + NetworkMonitor, NetworkMonitorDebug, NodeStatusAPI, NodeStatusAPIDebug, Rewarding, + TopologyCacher, +}; +use nym_config::legacy_helpers::nym_config::MigrationNymConfig; +use nym_validator_client::nyxd; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +use url::Url; + +const DEFAULT_NYM_API_PORT: u16 = 8080; +const MIXNET_CONTRACT_ADDRESS: &str = + "n17srjznxl9dvzdkpwpw24gg668wc73val88a6m5ajg6ankwvz9wtst0cznr"; +const VESTING_CONTRACT_ADDRESS: &str = + "n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw"; + +const DEFAULT_LOCAL_VALIDATOR: &str = "http://localhost:26657"; + +#[derive(Debug, Default, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct ConfigV1_1_27 { + #[serde(default)] + base: BaseV1_1_27, + + #[serde(default)] + network_monitor: NetworkMonitorV1_1_27, + + #[serde(default)] + node_status_api: NodeStatusAPIV1_1_27, + + #[serde(default)] + topology_cacher: TopologyCacher, + + #[serde(default)] + circulating_supply_cacher: CirculatingSupplyCacher, + + #[serde(default)] + rewarding: Rewarding, + + #[serde(default)] + coconut_signer: CoconutSignerV1_1_27, +} + +impl From for Config { + fn from(value: ConfigV1_1_27) -> Self { + // this value was never properly saved (probably a bug) + // so explicitly set it to the default + + Config { + base: Base { + id: value.base.id.clone(), + local_validator: value.base.local_validator, + mixnet_contract_address: value.base.mixnet_contract_address, + vesting_contract_address: value.base.vesting_contract_address, + mnemonic: value.base.mnemonic, + }, + network_monitor: NetworkMonitor { + enabled: value.network_monitor.enabled, + storage_paths: NetworkMonitorPaths { + credentials_database_path: value + .network_monitor + .storage_paths + .credentials_database_path, + }, + debug: NetworkMonitorDebug { + min_mixnode_reliability: value.network_monitor.debug.min_mixnode_reliability, + min_gateway_reliability: value.network_monitor.debug.min_gateway_reliability, + disabled_credentials_mode: value + .network_monitor + .debug + .disabled_credentials_mode, + run_interval: value.network_monitor.debug.run_interval, + gateway_ping_interval: value.network_monitor.debug.gateway_ping_interval, + gateway_sending_rate: value.network_monitor.debug.gateway_sending_rate, + max_concurrent_gateway_clients: value + .network_monitor + .debug + .max_concurrent_gateway_clients, + gateway_response_timeout: value.network_monitor.debug.gateway_response_timeout, + gateway_connection_timeout: value + .network_monitor + .debug + .gateway_connection_timeout, + packet_delivery_timeout: value.network_monitor.debug.packet_delivery_timeout, + test_routes: value.network_monitor.debug.test_routes, + minimum_test_routes: value.network_monitor.debug.minimum_test_routes, + route_test_packets: value.network_monitor.debug.route_test_packets, + per_node_test_packets: value.network_monitor.debug.per_node_test_packets, + }, + }, + node_status_api: NodeStatusAPI { + storage_paths: NodeStatusAPIPaths { + database_path: value.node_status_api.storage_paths.database_path, + }, + debug: NodeStatusAPIDebug { + caching_interval: value.node_status_api.debug.caching_interval, + }, + }, + topology_cacher: value.topology_cacher, + circulating_supply_cacher: value.circulating_supply_cacher, + rewarding: value.rewarding, + coconut_signer: CoconutSigner { + enabled: value.coconut_signer.enabled, + announce_address: value.base.announce_address, + storage_paths: CoconutSignerPaths { + dkg_persistent_state_path: value + .coconut_signer + .storage_paths + .dkg_persistent_state_path, + verification_key_path: value.coconut_signer.storage_paths.verification_key_path, + secret_key_path: value.coconut_signer.storage_paths.secret_key_path, + decryption_key_path: value.coconut_signer.storage_paths.decryption_key_path, + public_key_with_proof_path: value + .coconut_signer + .storage_paths + .public_key_with_proof_path, + }, + debug: CoconutSignerDebug { + dkg_contract_polling_rate: value.coconut_signer.debug.dkg_contract_polling_rate, + }, + }, + ephemera: Ephemera::new_default(&value.base.id), + } + } +} + +impl MigrationNymConfig for ConfigV1_1_27 { + fn default_root_directory() -> PathBuf { + dirs::home_dir() + .expect("Failed to evaluate $HOME value") + .join(".nym") + .join("nym-api") + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] +#[serde(default)] +#[serde(deny_unknown_fields)] +pub struct BaseV1_1_27 { + /// ID specifies the human readable ID of this particular nym-api. + id: String, + + local_validator: Url, + + /// Address announced to the directory server for the clients to connect to. + // It is useful, say, in NAT scenarios or wanting to more easily update actual IP address + // later on by using name resolvable with a DNS query, such as `nymtech.net`. + announce_address: Url, + + /// Address of the validator contract managing the network + mixnet_contract_address: nyxd::AccountId, + + /// Address of the vesting contract holding locked tokens + vesting_contract_address: nyxd::AccountId, + + /// Mnemonic used for rewarding and/or multisig operations + mnemonic: bip39::Mnemonic, +} + +impl Default for BaseV1_1_27 { + fn default() -> Self { + let default_validator: Url = DEFAULT_LOCAL_VALIDATOR + .parse() + .expect("default local validator is malformed!"); + let mut default_announce_address = default_validator.clone(); + default_announce_address + .set_port(Some(DEFAULT_NYM_API_PORT)) + .expect("default local validator is malformed!"); + + BaseV1_1_27 { + id: String::default(), + local_validator: default_validator, + announce_address: default_announce_address, + mixnet_contract_address: MIXNET_CONTRACT_ADDRESS.parse().unwrap(), + vesting_contract_address: VESTING_CONTRACT_ADDRESS.parse().unwrap(), + mnemonic: bip39::Mnemonic::generate(24).unwrap(), + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] +#[serde(default)] +#[serde(deny_unknown_fields)] +pub struct NetworkMonitorV1_1_27 { + /// Specifies whether network monitoring service is enabled in this process. + pub enabled: bool, + + pub storage_paths: NetworkMonitorPaths, + + #[serde(default)] + pub debug: NetworkMonitorDebug, +} + +impl Default for NetworkMonitorV1_1_27 { + fn default() -> Self { + NetworkMonitorV1_1_27 { + enabled: false, + storage_paths: NetworkMonitorPaths { + credentials_database_path: Default::default(), + }, + debug: Default::default(), + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] +#[serde(default)] +#[serde(deny_unknown_fields)] +pub struct NodeStatusAPIV1_1_27 { + // pub enabled: bool, + pub storage_paths: NodeStatusAPIPaths, + + #[serde(default)] + pub debug: NodeStatusAPIDebug, +} + +impl Default for NodeStatusAPIV1_1_27 { + fn default() -> Self { + NodeStatusAPIV1_1_27 { + storage_paths: NodeStatusAPIPaths { + database_path: Default::default(), + }, + debug: Default::default(), + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)] +#[serde(default)] +#[serde(deny_unknown_fields)] +pub struct CoconutSignerV1_1_27 { + /// Specifies whether rewarding service is enabled in this process. + pub enabled: bool, + + pub announce_address: Url, + + pub storage_paths: CoconutSignerPaths, + + #[serde(default)] + pub debug: CoconutSignerDebug, +} + +impl Default for CoconutSignerV1_1_27 { + fn default() -> Self { + let announce_address: Url = DEFAULT_LOCAL_VALIDATOR + .parse() + .expect("default local validator is malformed!"); + CoconutSignerV1_1_27 { + enabled: Default::default(), + announce_address, + storage_paths: CoconutSignerPaths { + dkg_persistent_state_path: Default::default(), + verification_key_path: Default::default(), + secret_key_path: Default::default(), + decryption_key_path: Default::default(), + public_key_with_proof_path: Default::default(), + }, + debug: Default::default(), + } + } +} diff --git a/nym-api/src/support/config/template.rs b/nym-api/src/support/config/template.rs index 7ef105d91d..32d4f5e087 100644 --- a/nym-api/src/support/config/template.rs +++ b/nym-api/src/support/config/template.rs @@ -130,4 +130,12 @@ decryption_key_path = '{{ coconut_signer.storage_paths.decryption_key_path }}' # Path to the dkg dealer public key with proof public_key_with_proof_path = '{{ coconut_signer.storage_paths.public_key_with_proof_path }}' +[ephemera] + +enabled = {{ ephemera.enabled }} + +[ephemera.args] + +ephemera_config = '{{ ephemera.args.ephemera_config }}' + "#; diff --git a/nym-api/src/support/http/mod.rs b/nym-api/src/support/http/mod.rs index 572d1c281b..b21ccdb381 100644 --- a/nym-api/src/support/http/mod.rs +++ b/nym-api/src/support/http/mod.rs @@ -3,6 +3,8 @@ use crate::circulating_supply_api::cache::CirculatingSupplyCache; use crate::coconut::{self, comm::QueryCommunicationChannel, InternalSignRequest}; +use crate::network::models::NetworkDetails; +use crate::network::network_routes; use crate::node_status_api::{self, NodeStatusCache}; use crate::nym_contract_cache::cache::NymContractCache; use crate::support::config::Config; @@ -19,13 +21,15 @@ pub(crate) mod openapi; pub(crate) async fn setup_rocket( config: &Config, - mix_denom: String, + network_details: NetworkDetails, _nyxd_client: nyxd::Client, coconut_keypair: coconut::keypair::KeyPair, ) -> anyhow::Result> { let openapi_settings = rocket_okapi::settings::OpenApiSettings::default(); let mut rocket = rocket::build(); + let mix_denom = network_details.network.chain_details.mix_denom.base.clone(); + mount_endpoints_and_merged_docs! { rocket, "/v1".to_owned(), @@ -34,9 +38,11 @@ pub(crate) async fn setup_rocket( "" => circulating_supply_api::circulating_supply_routes(&openapi_settings), "" => nym_contract_cache::nym_contract_cache_routes(&openapi_settings), "/status" => node_status_api::node_status_routes(&openapi_settings, config.network_monitor.enabled), + "/network" => network_routes(&openapi_settings), } let rocket = rocket + .manage(network_details) .mount("/swagger", make_swagger_ui(&openapi::get_docs())) .attach(setup_cors()?) .attach(NymContractCache::stage()) diff --git a/nym-api/src/support/nyxd/mod.rs b/nym-api/src/support/nyxd/mod.rs index e35098ee1f..790be3ca31 100644 --- a/nym-api/src/support/nyxd/mod.rs +++ b/nym-api/src/support/nyxd/mod.rs @@ -16,9 +16,11 @@ use nym_coconut_dkg_common::{ types::{EncodedBTEPublicKeyWithProof, Epoch, EpochId}, verification_key::{ContractVKShare, VerificationKeyShare}, }; -use nym_config::defaults::{ChainDetails, NymNetworkDetails, DEFAULT_NYM_API_PORT}; +use nym_config::defaults::ChainDetails; use nym_contracts_common::dealings::ContractSafeBytes; -use nym_mixnet_contract_common::families::{Family, FamilyHead}; +use nym_ephemera_common::msg::QueryMsg as EphemeraQueryMsg; +use nym_ephemera_common::types::JsonPeerInfo; +use nym_mixnet_contract_common::families::FamilyHead; use nym_mixnet_contract_common::mixnode::MixNodeDetails; use nym_mixnet_contract_common::reward_params::RewardingParams; use nym_mixnet_contract_common::{ @@ -27,31 +29,30 @@ use nym_mixnet_contract_common::{ }; use nym_name_service_common::msg::QueryMsg as NameServiceQueryMsg; use nym_service_provider_directory_common::msg::QueryMsg as SpQueryMsg; +use nym_validator_client::nyxd::contract_traits::{NameServiceQueryClient, PagedDkgQueryClient}; use nym_validator_client::nyxd::error::NyxdError; -use nym_validator_client::nyxd::traits::{ - MixnetQueryClient, MixnetSigningClient, SpDirectoryQueryClient, -}; use nym_validator_client::nyxd::{ - cosmwasm_client::types::ExecuteResult, - traits::{ - CoconutBandwidthQueryClient, DkgQueryClient, DkgSigningClient, GroupQueryClient, - MultisigQueryClient, MultisigSigningClient, NameServiceQueryClient, VestingQueryClient, + contract_traits::{ + CoconutBandwidthQueryClient, DkgQueryClient, DkgSigningClient, EphemeraQueryClient, + EphemeraSigningClient, GroupQueryClient, MixnetQueryClient, MixnetSigningClient, + MultisigQueryClient, MultisigSigningClient, NymContractsProvider, PagedEphemeraQueryClient, + PagedMixnetQueryClient, PagedMultisigQueryClient, PagedVestingQueryClient, + SpDirectoryQueryClient, }, - Fee, + cosmwasm_client::types::ExecuteResult, + CosmWasmClient, Fee, }; use nym_validator_client::nyxd::{ hash::{Hash, SHA256_HASH_SIZE}, - AccountId, Coin, DirectSigningNyxdClient, TendermintTime, + AccountId, Coin, TendermintTime, }; -use nym_validator_client::ValidatorClientError; +use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient}; use nym_vesting_contract_common::AccountVestingCoins; use serde::Deserialize; use std::sync::Arc; -use tokio::sync::RwLock; +use tokio::sync::{RwLock, RwLockReadGuard}; -pub(crate) struct Client( - pub(crate) Arc>>, -); +pub(crate) struct Client(pub(crate) Arc>); impl Clone for Client { fn clone(&self) -> Self { @@ -61,45 +62,41 @@ impl Clone for Client { impl Client { pub(crate) fn new(config: &Config) -> Self { - // the api address is irrelevant here as **WE ARE THE API** - // and we won't be talking on the socket here. - let api_url = format!("http://localhost:{}", DEFAULT_NYM_API_PORT) - .parse() - .unwrap(); + let details = config.get_network_details(); let nyxd_url = config.get_nyxd_url(); - let details = NymNetworkDetails::new_from_env() - .with_mixnet_contract(Some(config.get_mixnet_contract_address().as_ref())) - .with_vesting_contract(Some(config.get_vesting_contract_address().as_ref())); - - let client_config = nym_validator_client::Config::try_from_nym_network_details(&details) - .expect("failed to construct valid validator client config with the provided network") - .with_urls(nyxd_url, api_url); + let client_config = nyxd::Config::try_from_nym_network_details(&details) + .expect("failed to construct valid validator client config with the provided network"); let mnemonic = config.get_mnemonic(); - let inner = nym_validator_client::Client::new_signing(client_config, mnemonic) - .expect("Failed to connect to nyxd!"); + let inner = DirectSigningHttpRpcNyxdClient::connect_with_mnemonic( + client_config, + nyxd_url.as_str(), + mnemonic, + ) + .expect("Failed to connect to nyxd!"); Client(Arc::new(RwLock::new(inner))) } + pub(crate) async fn read(&self) -> RwLockReadGuard<'_, DirectSigningHttpRpcNyxdClient> { + self.0.read().await + } + pub(crate) async fn client_address(&self) -> AccountId { - self.0.read().await.nyxd.address().clone() + self.0.read().await.address() } pub(crate) async fn chain_details(&self) -> ChainDetails { - self.0.read().await.nyxd.current_chain_details().clone() + self.0.read().await.current_chain_details().clone() } - pub(crate) async fn get_rewarding_validator_address( - &self, - ) -> Result { + pub(crate) async fn get_rewarding_validator_address(&self) -> Result { let cosmwasm_addr = self .0 .read() .await - .nyxd .get_mixnet_contract_state() .await? .rewarding_validator_address @@ -113,21 +110,13 @@ impl Client { cosmwasm_addr .clone() .parse() - .map_err(|_| NyxdError::MalformedAccountAddress(cosmwasm_addr).into()) + .map_err(|_| NyxdError::MalformedAccountAddress(cosmwasm_addr)) } // a helper function for the future to obtain the current block timestamp #[allow(dead_code)] - pub(crate) async fn current_block_timestamp( - &self, - ) -> Result { - let time = self - .0 - .read() - .await - .nyxd - .get_current_block_timestamp() - .await?; + pub(crate) async fn current_block_timestamp(&self) -> Result { + let time = self.0.read().await.get_current_block_timestamp().await?; Ok(time) } @@ -142,8 +131,8 @@ impl Client { pub(crate) async fn get_block_hash( &self, height: u32, - ) -> Result, ValidatorClientError> { - let hash = match self.0.read().await.nyxd.get_block_hash(height).await? { + ) -> Result, NyxdError> { + let hash = match self.0.read().await.get_block_hash(height).await? { Hash::Sha256(hash) => Some(hash), Hash::None => None, }; @@ -151,101 +140,78 @@ impl Client { Ok(hash) } - pub(crate) async fn get_mixnodes(&self) -> Result, ValidatorClientError> { - self.0.read().await.get_all_nyxd_mixnodes_detailed().await + pub(crate) async fn get_mixnodes(&self) -> Result, NyxdError> { + self.0.read().await.get_all_mixnodes_detailed().await } - pub(crate) async fn get_gateways(&self) -> Result, ValidatorClientError> { - self.0.read().await.get_all_nyxd_gateways().await + pub(crate) async fn get_gateways(&self) -> Result, NyxdError> { + self.0.read().await.get_all_gateways().await } - pub(crate) async fn get_current_interval( - &self, - ) -> Result { - Ok(self.0.read().await.get_current_interval_details().await?) + pub(crate) async fn get_current_interval(&self) -> Result { + self.0.read().await.get_current_interval_details().await } - pub(crate) async fn get_current_epoch_status( - &self, - ) -> Result { - Ok(self.0.read().await.nyxd.get_current_epoch_status().await?) + pub(crate) async fn get_current_epoch_status(&self) -> Result { + self.0.read().await.get_current_epoch_status().await } pub(crate) async fn get_current_rewarding_parameters( &self, - ) -> Result { - Ok(self.0.read().await.get_rewarding_parameters().await?) + ) -> Result { + self.0.read().await.get_rewarding_parameters().await } pub(crate) async fn get_rewarded_set_mixnodes( &self, - ) -> Result, ValidatorClientError> { - self.0 - .read() - .await - .get_all_nyxd_rewarded_set_mixnodes() - .await + ) -> Result, NyxdError> { + self.0.read().await.get_all_rewarded_set_mixnodes().await } - pub(crate) async fn get_current_vesting_account_storage_key( - &self, - ) -> Result { + pub(crate) async fn get_current_vesting_account_storage_key(&self) -> Result { let guard = self.0.read().await; - let vesting_contract = guard.nyxd.vesting_contract_address(); + + // the expect is fine as we always construct the client with the vesting contract explicitly set + let vesting_contract = guard + .vesting_contract_address() + .expect("vesting contract address is not available"); // TODO: I don't like the usage of the hardcoded value here let res = guard - .nyxd .query_contract_raw(vesting_contract, b"key".to_vec()) .await?; if res.is_empty() { return Ok(0); } - Ok(serde_json::from_slice(&res).map_err(NyxdError::from)?) + serde_json::from_slice(&res).map_err(NyxdError::from) } pub(crate) async fn get_all_vesting_coins( &self, - ) -> Result, ValidatorClientError> { - Ok(self - .0 - .read() - .await - .nyxd - .get_all_accounts_vesting_coins() - .await?) - } - - #[allow(dead_code)] - pub(crate) async fn get_all_node_families(&self) -> Result, ValidatorClientError> { - self.0.read().await.get_all_node_families().await + ) -> Result, NyxdError> { + self.0.read().await.get_all_accounts_vesting_coins().await } pub(crate) async fn get_all_family_members( &self, - ) -> Result, ValidatorClientError> { + ) -> Result, NyxdError> { self.0.read().await.get_all_family_members().await } - pub(crate) async fn get_pending_events_count(&self) -> Result { + pub(crate) async fn get_pending_events_count(&self) -> Result { let pending = self.0.read().await.get_number_of_pending_events().await?; Ok(pending.epoch_events + pending.interval_events) } - pub(crate) async fn begin_epoch_transition(&self) -> Result<(), ValidatorClientError> { - self.0 - .write() - .await - .nyxd - .begin_epoch_transition(None) - .await?; + pub(crate) async fn begin_epoch_transition(&self) -> Result<(), NyxdError> { + self.0.write().await.begin_epoch_transition(None).await?; Ok(()) } pub(crate) async fn send_rewarding_messages( &self, nodes: &[MixnodeWithPerformance], - ) -> Result<(), ValidatorClientError> { + ) -> Result<(), NyxdError> { // for some reason, compiler complains if this is explicitly inline in code ¯\_(ツ)_/¯ #[inline] #[allow(unused_variables)] @@ -265,16 +231,21 @@ impl Client { } } - let contract = self.0.read().await.get_mixnet_contract_address(); + // "technically" we don't need a write access to the client, + // but we REALLY don't want to accidentally send any transactions while we're sending rewarding messages + // as that would have messed up sequence numbers + let guard = self.0.write().await; + + // the expect is fine as we always construct the client with the mixnet contract explicitly set + let mixnet_contract = guard + .mixnet_contract_address() + .expect("mixnet contract address is not available"); let msgs = generate_reward_messages(nodes); - self.0 - .write() - .await - .nyxd + guard .execute_multiple( - &contract, + mixnet_contract, msgs, Default::default(), format!("rewarding {} mixnodes", nodes.len()), @@ -287,24 +258,19 @@ impl Client { &self, new_rewarded_set: Vec, expected_active_set_size: u32, - ) -> Result<(), ValidatorClientError> { + ) -> Result<(), NyxdError> { self.0 .write() .await - .nyxd .advance_current_epoch(new_rewarded_set, expected_active_set_size, None) .await?; Ok(()) } - pub(crate) async fn reconcile_epoch_events( - &self, - limit: Option, - ) -> Result<(), ValidatorClientError> { + pub(crate) async fn reconcile_epoch_events(&self, limit: Option) -> Result<(), NyxdError> { self.0 .write() .await - .nyxd .reconcile_epoch_events(limit, None) .await?; Ok(()) @@ -317,25 +283,22 @@ impl crate::coconut::client::Client for Client { self.client_address().await } - async fn get_tx( - &self, - tx_hash: &str, - ) -> crate::coconut::error::Result { + async fn get_tx(&self, tx_hash: &str) -> crate::coconut::error::Result { let tx_hash: Hash = tx_hash .parse() .map_err(|_| CoconutError::TxHashParseError)?; - Ok(self.0.read().await.nyxd.get_tx(tx_hash).await?) + Ok(self.0.read().await.get_tx(tx_hash).await?) } async fn get_proposal( &self, proposal_id: u64, ) -> crate::coconut::error::Result { - Ok(self.0.read().await.nyxd.get_proposal(proposal_id).await?) + Ok(self.0.read().await.query_proposal(proposal_id).await?) } async fn list_proposals(&self) -> crate::coconut::error::Result> { - Ok(self.0.read().await.nyxd.get_all_proposals().await?) + Ok(self.0.read().await.get_all_proposals().await?) } async fn get_spent_credential( @@ -346,48 +309,35 @@ impl crate::coconut::client::Client for Client { .0 .read() .await - .nyxd .get_spent_credential(blinded_serial_number) .await?) } async fn get_current_epoch(&self) -> crate::coconut::error::Result { - Ok(self.0.read().await.nyxd.get_current_epoch().await?) + Ok(self.0.read().await.get_current_epoch().await?) } async fn group_member(&self, addr: String) -> crate::coconut::error::Result { - Ok(self.0.read().await.nyxd.member(addr).await?) + Ok(self.0.read().await.member(addr, None).await?) } async fn get_current_epoch_threshold( &self, ) -> crate::coconut::error::Result> { - Ok(self - .0 - .read() - .await - .nyxd - .get_current_epoch_threshold() - .await?) + Ok(self.0.read().await.get_current_epoch_threshold().await?) } async fn get_initial_dealers( &self, ) -> crate::coconut::error::Result> { - Ok(self.0.read().await.nyxd.get_initial_dealers().await?) + Ok(self.0.read().await.get_initial_dealers().await?) } async fn get_self_registered_dealer_details( &self, ) -> crate::coconut::error::Result { let self_address = &self.address().await; - Ok(self - .0 - .read() - .await - .nyxd - .get_dealer_details(self_address) - .await?) + Ok(self.0.read().await.get_dealer_details(self_address).await?) } async fn get_current_dealers(&self) -> crate::coconut::error::Result> { @@ -398,7 +348,12 @@ impl crate::coconut::client::Client for Client { &self, idx: usize, ) -> crate::coconut::error::Result> { - Ok(self.0.read().await.get_all_epoch_dealings(idx).await?) + Ok(self + .0 + .read() + .await + .get_all_epoch_dealings(idx as u64) + .await?) } async fn get_verification_key_shares( @@ -422,7 +377,6 @@ impl crate::coconut::client::Client for Client { self.0 .read() .await - .nyxd .vote_proposal(proposal_id, vote_yes, fee) .await?; Ok(()) @@ -432,19 +386,13 @@ impl crate::coconut::client::Client for Client { self.0 .read() .await - .nyxd .execute_proposal(proposal_id, None) .await?; Ok(()) } async fn advance_epoch_state(&self) -> crate::coconut::error::Result<()> { - self.0 - .write() - .await - .nyxd - .advance_dkg_epoch_state(None) - .await?; + self.0.write().await.advance_dkg_epoch_state(None).await?; Ok(()) } @@ -458,7 +406,6 @@ impl crate::coconut::client::Client for Client { .0 .write() .await - .nyxd .register_dealer(bte_key, announce_address, resharing, None) .await?) } @@ -472,7 +419,6 @@ impl crate::coconut::client::Client for Client { .0 .write() .await - .nyxd .submit_dealing_bytes(dealing_bytes, resharing, None) .await?) } @@ -486,19 +432,50 @@ impl crate::coconut::client::Client for Client { .0 .write() .await - .nyxd .submit_verification_key_share(share, resharing, None) .await?) } } +#[async_trait] +impl crate::ephemera::client::Client for Client { + async fn get_ephemera_peers(&self) -> crate::ephemera::error::Result> { + Ok(self.0.read().await.get_all_ephemera_peers().await?) + } + + async fn register_ephemera_peer( + &self, + peer_info: JsonPeerInfo, + ) -> crate::ephemera::error::Result { + Ok(self + .0 + .write() + .await + .register_as_peer(peer_info, None) + .await?) + } +} + #[async_trait] impl DkgQueryClient for Client { async fn query_dkg_contract(&self, query: DkgQueryMsg) -> std::result::Result where for<'a> T: Deserialize<'a>, { - self.0.read().await.nyxd.query_dkg_contract(query).await + self.0.read().await.query_dkg_contract(query).await + } +} + +#[async_trait] +impl EphemeraQueryClient for Client { + async fn query_ephemera_contract( + &self, + query: EphemeraQueryMsg, + ) -> std::result::Result + where + for<'a> T: Deserialize<'a>, + { + self.0.read().await.query_ephemera_contract(query).await } } @@ -514,7 +491,6 @@ impl SpDirectoryQueryClient for Client { self.0 .read() .await - .nyxd .query_service_provider_contract(query) .await } @@ -529,11 +505,6 @@ impl NameServiceQueryClient for Client { where for<'a> T: Deserialize<'a>, { - self.0 - .read() - .await - .nyxd - .query_name_service_contract(query) - .await + self.0.read().await.query_name_service_contract(query).await } } diff --git a/nym-api/tests/functional_test/circulating_supply/circulating-supply.test.ts b/nym-api/tests/functional_test/circulating_supply/circulating-supply.test.ts index c74d039175..b09d56df75 100644 --- a/nym-api/tests/functional_test/circulating_supply/circulating-supply.test.ts +++ b/nym-api/tests/functional_test/circulating_supply/circulating-supply.test.ts @@ -1,12 +1,9 @@ -import ConfigHandler from "../../src/config/configHandler"; import ContractCache from "../../src/endpoints/CirculatingSupply"; let contract: ContractCache; -let config: ConfigHandler; describe("Get circulating supply", (): void => { beforeAll(async (): Promise => { contract = new ContractCache(); - config = ConfigHandler.getInstance(); }); it("Get circulating supply amounts", async (): Promise => { diff --git a/nym-api/tests/functional_test/contract_cache/contract-cache-epochs.test.ts b/nym-api/tests/functional_test/contract_cache/contract-cache-epochs.test.ts index 482817f669..a2923e9c52 100644 --- a/nym-api/tests/functional_test/contract_cache/contract-cache-epochs.test.ts +++ b/nym-api/tests/functional_test/contract_cache/contract-cache-epochs.test.ts @@ -1,13 +1,10 @@ import ContractCache from "../../src/endpoints/ContractCache"; -import ConfigHandler from "../../src/config/configHandler"; let contract: ContractCache; -let config: ConfigHandler; describe("Get epoch info", (): void => { beforeAll(async (): Promise => { contract = new ContractCache(); - config = ConfigHandler.getInstance(); }); it("Get epoch reward params", async (): Promise => { diff --git a/nym-api/tests/functional_test/contract_cache/gateway/contract-cache-gateway.test.ts b/nym-api/tests/functional_test/contract_cache/gateway/contract-cache-gateway.test.ts index 465b6a9a12..e1dc896ad9 100644 --- a/nym-api/tests/functional_test/contract_cache/gateway/contract-cache-gateway.test.ts +++ b/nym-api/tests/functional_test/contract_cache/gateway/contract-cache-gateway.test.ts @@ -1,13 +1,10 @@ import ContractCache from "../../../src/endpoints/ContractCache"; -import ConfigHandler from "../../../src/config/configHandler"; let contract: ContractCache; -let config: ConfigHandler; describe("Get gateway data", (): void => { beforeAll(async (): Promise => { contract = new ContractCache(); - config = ConfigHandler.getInstance(); }); it("Get all gateways", async (): Promise => { @@ -40,8 +37,13 @@ describe("Get gateway data", (): void => { it("Get blacklisted gateways", async (): Promise => { const response = await contract.getBlacklistedGateways(); - response.forEach(function (value) { - expect(typeof value).toBe("string"); - }); + if (response === null) { + // no blacklisted gateways returns an empty array + expect(response).toBeNull(); + } else { + response.forEach(function (value) { + expect(typeof value).toBe("string"); + }); + } }); }); diff --git a/nym-api/tests/functional_test/contract_cache/mixnode/contract-cache-mixnode.test.ts b/nym-api/tests/functional_test/contract_cache/mixnode/contract-cache-mixnode.test.ts index 31fb8d959d..6182f8a63c 100644 --- a/nym-api/tests/functional_test/contract_cache/mixnode/contract-cache-mixnode.test.ts +++ b/nym-api/tests/functional_test/contract_cache/mixnode/contract-cache-mixnode.test.ts @@ -16,8 +16,12 @@ describe("Get mixnode data", (): void => { //bond information overview expect(typeof mixnode.bond_information.mix_id).toBe("number"); expect(typeof mixnode.bond_information.owner).toBe("string"); - expect(typeof mixnode.bond_information.original_pledge.amount).toBe("string"); - expect(typeof mixnode.bond_information.original_pledge.denom).toBe("string"); + expect(typeof mixnode.bond_information.original_pledge.amount).toBe( + "string" + ); + expect(typeof mixnode.bond_information.original_pledge.denom).toBe( + "string" + ); expect(typeof mixnode.bond_information.layer).toBe("number"); expect(typeof mixnode.bond_information.bonding_height).toBe("number"); expect(typeof mixnode.bond_information.is_unbonding).toBe("boolean"); @@ -30,11 +34,17 @@ describe("Get mixnode data", (): void => { //mixnode expect(typeof mixnode.bond_information.mix_node.host).toBe("string"); - expect(mixnode.bond_information.mix_node.http_api_port).toStrictEqual(8000); - expect(typeof mixnode.bond_information.mix_node.verloc_port).toBe("number"); + expect(mixnode.bond_information.mix_node.http_api_port).toStrictEqual( + 8000 + ); + expect(typeof mixnode.bond_information.mix_node.verloc_port).toBe( + "number" + ); expect(typeof mixnode.bond_information.mix_node.mix_port).toBe("number"); expect(mixnode.bond_information.mix_node.mix_port).toStrictEqual(1789); - expect(typeof mixnode.bond_information.mix_node.verloc_port).toBe("number"); + expect(typeof mixnode.bond_information.mix_node.verloc_port).toBe( + "number" + ); const identitykey = mixnode.bond_information.mix_node.identity_key; if (typeof identitykey === "string") { @@ -51,15 +61,27 @@ describe("Get mixnode data", (): void => { } //rewarding details - expect(typeof mixnode.rewarding_details.cost_params.profit_margin_percent).toBe("string"); - expect(typeof mixnode.rewarding_details.cost_params.interval_operating_cost.denom).toBe("string"); - expect(typeof mixnode.rewarding_details.cost_params.interval_operating_cost.amount).toBe("string"); + expect( + typeof mixnode.rewarding_details.cost_params.profit_margin_percent + ).toBe("string"); + expect( + typeof mixnode.rewarding_details.cost_params.interval_operating_cost + .denom + ).toBe("string"); + expect( + typeof mixnode.rewarding_details.cost_params.interval_operating_cost + .amount + ).toBe("string"); expect(typeof mixnode.rewarding_details.operator).toBe("string"); expect(typeof mixnode.rewarding_details.delegates).toBe("string"); expect(typeof mixnode.rewarding_details.total_unit_reward).toBe("string"); expect(typeof mixnode.rewarding_details.unit_delegation).toBe("string"); - expect(typeof mixnode.rewarding_details.last_rewarded_epoch).toBe("number"); - expect(typeof mixnode.rewarding_details.unique_delegations).toBe("number"); + expect(typeof mixnode.rewarding_details.last_rewarded_epoch).toBe( + "number" + ); + expect(typeof mixnode.rewarding_details.unique_delegations).toBe( + "number" + ); }); }); @@ -76,36 +98,66 @@ describe("Get mixnode data", (): void => { // expect(typeof mixnode.family).toBe("string"); //mixnode details bond info - expect(typeof mixnode.mixnode_details.bond_information.mix_id).toBe("number") - expect(typeof mixnode.mixnode_details.bond_information.owner).toBe("string"); - expect(typeof mixnode.mixnode_details.bond_information.original_pledge.amount).toBe("string"); - expect(typeof mixnode.mixnode_details.bond_information.original_pledge.denom).toBe("string"); - expect(typeof mixnode.mixnode_details.bond_information.layer).toBe("number"); - expect(typeof mixnode.mixnode_details.bond_information.bonding_height).toBe("number"); - expect(typeof mixnode.mixnode_details.bond_information.is_unbonding).toBe("boolean"); + expect(typeof mixnode.mixnode_details.bond_information.mix_id).toBe( + "number" + ); + expect(typeof mixnode.mixnode_details.bond_information.owner).toBe( + "string" + ); + expect( + typeof mixnode.mixnode_details.bond_information.original_pledge.amount + ).toBe("string"); + expect( + typeof mixnode.mixnode_details.bond_information.original_pledge.denom + ).toBe("string"); + expect(typeof mixnode.mixnode_details.bond_information.layer).toBe( + "number" + ); + expect( + typeof mixnode.mixnode_details.bond_information.bonding_height + ).toBe("number"); + expect(typeof mixnode.mixnode_details.bond_information.is_unbonding).toBe( + "boolean" + ); if (mixnode.mixnode_details.bond_information.proxy === null) { return true; } else { - expect(typeof mixnode.mixnode_details.bond_information.proxy).toBe("string"); + expect(typeof mixnode.mixnode_details.bond_information.proxy).toBe( + "string" + ); } //mixnode - expect(typeof mixnode.mixnode_details.bond_information.mix_node.host).toBe("string"); - expect(mixnode.mixnode_details.bond_information.mix_node.http_api_port).toStrictEqual(8000); - expect(typeof mixnode.mixnode_details.bond_information.mix_node.verloc_port).toBe("number"); - expect(typeof mixnode.mixnode_details.bond_information.mix_node.mix_port).toBe("number"); - expect(mixnode.mixnode_details.bond_information.mix_node.mix_port).toStrictEqual(1789); - expect(typeof mixnode.mixnode_details.bond_information.mix_node.verloc_port).toBe("number"); + expect( + typeof mixnode.mixnode_details.bond_information.mix_node.host + ).toBe("string"); + expect( + mixnode.mixnode_details.bond_information.mix_node.http_api_port + ).toStrictEqual(8000); + expect( + typeof mixnode.mixnode_details.bond_information.mix_node.verloc_port + ).toBe("number"); + expect( + typeof mixnode.mixnode_details.bond_information.mix_node.mix_port + ).toBe("number"); + expect( + mixnode.mixnode_details.bond_information.mix_node.mix_port + ).toStrictEqual(1789); + expect( + typeof mixnode.mixnode_details.bond_information.mix_node.verloc_port + ).toBe("number"); - const identitykey2 = mixnode.mixnode_details.bond_information.mix_node.identity_key + const identitykey2 = + mixnode.mixnode_details.bond_information.mix_node.identity_key; if (typeof identitykey2 === "string") { if (identitykey2.length === 43) { return true; } else expect(identitykey2).toHaveLength(44); } - const sphinx2 = mixnode.mixnode_details.bond_information.mix_node.sphinx_key + const sphinx2 = + mixnode.mixnode_details.bond_information.mix_node.sphinx_key; if (typeof sphinx2 === "string") { if (sphinx2.length === 43) { return true; @@ -113,22 +165,45 @@ describe("Get mixnode data", (): void => { } //mixnode rewarding info - expect(typeof mixnode.mixnode_details.rewarding_details.cost_params.profit_margin_percent).toBe("string"); - expect(typeof mixnode.mixnode_details.rewarding_details.cost_params.interval_operating_cost.denom).toBe("string"); - expect(typeof mixnode.mixnode_details.rewarding_details.cost_params.interval_operating_cost.amount).toBe("string"); - expect(typeof mixnode.mixnode_details.rewarding_details.operator).toBe("string"); - expect(typeof mixnode.mixnode_details.rewarding_details.delegates).toBe("string"); - expect(typeof mixnode.mixnode_details.rewarding_details.total_unit_reward).toBe("string"); - expect(typeof mixnode.mixnode_details.rewarding_details.unit_delegation).toBe("string"); - expect(typeof mixnode.mixnode_details.rewarding_details.last_rewarded_epoch).toBe("number"); - expect(typeof mixnode.mixnode_details.rewarding_details.unique_delegations).toBe("number"); + expect( + typeof mixnode.mixnode_details.rewarding_details.cost_params + .profit_margin_percent + ).toBe("string"); + expect( + typeof mixnode.mixnode_details.rewarding_details.cost_params + .interval_operating_cost.denom + ).toBe("string"); + expect( + typeof mixnode.mixnode_details.rewarding_details.cost_params + .interval_operating_cost.amount + ).toBe("string"); + expect(typeof mixnode.mixnode_details.rewarding_details.operator).toBe( + "string" + ); + expect(typeof mixnode.mixnode_details.rewarding_details.delegates).toBe( + "string" + ); + expect( + typeof mixnode.mixnode_details.rewarding_details.total_unit_reward + ).toBe("string"); + expect( + typeof mixnode.mixnode_details.rewarding_details.unit_delegation + ).toBe("string"); + expect( + typeof mixnode.mixnode_details.rewarding_details.last_rewarded_epoch + ).toBe("number"); + expect( + typeof mixnode.mixnode_details.rewarding_details.unique_delegations + ).toBe("number"); }); }); it("Get active mixnodes", async (): Promise => { const response = await contract.getActiveMixnodes(); response.forEach(function (mixnode) { - expect(mixnode.rewarding_details.cost_params.profit_margin_percent).toBeTruthy(); + expect( + mixnode.rewarding_details.cost_params.profit_margin_percent + ).toBeTruthy(); expect(typeof mixnode.bond_information.layer).toBe("number"); }); }); @@ -136,7 +211,10 @@ describe("Get mixnode data", (): void => { it("Get active mixnodes detailed", async (): Promise => { const response = await contract.getActiveMixnodesDetailed(); response.forEach(function (mixnode) { - expect(mixnode.mixnode_details.rewarding_details.cost_params.profit_margin_percent).toBeTruthy(); + expect( + mixnode.mixnode_details.rewarding_details.cost_params + .profit_margin_percent + ).toBeTruthy(); }); }); @@ -150,14 +228,21 @@ describe("Get mixnode data", (): void => { it("Get rewarded mixnodes detailed", async (): Promise => { const response = await contract.getRewardedMixnodesDetailed(); response.forEach(function (mixnode) { - expect(mixnode.mixnode_details.rewarding_details.last_rewarded_epoch).toBeTruthy(); + expect( + mixnode.mixnode_details.rewarding_details.last_rewarded_epoch + ).toBeTruthy(); }); }); it("Get blacklisted mixnodes", async (): Promise => { const response = await contract.getBlacklistedMixnodes(); - response.forEach(function (value) { - expect(typeof value).toBe("number"); - }); + if (response === null) { + // no blacklisted mixnodes returns an empty array + expect(response).toBeNull(); + } else { + response.forEach(function (value) { + expect(typeof value).toBe("number"); + }); + } }); }); diff --git a/nym-api/tests/functional_test/contract_cache/services.test.ts b/nym-api/tests/functional_test/contract_cache/services.test.ts index d38adf9d9d..711aaa0246 100644 --- a/nym-api/tests/functional_test/contract_cache/services.test.ts +++ b/nym-api/tests/functional_test/contract_cache/services.test.ts @@ -1,28 +1,24 @@ import ContractCache from "../../src/endpoints/ContractCache"; -import ConfigHandler from "../../src/config/configHandler"; - let contract: ContractCache; -let config: ConfigHandler; describe("Get service provider info", (): void => { - beforeAll(async (): Promise => { - contract = new ContractCache(); - config = ConfigHandler.getInstance(); - }); + beforeAll(async (): Promise => { + contract = new ContractCache(); + }); - it("Get service providers", async (): Promise => { - const response = await contract.getServiceProviders(); - if ("[service_id]" in response) { - response.services.forEach((x) => { - expect(typeof x.service.nym_address.address).toBe("string"); - expect(typeof x.service.service_type).toBe("string"); - expect(typeof x.service.block_height).toBe("number"); - expect(typeof x.service.announcer).toBe("string"); - expect(typeof x.service.deposit.amount).toBe("string"); - expect(typeof x.service.deposit.denom).toBe("string"); - }); - } else if ("[ ]" in response) { - return; - } - }); -}); \ No newline at end of file + it("Get service providers", async (): Promise => { + const response = await contract.getServiceProviders(); + if ("[service_id]" in response) { + response.services.forEach((x) => { + expect(typeof x.service.nym_address.address).toBe("string"); + expect(typeof x.service.service_type).toBe("string"); + expect(typeof x.service.block_height).toBe("number"); + expect(typeof x.service.announcer).toBe("string"); + expect(typeof x.service.deposit.amount).toBe("string"); + expect(typeof x.service.deposit.denom).toBe("string"); + }); + } else if ("[ ]" in response) { + return; + } + }); +}); diff --git a/nym-api/tests/functional_test/status/status-gateway.test.ts b/nym-api/tests/functional_test/status/status-gateway.test.ts index 92435e12d3..71f2becc6d 100644 --- a/nym-api/tests/functional_test/status/status-gateway.test.ts +++ b/nym-api/tests/functional_test/status/status-gateway.test.ts @@ -40,7 +40,9 @@ describe("Get gateway data", (): void => { expect(identity_key).toStrictEqual(response.identity); expect(typeof response.owner).toBe("string"); } else if ("message" in response) { - expect(response.message).toContain("could not find uptime history associated with gateway"); + expect(response.message).toContain( + "could not find uptime history associated with gateway" + ); } }); @@ -58,7 +60,9 @@ describe("Get gateway data", (): void => { expect(identity_key).toStrictEqual(response.identity); expect(typeof response.count).toBe("number"); } else if ("message" in response) { - expect(response.message).toContain("could not find uptime history associated with mixnode"); + expect(response.message).toContain( + "could not find uptime history associated with mixnode" + ); } }); diff --git a/nym-api/tests/functional_test/status/status-mixnode.test.ts b/nym-api/tests/functional_test/status/status-mixnode.test.ts index aaa8054057..6590843f02 100644 --- a/nym-api/tests/functional_test/status/status-mixnode.test.ts +++ b/nym-api/tests/functional_test/status/status-mixnode.test.ts @@ -55,7 +55,9 @@ describe("Get mixnode data", (): void => { expect(identity_key).toStrictEqual(response.mix_id); expect(typeof response.owner).toBe("string"); } else if ("message" in response) { - expect(response.message).toContain("could not find uptime history associated with mixnode"); + expect(response.message).toContain( + "could not find uptime history associated with mixnode" + ); } }); @@ -70,7 +72,9 @@ describe("Get mixnode data", (): void => { const identity_key = config.environmnetConfig.mix_id; const response = await status.getMixnodeRewardComputation(identity_key); if ("estimation" in response) { - expect(response.reward_params.interval.sybil_resistance).toStrictEqual("0.3"); + expect(response.reward_params.interval.sybil_resistance).toStrictEqual( + "0.3" + ); expect(response.reward_params.active_set_size).toStrictEqual(240); expect(typeof response.reward_params.interval.reward_pool).toBe("string"); } else if ("message" in response) { @@ -108,7 +112,9 @@ describe("Get mixnode data", (): void => { const response = await status.getUnfilteredMixnodes(); response.forEach((x) => { expect(typeof x.stake_saturation).toBe("string"); - expect(typeof x.mixnode_details.rewarding_details.last_rewarded_epoch).toBe("number"); + expect( + typeof x.mixnode_details.rewarding_details.last_rewarded_epoch + ).toBe("number"); }); }); @@ -116,7 +122,9 @@ describe("Get mixnode data", (): void => { const identity_key = config.environmnetConfig.mix_id; const response = await status.getMixnodeStatus(identity_key); const unfiltered_mixnodes_response = await status.getUnfilteredMixnodes(); - const mixnode = unfiltered_mixnodes_response.find(x => x.mixnode_details.bond_information.mix_id === identity_key); + const mixnode = unfiltered_mixnodes_response.find( + (x) => x.mixnode_details.bond_information.mix_id === identity_key + ); if (mixnode) { expect(response.status).toStrictEqual("active"); } else { @@ -127,7 +135,9 @@ describe("Get mixnode data", (): void => { it("Get all rewarded mixnodes", async (): Promise => { const response = await status.getDetailedRewardedMixnodes(); response.forEach((x) => { - expect(typeof x.mixnode_details.rewarding_details.last_rewarded_epoch).toBe("number"); + expect( + typeof x.mixnode_details.rewarding_details.last_rewarded_epoch + ).toBe("number"); }); }); @@ -146,7 +156,9 @@ describe("Get mixnode data", (): void => { it("with correct data", async (): Promise => { const mix_id = config.environmnetConfig.mix_id; - const response = await status.sendMixnodeRewardEstimatedComputation(mix_id); + const response = await status.sendMixnodeRewardEstimatedComputation( + mix_id + ); expect(typeof response.estimation.delegates).toBe("string"); }); }); diff --git a/nym-api/tests/package-lock.json b/nym-api/tests/package-lock.json index 020a0426be..32e34a1256 100644 --- a/nym-api/tests/package-lock.json +++ b/nym-api/tests/package-lock.json @@ -103,9 +103,9 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -158,9 +158,9 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -2883,9 +2883,9 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -3653,9 +3653,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -4273,9 +4273,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4758,9 +4758,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "engines": { "node": ">=0.10.0" } @@ -4918,9 +4918,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -4962,9 +4962,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -6970,9 +6970,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -7559,9 +7559,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -7984,9 +7984,9 @@ "dev": true }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -8318,9 +8318,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==" }, "wrap-ansi": { "version": "7.0.0", diff --git a/nym-api/tests/package.json b/nym-api/tests/package.json index e93160b7b6..31731d6b16 100644 --- a/nym-api/tests/package.json +++ b/nym-api/tests/package.json @@ -7,7 +7,7 @@ "test": "test" }, "scripts": { - "test:qa": "TEST_ENV=qa jest --forceExit --detectOpenHandles --passWithNoTests", + "test:sandbox": "TEST_ENV=sandbox jest --forceExit --detectOpenHandles --passWithNoTests", "test:prod": "TEST_ENV=prod jest --forceExit --detectOpenHandles --passWithNoTests", "build": "tsc", "lint": "eslint --ext .js,.ts,.tsx .", diff --git a/nym-api/tests/src/config/config.yaml b/nym-api/tests/src/config/config.yaml index 83a9d3fa92..85603a7efa 100644 --- a/nym-api/tests/src/config/config.yaml +++ b/nym-api/tests/src/config/config.yaml @@ -2,15 +2,17 @@ common: request_headers: Accept: application/json Content-Type: application/json -qa: - api_base_url: https://qwerty-validator-api.qa.nymte.ch/api/v1 - mix_id: 63 - identity_key: 4Yr4qmEHd9sgsuQ83191FR2hD88RfsbMmB4tzhhZWriz - gateway_identity: 336yuXAeGEgedRfqTJZsG2YV7P13QH1bHv1SjCZYarc9 - log_level: error -prod: - api_base_url: https://validator.nymtech.net/api/v1 - mixnode_identity: DLdMKLPywEy1vnu3yPrtXvzY7fw1puiiHpA9n9UQatiQ - gateway_identity: CgQrYP8etksSBf4nALNqp93SHPpgFwEUyTsjBNNLj5WM +sandbox: + api_base_url: https://sandbox-nym-api1.nymtech.net/api/v1 + mix_id: 7 + identity_key: 2wEzyW6Pihk19xbCK9kB81vYuDFUvTzTAr77arvxdfSg + gateway_identity: HjNEDJuotWV8VD4ufeA1jeheTnfNJ7Jorevp57hgaZua + log_level: error + time_zone: utc +prod: + api_base_url: https://validator.nymtech.net/api/v1 + mix_id: 730 + mixnode_identity: 3pMCJswCyA19MGYWGDWT5fBk2M8ybSZGXttyAoNY5gBB + gateway_identity: 2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh log_level: error time_zone: utc diff --git a/nym-api/tests/src/config/configHandler.ts b/nym-api/tests/src/config/configHandler.ts index 44177f0866..1d29402012 100644 --- a/nym-api/tests/src/config/configHandler.ts +++ b/nym-api/tests/src/config/configHandler.ts @@ -6,7 +6,7 @@ import YAML from "yaml"; class ConfigHandler { private static instance: ConfigHandler; - private validEnvironments = ["qa", "prod"]; + private validEnvironments = ["sandbox", "prod"]; public commonConfig: { request_headers: object }; @@ -23,7 +23,7 @@ class ConfigHandler { private constructor() { this.setCommonConfig(); - this.setEnvironmentConfig(process.env.TEST_ENV || "qa" || "prod"); + this.setEnvironmentConfig(process.env.TEST_ENV || "sandbox" || "prod"); } public static getInstance(): ConfigHandler { diff --git a/nym-api/tests/src/types/ContractCacheTypes.ts b/nym-api/tests/src/types/ContractCacheTypes.ts index f3cf31e401..e66c3018a8 100644 --- a/nym-api/tests/src/types/ContractCacheTypes.ts +++ b/nym-api/tests/src/types/ContractCacheTypes.ts @@ -125,7 +125,7 @@ export interface EpochLength { } export interface ServiceProviders { - services: (Services)[]; + services: Services[]; } export interface Services { service_id: number; @@ -145,4 +145,3 @@ export interface Deposit { denom: string; amount: string; } - diff --git a/nym-api/tests/yarn.lock b/nym-api/tests/yarn.lock index 9e5476cf80..2bcd113e3c 100644 --- a/nym-api/tests/yarn.lock +++ b/nym-api/tests/yarn.lock @@ -2566,16 +2566,16 @@ safe-buffer@~5.1.1: integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== semver@7.x, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + 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@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== shebang-command@^2.0.0: version "2.0.0" @@ -2866,9 +2866,9 @@ which@^2.0.1: isexe "^2.0.0" word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== wrap-ansi@^7.0.0: version "7.0.0" diff --git a/nym-connect/desktop/CHANGELOG.md b/nym-connect/desktop/CHANGELOG.md index 1149c3b227..ab9c1da100 100644 --- a/nym-connect/desktop/CHANGELOG.md +++ b/nym-connect/desktop/CHANGELOG.md @@ -2,6 +2,32 @@ ## [Unreleased] +## [v1.1.19-snickers] (2023-08-29) + +- NymConnect sometimes fails to connect because the gateway it fetches from the validator-api to use is running an old version (of the gateway binary) ([#3788]) + +[#3788]: https://github.com/nymtech/nym/issues/3788 + +## [1.1.18] (2023-08-22) + +- refactor(nc-desktop): use userdata storage to save user gateway&sp ([#3723]) + +[#3723]: https://github.com/nymtech/nym/pull/3723 + +## [1.1.17] (2023-08-16) + +- Add a "Send us your feedback" section in NC (on the main screen) to collect user feedback using Sentry ([#3619]) +- NC native android - deploy on FDroid ([#3483]) + +[#3619]: https://github.com/nymtech/nym/issues/3619 +[#3483]: https://github.com/nymtech/nym/issues/3483 + +## [v1.1.16] (2023-08-08) + +- Uncouple network-requester <-> gateway in nym-connect and harbourmaster ([#3472]) + +[#3472]: https://github.com/nymtech/nym/issues/3472 + ## [v1.1.15] (2023-07-25) - NC Desktop - remove sentry DSN from code ([#3694]) diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index 6287fadf38..e2b2acee27 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -25,9 +25,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aead" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", "generic-array 0.14.7", @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher 0.4.4", @@ -69,10 +69,21 @@ dependencies = [ ] [[package]] -name = "aho-corasick" -version = "0.7.20" +name = "ahash" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" dependencies = [ "memchr", ] @@ -93,10 +104,80 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.70" +name = "allocator-api2" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anyhow" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" dependencies = [ "backtrace", ] @@ -115,19 +196,130 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[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" -version = "0.1.67" +name = "async-broadcast" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ea188f25f0255d8f92797797c97ebf5631fa88178beb1a46fdf5622c9a00e4" +checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +dependencies = [ + "event-listener", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand 1.9.0", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock", + "autocfg", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix 0.37.23", + "slab", + "socket2 0.4.9", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-process" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" +dependencies = [ + "async-io", + "async-lock", + "autocfg", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 0.37.23", + "signal-hook", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-recursion" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.5", + "syn 2.0.28", +] + +[[package]] +name = "async-task" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" + +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -151,7 +343,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -173,20 +365,10 @@ dependencies = [ ] [[package]] -name = "attohttpc" -version = "0.22.0" +name = "atomic-waker" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7" -dependencies = [ - "flate2", - "http", - "log", - "native-tls", - "serde", - "serde_json", - "serde_urlencoded", - "url", -] +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "atty" @@ -215,7 +397,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide 0.7.1", + "miniz_oxide", "object", "rustc-demangle", ] @@ -272,7 +454,7 @@ dependencies = [ "pbkdf2", "rand_core 0.6.4", "ripemd", - "sha2 0.10.6", + "sha2 0.10.7", "subtle 2.4.1", "zeroize", ] @@ -284,8 +466,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" dependencies = [ "bitcoin_hashes", - "rand 0.7.3", - "rand_core 0.5.1", + "rand 0.8.5", + "rand_core 0.6.4", "serde", "unicode-normalization", "zeroize", @@ -305,9 +487,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.0.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "bitvec" @@ -335,12 +517,12 @@ 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 0.7.2", + "arrayvec 0.7.4", "cc", "cfg-if", "constant_time_eq", @@ -371,6 +553,21 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "blocking" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand 1.9.0", + "futures-lite", + "log", +] + [[package]] name = "bls12_381" version = "0.5.0" @@ -399,6 +596,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "brotli" version = "3.3.4" @@ -432,14 +635,14 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" dependencies = [ - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] name = "bstr" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", "serde", @@ -447,9 +650,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byte-tools" @@ -499,26 +702,27 @@ checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" dependencies = [ "glib-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] name = "cargo_toml" -version = "0.13.3" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497049e9477329f8f6a559972ee42e117487d01d1e8c2cc9f836ea6fa23a9e1a" +checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838" dependencies = [ "serde", - "toml 0.5.11", + "toml 0.7.6", ] [[package]] name = "cc" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -529,12 +733,13 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfb" -version = "0.6.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f89d248799e3f15f91b70917f65381062a01bb8e222700ea0e5a7ff9785f9c" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" dependencies = [ "byteorder", - "uuid 0.8.2", + "fnv", + "uuid 1.4.1", ] [[package]] @@ -548,11 +753,12 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.11.0" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" +checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" dependencies = [ "smallvec", + "target-lexicon", ] [[package]] @@ -573,9 +779,9 @@ dependencies = [ [[package]] name = "chacha20" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fc89c7c5b9e7a02dfe45cd2367bae382f9ed31c61ca8debe5f827c420a2f08" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", "cipher 0.4.4", @@ -595,6 +801,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "winapi", +] + [[package]] name = "cipher" version = "0.3.0" @@ -617,33 +836,41 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.11" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" dependencies = [ - "bitflags 2.0.2", + "clap_builder", "clap_derive", - "clap_lex", - "is-terminal", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", "strsim", - "termcolor", ] [[package]] name = "clap_complete" -version = "4.1.5" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37686beaba5ac9f3ab01ee3172f792fc6ffdd685bfb9e63cfef02c0571a4e8e1" +checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce" dependencies = [ "clap", ] [[package]] name = "clap_complete_fig" -version = "4.1.2" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2171bc6242ad7a1801422bff039574449b5bd832b715222e500714ce10f91a54" +checksum = "99fee1d30a51305a6c2ed3fc5709be3c8af626c9c958e04dd9ae94e27bcbce9f" dependencies = [ "clap", "clap_complete", @@ -651,25 +878,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.1.9" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck 0.4.1", - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] name = "clap_lex" -version = "0.3.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" -dependencies = [ - "os_str_bytes", -] +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "cocoa" @@ -709,25 +932,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] -name = "colored" -version = "1.9.3" +name = "colorchoice" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "colored" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f741c91823341bebf717d4c71bda820630ce065443b58bd1b7451af008355" dependencies = [ - "atty", + "is-terminal", "lazy_static", "winapi", ] [[package]] name = "colored" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ - "atty", + "is-terminal", "lazy_static", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -741,16 +970,25 @@ dependencies = [ ] [[package]] -name = "const-oid" -version = "0.9.2" +name = "concurrent-queue" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[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" @@ -770,9 +1008,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core-graphics" @@ -789,13 +1027,12 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" dependencies = [ "bitflags 1.3.2", "core-foundation", - "foreign-types", "libc", ] @@ -833,9 +1070,9 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.5" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75836a10cb9654c54e77ee56da94d592923092a10b369cdb0dbd56acefc16340" +checksum = "7e272708a9745dad8b591ef8a718571512130f2b39b33e3d7a27c558e3069394" dependencies = [ "digest 0.10.7", "ed25519-zebra", @@ -846,18 +1083,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.5" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" +checksum = "296db6a3caca5283425ae0cf347f4e46999ba3f6620dbea8939a0e00347831ce" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc" +checksum = "bb6b2fb76758ef59cddc77f2e2ae91c22f77da49037e9f182e9c2833f0e959b1" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -868,9 +1105,9 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c" +checksum = "2bfa39422f0d9f1c9a6fd3711573258495314dfa3aae738ea825ecd9964bc659" dependencies = [ "proc-macro2", "quote", @@ -879,11 +1116,12 @@ dependencies = [ [[package]] name = "cosmwasm-std" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb" +checksum = "1f6dc2ee23313add5ecacc3ccac217b9967ad9d2d11bd56e5da6aa65a9da6138" dependencies = [ "base64 0.13.1", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -891,17 +1129,16 @@ dependencies = [ "hex", "schemars", "serde", - "serde-json-wasm 0.5.1", - "sha2 0.10.6", + "serde-json-wasm", + "sha2 0.10.7", "thiserror", - "uint", ] [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -947,9 +1184,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -968,14 +1205,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", + "memoffset 0.9.0", "scopeguard", ] @@ -991,19 +1228,13 @@ 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", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.9" @@ -1078,12 +1309,12 @@ dependencies = [ [[package]] name = "cssparser-macros" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -1151,9 +1382,9 @@ dependencies = [ [[package]] name = "cw-controllers" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071" +checksum = "d5d8edce4b78785f36413f67387e4be7d0cb7d032b5d4164bcc024f9c3f3f2ea" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1166,9 +1397,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" +checksum = "3f0e92a069d62067f3472c62e30adedb4cab1754725c0f2a682b3128d2bf3c79" dependencies = [ "cosmwasm-std", "schemars", @@ -1185,29 +1416,30 @@ dependencies = [ "cosmwasm-std", "cw2", "schemars", - "semver 1.0.17", + "semver 1.0.18", "serde", "thiserror", ] [[package]] name = "cw2" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" +checksum = "29ac2dc7a55ad64173ca1e0a46697c31b7a5c51342f55a1e84a724da4eb99908" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", "serde", + "thiserror", ] [[package]] name = "cw20" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" +checksum = "011c45920f8200bd5d32d4fe52502506f64f2f75651ab408054d4cfc75ca3a9b" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1218,9 +1450,9 @@ dependencies = [ [[package]] name = "cw3" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa" +checksum = "171af3d9127de6805a7dd819fb070c7d2f6c3ea85f4193f42cef259f0a7f33d5" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1233,9 +1465,9 @@ dependencies = [ [[package]] name = "cw4" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b" +checksum = "a398696307efadaaa2d0850076f865fa706c959d493cb4203314f72be6b77a64" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1246,9 +1478,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -1256,51 +1488,40 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] name = "darling_macro" -version = "0.13.4" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] name = "dashmap" -version = "5.4.0" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" dependencies = [ "cfg-if", - "hashbrown 0.12.3", + "hashbrown 0.14.0", "lock_api", "once_cell", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "dbus" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" -dependencies = [ - "libc", - "libdbus-sys", - "winapi", + "parking_lot_core 0.9.8", ] [[package]] @@ -1310,7 +1531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ "serde", - "uuid 1.3.0", + "uuid 1.4.1", ] [[package]] @@ -1325,14 +1546,23 @@ dependencies = [ [[package]] name = "der" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" +dependencies = [ + "serde", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1469,30 +1699,30 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "dtoa" -version = "0.4.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] name = "dtoa-short" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde03329ae10e79ede66c9ce4dc930aa8599043b0743008548680f25b91502d6" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" dependencies = [ "dtoa", ] [[package]] name = "dunce" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "ecdsa" @@ -1512,7 +1742,7 @@ version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ - "der 0.7.7", + "der 0.7.8", "digest 0.10.7", "elliptic-curve 0.13.5", "rfc6979 0.4.0", @@ -1531,9 +1761,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" dependencies = [ "pkcs8 0.10.2", "signature 2.1.0", @@ -1583,9 +1813,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -1626,6 +1856,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embed-resource" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7f1e82a60222fc67bfd50d752a9c89da5cce4c39ed39decc84a443b07bbd69a" +dependencies = [ + "cc", + "rustc_version", + "toml 0.7.6", + "vswhom", + "winreg 0.11.0", +] + [[package]] name = "embed_plist" version = "1.2.2" @@ -1652,13 +1895,34 @@ dependencies = [ [[package]] name = "enum-iterator-derive" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355f93763ef7b0ae1c43c4d8eccc9d5848d84ad1a1d8ce61c421d1ac85a19d05" +checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", +] + +[[package]] +name = "enumflags2" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", ] [[package]] @@ -1676,19 +1940,19 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.2.8" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1727,12 +1991,27 @@ dependencies = [ ] [[package]] -name = "fern" -version = "0.6.1" +name = "fastrand" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" dependencies = [ - "colored 1.9.3", + "simd-adler32", +] + +[[package]] +name = "fern" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" +dependencies = [ + "colored 1.9.4", "log", ] @@ -1778,24 +2057,24 @@ dependencies = [ [[package]] name = "field-offset" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset", + "memoffset 0.9.0", "rustc_version", ] [[package]] name = "filetime" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys 0.45.0", + "redox_syscall 0.3.5", + "windows-sys 0.48.0", ] [[package]] @@ -1821,12 +2100,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", - "miniz_oxide 0.6.2", + "miniz_oxide", ] [[package]] @@ -1848,7 +2127,7 @@ dependencies = [ "futures-core", "futures-sink", "pin-project", - "spin 0.9.6", + "spin 0.9.8", ] [[package]] @@ -1874,9 +2153,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -1905,9 +2184,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -1920,9 +2199,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -1930,15 +2209,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -1958,38 +2237,53 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] [[package]] name = "futures-macro" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -2051,7 +2345,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -2068,7 +2362,21 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps 6.0.3", + "system-deps 6.1.1", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca49a59ad8cfdf36ef7330fe7bdfbe1d34323220cc16a0de2679ee773aee2c2" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps 6.1.1", ] [[package]] @@ -2080,21 +2388,21 @@ dependencies = [ "gdk-sys", "glib-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", "x11", ] [[package]] name = "generator" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a20a288a94683f5f4da0adecdbe095c94a77c295e514cc6484e9394dd8376e" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" dependencies = [ "cc", "libc", "log", "rustversion", - "windows 0.44.0", + "windows 0.48.0", ] [[package]] @@ -2188,7 +2496,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", "winapi", ] @@ -2227,9 +2535,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.15.11" +version = "0.15.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64" +checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" dependencies = [ "anyhow", "heck 0.4.1", @@ -2247,7 +2555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" dependencies = [ "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -2258,9 +2566,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" dependencies = [ "aho-corasick", "bstr", @@ -2289,7 +2597,7 @@ checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" dependencies = [ "glib-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -2376,14 +2684,14 @@ dependencies = [ "gobject-sys", "libc", "pango-sys", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] name = "gtk3-macros" -version = "0.15.4" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f518afe90c23fba585b2d7697856f9e6a7bbc62f65588035e66f6afb01a2e9" +checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" dependencies = [ "anyhow", "proc-macro-crate", @@ -2395,9 +2703,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.16" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -2405,7 +2713,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.2", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -2432,7 +2740,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash", + "ahash 0.7.6", ] [[package]] @@ -2441,7 +2749,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", ] [[package]] @@ -2449,6 +2757,10 @@ name = "hashbrown" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash 0.8.3", + "allocator-api2", +] [[package]] name = "hashlink" @@ -2461,11 +2773,11 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" +checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" dependencies = [ - "hashbrown 0.12.3", + "hashbrown 0.14.0", ] [[package]] @@ -2522,18 +2834,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -2596,17 +2899,6 @@ dependencies = [ "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]] name = "html5ever" version = "0.25.2" @@ -2629,7 +2921,7 @@ checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", - "itoa 1.0.6", + "itoa 1.0.9", ] [[package]] @@ -2657,9 +2949,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -2688,9 +2980,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -2701,9 +2993,9 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.6", + "itoa 1.0.9", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -2761,10 +3053,33 @@ dependencies = [ ] [[package]] -name = "ico" -version = "0.2.0" +name = "iana-time-zone" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031530fe562d8c8d71c0635013d6d155bbfe8ba0aa4b4d2d24ce8af6b71047bd" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows 0.48.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" dependencies = [ "byteorder", "png", @@ -2778,9 +3093,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -2788,11 +3103,10 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" dependencies = [ - "crossbeam-utils", "globset", "lazy_static", "log", @@ -2806,9 +3120,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.5" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" dependencies = [ "bytemuck", "byteorder", @@ -2825,9 +3139,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", @@ -2842,13 +3156,14 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ "equivalent", "hashbrown 0.14.0", + "serde", ] [[package]] name = "infer" -version = "0.7.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20b2b533137b9cad970793453d4f921c2e91312a6d88b1085c07bc15fc51bb3b" +checksum = "a898e4b7951673fce96614ce5751d13c40fc5674bc2d759288e46c3ab62598b3" dependencies = [ "cfb", ] @@ -2878,38 +3193,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", ] [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi 0.3.2", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "ipnet" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is-terminal" -version = "0.4.5" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.45.0", + "hermit-abi 0.3.2", + "rustix 0.38.8", + "windows-sys 0.48.0", ] [[package]] @@ -2929,9 +3240,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "javascriptcore-rs" @@ -2996,12 +3307,13 @@ dependencies = [ [[package]] name = "json-patch" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3fa5a61630976fc4c353c70297f2e93f1930e3ccee574d59d618ccbd5154ce" +checksum = "1f54898088ccb91df1b492cc80029a6fdf1c48ca0db7c6822a8babad69c94658" dependencies = [ "serde", "serde_json", + "thiserror", "treediff", ] @@ -3014,7 +3326,7 @@ dependencies = [ "cfg-if", "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -3027,7 +3339,7 @@ dependencies = [ "ecdsa 0.16.8", "elliptic-curve 0.13.5", "once_cell", - "sha2 0.10.6", + "sha2 0.10.7", "signature 2.1.0", ] @@ -3081,18 +3393,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.146" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" - -[[package]] -name = "libdbus-sys" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f8d7ae751e1cb825c840ae5e682f59b098cdfd213c350ac268b61449a5f58a0" -dependencies = [ - "pkg-config", -] +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libgit2-sys" @@ -3118,9 +3421,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "libsqlite3-sys" @@ -3135,9 +3438,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "libc", @@ -3162,9 +3465,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "lioness" @@ -3180,9 +3489,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -3190,11 +3499,10 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" dependencies = [ - "cfg-if", "serde", ] @@ -3221,9 +3529,9 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "mac-notification-sys" -version = "0.5.6" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e72d50edb17756489e79d52eb146927bec8eba9dd48faadf9ef08bca3791ad5" +checksum = "9402858e87f85f88bf518bd2e68450640df1c99b1ddb3ea28c4d5d823bb54cdd" dependencies = [ "cc", "dirs-next", @@ -3267,7 +3575,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -3284,9 +3592,18 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.8.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] @@ -3309,15 +3626,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881" -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.7.1" @@ -3325,18 +3633,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", + "simd-adler32", ] [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -3391,6 +3699,19 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", + "static_assertions", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -3413,10 +3734,11 @@ version = "4.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bfa211d18e360f08e36c364308f394b5eb23a6629150690e109a916dc6f610e" dependencies = [ - "dbus", "log", "mac-notification-sys", + "serde", "tauri-winrt-notification", + "zbus", ] [[package]] @@ -3463,9 +3785,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", "libm", @@ -3473,11 +3795,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 0.3.2", "libc", ] @@ -3562,6 +3884,7 @@ version = "1.1.15" dependencies = [ "async-trait", "base64 0.21.2", + "cfg-if", "dashmap", "dirs 4.0.0", "futures", @@ -3572,6 +3895,7 @@ dependencies = [ "nym-config", "nym-credential-storage", "nym-crypto", + "nym-explorer-api-requests", "nym-gateway-client", "nym-gateway-requests", "nym-network-defaults", @@ -3582,9 +3906,10 @@ dependencies = [ "nym-topology", "nym-validator-client", "rand 0.7.3", + "reqwest", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "sqlx 0.6.3", "tap", "thiserror", @@ -3596,8 +3921,8 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-timer", "wasm-utils", + "wasmtimer", "zeroize", ] @@ -3625,22 +3950,20 @@ dependencies = [ name = "nym-coconut-bandwidth-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "nym-multisig-contract-common", - "schemars", - "serde", ] [[package]] name = "nym-coconut-dkg-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-utils", "nym-contracts-common", "nym-multisig-contract-common", - "schemars", - "serde", ] [[package]] @@ -3663,13 +3986,13 @@ dependencies = [ "log", "nym-network-defaults", "serde", - "toml 0.7.4", + "toml 0.7.6", "url", ] [[package]] name = "nym-connect" -version = "1.1.13" +version = "1.1.18" dependencies = [ "anyhow", "bip39", @@ -3691,7 +4014,10 @@ dependencies = [ "nym-socks5-client-core", "nym-sphinx", "nym-task", + "nym-topology", + "nym-validator-client", "pretty_env_logger", + "rand 0.7.3", "rand 0.8.5", "reqwest", "rust-embed", @@ -3709,7 +4035,7 @@ dependencies = [ "thiserror", "time", "tokio", - "toml 0.7.4", + "toml 0.7.6", "ts-rs", "url", "yaml-rust", @@ -3720,6 +4046,7 @@ name = "nym-contracts-common" version = "0.5.0" dependencies = [ "bs58 0.4.0", + "cosmwasm-schema", "cosmwasm-std", "schemars", "serde", @@ -3732,7 +4059,7 @@ version = "0.1.0" dependencies = [ "async-trait", "log", - "sqlx 0.5.13", + "sqlx 0.5.11", "thiserror", "tokio", ] @@ -3743,6 +4070,7 @@ version = "0.1.0" dependencies = [ "bls12_381 0.5.0", "cosmrs", + "log", "nym-api-requests", "nym-coconut-interface", "nym-crypto", @@ -3754,7 +4082,7 @@ dependencies = [ name = "nym-crypto" version = "0.4.0" dependencies = [ - "aes 0.8.2", + "aes 0.8.3", "blake3", "bs58 0.4.0", "cipher 0.4.4", @@ -3794,6 +4122,27 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nym-ephemera-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "nym-contracts-common", +] + +[[package]] +name = "nym-explorer-api-requests" +version = "0.1.0" +dependencies = [ + "nym-api-requests", + "nym-contracts-common", + "nym-mixnet-contract-common", + "schemars", + "serde", +] + [[package]] name = "nym-gateway-client" version = "0.1.0" @@ -3821,8 +4170,8 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-timer", "wasm-utils", + "wasmtimer", ] [[package]] @@ -3862,13 +4211,14 @@ name = "nym-mixnet-contract-common" version = "0.6.0" dependencies = [ "bs58 0.4.0", + "cosmwasm-schema", "cosmwasm-std", "humantime-serde", "log", "nym-contracts-common", "schemars", "serde", - "serde-json-wasm 0.4.1", + "serde-json-wasm", "serde_repr", "thiserror", "time", @@ -3893,11 +4243,11 @@ dependencies = [ name = "nym-name-service-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-utils", "nym-contracts-common", - "schemars", "serde", "thiserror", ] @@ -3910,6 +4260,7 @@ dependencies = [ "dotenvy", "hex-literal", "once_cell", + "schemars", "serde", "thiserror", "url", @@ -3919,12 +4270,10 @@ dependencies = [ name = "nym-nonexhaustive-delayqueue" version = "0.1.0" dependencies = [ - "futures-core", - "slab", "tokio", "tokio-stream", "tokio-util", - "wasm-timer", + "wasmtimer", ] [[package]] @@ -3963,12 +4312,11 @@ dependencies = [ name = "nym-service-provider-directory-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-utils", "nym-contracts-common", - "schemars", - "serde", "thiserror", ] @@ -3995,7 +4343,9 @@ dependencies = [ "nym-bandwidth-controller", "nym-client-core", "nym-config", + "nym-contracts-common", "nym-credential-storage", + "nym-mixnet-contract-common", "nym-network-defaults", "nym-service-providers-common", "nym-socks5-proxy-helpers", @@ -4005,10 +4355,13 @@ dependencies = [ "nym-validator-client", "pin-project", "rand 0.7.3", + "reqwest", + "schemars", "serde", "tap", "thiserror", "tokio", + "url", ] [[package]] @@ -4220,9 +4573,12 @@ dependencies = [ "async-trait", "base64 0.13.1", "bip39", - "colored 2.0.0", + "colored 2.0.4", "cosmrs", "cosmwasm-std", + "cw-controllers", + "cw-utils", + "cw2", "cw3", "cw4", "eyre", @@ -4236,13 +4592,13 @@ dependencies = [ "nym-coconut-interface", "nym-config", "nym-contracts-common", + "nym-ephemera-common", "nym-group-contract-common", "nym-mixnet-contract-common", "nym-multisig-contract-common", "nym-name-service-common", "nym-network-defaults", "nym-service-provider-directory-common", - "nym-vesting-contract", "nym-vesting-contract-common", "openssl", "prost", @@ -4254,36 +4610,20 @@ dependencies = [ "thiserror", "tokio", "url", + "wasmtimer", "zeroize", ] -[[package]] -name = "nym-vesting-contract" -version = "1.4.0" -dependencies = [ - "cosmwasm-derive", - "cosmwasm-std", - "cw-storage-plus", - "cw2", - "nym-contracts-common", - "nym-mixnet-contract-common", - "nym-vesting-contract-common", - "schemars", - "semver 1.0.17", - "serde", - "thiserror", - "vergen", -] - [[package]] name = "nym-vesting-contract-common" version = "0.7.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "nym-contracts-common", "nym-mixnet-contract-common", - "schemars", "serde", + "thiserror", ] [[package]] @@ -4336,9 +4676,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -4364,9 +4704,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.47" +version = "0.10.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b277f87dacc05a6b709965d1cbafac4649d6ce9f3ce9ceb88508b5666dfec9" +checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" dependencies = [ "bitflags 1.3.2", "cfg-if", @@ -4379,13 +4719,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -4396,20 +4736,19 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.26.0+1.1.1u" +version = "111.27.0+1.1.1v" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37" +checksum = "06e8f197c82d7511c5b014030c9b1efeda40d7d5f99d23b4ceed3524a5e63f02" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.82" +version = "0.9.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a95792af3c4e0153c3914df2261bedd30a98476f94dc892b67dfe1d89d433a04" +checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" dependencies = [ - "autocfg", "cc", "libc", "openssl-src", @@ -4423,6 +4762,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "os_info" version = "3.7.0" @@ -4434,12 +4783,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - [[package]] name = "overload" version = "0.1.1" @@ -4486,9 +4829,15 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] +[[package]] +name = "parking" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" + [[package]] name = "parking_lot" version = "0.11.2" @@ -4507,7 +4856,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.7", + "parking_lot_core 0.9.8", ] [[package]] @@ -4519,29 +4868,29 @@ dependencies = [ "cfg-if", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.45.0", + "windows-targets", ] [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pathdiff" @@ -4599,15 +4948,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.5.6" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7" +checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" dependencies = [ "thiserror", "ucd-trie", @@ -4615,9 +4964,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.6" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a81186863f3d0a27340815be8f2078dd8050b14cd71913db9fbda795e5f707d7" +checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" dependencies = [ "pest", "pest_generator", @@ -4625,26 +4974,26 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.6" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b" +checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] name = "pest_meta" -version = "2.5.6" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e3b284b1f13a20dc5ebc90aff59a51b8d7137c221131b52a7260c08cbc1cc80" +checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" dependencies = [ "once_cell", "pest", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -4747,29 +5096,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -4793,40 +5142,57 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.7", + "der 0.7.8", "spki 0.7.2", ] [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plist" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590" +checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" dependencies = [ "base64 0.21.2", - "indexmap 1.9.2", + "indexmap 1.9.3", "line-wrap", - "quick-xml 0.28.1", + "quick-xml 0.29.0", "serde", "time", ] [[package]] name = "png" -version = "0.17.7" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" dependencies = [ "bitflags 1.3.2", "crc32fast", + "fdeflate", "flate2", - "miniz_oxide 0.6.2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", ] [[package]] @@ -4904,9 +5270,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -4966,18 +5332,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c1a97b1bc42b1d550bfb48d4262153fe400a12bab1511821736f7eac76d7e2" +checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -5081,9 +5447,9 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f851a03551ceefd30132e447f07f96cb7011d6b658374f3aed847333adb5559" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "rayon" @@ -5116,6 +5482,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -5123,19 +5498,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.10", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.7.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-automata 0.3.6", + "regex-syntax 0.7.4", ] [[package]] @@ -5144,7 +5520,18 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", ] [[package]] @@ -5154,10 +5541,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] -name = "reqwest" -version = "0.11.15" +name = "regex-syntax" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba30cc2c0cd02af1222ed216ba659cdb2f879dfe3181852fe7c50b1d0005949" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64 0.21.2", "bytes", @@ -5183,12 +5576,14 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-socks", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", - "winreg", + "winreg 0.10.1", ] [[package]] @@ -5262,9 +5657,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "6.6.0" +version = "6.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb133b9a38b5543fad3807fb2028ea47c5f2b566f4f5e28a11902f1a358348b6" +checksum = "a36224c3276f8c4ebc8c20f158eca7ca4359c8db89991c4925132aaaf6702661" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -5273,25 +5668,25 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "6.5.0" +version = "6.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4e0f0ced47ded9a68374ac145edd65a6c1fa13a96447b873660b2a568a0fd7" +checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac" dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 1.0.109", + "syn 2.0.28", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "7.5.0" +version = "7.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512b0ab6853f7e14e3c8754acb43d6f748bb9ced66aa5915a6553ac8213f7731" +checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74" dependencies = [ "globset", - "sha2 0.10.6", + "sha2 0.10.7", "walkdir", ] @@ -5307,21 +5702,34 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.17", + "semver 1.0.18", ] [[package]] name = "rustix" -version = "0.36.11" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", - "windows-sys 0.45.0", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys 0.4.5", + "windows-sys 0.48.0", ] [[package]] @@ -5363,24 +5771,24 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64 0.21.2", ] [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "safemem" @@ -5399,11 +5807,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -5413,7 +5821,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" dependencies = [ "dyn-clone", - "indexmap 1.9.2", + "indexmap 1.9.3", "schemars_derive", "serde", "serde_json", @@ -5439,9 +5847,9 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -5484,7 +5892,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", - "der 0.7.7", + "der 0.7.8", "generic-array 0.14.7", "pkcs8 0.10.2", "subtle 2.4.1", @@ -5493,9 +5901,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -5506,9 +5914,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -5545,9 +5953,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" dependencies = [ "serde", ] @@ -5688,54 +6096,45 @@ dependencies = [ "thiserror", "time", "url", - "uuid 1.3.0", + "uuid 1.4.1", ] [[package]] name = "serde" -version = "1.0.158" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] name = "serde-json-wasm" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" -dependencies = [ - "serde", -] - -[[package]] -name = "serde-json-wasm" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" dependencies = [ "serde", ] [[package]] name = "serde_bytes" -version = "0.11.9" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.5", + "syn 2.0.28", ] [[package]] @@ -5751,24 +6150,24 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ - "itoa 1.0.6", + "itoa 1.0.9", "ryu", "serde", ] [[package]] name = "serde_repr" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.5", + "syn 2.0.28", ] [[package]] @@ -5787,31 +6186,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.6", + "itoa 1.0.9", "ryu", "serde", ] [[package]] name = "serde_with" -version = "1.14.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +checksum = "1402f54f9a3b9e2efe71c1cea24e648acce55887983553eeb858cf3115acfd49" dependencies = [ + "base64 0.21.2", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.0.0", "serde", + "serde_json", "serde_with_macros", + "time", ] [[package]] name = "serde_with_macros" -version = "1.5.2" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +checksum = "9197f1ad0e3c173a0222d3c4404fb04c3afe87e962bcb327af73e8301fa203c7" dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -5885,9 +6291,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -5903,6 +6309,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -5932,6 +6348,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "siphasher" version = "0.3.10" @@ -5949,9 +6371,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "socket2" @@ -5963,6 +6385,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "soup2" version = "0.2.1" @@ -6023,9 +6455,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.6" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5d6e0250b93c8427a177b849d144a96d5acc57006149479403d7861ab721e34" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ "lock_api", ] @@ -6047,7 +6479,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der 0.7.7", + "der 0.7.8", ] [[package]] @@ -6074,9 +6506,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.5.13" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b" +checksum = "fc15591eb44ffb5816a4a70a7efd5dd87bfd3aa84c4c200401c4396140525826" dependencies = [ "sqlx-core 0.5.13", "sqlx-macros 0.5.13", @@ -6098,7 +6530,7 @@ version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" dependencies = [ - "ahash", + "ahash 0.7.6", "atoi 0.4.0", "bitflags 1.3.2", "byteorder", @@ -6115,8 +6547,8 @@ dependencies = [ "futures-util", "hashlink 0.7.0", "hex", - "indexmap 1.9.2", - "itoa 1.0.6", + "indexmap 1.9.3", + "itoa 1.0.9", "libc", "libsqlite3-sys", "log", @@ -6125,7 +6557,7 @@ dependencies = [ "paste", "percent-encoding", "rustls 0.19.1", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "sqlformat 0.1.8", "sqlx-rt 0.5.13", @@ -6143,7 +6575,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" dependencies = [ - "ahash", + "ahash 0.7.6", "atoi 1.0.0", "bitflags 1.3.2", "byteorder", @@ -6159,10 +6591,10 @@ dependencies = [ "futures-executor", "futures-intrusive", "futures-util", - "hashlink 0.8.1", + "hashlink 0.8.3", "hex", - "indexmap 1.9.2", - "itoa 1.0.6", + "indexmap 1.9.3", + "itoa 1.0.9", "libc", "libsqlite3-sys", "log", @@ -6172,7 +6604,7 @@ dependencies = [ "percent-encoding", "rustls 0.20.8", "rustls-pemfile", - "sha2 0.10.6", + "sha2 0.10.7", "smallvec", "sqlformat 0.2.1", "sqlx-rt 0.6.3", @@ -6195,7 +6627,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "sha2 0.10.6", + "sha2 0.10.7", "sqlx-core 0.5.13", "sqlx-rt 0.5.13", "syn 1.0.109", @@ -6214,7 +6646,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "sha2 0.10.6", + "sha2 0.10.7", "sqlx-core 0.6.3", "sqlx-rt 0.6.3", "syn 1.0.109", @@ -6292,9 +6724,9 @@ dependencies = [ [[package]] name = "stringprep" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +checksum = "db3737bde7edce97102e0e2b15365bf7a20bfdb5f60f4f9e8d7004258a51a8da" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -6315,27 +6747,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strum" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ac893c7d471c8a21f31cfe213ec4f6d9afeed25537c772e08ef3f005f8729e" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "subtle" version = "1.0.0" @@ -6376,27 +6787,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.5" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c2d1c76a26822187a1fbb5964e3fff108bc208f02e820ab9dac1234f6b388a" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - [[package]] name = "system-deps" version = "5.0.0" @@ -6412,22 +6811,22 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.0.3" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" +checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" dependencies = [ - "cfg-expr 0.11.0", + "cfg-expr 0.15.4", "heck 0.4.1", "pkg-config", - "toml 0.5.11", + "toml 0.7.6", "version-compare 0.1.1", ] [[package]] name = "tao" -version = "0.15.8" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8e6399427c8494f9849b58694754d7cc741293348a6836b6c8d2c5aa82d8e6" +checksum = "6a6d198e01085564cea63e976ad1566c1ba2c2e4cc79578e35d9f05521505e31" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -6441,6 +6840,7 @@ dependencies = [ "gdk", "gdk-pixbuf", "gdk-sys", + "gdkwayland-sys", "gdkx11-sys", "gio", "glib", @@ -6459,18 +6859,29 @@ dependencies = [ "objc", "once_cell", "parking_lot 0.12.1", - "paste", "png", "raw-window-handle", "scopeguard", "serde", + "tao-macros", "unicode-segmentation", - "uuid 1.3.0", + "uuid 1.4.1", "windows 0.39.0", "windows-implement", "x11-dl", ] +[[package]] +name = "tao-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec114582505d158b669b136e6851f85840c109819d77c42bb7c0709f727d18c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "tap" version = "1.0.1" @@ -6479,9 +6890,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" dependencies = [ "filetime", "libc", @@ -6489,14 +6900,20 @@ dependencies = [ ] [[package]] -name = "tauri" -version = "1.2.4" +name = "target-lexicon" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7e0f1d535e7cbbbab43c82be4fc992b84f9156c16c160955617e0260ebc449" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" + +[[package]] +name = "tauri" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbe522898e35407a8e60dc3870f7579fea2fc262a6a6072eccdd37ae1e1d91e" dependencies = [ "anyhow", - "attohttpc", - "base64 0.13.1", + "base64 0.21.2", + "bytes", "cocoa", "dirs-next", "embed_plist", @@ -6518,8 +6935,9 @@ dependencies = [ "rand 0.8.5", "raw-window-handle", "regex", + "reqwest", "rfd", - "semver 1.0.17", + "semver 1.0.18", "serde", "serde_json", "serde_repr", @@ -6535,7 +6953,7 @@ dependencies = [ "time", "tokio", "url", - "uuid 1.3.0", + "uuid 1.4.1", "webkit2gtk", "webview2-com", "windows 0.39.0", @@ -6544,27 +6962,28 @@ dependencies = [ [[package]] name = "tauri-build" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8807c85d656b2b93927c19fe5a5f1f1f348f96c2de8b90763b3c2d561511f9b4" +checksum = "7d2edd6a259b5591c8efdeb9d5702cb53515b82a6affebd55c7fd6d3a27b7d1b" dependencies = [ "anyhow", "cargo_toml", "heck 0.4.1", "json-patch", - "semver 1.0.17", + "semver 1.0.18", + "serde", "serde_json", "tauri-utils", - "winres", + "tauri-winres", ] [[package]] name = "tauri-codegen" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14388d484b6b1b5dc0f6a7d6cc6433b3b230bec85eaa576adcdf3f9fafa49251" +checksum = "54ad2d49fdeab4a08717f5b49a163bdc72efc3b1950b6758245fcde79b645e1a" dependencies = [ - "base64 0.13.1", + "base64 0.21.2", "brotli", "ico", "json-patch", @@ -6573,22 +6992,22 @@ dependencies = [ "proc-macro2", "quote", "regex", - "semver 1.0.17", + "semver 1.0.18", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "tauri-utils", "thiserror", "time", - "uuid 1.3.0", + "uuid 1.4.1", "walkdir", ] [[package]] name = "tauri-macros" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069319e5ecbe653a799b94b0690d9f9bf5d00f7b1d3989aa331c524d4e354075" +checksum = "8eb12a2454e747896929338d93b0642144bb51e0dddbb36e579035731f0d76b7" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -6600,9 +7019,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "0.12.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c507d954d08ac8705d235bc70ec6975b9054fb95ff7823af72dbb04186596f3b" +checksum = "108683199cb18f96d2d4134187bb789964143c845d2d154848dda209191fd769" dependencies = [ "gtk", "http", @@ -6613,16 +7032,17 @@ dependencies = [ "serde_json", "tauri-utils", "thiserror", - "uuid 1.3.0", + "url", + "uuid 1.4.1", "webview2-com", "windows 0.39.0", ] [[package]] name = "tauri-runtime-wry" -version = "0.12.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b1c5764a41a13176a4599b5b7bd0881bea7d94dfe45e1e755f789b98317e30" +checksum = "0b7aa256a1407a3a091b5d843eccc1a5042289baf0a43d1179d9f0fcfea37c1b" dependencies = [ "cocoa", "gtk", @@ -6631,7 +7051,7 @@ dependencies = [ "raw-window-handle", "tauri-runtime", "tauri-utils", - "uuid 1.3.0", + "uuid 1.4.1", "webkit2gtk", "webview2-com", "windows 0.39.0", @@ -6640,12 +7060,13 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5abbc109a6eb45127956ffcc26ef0e875d160150ac16cfa45d26a6b2871686f1" +checksum = "03fc02bb6072bb397e1d473c6f76c953cda48b4a2d0cce605df284aa74a12e84" dependencies = [ "brotli", "ctor", + "dunce", "glob", "heck 0.4.1", "html5ever", @@ -6656,7 +7077,7 @@ dependencies = [ "phf 0.10.1", "proc-macro2", "quote", - "semver 1.0.17", + "semver 1.0.18", "serde", "serde_json", "serde_with", @@ -6667,27 +7088,36 @@ dependencies = [ ] [[package]] -name = "tauri-winrt-notification" -version = "0.1.0" +name = "tauri-winres" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58de036c4d2e20717024de2a3c4bf56c301f07b21bc8ef9b57189fce06f1f3b" +checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" +dependencies = [ + "embed-resource", + "toml 0.7.6", +] + +[[package]] +name = "tauri-winrt-notification" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5bff1d532fead7c43324a0fa33643b8621a47ce2944a633be4cb6c0240898f" dependencies = [ "quick-xml 0.23.1", - "strum", "windows 0.39.0", ] [[package]] name = "tempfile" -version = "3.4.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" dependencies = [ "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.42.0", + "fastrand 2.0.0", + "redox_syscall 0.3.5", + "rustix 0.38.8", + "windows-sys 0.48.0", ] [[package]] @@ -6698,7 +7128,7 @@ checksum = "3f0a7d05cf78524782337f8edd55cbc578d159a16ad4affe2135c92f7dbac7f0" dependencies = [ "bytes", "digest 0.10.7", - "ed25519 2.2.1", + "ed25519 2.2.2", "ed25519-consensus", "flex-error", "futures", @@ -6712,7 +7142,7 @@ dependencies = [ "serde_bytes", "serde_json", "serde_repr", - "sha2 0.10.6", + "sha2 0.10.7", "signature 2.1.0", "subtle 2.4.1", "subtle-encoding", @@ -6770,7 +7200,7 @@ dependencies = [ "hyper-rustls", "peg", "pin-project", - "semver 1.0.17", + "semver 1.0.18", "serde", "serde_bytes", "serde_json", @@ -6816,22 +7246,22 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.5", + "syn 2.0.28", ] [[package]] @@ -6846,11 +7276,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" dependencies = [ - "itoa 1.0.6", + "deranged", + "itoa 1.0.9", "js-sys", "libc", "num_threads", @@ -6861,15 +7292,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" dependencies = [ "time-core", ] @@ -6891,32 +7322,31 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.26.0" +version = "1.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "40de3a2ba249dcb097e01be5e67a5ff53cf250397715a071a81543e8a832a920" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.3", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] @@ -6965,9 +7395,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -6990,9 +7420,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -7014,9 +7444,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ "serde", "serde_spanned", @@ -7035,9 +7465,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ "indexmap 2.0.0", "serde", @@ -7066,20 +7496,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -7098,9 +7528,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", @@ -7116,9 +7546,9 @@ dependencies = [ [[package]] name = "treediff" -version = "3.0.2" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761e8d5ad7ce14bb82b7e61ccc0ca961005a275a060b9644a2431aa11553c2ff" +checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" dependencies = [ "serde_json", ] @@ -7131,9 +7561,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "ts-rs" -version = "6.2.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4added4070a4fdf9df03457206cd2e4b12417c8560a2954d91ffcbe60177a56a" +checksum = "e1ff1f8c90369bc172200013ac17ae86e7b5def580687df4e6127883454ff2b0" dependencies = [ "thiserror", "ts-rs-macros", @@ -7141,14 +7571,14 @@ dependencies = [ [[package]] name = "ts-rs-macros" -version = "6.2.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f807fdb3151fee75df7485b901a89624358cd07a67a8fb1a5831bf5a07681ff" +checksum = "a6f41cc0aeb7a4a55730188e147d3795a7349b501f8334697fd37629b896cdc2" dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.28", "termcolor", ] @@ -7180,20 +7610,18 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] -name = "uint" -version = "0.9.5" +name = "uds_windows" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", + "tempfile", + "winapi", ] [[package]] @@ -7213,9 +7641,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -7232,12 +7660,6 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "unicode_categories" version = "0.1.1" @@ -7246,9 +7668,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "universal-hash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", "subtle 2.4.1", @@ -7275,9 +7697,9 @@ dependencies = [ [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -7305,9 +7727,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "getrandom 0.2.10", "serde", @@ -7360,6 +7782,26 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "vte" version = "0.10.1" @@ -7381,6 +7823,12 @@ dependencies = [ "quote", ] +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "walkdir" version = "2.3.3" @@ -7393,11 +7841,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -7480,14 +7927,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] -name = "wasm-timer" -version = "0.2.5" -source = "git+https://github.com/mmsinclair/wasm-timer?rev=b9d1a54ad514c2f230a026afe0dde341e98cd7b6#b9d1a54ad514c2f230a026afe0dde341e98cd7b6" +name = "wasm-streams" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" dependencies = [ - "futures", + "futures-util", "js-sys", - "parking_lot 0.11.2", - "pin-utils", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -7506,6 +7952,20 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasmtimer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.12.1", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "web-sys" version = "0.3.60" @@ -7560,7 +8020,7 @@ dependencies = [ "pango-sys", "pkg-config", "soup2-sys", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -7699,11 +8159,11 @@ dependencies = [ [[package]] name = "windows" -version = "0.44.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.42.2", + "windows-targets", ] [[package]] @@ -7747,44 +8207,20 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", @@ -7947,9 +8383,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d" dependencies = [ "memchr", ] @@ -7964,19 +8400,20 @@ dependencies = [ ] [[package]] -name = "winres" -version = "0.1.12" +name = "winreg" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" +checksum = "76a1a57ff50e9b408431e8f97d5456f2807f8eb2a2cd79b06068fc87f8ecf189" dependencies = [ - "toml 0.5.11", + "cfg-if", + "winapi", ] [[package]] name = "wry" -version = "0.23.4" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c1ad8e2424f554cc5bdebe8aa374ef5b433feff817aebabca0389961fc7ef98" +checksum = "33748f35413c8a98d45f7a08832d848c0c5915501803d1faade5a4ebcd258cea" dependencies = [ "base64 0.13.1", "block", @@ -7998,7 +8435,7 @@ dependencies = [ "once_cell", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "soup2", "tao", "thiserror", @@ -8053,13 +8490,23 @@ dependencies = [ [[package]] name = "xattr" -version = "0.2.3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" dependencies = [ "libc", ] +[[package]] +name = "xdg-home" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +dependencies = [ + "nix", + "winapi", +] + [[package]] name = "yaml-rust" version = "0.4.5" @@ -8069,6 +8516,72 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "zbus" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "byteorder", + "derivative", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "once_cell", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "winapi", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zeroize" version = "1.6.0" @@ -8080,23 +8593,60 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "synstructure", + "syn 2.0.28", ] [[package]] name = "zip" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "byteorder", "crc32fast", "crossbeam-utils", ] + +[[package]] +name = "zvariant" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/nym-connect/desktop/Cargo.toml b/nym-connect/desktop/Cargo.toml index 3ee3f20be3..5322fc260c 100644 --- a/nym-connect/desktop/Cargo.toml +++ b/nym-connect/desktop/Cargo.toml @@ -1,2 +1,3 @@ [workspace] members = ["src-tauri"] +resolver = "2" diff --git a/nym-connect/desktop/package.json b/nym-connect/desktop/package.json index 626b415b8f..a69bc51486 100644 --- a/nym-connect/desktop/package.json +++ b/nym-connect/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@nym/nym-connect", - "version": "1.1.15", + "version": "1.1.19", "main": "index.js", "license": "MIT", "scripts": { @@ -47,7 +47,7 @@ "react-markdown": "^8.0.4", "react-router-dom": "^6.7.0", "semver": "^6.3.0", - "yup": "^0.32.9" + "yup": "^1.2.0" }, "devDependencies": { "@babel/core": "^7.15.0", diff --git a/nym-connect/desktop/src-tauri/Cargo.toml b/nym-connect/desktop/src-tauri/Cargo.toml index e3bd49cb41..133b94a921 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.15" +version = "1.1.19" description = "nym-connect" authors = ["Nym Technologies SA"] license = "" @@ -30,7 +30,8 @@ itertools = "0.10.5" log = { version = "0.4", features = ["serde"] } pretty_env_logger = "0.4.0" rand = "0.8" -reqwest = { version = "0.11", features = ["json", "socks"] } +rand-07 = { package = "rand", version = "0.7.3" } +reqwest = { version = "0.11.18", features = ["json", "socks"] } rust-embed = { version = "6.4.2", features = ["include-exclude"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -41,7 +42,7 @@ tauri = { version = "^1.2.2", features = ["clipboard-write-text", "macos-private thiserror = "1.0" time = { version = "0.3.17", features = ["local-offset"] } tokio = { version = "1.24.1", features = ["sync", "time"] } -url = "2.2" +url = "2.4" yaml-rust = "0.4" toml = "0.7" sentry = { version = "0.31.5", features = [ "anyhow" ] } @@ -58,9 +59,11 @@ nym-bin-common = { path = "../../../common/bin-common"} nym-socks5-client-core = { path = "../../../common/socks5-client-core" } nym-sphinx = { path = "../../../common/nymsphinx" } nym-task = { path = "../../../common/task" } +nym-topology = { path = "../../../common/topology" } +nym-validator-client = { path = "../../../common/client-libs/validator-client" } [dev-dependencies] -ts-rs = "6.1.2" +ts-rs = "7.0.0" tempfile = "3.3.0" [features] diff --git a/nym-connect/desktop/src-tauri/src/config/mod.rs b/nym-connect/desktop/src-tauri/src/config/mod.rs index f2cbff0d46..538b5e5fbb 100644 --- a/nym-connect/desktop/src-tauri/src/config/mod.rs +++ b/nym-connect/desktop/src-tauri/src/config/mod.rs @@ -28,8 +28,7 @@ mod template; mod upgrade; mod user_data; -pub use user_data::PrivacyLevel; -pub use user_data::UserData; +pub use user_data::*; static SOCKS5_CONFIG_ID: &str = "nym-connect"; @@ -183,13 +182,14 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str let details_store = OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details); let init_details = nym_client_core::init::setup_gateway( - &gateway_setup, + gateway_setup, &key_store, &details_store, register_gateway, Some(&config.core.base.client.nym_api_urls), ) - .await?; + .await? + .details; config.save_to_default_location().tap_err(|_| { log::error!("Failed to save the config file"); diff --git a/nym-connect/desktop/src-tauri/src/config/user_data.rs b/nym-connect/desktop/src-tauri/src/config/user_data.rs index 3e9287f375..e7ac9000b6 100644 --- a/nym-connect/desktop/src-tauri/src/config/user_data.rs +++ b/nym-connect/desktop/src-tauri/src/config/user_data.rs @@ -14,6 +14,18 @@ pub enum PrivacyLevel { Medium, } +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +pub struct SelectedGateway { + address: Option, + is_active: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Default)] +pub struct SelectedSp { + address: Option, + is_active: Option, +} + // User data is read from and write on disk // Linux: $XDG_DATA_HOME or $HOME/.local/share/ // macOS: $HOME/Library/Application Support @@ -22,6 +34,8 @@ pub enum PrivacyLevel { pub struct UserData { pub monitoring: Option, pub privacy_level: Option, + pub selected_gateway: Option, + pub selected_sp: Option, } fn create_directory_path() -> Result<()> { @@ -64,4 +78,16 @@ impl UserData { fs::write(data_path, toml)?; Ok(()) } + + pub fn clear(&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); + fs::write(data_path, vec![])?; + Ok(()) + } } diff --git a/nym-connect/desktop/src-tauri/src/error.rs b/nym-connect/desktop/src-tauri/src/error.rs index 651e741aea..1f3b38864b 100644 --- a/nym-connect/desktop/src-tauri/src/error.rs +++ b/nym-connect/desktop/src-tauri/src/error.rs @@ -45,6 +45,21 @@ pub enum BackendError { #[from] source: crate::operations::growth::api_client::ApiClientError, }, + #[error("{source}")] + EnvError { + #[from] + source: std::env::VarError, + }, + #[error("{source}")] + UrlError { + #[from] + source: url::ParseError, + }, + #[error("{source}")] + APIError { + #[from] + source: nym_validator_client::nym_api::error::NymAPIError, + }, #[error("could not send disconnect signal to the SOCKS5 client")] CoundNotSendDisconnectSignal, diff --git a/nym-connect/desktop/src-tauri/src/main.rs b/nym-connect/desktop/src-tauri/src/main.rs index 12c7ad06ac..9a3c8c7b6c 100644 --- a/nym-connect/desktop/src-tauri/src/main.rs +++ b/nym-connect/desktop/src-tauri/src/main.rs @@ -4,6 +4,7 @@ )] use std::env; +use std::path::PathBuf; use std::sync::Arc; use nym_config::defaults::setup_env; @@ -30,7 +31,8 @@ mod window; fn main() { dotenvy::dotenv().ok(); - setup_env(None); + + setup_env(env::args().nth(1).map(PathBuf::from).as_ref()); println!("Starting up..."); // As per breaking change description here @@ -72,6 +74,8 @@ fn main() { crate::operations::common::get_user_data, crate::operations::common::set_monitoring, crate::operations::common::set_privacy_level, + crate::operations::common::set_selected_gateway, + crate::operations::common::set_selected_sp, crate::operations::connection::connect::get_gateway, crate::operations::connection::connect::get_service_provider, crate::operations::connection::connect::set_gateway, @@ -82,8 +86,10 @@ fn main() { crate::operations::connection::status::get_connection_status, 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_gateway_with_low_latency, crate::operations::directory::get_gateways, + crate::operations::directory::get_services, + crate::operations::directory::select_gateway_with_low_latency_from_list, crate::operations::export::export_keys, crate::operations::window::hide_window, crate::operations::growth::test_and_earn::growth_tne_get_client_id, diff --git a/nym-connect/desktop/src-tauri/src/operations/common.rs b/nym-connect/desktop/src-tauri/src/operations/common.rs index a4e9604d60..1b8bd1d1db 100644 --- a/nym-connect/desktop/src-tauri/src/operations/common.rs +++ b/nym-connect/desktop/src-tauri/src/operations/common.rs @@ -1,4 +1,4 @@ -use crate::config::PrivacyLevel; +use crate::config::{PrivacyLevel, SelectedGateway, SelectedSp}; use crate::error::Result; use crate::{config::UserData, state::State}; use std::env; @@ -36,3 +36,21 @@ pub async fn set_privacy_level( let mut guard = state.write().await; guard.set_privacy_level(privacy_level) } + +#[tauri::command] +pub async fn set_selected_gateway( + gateway: Option, + state: tauri::State<'_, Arc>>, +) -> Result<()> { + let mut guard = state.write().await; + guard.set_user_selected_gateway(gateway) +} + +#[tauri::command] +pub async fn set_selected_sp( + service_provider: Option, + state: tauri::State<'_, Arc>>, +) -> Result<()> { + let mut guard = state.write().await; + guard.set_user_selected_sp(service_provider) +} 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 52b47057b1..8344b5805b 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 @@ -1,6 +1,8 @@ +use crate::operations::directory::WELLKNOWN_DIR; +use nym_config::defaults::var_names::NETWORK_NAME; use serde::{Deserialize, Serialize}; -static HEALTH_CHECK_URL: &str = "https://nymtech.net/.wellknown/connect/healthcheck.json"; +static HEALTH_CHECK_URL: &str = "connect/healthcheck.json"; #[derive(Serialize, Deserialize, Debug)] struct ConnectionSuccess { @@ -9,7 +11,9 @@ struct ConnectionSuccess { pub async fn run_health_check() -> bool { log::info!("Running network health check"); - match crate::operations::http::socks5_get::<_, ConnectionSuccess>(HEALTH_CHECK_URL).await { + let network_name = std::env::var(NETWORK_NAME).expect("network name not set"); + let url = format!("{}/{}/{}", WELLKNOWN_DIR, network_name, HEALTH_CHECK_URL); + match crate::operations::http::socks5_get::<_, ConnectionSuccess>(url).await { Ok(res) if res.status == "ok" => { log::info!("✅✅✅ Healthcheck success!"); true 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 a4f8bf53a9..05e0b9de6e 100644 --- a/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs +++ b/nym-connect/desktop/src-tauri/src/operations/directory/mod.rs @@ -8,16 +8,22 @@ use crate::{ }; use itertools::Itertools; use nym_api_requests::models::GatewayBondAnnotated; +use nym_bin_common::version_checker::is_minor_version_compatible; +use nym_config::defaults::var_names::{NETWORK_NAME, NYM_API}; use nym_contracts_common::types::Percent; +use nym_topology::gateway; +use nym_validator_client::nym_api::Client as ApiClient; +use std::str::FromStr; use std::sync::Arc; use tokio::sync::RwLock; +use url::Url; -static SERVICE_PROVIDER_WELLKNOWN_URL: &str = - "https://nymtech.net/.wellknown/connect/service-providers.json"; +pub(crate) static WELLKNOWN_DIR: &str = "https://nymtech.net/.wellknown"; + +static SERVICE_PROVIDER_URL_PATH: &str = "connect/service-providers.json"; // List of network-requesters running with medium toggle enabled, for testing -static SERVICE_PROVIDER_WELLKNOWN_URL_MEDIUM: &str = - "https://nymtech.net/.wellknown/connect/service-providers-medium.json"; +static SERVICE_PROVIDER_MEDIUM_URL_PATH: &str = "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"; @@ -25,9 +31,6 @@ static HARBOUR_MASTER_URL: &str = "https://harbourmaster.nymtech.net/v1/services // 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"; - // Only use gateways with a performnnce score above this const GATEWAY_PERFORMANCE_SCORE_THRESHOLD: u64 = 90; @@ -80,11 +83,13 @@ pub async fn get_services( 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, + PrivacyLevel::Medium => SERVICE_PROVIDER_MEDIUM_URL_PATH, + _ => SERVICE_PROVIDER_URL_PATH, }; - let services_res = reqwest::get(services_url) + let network_name = std::env::var(NETWORK_NAME)?; + let url = format!("{}/{}/{}", WELLKNOWN_DIR, network_name, services_url); + let services_res = reqwest::get(url) .await? .json::>() .await?; @@ -116,31 +121,61 @@ fn filter_out_inactive_services( } async fn fetch_gateways() -> Result> { - Ok(reqwest::get(GATEWAYS_DETAILED_URL) - .await? - .json::>() - .await?) + let api_client = ApiClient::new(Url::from_str(&std::env::var(NYM_API)?)?); + let gateways = api_client.get_gateways_detailed().await?; + let our_version = env!("CARGO_PKG_VERSION"); + log::debug!( + "Our version that we use to filter compatible gateways: {}", + our_version + ); + let gateways = gateways + .into_iter() + .filter(|g| is_minor_version_compatible(&g.gateway_bond.gateway.version, our_version)) + .collect(); + Ok(gateways) } +fn filter_out_low_performance_gateways( + gateways: Vec, +) -> Vec { + gateways + .into_iter() + .filter(|g| { + g.node_performance.most_recent + > Percent::from_percentage_value(GATEWAY_PERFORMANCE_SCORE_THRESHOLD).unwrap() + }) + .collect() +} + +async fn select_gateway_by_latency(gateways: Vec) -> Result { + let gateways_as_nodes: Vec = gateways + .into_iter() + .filter_map(|g| g.gateway_bond.try_into().ok()) + .collect(); + + let mut rng = rand_07::rngs::OsRng; + let selected_gateway = + nym_client_core::init::helpers::choose_gateway_by_latency(&mut rng, &gateways_as_nodes) + .await?; + Ok(selected_gateway) +} + +// Get all gateways satisfying the performance threshold. #[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() - }) + let gateways_filtered = filter_out_low_performance_gateways(all_gateways.clone()) + .into_iter() .map(|g| Gateway { identity: g.identity().clone(), }) .collect_vec(); - log::trace!("Filtered: {:#?}", filtered_gateways); + log::trace!("Filtered: {:#?}", gateways_filtered); - if filtered_gateways.is_empty() { + if gateways_filtered.is_empty() { log::warn!("No gateways with high enough performance score found! Using all gateways instead as fallback"); return Ok(all_gateways .iter() @@ -150,5 +185,38 @@ pub async fn get_gateways() -> Result> { .collect_vec()); } - Ok(filtered_gateways) + Ok(gateways_filtered) +} + +// Lookup and select a single gateway with low latency. +#[tauri::command] +pub async fn get_gateway_with_low_latency() -> Result { + log::trace!("Fetching gateways"); + let all_gateways = fetch_gateways().await?; + log::trace!("Received: {:#?}", all_gateways); + + let gateways_filtered = filter_out_low_performance_gateways(all_gateways); + let selected_gateway = select_gateway_by_latency(gateways_filtered).await?; + log::debug!("Selected gateway: {}", selected_gateway); + Ok(Gateway { + identity: selected_gateway.identity().to_base58_string(), + }) +} + +// From a given list of gateways, select the one with low latency. +#[tauri::command] +pub async fn select_gateway_with_low_latency_from_list(gateways: Vec) -> Result { + log::debug!("Selecting a gateway with low latency"); + let gateways = gateways.into_iter().map(|g| g.identity).collect_vec(); + let all_gateways = fetch_gateways().await?; + let gateways_union_set: Vec = all_gateways + .into_iter() + .filter(|g| gateways.contains(g.identity())) + .collect(); + let gateways_filtered = filter_out_low_performance_gateways(gateways_union_set); + let selected_gateway = select_gateway_by_latency(gateways_filtered).await?; + log::debug!("Selected gateway: {}", selected_gateway); + Ok(Gateway { + identity: selected_gateway.identity().to_base58_string(), + }) } diff --git a/nym-connect/desktop/src-tauri/src/operations/help/storage.rs b/nym-connect/desktop/src-tauri/src/operations/help/storage.rs index ab47ffa529..7e7ca7e3d9 100644 --- a/nym-connect/desktop/src-tauri/src/operations/help/storage.rs +++ b/nym-connect/desktop/src-tauri/src/operations/help/storage.rs @@ -1,6 +1,9 @@ -use crate::error::BackendError; +use std::sync::Arc; + +use crate::{error::BackendError, state::State}; use serde::Serialize; use tauri::Manager; +use tokio::sync::RwLock; #[derive(Debug, Serialize, Clone)] struct ClearStorageEvent { @@ -9,12 +12,15 @@ struct ClearStorageEvent { #[tauri::command] pub fn help_clear_storage(app_handle: tauri::AppHandle) -> Result<(), BackendError> { - log::info!("Sending event to clear local storage..."); + log::info!("Clearing user data"); - let event = ClearStorageEvent { - kind: "local_storage".to_string(), - }; - app_handle.emit_all("help://clear-storage", event)?; + let state = app_handle.try_state::>>(); + if let Some(s) = state { + let mut guard = s.blocking_write(); + guard.clear_user_data().ok(); + } else { + log::warn!("fail to retrieve the state, user data has not been cleared"); + } Ok(()) } diff --git a/nym-connect/desktop/src-tauri/src/state.rs b/nym-connect/desktop/src-tauri/src/state.rs index 044b185b86..f9abbe2b8f 100644 --- a/nym-connect/desktop/src-tauri/src/state.rs +++ b/nym-connect/desktop/src-tauri/src/state.rs @@ -9,6 +9,8 @@ use tokio::time::Instant; use crate::config::Config; use crate::config::PrivacyLevel; +use crate::config::SelectedGateway; +use crate::config::SelectedSp; use crate::config::UserData; use crate::{ config::{self, socks5_config_id_appended_with}, @@ -25,7 +27,7 @@ use crate::{ // certain duration then we assume it's all good. const GATEWAY_CONNECTIVITY_TIMEOUT_SECS: u64 = 20; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum GatewayConnectivity { Good, Bad { when: Instant }, @@ -48,6 +50,7 @@ impl TryFrom<&ClientCoreStatusMessage> for GatewayConnectivity { } } +#[derive(Debug)] pub struct State { /// The current connection status status: ConnectionStatusKind, @@ -104,6 +107,13 @@ impl State { &self.user_data } + pub fn clear_user_data(&mut self) -> Result<()> { + self.user_data.clear().map_err(|e| { + error!("Failed to clear user data {e}"); + BackendError::UserDataWriteError + }) + } + pub fn set_monitoring(&mut self, enabled: bool) -> Result<()> { self.user_data.monitoring = Some(enabled); self.user_data.write().map_err(|e| { @@ -120,6 +130,22 @@ impl State { }) } + pub fn set_user_selected_gateway(&mut self, gateway: Option) -> Result<()> { + self.user_data.selected_gateway = gateway; + self.user_data.write().map_err(|e| { + error!("Failed to write user data to disk {e}"); + BackendError::UserDataWriteError + }) + } + + pub fn set_user_selected_sp(&mut self, service_provider: Option) -> Result<()> { + self.user_data.selected_sp = service_provider; + 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 } diff --git a/nym-connect/desktop/src-tauri/src/tasks.rs b/nym-connect/desktop/src-tauri/src/tasks.rs index 8c69b1be7e..b18cb8277d 100644 --- a/nym-connect/desktop/src-tauri/src/tasks.rs +++ b/nym-connect/desktop/src-tauri/src/tasks.rs @@ -1,17 +1,20 @@ use futures::{channel::mpsc, StreamExt}; -use nym_client_core::client::base_client::storage::gateway_details::GatewayDetailsStore; -use nym_client_core::client::base_client::storage::{MixnetClientStorage, OnDiskPersistent}; -use nym_client_core::{config::GatewayEndpointConfig, error::ClientCoreStatusMessage}; -use nym_socks5_client_core::NymClient as Socks5NymClient; -use nym_socks5_client_core::Socks5ControlMessageSender; +use nym_client_core::{ + client::base_client::storage::{ + gateway_details::GatewayDetailsStore, MixnetClientStorage, OnDiskPersistent, + }, + config::{GatewayEndpointConfig, GroupBy, TopologyStructure}, + error::ClientCoreStatusMessage, +}; +use nym_socks5_client_core::{NymClient as Socks5NymClient, Socks5ControlMessageSender}; use nym_sphinx::params::PacketSize; use nym_task::manager::TaskStatus; use std::sync::Arc; use tap::TapFallible; use tokio::sync::RwLock; -use crate::config::{Config, PrivacyLevel}; use crate::{ + config::{Config, PrivacyLevel}, error::Result, events::{self, emit_event, emit_status_event}, models::{ConnectionStatusKind, ConnectivityTestResult}, @@ -46,6 +49,19 @@ fn override_config_from_env(config: &mut Config, privacy_level: &PrivacyLevel) { log::warn!("Disabling per-hop delay"); config.core.base.set_no_per_hop_delays(); + + // TODO: selectable in the UI + let address = config + .core + .socks5 + .provider_mix_address + .parse() + .expect("failed to parse provider mix address"); + log::warn!("Using geo-aware mixnode selection based on the location of: {address}"); + config + .core + .base + .set_topology_structure(TopologyStructure::GeoAware(GroupBy::NymAddress(address))); } } diff --git a/nym-connect/desktop/src-tauri/tauri.conf.json b/nym-connect/desktop/src-tauri/tauri.conf.json index b95859ca53..555daaa1b7 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.15" + "version": "1.1.19" }, "build": { "distDir": "../dist", @@ -51,7 +51,7 @@ "updater": { "active": true, "endpoints": [ - "https://nymtech.net/.wellknown/connect/updater.json" + "https://nymtech.net/.wellknown/mainnet/connect/updater.json" ], "dialog": true, "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IENCNzQ2M0E5N0VFODE2NApSV1JrZ2U2WE9rYTNETTg1OTBKdE5uWUEra0hML2syOVUvQ2lxZmFZRzZ1T3NWbGM0eVRzUTVhVwo=" diff --git a/nym-connect/desktop/src/components/AppWindowFrame.tsx b/nym-connect/desktop/src/components/AppWindowFrame.tsx index 90e01fffa7..ca49204e11 100644 --- a/nym-connect/desktop/src/components/AppWindowFrame.tsx +++ b/nym-connect/desktop/src/components/AppWindowFrame.tsx @@ -16,7 +16,7 @@ export const AppWindowFrame: FCWithChildren = ({ children }) => { return () => { // when the user moves away from the settings page and the gateway is not valid // set isActive to false - if (!userDefinedGateway?.gateway) { + if (!userDefinedGateway?.address) { setUserDefinedGateway((current) => ({ ...current, isActive: false })); } }; diff --git a/nym-connect/desktop/src/context/main.tsx b/nym-connect/desktop/src/context/main.tsx index 46d3312fac..2f553b5153 100644 --- a/nym-connect/desktop/src/context/main.tsx +++ b/nym-connect/desktop/src/context/main.tsx @@ -6,15 +6,11 @@ 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, PrivacyLevel, UserData } from '../types'; import { ConnectionStatsItem } from '../components/ConnectionStats'; 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'; - type ModeType = 'light' | 'dark'; export type TClientContext = { @@ -33,6 +29,7 @@ export type TClientContext = { userDefinedSPAddress: UserDefinedSPAddress; serviceProviders?: ServiceProvider[]; gateways?: Gateway[]; + showFeedbackNote: boolean; setMode: (mode: ModeType) => void; clearError: () => void; setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void; @@ -47,6 +44,7 @@ export type TClientContext = { setUserDefinedSPAddress: React.Dispatch>; setMonitoring: (value: boolean) => Promise; setPrivacyLevel: (value: PrivacyLevel) => Promise; + setShowFeedbackNote: (value: boolean) => void; }; function getRandomFromList(items: T[]): T { @@ -70,13 +68,14 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { const [showInfoModal, setShowInfoModal] = useState(false); const [userDefinedGateway, setUserDefinedGateway] = useState({ isActive: false, - gateway: undefined, + address: undefined, }); const [userDefinedSPAddress, setUserDefinedSPAddress] = useState({ isActive: false, address: undefined, }); const [userData, setUserData] = useState(); + const [showFeedbackNote, setShowFeedbackNote] = useState(true); const getAppVersion = async () => { const version = await getVersion(); @@ -89,6 +88,15 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { data.privacy_level = 'High'; } setUserData(data); + if (data.selected_gateway) { + setUserDefinedGateway({ + address: data.selected_gateway.address, + isActive: data.selected_gateway.is_active || false, + }); + } + if (data.selected_sp) { + setUserDefinedSPAddress({ address: data.selected_sp.address, isActive: data.selected_sp.is_active || false }); + } return data; }; @@ -104,30 +112,31 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { }, []); useEffect(() => { - setItemInStorage({ key: FORAGE_GATEWAY_KEY, value: userDefinedGateway }); + const saveUserGateway = async () => { + await invoke('set_selected_gateway', { + gateway: { address: userDefinedGateway.address, is_active: userDefinedGateway.isActive }, + }); + }; + saveUserGateway(); }, [userDefinedGateway]); useEffect(() => { - setItemInStorage({ key: FORAGE_SP_KEY, value: userDefinedSPAddress }); + const saveUserServiceProvider = async () => { + await invoke('set_selected_sp', { + serviceProvider: { address: userDefinedSPAddress.address, is_active: userDefinedSPAddress.isActive }, + }); + }; + saveUserServiceProvider(); }, [userDefinedSPAddress]); const initialiseApp = async () => { 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(fetchedServices); setGateways(fetchedGateways); - - if (storedUserDefinedGateway) { - setUserDefinedGateway(storedUserDefinedGateway); - } - if (storedUserDefinedSP) { - setUserDefinedSPAddress(storedUserDefinedSP); - } }; useEvents({ @@ -161,16 +170,22 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { } }, []); + const afterDisconnection = useCallback(async () => { + setGatewayPerformance('Good'); + setConnectedSince(undefined); + }, []); + const startDisconnecting = useCallback(async () => { try { await invoke('start_disconnecting'); + afterDisconnection(); } catch (e) { console.log(e); Sentry.captureException(e); } }, []); - const shouldUseUserGateway = !!userDefinedGateway.gateway && userDefinedGateway.isActive; + const shouldUseUserGateway = !!userDefinedGateway.address && userDefinedGateway.isActive; const shouldUseUserSP = !!userDefinedSPAddress.address && userDefinedSPAddress.isActive; const buildServiceProvider = async (serviceProvider: ServiceProvider) => { @@ -181,7 +196,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { const buildGateway = async (gateway: Gateway) => { const gw = { ...gateway }; - if (shouldUseUserGateway) gw.identity = userDefinedGateway.gateway as string; + if (shouldUseUserGateway) gw.identity = userDefinedGateway.address as string; return gw; }; @@ -199,10 +214,15 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { const setGateway = async () => { if (gateways) { - const randomGateway = getRandomFromList(gateways); + let randomGateway; + if (userData?.privacy_level === 'Medium') { + randomGateway = await invoke('select_gateway_with_low_latency_from_list', { gateways }); + } else { + randomGateway = getRandomFromList(gateways); + } const withUserDefinitions = await buildGateway(randomGateway); await invoke('set_gateway', { - gateway: shouldUseUserGateway ? userDefinedGateway.gateway : withUserDefinitions.identity, + gateway: shouldUseUserGateway ? userDefinedGateway.address : withUserDefinitions.identity, }); setSelectedGateway(withUserDefinitions); } @@ -240,6 +260,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { serviceProviders, connectedSince, userData, + showFeedbackNote, setConnectedSince, setServiceProvider, setGateway, @@ -253,6 +274,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { setUserDefinedSPAddress, setMonitoring, setPrivacyLevel, + setShowFeedbackNote, }), [ mode, @@ -270,6 +292,7 @@ export const ClientContextProvider: FCWithChildren = ({ children }) => { userDefinedGateway, userDefinedSPAddress, userData, + showFeedbackNote, ], ); diff --git a/nym-connect/desktop/src/context/mocks/main.tsx b/nym-connect/desktop/src/context/mocks/main.tsx index 46a5f50298..02de84aa69 100644 --- a/nym-connect/desktop/src/context/mocks/main.tsx +++ b/nym-connect/desktop/src/context/mocks/main.tsx @@ -9,9 +9,10 @@ const mockValues: TClientContext = { selectedProvider: { id: '1', description: 'Keybase service provider', address: '123abc' }, gatewayPerformance: 'Good', showInfoModal: false, - userDefinedGateway: { isActive: false, gateway: '' }, + userDefinedGateway: { isActive: false, address: '' }, userDefinedSPAddress: { isActive: false, address: '' }, userData: { monitoring: false, privacy_level: 'High' }, + showFeedbackNote: false, setShowInfoModal: () => {}, setMode: () => {}, clearError: () => {}, @@ -26,6 +27,7 @@ const mockValues: TClientContext = { setUserDefinedSPAddress: () => {}, setMonitoring: async () => {}, setPrivacyLevel: async () => {}, + setShowFeedbackNote: () => {}, }; export const MockProvider: FCWithChildren<{ diff --git a/nym-connect/desktop/src/pages/connection/Disconnected.tsx b/nym-connect/desktop/src/pages/connection/Disconnected.tsx index 127a245d4e..00e51b9221 100644 --- a/nym-connect/desktop/src/pages/connection/Disconnected.tsx +++ b/nym-connect/desktop/src/pages/connection/Disconnected.tsx @@ -1,5 +1,6 @@ import React from 'react'; -import { Stack, Typography } from '@mui/material'; +import { Alert, Link, Stack, Typography } from '@mui/material'; +import { Link as RouterLink } from 'react-router-dom'; import { ConnectionStatus } from 'src/components/ConnectionStatus'; import { ConnectionTimer } from 'src/components/ConntectionTimer'; import { InfoModal } from 'src/components/InfoModal'; @@ -10,6 +11,7 @@ import { ConnectionStatusKind } from 'src/types'; import { PowerButton } from 'src/components/PowerButton/PowerButton'; import { Box } from '@mui/system'; import { ConnectionLayout } from 'src/layouts/ConnectionLayout'; +import { useClientContext } from '../../context/main'; export const Disconnected: FCWithChildren<{ error?: Error; @@ -20,32 +22,53 @@ export const Disconnected: FCWithChildren<{ serviceProvider?: ServiceProvider; clearError: () => void; onConnectClick: (status: ConnectionStatusKind) => void; -}> = ({ status, error, onConnectClick, clearError }) => ( - <> - {error && } - - - - - } - ConnectButton={} - BottomContent={ - - - You are not protected - - - - } - /> - -); +}> = ({ status, error, onConnectClick, clearError }) => { + const { showFeedbackNote, setShowFeedbackNote } = useClientContext(); + + return ( + <> + {error && } + + + + + } + ConnectButton={} + BottomContent={ + + + You are not protected + + + {showFeedbackNote ? ( + setShowFeedbackNote(false)}> + Help improve NymConnect +
+ setShowFeedbackNote(false)} + component={RouterLink} + color="secondary" + underline="hover" + > + Send feedback + +
+ ) : ( + + )} +
+ } + /> + + ); +}; diff --git a/nym-connect/desktop/src/pages/connection/index.tsx b/nym-connect/desktop/src/pages/connection/index.tsx index 2f7ebc5899..725bfb23de 100644 --- a/nym-connect/desktop/src/pages/connection/index.tsx +++ b/nym-connect/desktop/src/pages/connection/index.tsx @@ -1,9 +1,7 @@ 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'; import { Connected } from './Connected'; import { Disconnected } from './Disconnected'; @@ -11,17 +9,6 @@ export const ConnectionPage = () => { const context = useClientContext(); const [busy, setBusy] = React.useState(); - useTauriEvents('help://clear-storage', (_event) => { - console.log('About to clear local storage...'); - // clear local storage - try { - forage.clear()(); - console.log('Local storage cleared'); - } catch (e) { - console.error('Failed to clear local storage', e); - } - }); - const handleConnectClick = async () => { const currentStatus = context.connectionStatus; if (currentStatus === 'connected' || currentStatus === 'disconnected') { diff --git a/nym-connect/desktop/src/pages/menu/settings/PrivacyLevelSettings.tsx b/nym-connect/desktop/src/pages/menu/PrivacyLevelSettings.tsx similarity index 80% rename from nym-connect/desktop/src/pages/menu/settings/PrivacyLevelSettings.tsx rename to nym-connect/desktop/src/pages/menu/PrivacyLevelSettings.tsx index 5c2241b4d0..81582a2797 100644 --- a/nym-connect/desktop/src/pages/menu/settings/PrivacyLevelSettings.tsx +++ b/nym-connect/desktop/src/pages/menu/PrivacyLevelSettings.tsx @@ -1,10 +1,11 @@ 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'; +import { useClientContext } from '../../context/main'; +import { ConnectionStatusKind } from '../../types'; export const PrivacyLevelSettings = () => { - const { userData, setPrivacyLevel } = useClientContext(); + const { userData, setPrivacyLevel, connectionStatus } = useClientContext(); const [speedBoost, setSpeedBoost] = useState(userData?.privacy_level !== 'High'); const [loading, setLoading] = useState(false); @@ -29,7 +30,7 @@ export const PrivacyLevelSettings = () => { @@ -37,8 +38,8 @@ export const PrivacyLevelSettings = () => { label="Enable" /> - By activating this option, the connection speed will be relatively faster in exchange of relaxing some - protections + By activating this option, the connection speed will be relatively faster in exchange for relaxing some + privacy protections diff --git a/nym-connect/desktop/src/pages/menu/index.tsx b/nym-connect/desktop/src/pages/menu/index.tsx index 67576dc88c..285d368061 100644 --- a/nym-connect/desktop/src/pages/menu/index.tsx +++ b/nym-connect/desktop/src/pages/menu/index.tsx @@ -8,8 +8,8 @@ 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' }, + { title: 'Help improve the app', icon: BugReport, path: 'reporting' }, ]; export const Menu = () => ( diff --git a/nym-connect/desktop/src/pages/menu/settings/MonitoringSettings.tsx b/nym-connect/desktop/src/pages/menu/reporting/ErrorReporting.tsx similarity index 62% rename from nym-connect/desktop/src/pages/menu/settings/MonitoringSettings.tsx rename to nym-connect/desktop/src/pages/menu/reporting/ErrorReporting.tsx index 8b899cbe72..baf465944c 100644 --- a/nym-connect/desktop/src/pages/menu/settings/MonitoringSettings.tsx +++ b/nym-connect/desktop/src/pages/menu/reporting/ErrorReporting.tsx @@ -1,9 +1,8 @@ import React, { ChangeEvent, useState } from 'react'; -import { Warning as WarningIcon } from '@mui/icons-material'; import { Box, FormControl, FormControlLabel, FormHelperText, Stack, Switch, Typography } from '@mui/material'; -import { useClientContext } from 'src/context/main'; +import { useClientContext } from '../../../context/main'; -export const MonitoringSettings = () => { +export const ErrorReporting = () => { const { userData, setMonitoring } = useClientContext(); const [enabled, setEnabled] = useState(userData?.monitoring || false); const [loading, setLoading] = useState(false); @@ -20,7 +19,7 @@ export const MonitoringSettings = () => { - Error reporting and performance monitoring + Turn on error reporting and performance monitoring { sx={{ ml: 1, mr: 1 }} /> } - label="Enable" + label={enabled ? 'On' : 'Off'} /> - Help Nym developers to fix errors, crashes and improve the application by enabling this option. If errors - occur or if the app crashes, it will automatically send a report. Also it tracks various performance - metrics. We use sentry.io service to handle this. + Help Nym developers fix errors, crashes and improve the application by enabling this option. If errors + occur or if the app crashes, it will automatically send a report. It also tracks various performance + metrics. We use Sentry.io service to handle this. - - - - You must restart the application for the change to take effect. - - + + You must restart the application for the change to take effect. + diff --git a/nym-connect/desktop/src/pages/menu/reporting/UserFeedback.tsx b/nym-connect/desktop/src/pages/menu/reporting/UserFeedback.tsx new file mode 100644 index 0000000000..67b37fcc5f --- /dev/null +++ b/nym-connect/desktop/src/pages/menu/reporting/UserFeedback.tsx @@ -0,0 +1,124 @@ +import React, { useState } from 'react'; +import { Link as RouterLink } from 'react-router-dom'; +import { Alert, Box, Button, FormControl, Link, Snackbar, Stack, TextField, Typography } from '@mui/material'; +import * as Sentry from '@sentry/react'; +import { Controller, SubmitHandler, useForm } from 'react-hook-form'; +import { object, string } from 'yup'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { useClientContext } from '../../../context/main'; + +type FormValues = { + email?: string; + feedback: string; +}; + +const schema = object({ + email: string().email(), + feedback: string().required().min(20).max(512), +}).required(); + +export const UserFeedback = () => { + const [isBusy, setIsBusy] = useState(false); + const { userData } = useClientContext(); + + const { + handleSubmit, + control, + formState: { errors }, + reset, + } = useForm({ + defaultValues: { + email: '', + feedback: '', + }, + // inferred type is fucked, so use `any` to make TS happy + resolver: yupResolver(schema as any), + }); + + const onSubmit: SubmitHandler = (data) => { + const eventId = Sentry.captureMessage('user feedback'); + Sentry.captureUserFeedback({ + event_id: eventId, + name: 'nym', + email: data.email, + comments: data.feedback, + }); + setIsBusy(true); + reset(); + }; + + const handleClose = () => { + setIsBusy(false); + }; + + if (!userData?.monitoring) { + return ( + + + The error reporting option must be enabled in order to report feedback. Turn it on{' '} + + here + + . + + + ); + } + + return ( + + + + Feedback sent successfuly + + + + + + Send us your feedback about the app + +
+ + ( + + )} + name="email" + control={control} + /> + + + ( + + )} + name="feedback" + control={control} + /> + + + + + +
+
+
+ ); +}; diff --git a/nym-connect/mobile/src/pages/menu/settings/index.tsx b/nym-connect/desktop/src/pages/menu/reporting/index.tsx similarity index 77% rename from nym-connect/mobile/src/pages/menu/settings/index.tsx rename to nym-connect/desktop/src/pages/menu/reporting/index.tsx index b8d03d1374..ccc2c0bd1f 100644 --- a/nym-connect/mobile/src/pages/menu/settings/index.tsx +++ b/nym-connect/desktop/src/pages/menu/reporting/index.tsx @@ -4,13 +4,13 @@ import { Link, List, ListItem, ListItemButton, ListItemText, Stack } from '@mui/ import { AppVersion } from 'src/components/AppVersion'; const menuSchema = [ - { title: 'Select your gateway', path: 'gateway' }, - { title: 'Select a service provider', path: 'service-provider' }, + { title: 'Turn on error reporting', path: 'error-reporting' }, + { title: 'Send us your feedback', path: 'user-feedback' }, ]; -export const SettingsMenu = () => ( +export const ReportingMenu = () => ( - + {menuSchema.map((item) => ( diff --git a/nym-connect/desktop/src/pages/menu/settings/GatewaySettings.tsx b/nym-connect/desktop/src/pages/menu/settings/GatewaySettings.tsx index 80efa7988a..c4de721ef8 100644 --- a/nym-connect/desktop/src/pages/menu/settings/GatewaySettings.tsx +++ b/nym-connect/desktop/src/pages/menu/settings/GatewaySettings.tsx @@ -7,7 +7,7 @@ import { AppVersion } from 'src/components/AppVersion'; export const GatewaySettings = () => { const { userDefinedGateway, setUserDefinedGateway, connectionStatus } = useClientContext(); - const [gatewayKey, setGatewayKey] = useState(userDefinedGateway?.gateway); + const [gatewayKey, setGatewayKey] = useState(userDefinedGateway?.address); const handleIsValidGatewayKey = (isValid: boolean) => { let gateway: string | undefined; @@ -16,7 +16,7 @@ export const GatewaySettings = () => { gateway = gatewayKey; } - setUserDefinedGateway((current) => ({ ...current, gateway })); + setUserDefinedGateway((current) => ({ ...current, address: gateway })); }; const handleChange = (e: ChangeEvent) => { diff --git a/nym-connect/desktop/src/routes/index.tsx b/nym-connect/desktop/src/routes/index.tsx index f1e4b1da67..67bd471460 100644 --- a/nym-connect/desktop/src/routes/index.tsx +++ b/nym-connect/desktop/src/routes/index.tsx @@ -8,9 +8,11 @@ import { HelpGuide } from 'src/pages/menu/Guide'; 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 { ErrorReporting } from '../pages/menu/reporting/ErrorReporting'; +import { PrivacyLevelSettings } from '../pages/menu/PrivacyLevelSettings'; import { useClientContext } from '../context/main'; +import { ReportingMenu } from '../pages/menu/reporting'; +import { UserFeedback } from '../pages/menu/reporting/UserFeedback'; const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes); @@ -27,12 +29,16 @@ export const AppRoutes = () => { } /> } /> } /> - } /> } /> } /> } /> + + } /> + } /> + } /> + ); diff --git a/nym-connect/desktop/src/types/service-provider.ts b/nym-connect/desktop/src/types/service-provider.ts index ed370e2755..21cfee1174 100644 --- a/nym-connect/desktop/src/types/service-provider.ts +++ b/nym-connect/desktop/src/types/service-provider.ts @@ -1,6 +1,6 @@ export interface UserDefinedGateway { isActive: boolean; - gateway?: string; + address?: string; } export interface UserDefinedSPAddress { diff --git a/nym-connect/desktop/src/types/storage.ts b/nym-connect/desktop/src/types/storage.ts deleted file mode 100644 index ca792b65d9..0000000000 --- a/nym-connect/desktop/src/types/storage.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface StorageKeyValue { - key: string; - value: T; -} diff --git a/nym-connect/desktop/src/types/user-data.ts b/nym-connect/desktop/src/types/user-data.ts index 8bd43a941e..db09b32b74 100644 --- a/nym-connect/desktop/src/types/user-data.ts +++ b/nym-connect/desktop/src/types/user-data.ts @@ -1,6 +1,18 @@ export type PrivacyLevel = 'High' | 'Medium'; +export type SelectedGateway = { + address?: string; + is_active?: boolean; +}; + +export type SelectedSp = { + address?: string; + is_active?: boolean; +}; + export type UserData = { monitoring?: boolean; privacy_level?: PrivacyLevel; + selected_gateway?: SelectedGateway; + selected_sp?: SelectedSp; }; diff --git a/nym-connect/desktop/src/utils/index.ts b/nym-connect/desktop/src/utils/index.ts index 04b415c026..40aba86f80 100644 --- a/nym-connect/desktop/src/utils/index.ts +++ b/nym-connect/desktop/src/utils/index.ts @@ -1,48 +1,5 @@ -import { useEffect, useRef } from 'react'; -import { EventName, listen, UnlistenFn, EventCallback } from '@tauri-apps/api/event'; import { invoke } from '@tauri-apps/api'; -import { forage } from '@tauri-apps/tauri-forage'; -import { StorageKeyValue } from 'src/types/storage'; - -export const useTauriEvents = (event: EventName, handler: EventCallback) => { - const unlisten = useRef(); - - // list for events to clear local storage - useEffect(() => { - listen(event, handler).then((fn) => { - unlisten.current = fn; - }); - - return () => { - if (unlisten.current) { - unlisten.current(); - } - }; - }, []); -}; export const toggleLogViewer = async () => { await invoke('help_log_toggle_window'); }; - -export async function setItemInStorage({ key, value }: StorageKeyValue) { - try { - await forage.setItem({ - key, - value, - } as any)(); - } catch (e) { - console.warn(e); - } - return undefined; -} - -export const getItemFromStorage = async ({ key }: Pick, 'key'>) => { - try { - const gatewayFromStorage = await forage.getItem({ key })(); - return gatewayFromStorage; - } catch (e) { - console.warn(e); - } - return undefined; -}; diff --git a/nym-connect/mobile/.babelrc b/nym-connect/mobile/.babelrc deleted file mode 100644 index 7a840ad24e..0000000000 --- a/nym-connect/mobile/.babelrc +++ /dev/null @@ -1,15 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "esmodules": true - } - } - ], - "@babel/preset-react", - "@babel/preset-typescript" - ], - "plugins": ["@babel/plugin-transform-async-to-generator"] -} diff --git a/nym-connect/mobile/.env.sample b/nym-connect/mobile/.env.sample deleted file mode 100644 index cc3febee19..0000000000 --- a/nym-connect/mobile/.env.sample +++ /dev/null @@ -1 +0,0 @@ -ADMIN_ADDRESS= \ No newline at end of file diff --git a/nym-connect/mobile/.eslintrc b/nym-connect/mobile/.eslintrc deleted file mode 100644 index 3ea1281af1..0000000000 --- a/nym-connect/mobile/.eslintrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": [ - "@nymproject/eslint-config-react-typescript" - ], - "parserOptions": { - "project": "./tsconfig.eslint.json" - } -} diff --git a/nym-connect/mobile/.nvmrc b/nym-connect/mobile/.nvmrc deleted file mode 100644 index 25bf17fc5a..0000000000 --- a/nym-connect/mobile/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -18 \ No newline at end of file diff --git a/nym-connect/mobile/.prettierrc b/nym-connect/mobile/.prettierrc deleted file mode 100644 index 1bc34d0c3f..0000000000 --- a/nym-connect/mobile/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "trailingComma": "all", - "singleQuote": true, - "printWidth": 120, - "tabWidth": 2, - "semi": true -} diff --git a/nym-connect/mobile/.storybook/main.js b/nym-connect/mobile/.storybook/main.js deleted file mode 100644 index 344c27778e..0000000000 --- a/nym-connect/mobile/.storybook/main.js +++ /dev/null @@ -1,73 +0,0 @@ -/* eslint-disable no-param-reassign */ -const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); -const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); - -module.exports = { - stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], - addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'], - framework: '@storybook/react', - core: { - builder: 'webpack5', - }, - typescript: { reactDocgen: false }, - // webpackFinal: async (config, { configType }) => { - // // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION' - // // You can change the configuration based on that. - // // 'PRODUCTION' is used when building the static version of storybook. - webpackFinal: async (config) => { - config.module.rules.forEach((rule) => { - // look for SVG import rule and replace - // NOTE: the rule before modification is /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/ - if (rule.test?.toString().includes('svg')) { - rule.test = /\.(ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/; - } - }); - - // handle asset loading with this - config.module.rules.unshift({ - test: /\.svg(\?.*)?$/i, - issuer: /\.[jt]sx?$/, - use: ['@svgr/webpack'], - }); - - config.module.rules.unshift({ - test: /\.ya?ml$/, - type: 'json', - use: [ - { - loader: 'yaml-loader', - options: { - asJSON: true, - }, - }, - ], - }); - - config.resolve.extensions = ['.tsx', '.ts', '.js']; - config.resolve.plugins = [new TsconfigPathsPlugin()]; - - config.plugins.push( - new ForkTsCheckerWebpackPlugin({ - typescript: { - mode: 'write-references', - diagnosticOptions: { - semantic: true, - syntactic: true, - }, - }, - }), - ); - - if (!config.resolve.alias) { - config.resolve.alias = {}; - } - - config.resolve.alias['@tauri-apps/api'] = `${__dirname}/mocks/tauri`; - - // Return the altered config - return config; - }, - features: { - emotionAlias: false, - }, -}; diff --git a/nym-connect/mobile/.storybook/mocks/tauri/app.js b/nym-connect/mobile/.storybook/mocks/tauri/app.js deleted file mode 100644 index 83d80d7037..0000000000 --- a/nym-connect/mobile/.storybook/mocks/tauri/app.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * This is a mock for Tauri's API package (@tauri-apps/api/app), to prevent stories from being excluded, because they either use - * or import dependencies that use Tauri. - */ - -module.exports = { - getVersion: () => undefined, -}; diff --git a/nym-connect/mobile/.storybook/mocks/tauri/clipboard.js b/nym-connect/mobile/.storybook/mocks/tauri/clipboard.js deleted file mode 100644 index a34847db86..0000000000 --- a/nym-connect/mobile/.storybook/mocks/tauri/clipboard.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * This is a mock for Tauri's API package (@tauri-apps/api/clipboard), to prevent stories from being excluded, because they either use - * or import dependencies that use Tauri. - */ - -module.exports = { - writeText: async () => undefined, -} diff --git a/nym-connect/mobile/.storybook/mocks/tauri/event.js b/nym-connect/mobile/.storybook/mocks/tauri/event.js deleted file mode 100644 index a100d30337..0000000000 --- a/nym-connect/mobile/.storybook/mocks/tauri/event.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * This is a mock for Tauri's API package (@tauri-apps/api/clipboard), to prevent stories from being excluded, because they either use - * or import dependencies that use Tauri. - */ - -module.exports = { - listen: async () => undefined, -} diff --git a/nym-connect/mobile/.storybook/mocks/tauri/index.js b/nym-connect/mobile/.storybook/mocks/tauri/index.js deleted file mode 100644 index c411c3bf18..0000000000 --- a/nym-connect/mobile/.storybook/mocks/tauri/index.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * This is a mock for Tauri's API package (@tauri-apps/api), to prevent stories from being excluded, because they either use - * or import dependencies that use Tauri. - */ -module.exports = { - invoke: (operation, args) => { - console.error( - `Tauri cannot be used in Storybook. The operation requested was "${operation}". You can add mock responses to "nym_connect/.storybook/mocks/tauri.js" if you need. The default response is "void".`, - ); - return new Promise((resolve, reject) => { - reject(new Error(`Tauri operation ${operation} not available in storybook.`)); - }); - }, -}; diff --git a/nym-connect/mobile/.storybook/mocks/tauri/notification.js b/nym-connect/mobile/.storybook/mocks/tauri/notification.js deleted file mode 100644 index dcbe18e574..0000000000 --- a/nym-connect/mobile/.storybook/mocks/tauri/notification.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * This is a mock for Tauri's API package (@tauri-apps/api/notification), to prevent stories from being excluded, because they either use - * or import dependencies that use Tauri. - */ - -module.exports = { - isPermissionGranted: () => undefined, - requestPermission: () => undefined, - sendNotification: () => undefined, -}; \ No newline at end of file diff --git a/nym-connect/mobile/.storybook/mocks/tauri/window.js b/nym-connect/mobile/.storybook/mocks/tauri/window.js deleted file mode 100644 index 9f0afeeebd..0000000000 --- a/nym-connect/mobile/.storybook/mocks/tauri/window.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * This is a mock for Tauri's API package (@tauri-apps/api/window), to prevent stories from being excluded, because they either use - * or import dependencies that use Tauri. - */ - -module.exports = { - appWindow: { - maximize: () => undefined, - } -} diff --git a/nym-connect/mobile/.storybook/preview-fonts.js b/nym-connect/mobile/.storybook/preview-fonts.js deleted file mode 100644 index a7989fca9a..0000000000 --- a/nym-connect/mobile/.storybook/preview-fonts.js +++ /dev/null @@ -1,3 +0,0 @@ -import '../src/fonts/fonts.css'; - -export const Fonts = ({ children }) => <>{children}; \ No newline at end of file diff --git a/nym-connect/mobile/.storybook/preview.js b/nym-connect/mobile/.storybook/preview.js deleted file mode 100644 index 9c14c3ec99..0000000000 --- a/nym-connect/mobile/.storybook/preview.js +++ /dev/null @@ -1,26 +0,0 @@ -import { NymMixnetTheme } from '../src/theme'; -import { Fonts } from './preview-fonts'; -import { MockProvider } from '../src/context/mocks/main'; -const withThemeProvider = (Story, context) => { - return ( - - - - - - - - ); -}; - -export const decorators = [withThemeProvider]; - -export const parameters = { - actions: { argTypesRegex: '^on[A-Z].*' }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, -}; diff --git a/nym-connect/mobile/CHANGELOG.md b/nym-connect/mobile/CHANGELOG.md deleted file mode 100644 index cddba036e0..0000000000 --- a/nym-connect/mobile/CHANGELOG.md +++ /dev/null @@ -1,98 +0,0 @@ -# Changelog - -## UNRELEASED - -## [nym-connect-v1.1.7](https://github.com/nymtech/nym/tree/nym-connect-v1.1.7) (2023-01-24) - -- Remove test and earn ([#2865]) - -[#2865]: https://github.com/nymtech/nym/issue/2865 - -## [nym-connect-v1.1.6](https://github.com/nymtech/nym/tree/nym-connect-v1.1.6) (2023-01-17) - -- part (1) show gateway status on the UI if the gateway is not live, is overloaded or is slow ([#2824]) - -[#2824]: https://github.com/nymtech/nym/pull/2824 - -## [nym-connect-v1.1.5](https://github.com/nymtech/nym/tree/nym-connect-v1.1.5) (2023-01-10) - -- get version number from tauri and display by @fmtabbara in https://github.com/nymtech/nym/pull/2684 -- Feature/nym connect experimental software text by @fmtabbara in https://github.com/nymtech/nym/pull/2692 -- NymConnect - Display service info in tooltip **1.1.5 Release** by @fmtabbara in https://github.com/nymtech/nym/pull/2704 - -## [nym-connect-v1.1.4](https://github.com/nymtech/nym/tree/nym-connect-v1.1.4) (2022-12-20) - -This release contains the new opt-in Test & Earn program, and it uses a stress-tested directory of network requesters to improve reliability. It also has some bugfixes, performance improvements, and better error handling. - -- nym-connect: send status messages from socks5 task to tauri backend by @octol in https://github.com/nymtech/nym/pull/1882 -- socks5: rework waiting in inbound.rs by @octol in https://github.com/nymtech/nym/pull/1880 -- Test&Earn by @mmsinclair in https://github.com/nymtech/nym/pull/2729 - -## [nym-connect-v1.1.3](https://github.com/nymtech/nym/tree/nym-connect-v1.1.3) (2022-12-13) - -- socks5-client: added support for socks4a. - -## [nym-connect-v1.1.2](https://github.com/nymtech/nym/tree/nym-connect-v1.1.2) (2022-12-06) - -- socks5-client: fix error with client failing and disconnecting unnecessarily. - -## [nym-connect-v1.1.1](https://github.com/nymtech/nym/tree/nym-connect-v1.1.1) (2022-11-29) - -- socks5-client: fix multiplex concurrent connections ([#1720], [#1777]) -- socks5-client: fix wait closing inbound connection until data is sent, and throttle incoming data in general ([#1772], [#1783],[#1789]) -- socks5-client: fix shutting down all background workers if anyone of them panics or errors out. This fixes an issue where the nym-connect UI was showing connected even though the socks5 tunnel was non-functional. ([#1805]) -- gateway-libs: fix decryping messages stored on the gateway between reconnects ([#1786]) - -- nymconnect: updated UI -- nymconnect: new help area -- nymconnect: listen for service errors and display on frontend - -[#1720]: https://github.com/nymtech/nym/pull/1720 -[#1772]: https://github.com/nymtech/nym/pull/1772 -[#1777]: https://github.com/nymtech/nym/pull/1777 -[#1783]: https://github.com/nymtech/nym/pull/1783 -[#1786]: https://github.com/nymtech/nym/pull/1786 -[#1789]: https://github.com/nymtech/nym/pull/1789 -[#1805]: https://github.com/nymtech/nym/pull/1805 - -## [nym-connect-v1.1.0](https://github.com/nymtech/nym/tree/nym-connect-v1.1.0) (2022-11-09) - -- nym-connect: rework of rewarding changes the directory data structures that describe the mixnet topology ([#1472]) -- clients: add testing-only support for two more extended packet sizes (8kb and 16kb). -- native-client/socks5-client: `disable_loop_cover_traffic_stream` Debug config option to disable the separate loop cover traffic stream ([#1666]) -- native-client/socks5-client: `disable_main_poisson_packet_distribution` Debug config option to make the client ignore poisson distribution in the main packet stream and ONLY send real message (and as fast as they come) ([#1664]) -- native-client/socks5-client: `use_extended_packet_size` Debug config option to make the client use 'ExtendedPacketSize' for its traffic (32kB as opposed to 2kB in 1.0.2) ([#1671]) -- network-requester: added additional Blockstream Green wallet endpoint to `example.allowed.list` ([#1611]) -- validator-client: added `query_contract_smart` and `query_contract_raw` on `NyxdClient` ([#1558]) - -[#1472]: https://github.com/nymtech/nym/pull/1472 -[#1558]: https://github.com/nymtech/nym/pull/1558 -[#1611]: https://github.com/nymtech/nym/pull/1611 -[#1664]: https://github.com/nymtech/nym/pull/1664 -[#1666]: https://github.com/nymtech/nym/pull/1666 -[#1671]: https://github.com/nymtech/nym/pull/1671 - -## [nym-connect-v1.0.2](https://github.com/nymtech/nym/tree/nym-connect-v1.0.2) (2022-08-18) - -### Changed - -- nym-connect: "load balance" the service providers by picking a random Service Provider for each Service and storing in local storage so it remains sticky for the user ([#1540]) -- nym-connect: the ServiceProviderSelector only displays the available Services, and picks a random Service Provider for Services the user has never used before ([#1540]) -- nym-connect: add `local-forage` for storing user settings ([#1540]) - -[#1540]: https://github.com/nymtech/nym/pull/1540 - -## [nym-connect-v1.0.1](https://github.com/nymtech/nym/tree/nym-connect-v1.0.1) (2022-07-22) - -### Added - -- nym-connect: initial proof-of-concept of a UI around the socks5 client was added -- nym-connect: add ability to select network requester and gateway ([#1427]) -- nym-connect: add ability to export gateway keys as JSON -- nym-connect: add auto updater - -### Changed - -- nym-connect: reuse config id instead of creating a new id on each connection - -[#1427]: https://github.com/nymtech/nym/pull/1427 diff --git a/nym-connect/mobile/README.md b/nym-connect/mobile/README.md deleted file mode 100644 index efb522dd81..0000000000 --- a/nym-connect/mobile/README.md +++ /dev/null @@ -1,71 +0,0 @@ - - -# Nym Connect - Mobile - -[Nym Connect](https://github.com/nymtech/nym/tree/develop/nym-connect) application for Mobile. - -Nym Connects sets up a SOCKS5 proxy for local applications to use. - -**NOTE**: Currently we only focus on Android, -the remaining docs apply for Android development only. - -## Installation prerequisites - Linux / Mac - -- `Yarn` -- `NodeJS >= v16` -- `Rust & cargo` -- Android development environment (JDK, SDK/NDK, AVD etc...) - -For setting up an Android development environment see -https://next--tauri.netlify.app/next/guides/getting-started/prerequisites/linux#android - -## Installation - -Inside the `mobile/nym-connect` directory, run the following commands: - -``` -yarn install -yarn prewebpack:dev -``` - -## Development - -Assuming there is a running android [emulator](https://developer.android.com/studio/run/emulator) -or a real [device](https://developer.android.com/studio/run/device) connected. -Inside the `mobile/nym-connect/src-tauri` directory, run the following command: - -``` -yarn dev:android -``` - -#### Debugging - -https://next--tauri.netlify.app/next/guides/debugging/application#mobile - -## Production - -To build the APK, run the build commands. - -``` -yarn webpack:prod -WRY_ANDROID_PACKAGE=net.nymtech.nym_connect WRY_ANDROID_LIBRARY=nym_connect cargo tauri android build --debug --apk -``` - -**NOTE**: Production build without the `--debug` flag requires a signed build. - -# Storybook - -Run storybook with: - -``` -yarn storybook -``` - -And build storybook static site with: - -``` -yarn storybook:build -``` diff --git a/nym-connect/mobile/dist/.gitkeep b/nym-connect/mobile/dist/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/nym-connect/mobile/package.json.bak b/nym-connect/mobile/package.json.bak deleted file mode 100644 index 20c4aceb65..0000000000 --- a/nym-connect/mobile/package.json.bak +++ /dev/null @@ -1,119 +0,0 @@ -{ - "name": "@nym/nym-connect-mobile", - "version": "1.1.7", - "main": "index.js", - "license": "MIT", - "scripts": { - "prewebpack:dev": "yarn --cwd ../.. build", - "webpack:dev": "yarn webpack serve --config webpack.dev.js", - "webpack:dev:onlyThis": "yarn webpack serve --config webpack.dev.js", - "webpack:prod": "yarn webpack --progress --config webpack.prod.js", - "tauri": "tauri", - "tauri:dev:android": "WRY_ANDROID_PACKAGE=net.nymtech.nym_connect WRY_ANDROID_LIBRARY=nym_connect cargo tauri android dev", - "tauri:build:android": "WRY_ANDROID_PACKAGE=net.nymtech.nym_connect WRY_ANDROID_LIBRARY=nym_connect cargo tauri android build", - "dev:android": "run-p webpack:dev:onlyThis tauri:dev:android", - "prebuild": "yarn --cwd ../.. build", - "build:android": "run-s webpack:prod tauri:build:android", - "storybook": "start-storybook -p 6006", - "prestorybook:build": "yarn --cwd ../.. build", - "storybook:build": "build-storybook", - "tsc": "tsc --noEmit true", - "tsc:watch": "tsc --noEmit true --watch", - "lint": "eslint src", - "lint:fix": "eslint src --fix", - "test": "jest", - "test:watch": "jest --watch", - "clean:dist": "rm -rf dist", - "clean:node": "rm -rf node_modules", - "clean:rust": "cargo clean --manifest-path src-tauri/Cargo.toml", - "clean:android": "cd src-tauri/gen/android/nym_connect/ && gradlew clean", - "clean:": "run-p clean:node clean:rust clean:android" - }, - "dependencies": { - "@emotion/react": "^11.7.0", - "@emotion/styled": "^11.6.0", - "@hookform/resolvers": "^2.8.0", - "@mui/icons-material": "^5.2.0", - "@mui/material": "^5.2.2", - "@mui/styles": "^5.2.2", - "@mui/system": ">= 5", - "@mui/lab": "^5.0.0-alpha.72", - "@nymproject/react": "^1.0.0", - "@tauri-apps/api": "^2.0.0-alpha.0", - "@tauri-apps/tauri-forage": "^1.0.0-beta.2", - "clsx": "^1.1.1", - "luxon": "^2.3.0", - "pretty-bytes": "^6.0.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-error-boundary": "^3.1.3", - "react-hook-form": "^7.14.2", - "react-markdown": "^8.0.4", - "react-router-dom": "^6.7.0", - "semver": "^6.3.0", - "yup": "^0.32.9" - }, - "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", - "@mdx-js/loader": "^2.1.5", - "@nymproject/eslint-config-react-typescript": "^1.0.0", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4", - "@storybook/react": "^6.5.15", - "@svgr/webpack": "^6.1.1", - "@tauri-apps/cli": "^2.0.0-alpha.2", - "@testing-library/jest-dom": "^5.14.1", - "@testing-library/react": "^12.0.0", - "@types/jest": "^27.0.1", - "@types/luxon": "^2.3.2", - "@types/node": "^16.7.13", - "@types/react": "^18.0.26", - "@types/react-dom": "^18.0.10", - "@types/semver": "^7.3.8", - "@types/uuid": "^8.3.4", - "@typescript-eslint/eslint-plugin": "^5.13.0", - "@typescript-eslint/parser": "^5.13.0", - "babel-loader": "^8.3.0", - "babel-plugin-root-import": "^6.6.0", - "clean-webpack-plugin": "^4.0.0", - "css-loader": "^6.7.3", - "css-minimizer-webpack-plugin": "^3.0.2", - "dotenv-webpack": "^7.0.3", - "eslint": "^8.10.0", - "eslint-config-airbnb": "^19.0.4", - "eslint-config-airbnb-typescript": "^16.1.0", - "eslint-config-prettier": "^8.5.0", - "eslint-import-resolver-root-import": "^1.0.4", - "eslint-plugin-import": "^2.25.4", - "eslint-plugin-jest": "^26.1.1", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-react": "^7.29.2", - "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-storybook": "^0.5.12", - "file-loader": "^6.2.0", - "fork-ts-checker-webpack-plugin": "^7.2.1", - "html-webpack-plugin": "^5.3.2", - "jest": "^27.1.0", - "mini-css-extract-plugin": "^2.2.2", - "npm-run-all": "^4.1.5", - "prettier": "^2.8.7", - "react-refresh": "^0.10.0", - "react-refresh-typescript": "^2.0.2", - "style-loader": "^3.3.1", - "thread-loader": "^3.0.4", - "ts-jest": "^27.0.5", - "ts-loader": "^9.4.2", - "tsconfig-paths-webpack-plugin": "^3.5.2", - "typescript": "^4.6.2", - "url-loader": "^4.1.1", - "webpack": "^5.75.0", - "webpack-cli": "^4.8.0", - "webpack-dev-server": "^4.5.0", - "webpack-merge": "^5.8.0", - "yaml-loader": "^0.8.0" - } -} diff --git a/nym-connect/mobile/public/favicon.ico b/nym-connect/mobile/public/favicon.ico deleted file mode 100644 index bc951a1c6f..0000000000 Binary files a/nym-connect/mobile/public/favicon.ico and /dev/null differ diff --git a/nym-connect/mobile/public/favicon.png b/nym-connect/mobile/public/favicon.png deleted file mode 100644 index fcf1e747d0..0000000000 Binary files a/nym-connect/mobile/public/favicon.png and /dev/null differ diff --git a/nym-connect/mobile/public/index.html b/nym-connect/mobile/public/index.html deleted file mode 100644 index 2ba643a663..0000000000 --- a/nym-connect/mobile/public/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Nym Connect - - -
- - diff --git a/nym-connect/mobile/src-tauri/.gitignore b/nym-connect/mobile/src-tauri/.gitignore deleted file mode 100644 index 296ceb2535..0000000000 --- a/nym-connect/mobile/src-tauri/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -WixTools -/.cargo/ diff --git a/nym-connect/mobile/src-tauri/Cargo.lock b/nym-connect/mobile/src-tauri/Cargo.lock deleted file mode 100644 index 82622e8759..0000000000 --- a/nym-connect/mobile/src-tauri/Cargo.lock +++ /dev/null @@ -1,7349 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aead" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" -dependencies = [ - "crypto-common", - "generic-array 0.14.6", -] - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "ctr 0.8.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aes" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" -dependencies = [ - "cfg-if", - "cipher 0.4.4", - "cpufeatures", -] - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.8", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "anyhow" -version = "1.0.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "async-trait" -version = "0.1.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ea188f25f0255d8f92797797c97ebf5631fa88178beb1a46fdf5622c9a00e4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.5", -] - -[[package]] -name = "atk" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39991bc421ddf72f70159011b323ff49b0f783cc676a7287c59453da2e2531cf" -dependencies = [ - "atk-sys", - "bitflags 1.3.2", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ad703eb64dc058024f0e57ccfa069e15a413b98dbd50a1a950e743b7f11148" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "atoi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atoi" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" -dependencies = [ - "num-traits", -] - -[[package]] -name = "attohttpc" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2" -dependencies = [ - "flate2", - "http", - "log", - "native-tls", - "serde", - "serde_json", - "serde_urlencoded", - "url", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bip32" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873faa4363bfc54c36a48321da034c92a0645a363eed34d948683ffc1706e37f" -dependencies = [ - "bs58", - "hmac 0.11.0", - "k256", - "once_cell", - "pbkdf2", - "rand_core 0.6.4", - "ripemd160", - "sha2 0.9.9", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "bip39" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" -dependencies = [ - "bitcoin_hashes", - "rand 0.8.5", - "rand_core 0.6.4", - "serde", - "unicode-normalization", - "zeroize", -] - -[[package]] -name = "bitcoin_hashes" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" -dependencies = [ - "byte-tools", - "crypto-mac 0.7.0", - "digest 0.8.1", - "opaque-debug 0.2.3", -] - -[[package]] -name = "blake3" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" -dependencies = [ - "arrayref", - "arrayvec 0.7.2", - "cc", - "cfg-if", - "constant_time_eq", - "digest 0.10.6", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "block-padding", - "generic-array 0.14.6", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.6", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54757888b09a69be70b5ec303e382a74227392086ba808cb01eeca29233a2397" -dependencies = [ - "digest 0.9.0", - "ff 0.10.1", - "group 0.10.0", - "pairing 0.20.0", - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[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 0.11.1", - "group 0.11.0", - "pairing 0.21.0", - "rand_core 0.6.4", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "brotli" -version = "3.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" -dependencies = [ - "sha2 0.9.9", -] - -[[package]] -name = "bstr" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cairo-rs" -version = "0.16.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", - "thiserror", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "cargo_toml" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfbc36312494041e2cdd5f06697b7e89d4b76f42773a0b5556ac290ff22acc2" -dependencies = [ - "serde", - "toml", -] - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid 1.3.0", -] - -[[package]] -name = "cfg-expr" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" -dependencies = [ - "smallvec", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chacha" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862" -dependencies = [ - "byteorder", - "keystream", -] - -[[package]] -name = "chacha20" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fc89c7c5b9e7a02dfe45cd2367bae382f9ed31c61ca8debe5f827c420a2f08" -dependencies = [ - "cfg-if", - "cipher 0.4.4", - "cpufeatures", -] - -[[package]] -name = "chacha20poly1305" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" -dependencies = [ - "aead", - "chacha20", - "cipher 0.4.4", - "poly1305", - "zeroize", -] - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array 0.14.6", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", - "zeroize", -] - -[[package]] -name = "clap" -version = "4.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098" -dependencies = [ - "bitflags 2.0.2", - "clap_derive", - "clap_lex", - "is-terminal", - "once_cell", - "strsim", - "termcolor", -] - -[[package]] -name = "clap_complete" -version = "4.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37686beaba5ac9f3ab01ee3172f792fc6ffdd685bfb9e63cfef02c0571a4e8e1" -dependencies = [ - "clap", -] - -[[package]] -name = "clap_complete_fig" -version = "4.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2171bc6242ad7a1801422bff039574449b5bd832b715222e500714ce10f91a54" -dependencies = [ - "clap", - "clap_complete", -] - -[[package]] -name = "clap_derive" -version = "4.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644" -dependencies = [ - "heck 0.4.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "clap_lex" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "client-core" -version = "1.1.13" -dependencies = [ - "async-trait", - "dashmap", - "dirs", - "futures", - "gateway-client", - "gateway-requests", - "gloo-timers", - "humantime-serde", - "log", - "nym-bandwidth-controller", - "nym-config", - "nym-credential-storage", - "nym-crypto", - "nym-nonexhaustive-delayqueue", - "nym-pemstore", - "nym-sphinx", - "nym-task", - "nym-topology", - "rand 0.7.3", - "serde", - "serde_json", - "sqlx 0.6.3", - "tap", - "thiserror", - "time", - "tokio", - "tokio-stream", - "tokio-tungstenite", - "tungstenite", - "url", - "validator-client", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-timer", - "wasm-utils", -] - -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "colored" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - -[[package]] -name = "combine" -version = "4.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "const-oid" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" - -[[package]] -name = "constant_time_eq" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "foreign-types", - "libc", -] - -[[package]] -name = "cosmos-sdk-proto" -version = "0.12.3" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" -dependencies = [ - "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" -dependencies = [ - "bip32", - "cosmos-sdk-proto", - "ecdsa", - "eyre", - "getrandom 0.2.8", - "k256", - "prost", - "prost-types", - "rand_core 0.6.4", - "serde", - "serde_json", - "subtle-encoding", - "tendermint", - "tendermint-rpc", - "thiserror", -] - -[[package]] -name = "cosmwasm-crypto" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb0afef2325df81aadbf9be1233f522ed8f6e91df870c764bc44cca2b1415bd" -dependencies = [ - "digest 0.9.0", - "ed25519-zebra", - "k256", - "rand_core 0.6.4", - "thiserror", -] - -[[package]] -name = "cosmwasm-derive" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4" -dependencies = [ - "syn 1.0.109", -] - -[[package]] -name = "cosmwasm-std" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195" -dependencies = [ - "base64 0.13.1", - "cosmwasm-crypto", - "cosmwasm-derive", - "forward_ref", - "schemars", - "serde", - "serde-json-wasm", - "thiserror", - "uint", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" -dependencies = [ - "crc-catalog 1.1.1", -] - -[[package]] -name = "crc" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" -dependencies = [ - "crc-catalog 2.2.0", -] - -[[package]] -name = "crc-catalog" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" - -[[package]] -name = "crc-catalog" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" -dependencies = [ - "generic-array 0.14.6", - "rand_core 0.6.4", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.6", - "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -dependencies = [ - "generic-array 0.12.4", - "subtle 1.0.0", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array 0.14.6", - "subtle 2.4.1", -] - -[[package]] -name = "cssparser" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", - "smallvec", - "syn 1.0.109", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ct-logs" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8" -dependencies = [ - "sct 0.6.1", -] - -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ctr" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" -dependencies = [ - "cipher 0.3.0", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher 0.4.4", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "cw-storage-plus" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-utils" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw2" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "serde", -] - -[[package]] -name = "cw3" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b" -dependencies = [ - "cosmwasm-std", - "cw-utils", - "schemars", - "serde", -] - -[[package]] -name = "cw3-fixed-multisig" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54aa54c13f405ec4ab36b6217538bc957d439eee58f89312db05a79caf6706" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "cw-utils", - "cw2", - "cw3", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw4" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "serde", -] - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "dashmap" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" -dependencies = [ - "cfg-if", - "hashbrown 0.12.3", - "lock_api", - "once_cell", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "dbus" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" -dependencies = [ - "libc", - "libdbus-sys", - "winapi", -] - -[[package]] -name = "der" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" -dependencies = [ - "const-oid", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.6", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", - "subtle 2.4.1", -] - -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[package]] -name = "dotenvy" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0" - -[[package]] -name = "dtoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" - -[[package]] -name = "dtoa-short" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde03329ae10e79ede66c9ce4dc930aa8599043b0743008548680f25b91502d6" -dependencies = [ - "dtoa", -] - -[[package]] -name = "dunce" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" - -[[package]] -name = "dyn-clone" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" - -[[package]] -name = "ecdsa" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand 0.7.3", - "serde", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "ed25519-zebra" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" -dependencies = [ - "curve25519-dalek", - "hashbrown 0.12.3", - "hex", - "rand_core 0.6.4", - "serde", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "elliptic-curve" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "ff 0.11.1", - "generic-array 0.14.6", - "group 0.11.0", - "rand_core 0.6.4", - "sec1", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "embed_plist" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" - -[[package]] -name = "encoding_rs" -version = "0.8.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enum-iterator" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a0ac4aeb3a18f92eaf09c6bb9b3ac30ff61ca95514fc58cbead1c9a6bf5401" -dependencies = [ - "enum-iterator-derive", -] - -[[package]] -name = "enum-iterator-derive" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355f93763ef7b0ae1c43c4d8eccc9d5848d84ad1a1d8ce61c421d1ac85a19d05" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime 1.3.0", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "eyre" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fern" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a" -dependencies = [ - "colored 1.9.3", - "log", -] - -[[package]] -name = "ff" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f" -dependencies = [ - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[package]] -name = "ff" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" -dependencies = [ - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[package]] -name = "field-offset" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" -dependencies = [ - "memoffset", - "rustc_version", -] - -[[package]] -name = "filetime" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "windows-sys 0.45.0", -] - -[[package]] -name = "fix-path-env" -version = "0.1.0" -source = "git+https://github.com/tauri-apps/fix-path-env-rs?branch=release#b7905421de7e4f589baf6d34a6dd0c3cb83197c3" -dependencies = [ - "strip-ansi-escapes", - "thiserror", -] - -[[package]] -name = "flate2" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "flex-error" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" -dependencies = [ - "eyre", - "paste", -] - -[[package]] -name = "flume" -version = "0.10.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" -dependencies = [ - "futures-core", - "futures-sink", - "pin-project", - "spin 0.9.6", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" - -[[package]] -name = "futures-executor" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-intrusive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot 0.11.2", -] - -[[package]] -name = "futures-io" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" - -[[package]] -name = "futures-macro" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "futures-sink" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" - -[[package]] -name = "futures-task" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" - -[[package]] -name = "futures-util" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gdk" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9cb33da481c6c040404a11f8212d193889e9b435db2c14fd86987f630d3ce1" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.16.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3578c60dee9d029ad86593ed88cb40f35c1b83360e12498d055022385dd9a05" -dependencies = [ - "bitflags 1.3.2", - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3092cf797a5f1210479ea38070d9ae8a5b8e9f8f1be9f32f4643c529c7d70016" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76354f97a913e55b984759a997b693aa7dc71068c9e98bcce51aa167a0a5c5a" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - -[[package]] -name = "gdkx11-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa2bf8b5b8c414bc5d05e48b271896d0fd3ddb57464a3108438082da61de6af" -dependencies = [ - "gdk-sys", - "glib-sys", - "libc", - "system-deps", - "x11", -] - -[[package]] -name = "generator" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a20a288a94683f5f4da0adecdbe095c94a77c295e514cc6484e9394dd8376e" -dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "windows 0.44.0", -] - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getset" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "gio" -version = "0.16.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1c84b4534a290a29160ef5c6eff2a9c95833111472e824fc5cb78b513dd092" -dependencies = [ - "bitflags 1.3.2", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "once_cell", - "pin-project-lite", - "smallvec", - "thiserror", -] - -[[package]] -name = "gio-sys" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[package]] -name = "git2" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" -dependencies = [ - "bitflags 1.3.2", - "libc", - "libgit2-sys", - "log", - "url", -] - -[[package]] -name = "glib" -version = "0.16.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd4df61a866ed7259d6189b8bcb1464989a77f1d85d25d002279bbe9dd38b2f" -dependencies = [ - "bitflags 1.3.2", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "once_cell", - "smallvec", - "thiserror", -] - -[[package]] -name = "glib-macros" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf" -dependencies = [ - "anyhow", - "heck 0.4.1", - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "glib-sys" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "globset" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", -] - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "gobject-sys" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "group" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912" -dependencies = [ - "byteorder", - "ff 0.10.1", - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[package]] -name = "group" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" -dependencies = [ - "byteorder", - "ff 0.11.1", - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[package]] -name = "gtk" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4d3507d43908c866c805f74c9dd593c0ce7ba5c38e576e41846639cdcd4bee6" -dependencies = [ - "atk", - "bitflags 1.3.2", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "once_cell", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b5f8946685d5fe44497007786600c2f368ff6b1e61a16251c89f72a97520a3" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] -name = "gtk3-macros" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfd6557b1018b773e43c8de9d0d13581d6b36190d0501916cbec4731db5ccff" -dependencies = [ - "anyhow", - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "h2" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "handlebars" -version = "3.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3" -dependencies = [ - "log", - "pest", - "pest_derive", - "quick-error 2.0.1", - "serde", - "serde_json", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" -dependencies = [ - "hashbrown 0.11.2", -] - -[[package]] -name = "hashlink" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" -dependencies = [ - "hashbrown 0.12.3", -] - -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - -[[package]] -name = "hkdf" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" -dependencies = [ - "digest 0.9.0", - "hmac 0.11.0", -] - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac 0.11.1", - "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.6", -] - -[[package]] -name = "html5ever" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148" -dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.6", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "http-range" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error 1.2.3", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "humantime-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" -dependencies = [ - "humantime 2.1.0", - "serde", -] - -[[package]] -name = "hyper" -version = "0.14.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa 1.0.6", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-proxy" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" -dependencies = [ - "bytes", - "futures", - "headers", - "http", - "hyper", - "hyper-rustls", - "rustls-native-certs", - "tokio", - "tokio-rustls 0.22.0", - "tower-service", - "webpki 0.21.4", -] - -[[package]] -name = "hyper-rustls" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" -dependencies = [ - "ct-logs", - "futures-util", - "hyper", - "log", - "rustls 0.19.1", - "rustls-native-certs", - "tokio", - "tokio-rustls 0.22.0", - "webpki 0.21.4", - "webpki-roots 0.21.1", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "ico" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031530fe562d8c8d71c0635013d6d155bbfe8ba0aa4b4d2d24ce8af6b71047bd" -dependencies = [ - "byteorder", - "png", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "ignore" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" -dependencies = [ - "crossbeam-utils", - "globset", - "lazy_static", - "log", - "memchr", - "regex", - "same-file", - "thread_local", - "walkdir", - "winapi-util", -] - -[[package]] -name = "image" -version = "0.24.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "num-rational", - "num-traits", -] - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "infer" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a898e4b7951673fce96614ce5751d13c40fc5674bc2d759288e46c3ab62598b3" -dependencies = [ - "cfb", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array 0.14.6", -] - -[[package]] -name = "input_buffer" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413" -dependencies = [ - "bytes", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.45.0", -] - -[[package]] -name = "ipnet" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" - -[[package]] -name = "is-terminal" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.45.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - -[[package]] -name = "javascriptcore-rs" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "110b9902c80c12bf113c432d0b71c7a94490b294a8234f326fd0abca2fac0b00" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98a216519a52cd941a733a0ad3f1023cfdb1cd47f3955e8e863ed56f558f916c" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "jni" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "json-patch" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3fa5a61630976fc4c353c70297f2e93f1930e3ccee574d59d618ccbd5154ce" -dependencies = [ - "serde", - "serde_json", - "treediff 3.0.2", -] - -[[package]] -name = "json-patch" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e712e62827c382a77b87f590532febb1f8b2fdbc3eefa1ee37fe7281687075ef" -dependencies = [ - "serde", - "serde_json", - "thiserror", - "treediff 4.0.2", -] - -[[package]] -name = "k256" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sec1", - "sha2 0.9.9", - "sha3", -] - -[[package]] -name = "keccak" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "keystream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28" - -[[package]] -name = "kuchiki" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358" -dependencies = [ - "cssparser", - "html5ever", - "matches", - "selectors", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.140" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" - -[[package]] -name = "libdbus-sys" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f8d7ae751e1cb825c840ae5e682f59b098cdfd213c350ac268b61449a5f58a0" -dependencies = [ - "pkg-config", -] - -[[package]] -name = "libgit2-sys" -version = "0.13.5+1.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e5ea06c26926f1002dd553fded6cfcdc9784c1f60feeb58368b4d9b07b6dba" -dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", -] - -[[package]] -name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - -[[package]] -name = "libsqlite3-sys" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "line-wrap" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "lioness" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9" -dependencies = [ - "arrayref", - "blake2", - "chacha", - "keystream", -] - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", - "serde", -] - -[[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "mac-notification-sys" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e72d50edb17756489e79d52eb146927bec8eba9dd48faadf9ef08bca3791ad5" -dependencies = [ - "cc", - "dirs-next", - "objc-foundation", - "objc_id", - "time", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "markup5ever" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" -dependencies = [ - "log", - "phf 0.8.0", - "phf_codegen", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "ndk" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" -dependencies = [ - "bitflags 1.3.2", - "jni-sys", - "ndk-sys", - "num_enum", - "thiserror", -] - -[[package]] -name = "ndk-context" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" - -[[package]] -name = "ndk-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "notify-rust" -version = "4.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfa211d18e360f08e36c364308f394b5eb23a6629150690e109a916dc6f610e" -dependencies = [ - "dbus", - "log", - "mac-notification-sys", - "tauri-winrt-notification", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - -[[package]] -name = "nym-api-requests" -version = "0.1.0" -dependencies = [ - "bs58", - "cosmrs", - "cosmwasm-std", - "getset", - "nym-coconut-interface", - "nym-mixnet-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "nym-bandwidth-controller" -version = "0.1.0" -dependencies = [ - "bip39", - "nym-coconut-interface", - "nym-credential-storage", - "nym-credentials", - "nym-crypto", - "nym-network-defaults", - "rand 0.7.3", - "thiserror", - "url", - "validator-client", -] - -[[package]] -name = "nym-bin-common" -version = "0.3.0" -dependencies = [ - "atty", - "clap", - "clap_complete", - "clap_complete_fig", - "log", - "pretty_env_logger", - "semver 0.11.0", - "serde", - "vergen", -] - -[[package]] -name = "nym-client-core" -version = "1.1.13" -dependencies = [ - "async-trait", - "dashmap", - "dirs", - "futures", - "gloo-timers", - "humantime-serde", - "log", - "nym-config", - "nym-crypto", - "nym-gateway-client", - "nym-gateway-requests", - "nym-nonexhaustive-delayqueue", - "nym-pemstore", - "nym-sphinx", - "nym-task", - "nym-topology", - "nym-validator-client", - "rand 0.7.3", - "serde", - "serde_json", - "sqlx 0.6.3", - "tap", - "thiserror", - "time", - "tokio", - "tokio-stream", - "tokio-tungstenite", - "tungstenite", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-timer", - "wasm-utils", -] - -[[package]] -name = "nym-coconut" -version = "0.5.0" -dependencies = [ - "bls12_381 0.6.0", - "bs58", - "digest 0.9.0", - "ff 0.11.1", - "getrandom 0.2.8", - "group 0.11.0", - "itertools", - "nym-dkg", - "nym-pemstore", - "rand 0.8.5", - "serde", - "serde_derive", - "sha2 0.9.9", - "thiserror", -] - -[[package]] -name = "nym-coconut-bandwidth-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "nym-multisig-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "nym-coconut-dkg-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "cw-utils", - "nym-contracts-common", - "nym-multisig-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "nym-coconut-interface" -version = "0.1.0" -dependencies = [ - "bs58", - "getset", - "nym-coconut", - "serde", - "thiserror", -] - -[[package]] -name = "nym-config" -version = "0.1.0" -dependencies = [ - "cfg-if", - "handlebars", - "log", - "nym-network-defaults", - "serde", - "toml", - "url", -] - -[[package]] -name = "nym-connect" -version = "1.1.12" -dependencies = [ - "anyhow", - "bip39", - "dirs", - "eyre", - "fern", - "fix-path-env", - "futures", - "itertools", - "log", - "nym-api-requests", - "nym-bin-common", - "nym-client-core", - "nym-config", - "nym-contracts-common", - "nym-credential-storage", - "nym-crypto", - "nym-socks5-client-core", - "nym-task", - "pretty_env_logger", - "rand 0.8.5", - "reqwest", - "rust-embed", - "serde", - "serde_json", - "serde_repr", - "tap", - "tauri", - "tauri-build", - "tempfile", - "tendermint-rpc", - "thiserror", - "time", - "tokio", - "ts-rs", - "url", - "yaml-rust", -] - -[[package]] -name = "nym-contracts-common" -version = "0.3.0" -dependencies = [ - "bs58", - "cosmwasm-std", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "nym-credential-storage" -version = "0.1.0" -dependencies = [ - "async-trait", - "log", - "sqlx 0.5.13", - "thiserror", - "tokio", -] - -[[package]] -name = "nym-credentials" -version = "0.1.0" -dependencies = [ - "bls12_381 0.5.0", - "cosmrs", - "nym-api-requests", - "nym-coconut-interface", - "nym-crypto", - "nym-validator-client", - "thiserror", -] - -[[package]] -name = "nym-crypto" -version = "0.2.0" -dependencies = [ - "aes 0.8.2", - "blake3", - "bs58", - "cipher 0.4.4", - "ctr 0.9.2", - "digest 0.10.6", - "ed25519-dalek", - "generic-array 0.14.6", - "hkdf 0.12.3", - "hmac 0.12.1", - "nym-pemstore", - "nym-sphinx-types", - "rand 0.7.3", - "subtle-encoding", - "thiserror", - "x25519-dalek", -] - -[[package]] -name = "nym-dkg" -version = "0.1.0" -dependencies = [ - "bitvec", - "bls12_381 0.6.0", - "bs58", - "ff 0.11.1", - "group 0.11.0", - "lazy_static", - "nym-pemstore", - "rand 0.8.5", - "rand_chacha 0.3.1", - "rand_core 0.6.4", - "serde", - "serde_derive", - "sha2 0.9.9", - "thiserror", - "zeroize", -] - -[[package]] -name = "nym-gateway-client" -version = "0.1.0" -dependencies = [ - "async-trait", - "futures", - "getrandom 0.2.8", - "log", - "nym-coconut-interface", - "nym-credential-storage", - "nym-credentials", - "nym-crypto", - "nym-gateway-requests", - "nym-mobile-storage", - "nym-network-defaults", - "nym-pemstore", - "nym-sphinx", - "nym-task", - "nym-validator-client", - "rand 0.7.3", - "serde", - "thiserror", - "tokio", - "tokio-stream", - "tokio-tungstenite", - "tungstenite", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-timer", - "wasm-utils", -] - -[[package]] -name = "nym-gateway-requests" -version = "0.1.0" -dependencies = [ - "bs58", - "futures", - "log", - "nym-coconut-interface", - "nym-credentials", - "nym-crypto", - "nym-pemstore", - "nym-sphinx", - "rand 0.7.3", - "serde", - "serde_json", - "thiserror", - "tungstenite", -] - -[[package]] -name = "nym-group-contract-common" -version = "0.1.0" -dependencies = [ - "cw4", - "schemars", - "serde", -] - -[[package]] -name = "nym-mixnet-contract-common" -version = "0.3.0" -dependencies = [ - "bs58", - "cosmwasm-std", - "humantime-serde", - "log", - "nym-contracts-common", - "schemars", - "serde", - "serde-json-wasm", - "serde_repr", - "thiserror", - "time", -] - -[[package]] -name = "nym-mobile-storage" -version = "0.1.0" -dependencies = [ - "async-trait", - "thiserror", -] - -[[package]] -name = "nym-multisig-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "cw-utils", - "cw3", - "cw3-fixed-multisig", - "cw4", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "nym-network-defaults" -version = "0.1.0" -dependencies = [ - "cfg-if", - "dotenvy", - "hex-literal", - "once_cell", - "serde", - "thiserror", - "url", -] - -[[package]] -name = "nym-nonexhaustive-delayqueue" -version = "0.1.0" -dependencies = [ - "futures-core", - "slab", - "tokio", - "tokio-stream", - "tokio-util", - "wasm-timer", -] - -[[package]] -name = "nym-ordered-buffer" -version = "0.1.0" -dependencies = [ - "log", - "thiserror", -] - -[[package]] -name = "nym-outfox" -version = "0.1.0" -dependencies = [ - "blake3", - "chacha20", - "chacha20poly1305", - "curve25519-dalek", - "getrandom 0.2.8", - "rayon", - "thiserror", - "zeroize", -] - -[[package]] -name = "nym-pemstore" -version = "0.2.0" -dependencies = [ - "pem", -] - -[[package]] -name = "nym-service-providers-common" -version = "0.1.0" -dependencies = [ - "async-trait", - "log", - "nym-bin-common", - "nym-sphinx-anonymous-replies", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "nym-socks5-client-core" -version = "0.1.0" -dependencies = [ - "dirs", - "futures", - "log", - "nym-bandwidth-controller", - "nym-config", - "nym-credential-storage", - "nym-network-defaults", - "nym-service-providers-common", - "nym-socks5-proxy-helpers", - "nym-socks5-requests", - "nym-sphinx", - "nym-task", - "nym-validator-client", - "pin-project", - "rand 0.7.3", - "serde", - "tap", - "thiserror", - "tokio", -] - -[[package]] -name = "nym-socks5-proxy-helpers" -version = "0.1.0" -dependencies = [ - "bytes", - "futures", - "log", - "nym-ordered-buffer", - "nym-socks5-requests", - "nym-task", - "tokio", - "tokio-util", -] - -[[package]] -name = "nym-socks5-requests" -version = "0.1.0" -dependencies = [ - "nym-service-providers-common", - "nym-sphinx-addressing", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "nym-sphinx" -version = "0.1.0" -dependencies = [ - "log", - "nym-crypto", - "nym-outfox", - "nym-sphinx-acknowledgements", - "nym-sphinx-addressing", - "nym-sphinx-anonymous-replies", - "nym-sphinx-chunking", - "nym-sphinx-cover", - "nym-sphinx-forwarding", - "nym-sphinx-framing", - "nym-sphinx-params", - "nym-sphinx-types", - "nym-topology", - "rand 0.7.3", - "rand_distr", - "thiserror", - "tokio", -] - -[[package]] -name = "nym-sphinx-acknowledgements" -version = "0.1.0" -dependencies = [ - "nym-crypto", - "nym-pemstore", - "nym-sphinx-addressing", - "nym-sphinx-params", - "nym-sphinx-types", - "nym-topology", - "rand 0.7.3", - "thiserror", -] - -[[package]] -name = "nym-sphinx-addressing" -version = "0.1.0" -dependencies = [ - "nym-crypto", - "nym-sphinx-types", - "serde", - "thiserror", -] - -[[package]] -name = "nym-sphinx-anonymous-replies" -version = "0.1.0" -dependencies = [ - "bs58", - "nym-crypto", - "nym-sphinx-addressing", - "nym-sphinx-params", - "nym-sphinx-types", - "nym-topology", - "rand 0.7.3", - "serde", - "thiserror", - "wasm-bindgen", -] - -[[package]] -name = "nym-sphinx-chunking" -version = "0.1.0" -dependencies = [ - "log", - "nym-sphinx-addressing", - "nym-sphinx-params", - "nym-sphinx-types", - "rand 0.7.3", - "thiserror", -] - -[[package]] -name = "nym-sphinx-cover" -version = "0.1.0" -dependencies = [ - "nym-crypto", - "nym-sphinx-acknowledgements", - "nym-sphinx-addressing", - "nym-sphinx-chunking", - "nym-sphinx-forwarding", - "nym-sphinx-params", - "nym-sphinx-types", - "nym-topology", - "rand 0.7.3", - "thiserror", -] - -[[package]] -name = "nym-sphinx-forwarding" -version = "0.1.0" -dependencies = [ - "nym-sphinx-addressing", - "nym-sphinx-params", - "nym-sphinx-types", -] - -[[package]] -name = "nym-sphinx-framing" -version = "0.1.0" -dependencies = [ - "bytes", - "nym-sphinx-params", - "nym-sphinx-types", - "thiserror", - "tokio-util", -] - -[[package]] -name = "nym-sphinx-params" -version = "0.1.0" -dependencies = [ - "nym-crypto", - "nym-sphinx-types", - "thiserror", -] - -[[package]] -name = "nym-sphinx-types" -version = "0.2.0" -dependencies = [ - "sphinx-packet", -] - -[[package]] -name = "nym-task" -version = "0.1.0" -dependencies = [ - "futures", - "log", - "thiserror", - "tokio", - "wasm-bindgen", - "wasm-bindgen-futures", -] - -[[package]] -name = "nym-topology" -version = "0.1.0" -dependencies = [ - "async-trait", - "bs58", - "log", - "nym-bin-common", - "nym-crypto", - "nym-mixnet-contract-common", - "nym-sphinx-addressing", - "nym-sphinx-types", - "rand 0.7.3", - "thiserror", -] - -[[package]] -name = "nym-validator-client" -version = "0.1.0" -dependencies = [ - "async-trait", - "base64 0.13.1", - "bip39", - "colored 2.0.0", - "cosmrs", - "cosmwasm-std", - "cw3", - "cw4", - "eyre", - "flate2", - "futures", - "itertools", - "log", - "nym-api-requests", - "nym-coconut-bandwidth-contract-common", - "nym-coconut-dkg-common", - "nym-coconut-interface", - "nym-config", - "nym-contracts-common", - "nym-group-contract-common", - "nym-mixnet-contract-common", - "nym-multisig-contract-common", - "nym-network-defaults", - "nym-vesting-contract", - "nym-vesting-contract-common", - "prost", - "reqwest", - "serde", - "serde_json", - "sha2 0.9.9", - "thiserror", - "tokio", - "url", - "zeroize", -] - -[[package]] -name = "nym-vesting-contract" -version = "1.2.0" -dependencies = [ - "cosmwasm-derive", - "cosmwasm-std", - "cw-storage-plus", - "cw2", - "nym-contracts-common", - "nym-mixnet-contract-common", - "nym-vesting-contract-common", - "schemars", - "semver 1.0.17", - "serde", - "thiserror", - "vergen", -] - -[[package]] -name = "nym-vesting-contract-common" -version = "0.3.0" -dependencies = [ - "cosmwasm-std", - "nym-contracts-common", - "nym-mixnet-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "open" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" -dependencies = [ - "pathdiff", - "windows-sys 0.42.0", -] - -[[package]] -name = "openssl" -version = "0.10.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b277f87dacc05a6b709965d1cbafac4649d6ce9f3ce9ceb88508b5666dfec9" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-src" -version = "111.25.2+1.1.1t" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320708a054ad9b3bf314688b5db87cf4d6683d64cfc835e2337924ae62bf4431" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a95792af3c4e0153c3914df2261bedd30a98476f94dc892b67dfe1d89d433a04" -dependencies = [ - "autocfg", - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "pairing" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de9d09263c9966e8196fe0380c9dbbc7ea114b5cf371ba29004bc1f9c6db7f3" -dependencies = [ - "group 0.10.0", -] - -[[package]] -name = "pairing" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2e415e349a3006dd7d9482cdab1c980a845bed1377777d768cb693a44540b42" -dependencies = [ - "group 0.11.0", -] - -[[package]] -name = "pango" -version = "0.16.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94" -dependencies = [ - "bitflags 1.3.2", - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e134909a9a293e04d2cc31928aa95679c5e4df954d0b85483159bd20d8f047f" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "paste" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" - -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - -[[package]] -name = "pbkdf2" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" -dependencies = [ - "crypto-mac 0.11.1", -] - -[[package]] -name = "peg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a" -dependencies = [ - "peg-macros", - "peg-runtime", -] - -[[package]] -name = "peg-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" -dependencies = [ - "peg-runtime", - "proc-macro2", - "quote", -] - -[[package]] -name = "peg-runtime" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088" - -[[package]] -name = "pem" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" -dependencies = [ - "base64 0.13.1", - "once_cell", - "regex", -] - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pest" -version = "2.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7" -dependencies = [ - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a81186863f3d0a27340815be8f2078dd8050b14cd71913db9fbda795e5f707d7" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "pest_meta" -version = "2.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e3b284b1f13a20dc5ebc90aff59a51b8d7137c221131b52a7260c08cbc1cc80" -dependencies = [ - "once_cell", - "pest", - "sha2 0.10.6", -] - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_macros 0.10.0", - "phf_shared 0.10.0", - "proc-macro-hack", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_macros" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" -dependencies = [ - "der", - "spki", - "zeroize", -] - -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - -[[package]] -name = "plist" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590" -dependencies = [ - "base64 0.21.0", - "indexmap", - "line-wrap", - "quick-xml 0.28.1", - "serde", - "time", -] - -[[package]] -name = "png" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" -dependencies = [ - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "pretty_env_logger" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" -dependencies = [ - "env_logger", - "log", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e" -dependencies = [ - "bytes", - "prost-derive", -] - -[[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", -] - -[[package]] -name = "prost-types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" -dependencies = [ - "bytes", - "prost", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quick-xml" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea" -dependencies = [ - "memchr", -] - -[[package]] -name = "quick-xml" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c1a97b1bc42b1d550bfb48d4262153fe400a12bab1511821736f7eac76d7e2" -dependencies = [ - "memchr", -] - -[[package]] -name = "quote" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.8", -] - -[[package]] -name = "rand_distr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9532ada3929fb8b2e9dbe28d1e06c9b2cc65813f074fcb6bd5fbefeff9d56" -dependencies = [ - "num-traits", - "rand 0.7.3", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f851a03551ceefd30132e447f07f96cb7011d6b658374f3aed847333adb5559" - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom 0.2.8", - "redox_syscall", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "reqwest" -version = "0.11.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba30cc2c0cd02af1222ed216ba659cdb2f879dfe3181852fe7c50b1d0005949" -dependencies = [ - "base64 0.21.0", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tokio-socks", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "rfc6979" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" -dependencies = [ - "crypto-bigint", - "hmac 0.11.0", - "zeroize", -] - -[[package]] -name = "rfd" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b344b73e5688c6ad9389095d8e78a877637078f490ae17259937371113ebcb67" -dependencies = [ - "block", - "dispatch", - "glib-sys", - "gobject-sys", - "gtk-sys", - "js-sys", - "log", - "objc", - "objc-foundation", - "objc_id", - "raw-window-handle", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows 0.44.0", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "ripemd160" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "rust-embed" -version = "6.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb133b9a38b5543fad3807fb2028ea47c5f2b566f4f5e28a11902f1a358348b6" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4e0f0ced47ded9a68374ac145edd65a6c1fa13a96447b873660b2a568a0fd7" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn 1.0.109", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "7.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512b0ab6853f7e14e3c8754acb43d6f748bb9ced66aa5915a6553ac8213f7731" -dependencies = [ - "globset", - "sha2 0.10.6", - "walkdir", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver 1.0.17", -] - -[[package]] -name = "rustix" -version = "0.36.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64 0.13.1", - "log", - "ring", - "sct 0.6.1", - "webpki 0.21.4", -] - -[[package]] -name = "rustls" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct 0.7.0", - "webpki 0.22.0", -] - -[[package]] -name = "rustls-native-certs" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" -dependencies = [ - "openssl-probe", - "rustls 0.19.1", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" -dependencies = [ - "base64 0.21.0", -] - -[[package]] -name = "rustversion" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" - -[[package]] -name = "ryu" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" - -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" -dependencies = [ - "windows-sys 0.42.0", -] - -[[package]] -name = "schemars" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" -dependencies = [ - "dyn-clone", - "indexmap", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 1.0.109", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sec1" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" -dependencies = [ - "der", - "generic-array 0.14.6", - "pkcs8", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "selectors" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" -dependencies = [ - "bitflags 1.3.2", - "cssparser", - "derive_more", - "fxhash", - "log", - "matches", - "phf 0.8.0", - "phf_codegen", - "precomputed-hash", - "servo_arc", - "smallvec", - "thin-slice", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" -dependencies = [ - "serde", -] - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-json-wasm" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.5", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "serde_json" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" -dependencies = [ - "itoa 1.0.6", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.5", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa 1.0.6", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" -dependencies = [ - "serde", - "serde_with_macros", -] - -[[package]] -name = "serde_with_macros" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "serialize-to-javascript" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" -dependencies = [ - "serde", - "serde_json", - "serialize-to-javascript-impl", -] - -[[package]] -name = "serialize-to-javascript-impl" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "servo_arc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.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", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" -dependencies = [ - "digest 0.9.0", - "rand_core 0.6.4", -] - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "soup3" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82bc46048125fefd69d30b32b9d263d6556c9ffe82a7a7df181a86d912da5616" -dependencies = [ - "bitflags 1.3.2", - "futures-channel", - "gio", - "glib", - "libc", - "once_cell", - "soup3-sys", -] - -[[package]] -name = "soup3-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "014bbeb1c4cdb30739dc181e8d98b7908f124d9555843afa89b5570aaf4ec62b" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "sphinx-packet" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc43eda802856ee82a7555c7b75ceb9e07451741c7a2f5f23d036020e01189d4" -dependencies = [ - "aes 0.7.5", - "arrayref", - "blake2", - "bs58", - "byteorder", - "chacha", - "curve25519-dalek", - "digest 0.9.0", - "hkdf 0.11.0", - "hmac 0.11.0", - "lioness", - "log", - "rand 0.7.3", - "rand_distr", - "sha2 0.9.9", - "subtle 2.4.1", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5d6e0250b93c8427a177b849d144a96d5acc57006149479403d7861ab721e34" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "sqlformat" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4" -dependencies = [ - "itertools", - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlformat" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" -dependencies = [ - "itertools", - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b" -dependencies = [ - "sqlx-core 0.5.13", - "sqlx-macros 0.5.13", -] - -[[package]] -name = "sqlx" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" -dependencies = [ - "sqlx-core 0.6.3", - "sqlx-macros 0.6.3", -] - -[[package]] -name = "sqlx-core" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" -dependencies = [ - "ahash", - "atoi 0.4.0", - "bitflags 1.3.2", - "byteorder", - "bytes", - "crc 2.1.0", - "crossbeam-queue", - "either", - "event-listener", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "hashlink 0.7.0", - "hex", - "indexmap", - "itoa 1.0.6", - "libc", - "libsqlite3-sys", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rustls 0.19.1", - "sha2 0.10.6", - "smallvec", - "sqlformat 0.1.8", - "sqlx-rt 0.5.13", - "stringprep", - "thiserror", - "tokio-stream", - "url", - "webpki 0.21.4", - "webpki-roots 0.21.1", -] - -[[package]] -name = "sqlx-core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" -dependencies = [ - "ahash", - "atoi 1.0.0", - "bitflags 1.3.2", - "byteorder", - "bytes", - "crc 3.0.1", - "crossbeam-queue", - "dotenvy", - "either", - "event-listener", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "hashlink 0.8.1", - "hex", - "indexmap", - "itoa 1.0.6", - "libc", - "libsqlite3-sys", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rustls 0.20.8", - "rustls-pemfile", - "sha2 0.10.6", - "smallvec", - "sqlformat 0.2.1", - "sqlx-rt 0.6.3", - "stringprep", - "thiserror", - "tokio-stream", - "url", - "webpki-roots 0.22.6", -] - -[[package]] -name = "sqlx-macros" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1" -dependencies = [ - "dotenv", - "either", - "heck 0.4.1", - "once_cell", - "proc-macro2", - "quote", - "sha2 0.10.6", - "sqlx-core 0.5.13", - "sqlx-rt 0.5.13", - "syn 1.0.109", - "url", -] - -[[package]] -name = "sqlx-macros" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" -dependencies = [ - "dotenvy", - "either", - "heck 0.4.1", - "once_cell", - "proc-macro2", - "quote", - "sha2 0.10.6", - "sqlx-core 0.6.3", - "sqlx-rt 0.6.3", - "syn 1.0.109", - "url", -] - -[[package]] -name = "sqlx-rt" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae" -dependencies = [ - "once_cell", - "tokio", - "tokio-rustls 0.22.0", -] - -[[package]] -name = "sqlx-rt" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" -dependencies = [ - "once_cell", - "tokio", - "tokio-rustls 0.23.4", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "state" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" -dependencies = [ - "loom", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot 0.12.1", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - -[[package]] -name = "stringprep" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "strip-ansi-escapes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" -dependencies = [ - "vte", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strum" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ac893c7d471c8a21f31cfe213ec4f6d9afeed25537c772e08ef3f005f8729e" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "subtle-encoding" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" -dependencies = [ - "zeroize", -] - -[[package]] -name = "swift-rs" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eab55f44d02ced20ffa22fd5b4bd083ab59c940c7637d37fec4426b1a01d769" -dependencies = [ - "base64 0.21.0", - "serde", - "serde_json", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c2d1c76a26822187a1fbb5964e3fff108bc208f02e820ab9dac1234f6b388a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - -[[package]] -name = "system-deps" -version = "6.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" -dependencies = [ - "cfg-expr", - "heck 0.4.1", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "tao" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d021218bcb43d4bf42112b1da5f7d8454502ffb188489ba0089fe4e3e34a8f6" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "cc", - "cocoa", - "core-foundation", - "core-graphics", - "crossbeam-channel", - "dispatch", - "gdk", - "gdk-pixbuf", - "gdk-sys", - "gdkx11-sys", - "gio", - "glib", - "glib-sys", - "gtk", - "image", - "instant", - "jni", - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "once_cell", - "parking_lot 0.12.1", - "png", - "raw-window-handle", - "scopeguard", - "serde", - "tao-macros", - "unicode-segmentation", - "uuid 1.3.0", - "windows 0.39.0", - "windows-implement 0.39.0", - "x11-dl", -] - -[[package]] -name = "tao-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b27a4bcc5eb524658234589bdffc7e7bfb996dbae6ce9393bfd39cb4159b445" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tar" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" -dependencies = [ - "filetime", - "libc", - "xattr", -] - -[[package]] -name = "tauri" -version = "2.0.0-alpha.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3eb6015452c545d13d2eb04166376474ed0296bc647229f798937f75d3e9908" -dependencies = [ - "anyhow", - "attohttpc", - "cocoa", - "dirs-next", - "embed_plist", - "encoding_rs", - "flate2", - "futures-util", - "glib", - "glob", - "gtk", - "heck 0.4.1", - "http", - "ignore", - "libc", - "log", - "notify-rust", - "objc", - "once_cell", - "open", - "paste", - "percent-encoding", - "rand 0.8.5", - "raw-window-handle", - "regex", - "rfd", - "semver 1.0.17", - "serde", - "serde_json", - "serde_repr", - "serialize-to-javascript", - "state", - "tar", - "tauri-macros", - "tauri-runtime", - "tauri-runtime-wry", - "tauri-utils", - "tempfile", - "thiserror", - "tokio", - "url", - "uuid 1.3.0", - "webkit2gtk", - "webview2-com", - "windows 0.39.0", -] - -[[package]] -name = "tauri-build" -version = "2.0.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383f94e1b46fb4e249e4eafbd0589ffbe02b241b49dadebd47640598a17e584e" -dependencies = [ - "anyhow", - "cargo_toml", - "filetime", - "heck 0.4.1", - "json-patch 0.3.0", - "semver 1.0.17", - "serde", - "serde_json", - "swift-rs", - "tauri-utils", - "tauri-winres", - "walkdir", -] - -[[package]] -name = "tauri-codegen" -version = "2.0.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7d3464109ac3b7fbff0d78f1bc6b309af49c3d6267ebace90c5a47fbac58cb" -dependencies = [ - "base64 0.13.1", - "brotli", - "ico", - "json-patch 0.2.7", - "plist", - "png", - "proc-macro2", - "quote", - "regex", - "semver 1.0.17", - "serde", - "serde_json", - "sha2 0.10.6", - "tauri-utils", - "thiserror", - "time", - "url", - "uuid 1.3.0", - "walkdir", -] - -[[package]] -name = "tauri-macros" -version = "2.0.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969f323a5c603fe2d113dd1098b6d1a965fd92b78e7bd7f74eb0bb6d7d948183" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "tauri-codegen", - "tauri-utils", -] - -[[package]] -name = "tauri-runtime" -version = "0.13.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7369709ff9ac16a1235906801033804d8ea33e4d023e066f06df0c5c9592a0" -dependencies = [ - "gtk", - "http", - "http-range", - "rand 0.8.5", - "raw-window-handle", - "serde", - "serde_json", - "tauri-utils", - "thiserror", - "uuid 1.3.0", - "webview2-com", - "windows 0.39.0", -] - -[[package]] -name = "tauri-runtime-wry" -version = "0.13.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b914d691ef830dfa98658409eea9d9116240ae0c9d3eb228eed2a9c139a60916" -dependencies = [ - "cocoa", - "gtk", - "percent-encoding", - "rand 0.8.5", - "raw-window-handle", - "tauri-runtime", - "tauri-utils", - "uuid 1.3.0", - "webkit2gtk", - "webview2-com", - "windows 0.39.0", - "wry", -] - -[[package]] -name = "tauri-utils" -version = "2.0.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a488bb0c60e2ce9efbe8283a11299675e01f55caf12304889f8639b3c35713c" -dependencies = [ - "brotli", - "ctor", - "glob", - "heck 0.4.1", - "html5ever", - "infer", - "json-patch 0.3.0", - "kuchiki", - "memchr", - "phf 0.10.1", - "proc-macro2", - "quote", - "semver 1.0.17", - "serde", - "serde_json", - "serde_with", - "thiserror", - "url", - "walkdir", - "windows 0.44.0", -] - -[[package]] -name = "tauri-winres" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b7a78dc04f75fb5ab815e66ac561c81e92a968a40f29e7c21afd152d694fad8" -dependencies = [ - "toml", - "version_check", -] - -[[package]] -name = "tauri-winrt-notification" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58de036c4d2e20717024de2a3c4bf56c301f07b21bc8ef9b57189fce06f1f3b" -dependencies = [ - "quick-xml 0.23.1", - "strum", - "windows 0.39.0", -] - -[[package]] -name = "tempfile" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.42.0", -] - -[[package]] -name = "tendermint" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca881fa4dedd2b46334f13be7fbc8cc1549ba4be5a833fe4e73d1a1baaf7949" -dependencies = [ - "async-trait", - "bytes", - "ed25519", - "ed25519-dalek", - "flex-error", - "futures", - "k256", - "num-traits", - "once_cell", - "prost", - "prost-types", - "ripemd160", - "serde", - "serde_bytes", - "serde_json", - "serde_repr", - "sha2 0.9.9", - "signature", - "subtle 2.4.1", - "subtle-encoding", - "tendermint-proto", - "time", - "zeroize", -] - -[[package]] -name = "tendermint-config" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c56ee93f4e9b7e7daba86d171f44572e91b741084384d0ae00df7991873dfd" -dependencies = [ - "flex-error", - "serde", - "serde_json", - "tendermint", - "toml", - "url", -] - -[[package]] -name = "tendermint-proto" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71f925d74903f4abbdc4af0110635a307b3cb05b175fdff4a7247c14a4d0874" -dependencies = [ - "bytes", - "flex-error", - "num-derive", - "num-traits", - "prost", - "prost-types", - "serde", - "serde_bytes", - "subtle-encoding", - "time", -] - -[[package]] -name = "tendermint-rpc" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63f57ee05a1e927887191c76d1b139de9fa40c180b9f8727ee44377242a6" -dependencies = [ - "async-trait", - "bytes", - "flex-error", - "futures", - "getrandom 0.2.8", - "http", - "hyper", - "hyper-proxy", - "hyper-rustls", - "peg", - "pin-project", - "serde", - "serde_bytes", - "serde_json", - "subtle-encoding", - "tendermint", - "tendermint-config", - "tendermint-proto", - "thiserror", - "time", - "tokio", - "tracing", - "url", - "uuid 0.8.2", - "walkdir", -] - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - -[[package]] -name = "thiserror" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.5", -] - -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" -dependencies = [ - "itoa 1.0.6", - "js-sys", - "libc", - "num_threads", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - -[[package]] -name = "time-macros" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" -dependencies = [ - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.45.0", -] - -[[package]] -name = "tokio-macros" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls 0.19.1", - "tokio", - "webpki 0.21.4", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.8", - "tokio", - "webpki 0.22.0", -] - -[[package]] -name = "tokio-socks" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" -dependencies = [ - "either", - "futures-util", - "thiserror", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e96bb520beab540ab664bd5a9cfeaa1fcd846fa68c830b42e2c8963071251d2" -dependencies = [ - "futures-util", - "log", - "pin-project", - "tokio", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "slab", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_datetime" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" - -[[package]] -name = "toml_edit" -version = "0.19.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "treediff" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761e8d5ad7ce14bb82b7e61ccc0ca961005a275a060b9644a2431aa11553c2ff" -dependencies = [ - "serde_json", -] - -[[package]] -name = "treediff" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" -dependencies = [ - "serde_json", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "ts-rs" -version = "6.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4added4070a4fdf9df03457206cd2e4b12417c8560a2954d91ffcbe60177a56a" -dependencies = [ - "thiserror", - "ts-rs-macros", -] - -[[package]] -name = "ts-rs-macros" -version = "6.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f807fdb3151fee75df7485b901a89624358cd07a67a8fb1a5831bf5a07681ff" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "syn 1.0.109", - "termcolor", -] - -[[package]] -name = "tungstenite" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093" -dependencies = [ - "base64 0.13.1", - "byteorder", - "bytes", - "http", - "httparse", - "input_buffer", - "log", - "rand 0.8.5", - "sha-1", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "universal-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" -dependencies = [ - "crypto-common", - "subtle 2.4.1", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" - -[[package]] -name = "uuid" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" -dependencies = [ - "getrandom 0.2.8", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vergen" -version = "7.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447f9238a4553957277b3ee09d80babeae0811f1b3baefb093de1c0448437a37" -dependencies = [ - "anyhow", - "cfg-if", - "enum-iterator", - "getset", - "git2", - "rustc_version", - "rustversion", - "thiserror", - "time", -] - -[[package]] -name = "version-compare" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "vte" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" -dependencies = [ - "arrayvec 0.5.2", - "utf8parse", - "vte_generate_state_changes", -] - -[[package]] -name = "vte_generate_state_changes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "walkdir" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 1.0.109", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "git+https://github.com/mmsinclair/wasm-timer?rev=b9d1a54ad514c2f230a026afe0dde341e98cd7b6#b9d1a54ad514c2f230a026afe0dde341e98cd7b6" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasm-utils" -version = "0.1.0" -dependencies = [ - "futures", - "js-sys", - "tungstenite", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webkit2gtk" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8eea819afe15eb8dcdff4f19d8bfda540bae84d874c10e6f4b8faf2d6704bd1" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", - "once_cell", - "soup3", - "webkit2gtk-sys", -] - -[[package]] -name = "webkit2gtk-sys" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ac7a95ddd3fdfcaf83d8e513b4b1ad101b95b413b6aa6662ed95f284fc3d5b" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pkg-config", - "soup3-sys", - "system-deps", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" -dependencies = [ - "webpki 0.21.4", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki 0.22.0", -] - -[[package]] -name = "webview2-com" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" -dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows 0.39.0", - "windows-implement 0.39.0", -] - -[[package]] -name = "webview2-com-macros" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "webview2-com-sys" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" -dependencies = [ - "regex", - "serde", - "serde_json", - "thiserror", - "windows 0.39.0", - "windows-bindgen", - "windows-metadata", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" -dependencies = [ - "windows-implement 0.39.0", - "windows_aarch64_msvc 0.39.0", - "windows_i686_gnu 0.39.0", - "windows_i686_msvc 0.39.0", - "windows_x86_64_gnu 0.39.0", - "windows_x86_64_msvc 0.39.0", -] - -[[package]] -name = "windows" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" -dependencies = [ - "windows-implement 0.44.0", - "windows-interface", - "windows-targets", -] - -[[package]] -name = "windows-bindgen" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" -dependencies = [ - "windows-metadata", - "windows-tokens", -] - -[[package]] -name = "windows-implement" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" -dependencies = [ - "syn 1.0.109", - "windows-tokens", -] - -[[package]] -name = "windows-implement" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "windows-interface" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "windows-metadata" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-tokens" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_i686_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "winnow" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "wry" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f8052d7e38efb90235f1895d43904fc152e1efd96383fe57e30ea596738fa3b" -dependencies = [ - "base64 0.13.1", - "block", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dunce", - "gdk", - "gio", - "glib", - "gtk", - "html5ever", - "http", - "kuchiki", - "libc", - "log", - "objc", - "objc_id", - "once_cell", - "serde", - "serde_json", - "sha2 0.10.6", - "soup3", - "tao", - "thiserror", - "url", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows 0.39.0", - "windows-implement 0.39.0", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "x25519-dalek" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" -dependencies = [ - "curve25519-dalek", - "rand_core 0.5.1", - "zeroize", -] - -[[package]] -name = "xattr" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" -dependencies = [ - "libc", -] - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "zeroize" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", -] - -[[patch.unused]] -name = "tauri-mobile" -version = "0.2.5" -source = "git+https://github.com/tauri-apps/tauri-mobile?branch=dev#f9791c2d908ab49bdf44907c53b394c2560f3306" diff --git a/nym-connect/mobile/src-tauri/Cargo.toml b/nym-connect/mobile/src-tauri/Cargo.toml deleted file mode 100644 index 28ea437cdc..0000000000 --- a/nym-connect/mobile/src-tauri/Cargo.toml +++ /dev/null @@ -1,79 +0,0 @@ -[package] -name = "nym-connect" -version = "1.1.12" -description = "nym-connect mobile" -authors = ["Nym Technologies SA"] -license = "" -repository = "" -default-run = "nym-connect" -edition = "2021" -build = "src/build.rs" -rust-version = "1.58" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -crate-type = ["staticlib", "cdylib", "rlib"] - -[build-dependencies] -tauri-build = { version = "2.0.0-alpha.1", features = [] } -# tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = [] } - -# TODO untill new tauri version includes https://github.com/tauri-apps/tauri-mobile/pull/111 -[patch.crates-io] -tauri-mobile = { git = "https://github.com/tauri-apps/tauri-mobile", branch = "dev" } - -[dependencies] -anyhow = "1.0" -bip39 = { version = "2.0.0", features = ["zeroize"] } -dirs = "4.0" -eyre = "0.6.5" -fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs", branch = "release"} -futures = "0.3" -fern = { version = "0.6.1", features = ["colored"] } -itertools = "0.10.5" -log = { version = "0.4", features = ["serde"] } -pretty_env_logger = "0.4.0" -rand = "0.8" -reqwest = { version = "0.11", features = ["json", "socks"] } -rust-embed = { version = "6.4.2", features = ["include-exclude"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -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" -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" - -nym-client-core = { path = "../../../common/client-core", features = ["mobile"], default-features = false } -nym-api-requests = { path = "../../../nym-api/nym-api-requests" } -nym-contracts-common = { path = "../../../common/cosmwasm-smart-contracts/contracts-common"} -nym-config-common = { path = "../../../common/config", package = "nym-config" } -nym-credential-storage = { path = "../../../common/credential-storage" } -nym-crypto = { path = "../../../common/crypto" } -nym-bin-common = { path = "../../../common/bin-common"} -nym-socks5-client-core = { path = "../../../common/socks5-client-core", default-features = false } -nym-task = { path = "../../../common/task" } - -[dev-dependencies] -ts-rs = "6.1.2" -tempfile = "3.3.0" - -[features] -default = ["custom-protocol"] -custom-protocol = ["tauri/custom-protocol"] - -# [profile.dev] -# strip = true -# opt-level = "s" -# lto = true - -[profile.release] -strip = true -opt-level = "s" -lto = true diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/.editorconfig b/nym-connect/mobile/src-tauri/gen/android/nym_connect/.editorconfig deleted file mode 100644 index ebe51d3bfa..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/.editorconfig +++ /dev/null @@ -1,12 +0,0 @@ -# EditorConfig is awesome: https://EditorConfig.org - -# top-most EditorConfig file -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = false -insert_final_newline = false \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/.gitignore b/nym-connect/mobile/src-tauri/gen/android/nym_connect/.gitignore deleted file mode 100644 index edf9888947..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml -.DS_Store -build -/captures -.externalNativeBuild -.cxx -local.properties -app/src/main/jniLibs/** diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/.gitignore b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/.gitignore deleted file mode 100644 index 1b13319aad..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/src/main/java/net/nymtech/nym_connect/generated \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/build.gradle.kts b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/build.gradle.kts deleted file mode 100644 index 3b092d967a..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/build.gradle.kts +++ /dev/null @@ -1,112 +0,0 @@ -plugins { - id("com.android.application") - id("org.jetbrains.kotlin.android") - id("rustPlugin") -} - -android { - compileSdk = 33 - defaultConfig { - manifestPlaceholders["usesCleartextTraffic"] = "false" - applicationId = "net.nymtech.nym_connect" - minSdk = 24 - targetSdk = 33 - versionCode = 1 - versionName = "1.1.9" - } - sourceSets.getByName("main") { - // Vulkan validation layers - val ndkHome = System.getenv("NDK_HOME") - jniLibs.srcDir("${ndkHome}/sources/third_party/vulkan/src/build-android/jniLibs") - } - buildTypes { - getByName("debug") { - manifestPlaceholders["usesCleartextTraffic"] = "true" - isDebuggable = true - isJniDebuggable = true - isMinifyEnabled = false - packagingOptions { - jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so") - - jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so") - - jniLibs.keepDebugSymbols.add("*/x86/*.so") - - jniLibs.keepDebugSymbols.add("*/x86_64/*.so") - } - } - getByName("release") { - isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") - } - } - flavorDimensions.add("abi") - productFlavors { - create("universal") { - val abiList = findProperty("abiList") as? String - - dimension = "abi" - ndk { - abiFilters += abiList?.split(",")?.map { it.trim() } ?: listOf( "arm64-v8a", "armeabi-v7a", "x86", "x86_64", - ) - } - } - create("arm64") { - dimension = "abi" - ndk { - abiFilters += listOf("arm64-v8a") - } - } - - create("arm") { - dimension = "abi" - ndk { - abiFilters += listOf("armeabi-v7a") - } - } - - create("x86") { - dimension = "abi" - ndk { - abiFilters += listOf("x86") - } - } - - create("x86_64") { - dimension = "abi" - ndk { - abiFilters += listOf("x86_64") - } - } - } - - assetPacks += mutableSetOf() - namespace = "net.nymtech.nym_connect" -} - -rust { - rootDirRel = "../../../../" - targets = listOf("aarch64", "armv7", "i686", "x86_64") - arches = listOf("arm64", "arm", "x86", "x86_64") -} - -dependencies { - implementation("androidx.webkit:webkit:1.5.0") - implementation("androidx.appcompat:appcompat:1.5.1") - implementation("com.google.android.material:material:1.7.0") - implementation("androidx.core:core-splashscreen:1.0.0") - testImplementation("junit:junit:4.13.2") - androidTestImplementation("androidx.test.ext:junit:1.1.4") - androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") -} - -afterEvaluate { - android.applicationVariants.all { - tasks["mergeUniversalReleaseJniLibFolders"].dependsOn(tasks["rustBuildRelease"]) - tasks["mergeUniversalDebugJniLibFolders"].dependsOn(tasks["rustBuildDebug"]) - productFlavors.filter{ it.name != "universal" }.forEach { _ -> - val archAndBuildType = name.capitalize() - tasks["merge${archAndBuildType}JniLibFolders"].dependsOn(tasks["rustBuild${archAndBuildType}"]) - } - } -} diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/proguard-rules.pro b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/proguard-rules.pro deleted file mode 100644 index 481bb43481..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/AndroidManifest.xml b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/AndroidManifest.xml deleted file mode 100644 index 1c575ab17a..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/java/net/nymtech/nym_connect/MainActivity.kt b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/java/net/nymtech/nym_connect/MainActivity.kt deleted file mode 100644 index 1162a2e43b..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/java/net/nymtech/nym_connect/MainActivity.kt +++ /dev/null @@ -1,3 +0,0 @@ -package net.nymtech.nym_connect - -class MainActivity : TauriActivity() diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/java/net/nymtech/nym_connect/generated/.gitkeep b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/java/net/nymtech/nym_connect/generated/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1146..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/drawable/ic_launcher_background.xml b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9cbf..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/layout/activity_main.xml b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 4fc244418b..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 036d09bc5f..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 036d09bc5f..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-hdpi/ic_launcher.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 4974445c9a..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png deleted file mode 100644 index 483ace7edf..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 59b2f1db44..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-mdpi/ic_launcher.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 307fb6aab9..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png deleted file mode 100644 index e634763b42..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 37a8d502c8..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 5b412e78a0..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png deleted file mode 100644 index 580c767c54..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 51a588fdee..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 65cefcb196..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 312d818bbe..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 5070e583a0..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 18252905f8..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 514ec36652..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 83e115a87d..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values-night/themes.xml b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values-night/themes.xml deleted file mode 100644 index 894b25a6c9..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/colors.xml b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/colors.xml deleted file mode 100644 index 0018ee4a8a..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - #1d2125 - #424242 - #4caf50 - #1b5e20 - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/ic_launcher_background.xml b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/ic_launcher_background.xml deleted file mode 100644 index a3b9258141..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #1D2125 - \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/strings.xml b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/strings.xml deleted file mode 100644 index 74cf5d6f34..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Nym Connect - \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/themes.xml b/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/themes.xml deleted file mode 100644 index 894b25a6c9..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/build.gradle.kts b/nym-connect/mobile/src-tauri/gen/android/nym_connect/build.gradle.kts deleted file mode 100644 index 8c6fe5848c..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/build.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - repositories { - google() - mavenCentral() - } - dependencies { - classpath("com.android.tools.build:gradle:7.3.1") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10") - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -tasks.register("clean").configure { - delete("build") -} - diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/buildSrc/build.gradle.kts b/nym-connect/mobile/src-tauri/gen/android/nym_connect/buildSrc/build.gradle.kts deleted file mode 100644 index 45981f7511..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/buildSrc/build.gradle.kts +++ /dev/null @@ -1,23 +0,0 @@ -plugins { - `kotlin-dsl` -} - -gradlePlugin { - plugins { - create("pluginsForCoolKids") { - id = "rustPlugin" - implementationClass = "net.nymtech.RustPlugin" - } - } -} - -repositories { - google() - mavenCentral() -} - -dependencies { - compileOnly(gradleApi()) - implementation("com.android.tools.build:gradle:7.3.1") -} - diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/buildSrc/src/main/java/net/nymtech/nym_connect/kotlin/BuildTask.kt b/nym-connect/mobile/src-tauri/gen/android/nym_connect/buildSrc/src/main/java/net/nymtech/nym_connect/kotlin/BuildTask.kt deleted file mode 100644 index 5f63ba21c5..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/buildSrc/src/main/java/net/nymtech/nym_connect/kotlin/BuildTask.kt +++ /dev/null @@ -1,54 +0,0 @@ -package net.nymtech - -import java.io.File -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.logging.LogLevel -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity -import org.gradle.api.tasks.TaskAction - -open class BuildTask : DefaultTask() { - @InputDirectory - @PathSensitive(PathSensitivity.RELATIVE) - var rootDirRel: File? = null - - @Input - var target: String? = null - - @Input - var release: Boolean? = null - - @TaskAction - fun build() { - val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") - val target = target ?: throw GradleException("target cannot be null") - val release = release ?: throw GradleException("release cannot be null") - val nodeExecutable = System.getenv("NODE_TAURI_CLI") - val home = (System.getenv("HOME") ?: "") - val cargoHome = (System.getenv("CARGO_HOME") ?: "$home/.cargo") - val rustExecutable = "$cargoHome/bin/cargo-tauri" - val tauriCli = when { - !nodeExecutable.isNullOrEmpty() && File(nodeExecutable).isFile() -> nodeExecutable - File(rustExecutable).isFile() -> rustExecutable - else -> throw GradleException("couldn't find tauri-cli executable") - } - println("gradle Rust plugin, using tauri cli executable: $tauriCli") - project.exec { - workingDir(File(project.projectDir, rootDirRel.path)) - executable(tauriCli) - args(listOf("android", "android-studio-script")) - if (project.logger.isEnabled(LogLevel.DEBUG)) { - args("-vv") - } else if (project.logger.isEnabled(LogLevel.INFO)) { - args("-v") - } - if (release) { - args("--release") - } - args(listOf("--target", target)) - }.assertNormalExitValue() - } -} \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/buildSrc/src/main/java/net/nymtech/nym_connect/kotlin/RustPlugin.kt b/nym-connect/mobile/src-tauri/gen/android/nym_connect/buildSrc/src/main/java/net/nymtech/nym_connect/kotlin/RustPlugin.kt deleted file mode 100644 index 5e8dcdb3ac..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/buildSrc/src/main/java/net/nymtech/nym_connect/kotlin/RustPlugin.kt +++ /dev/null @@ -1,59 +0,0 @@ -package net.nymtech - -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.Plugin -import org.gradle.api.Project -import java.io.File -import java.util.* - -const val TASK_GROUP = "rust" - -open class Config { - var rootDirRel: String? = null - var targets: List? = null - var arches: List? = null -} - -open class RustPlugin : Plugin { - private lateinit var config: Config - - override fun apply(project: Project) { - config = project.extensions.create("rust", Config::class.java) - project.afterEvaluate { - if (config.targets == null) { - throw GradleException("targets cannot be null") - } - if (config.arches == null) { - throw GradleException("arches cannot be null") - } - for (profile in listOf("debug", "release")) { - val profileCapitalized = profile.capitalize(Locale.ROOT) - val buildTask = project.tasks.maybeCreate( - "rustBuild$profileCapitalized", - DefaultTask::class.java - ).apply { - group = TASK_GROUP - description = "Build dynamic library in $profile mode for all targets" - } - for (targetPair in config.targets!!.withIndex()) { - val targetName = targetPair.value - val targetArch = config.arches!![targetPair.index] - val targetArchCapitalized = targetArch.capitalize(Locale.ROOT) - val targetBuildTask = project.tasks.maybeCreate( - "rustBuild$targetArchCapitalized$profileCapitalized", - BuildTask::class.java - ).apply { - group = TASK_GROUP - description = "Build dynamic library in $profile mode for $targetArch" - rootDirRel = config.rootDirRel?.let { File(it) } - target = targetName - release = profile == "release" - } - buildTask.dependsOn(targetBuildTask) - project.tasks.findByName("preBuild")?.mustRunAfter(targetBuildTask) - } - } - } - } -} diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradle.properties b/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradle.properties deleted file mode 100644 index cd0519bb2a..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradle.properties +++ /dev/null @@ -1,23 +0,0 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app"s APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true -# Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official -# Enables namespacing of each library's R class so that its R class includes only the -# resources declared in the library itself and none from the library's dependencies, -# thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradle/wrapper/gradle-wrapper.jar b/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e708b1c023..0000000000 Binary files a/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradle/wrapper/gradle-wrapper.properties b/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index de8c362b62..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Tue May 10 19:22:52 CST 2022 -distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip -distributionPath=wrapper/dists -zipStorePath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradlew b/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradlew deleted file mode 100755 index 4f906e0c81..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradlew +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradlew.bat b/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradlew.bat deleted file mode 100644 index 107acd32c4..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/nym-connect/mobile/src-tauri/gen/android/nym_connect/settings.gradle b/nym-connect/mobile/src-tauri/gen/android/nym_connect/settings.gradle deleted file mode 100644 index e7b4def49c..0000000000 --- a/nym-connect/mobile/src-tauri/gen/android/nym_connect/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app' diff --git a/nym-connect/mobile/src-tauri/icons/128x128.png b/nym-connect/mobile/src-tauri/icons/128x128.png deleted file mode 100644 index 12daccf30f..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/128x128.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/128x128@2x.png b/nym-connect/mobile/src-tauri/icons/128x128@2x.png deleted file mode 100644 index d98b024d26..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/128x128@2x.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/32x32.png b/nym-connect/mobile/src-tauri/icons/32x32.png deleted file mode 100644 index 5463a8ff1c..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/32x32.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/Square107x107Logo.png b/nym-connect/mobile/src-tauri/icons/Square107x107Logo.png deleted file mode 100644 index 468b6cf29b..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/Square107x107Logo.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/Square142x142Logo.png b/nym-connect/mobile/src-tauri/icons/Square142x142Logo.png deleted file mode 100644 index ce58b768ca..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/Square142x142Logo.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/Square150x150Logo.png b/nym-connect/mobile/src-tauri/icons/Square150x150Logo.png deleted file mode 100644 index b6bc59dfcf..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/Square150x150Logo.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/Square284x284Logo.png b/nym-connect/mobile/src-tauri/icons/Square284x284Logo.png deleted file mode 100644 index 26311d4604..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/Square284x284Logo.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/Square30x30Logo.png b/nym-connect/mobile/src-tauri/icons/Square30x30Logo.png deleted file mode 100644 index 8916d14e49..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/Square30x30Logo.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/Square310x310Logo.png b/nym-connect/mobile/src-tauri/icons/Square310x310Logo.png deleted file mode 100644 index 6fd3c36def..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/Square310x310Logo.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/Square44x44Logo.png b/nym-connect/mobile/src-tauri/icons/Square44x44Logo.png deleted file mode 100644 index e161bd7c4a..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/Square44x44Logo.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/Square71x71Logo.png b/nym-connect/mobile/src-tauri/icons/Square71x71Logo.png deleted file mode 100644 index 45b5126bf6..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/Square71x71Logo.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/Square89x89Logo.png b/nym-connect/mobile/src-tauri/icons/Square89x89Logo.png deleted file mode 100644 index 386c8bf119..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/Square89x89Logo.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/StoreLogo.png b/nym-connect/mobile/src-tauri/icons/StoreLogo.png deleted file mode 100644 index 692f1a03f2..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/StoreLogo.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/icon.icns b/nym-connect/mobile/src-tauri/icons/icon.icns deleted file mode 100644 index 129da9bf18..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/icon.icns and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/icon.ico b/nym-connect/mobile/src-tauri/icons/icon.ico deleted file mode 100644 index 6164f98f9c..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/icon.ico and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/icon.png b/nym-connect/mobile/src-tauri/icons/icon.png deleted file mode 100644 index fa552c309a..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/icon.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/tray_icon.ico b/nym-connect/mobile/src-tauri/icons/tray_icon.ico deleted file mode 100644 index 25785da3d9..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/tray_icon.ico and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/icons/tray_icon.png b/nym-connect/mobile/src-tauri/icons/tray_icon.png deleted file mode 100644 index afcb965219..0000000000 Binary files a/nym-connect/mobile/src-tauri/icons/tray_icon.png and /dev/null differ diff --git a/nym-connect/mobile/src-tauri/src/build.rs b/nym-connect/mobile/src-tauri/src/build.rs deleted file mode 100644 index 261851f6b6..0000000000 --- a/nym-connect/mobile/src-tauri/src/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - tauri_build::build(); -} diff --git a/nym-connect/mobile/src-tauri/src/config/mod.rs b/nym-connect/mobile/src-tauri/src/config/mod.rs deleted file mode 100644 index ba0d2ebf13..0000000000 --- a/nym-connect/mobile/src-tauri/src/config/mod.rs +++ /dev/null @@ -1,160 +0,0 @@ -use crate::{ - error::{BackendError, Result}, - state::State, -}; -use nym_client_core::{client::key_manager::KeyManager, config::Config as BaseClientConfig}; -use nym_config_common::NymConfig; -use nym_credential_storage::ephemeral_storage::EphemeralStorage; -use nym_crypto::asymmetric::identity; -use nym_socks5_client_core::config::{Config as Socks5Config, Socks5}; -use std::path::PathBuf; -use std::sync::Arc; -use tap::TapFallible; -use tokio::sync::RwLock; - -static SOCKS5_CONFIG_ID: &str = "nym-connect"; - -pub fn socks5_config_id_appended_with(gateway_id: &str) -> Result { - use std::fmt::Write as _; - let mut id = SOCKS5_CONFIG_ID.to_string(); - write!(id, "-{gateway_id}")?; - Ok(id) -} - -#[tauri::command] -pub async fn get_config_id(state: tauri::State<'_, Arc>>) -> Result { - state.read().await.get_config_id() -} - -#[tauri::command] -pub fn get_config_file_location() -> Result { - Err(BackendError::CouldNotGetConfigFilename) -} - -#[derive(Debug)] -pub struct Config { - pub socks5: Socks5Config, -} - -impl Config { - pub fn new>(id: S, provider_mix_address: S) -> Self { - Config { - socks5: Socks5Config::new(id, provider_mix_address), - } - } - - #[allow(unused)] - pub fn new_with_port>(id: S, provider_mix_address: S, port: u16) -> Self { - Config { - socks5: Socks5Config::new(id, provider_mix_address).with_port(port), - } - } - - pub fn get_config(&self) -> &Socks5Config { - &self.socks5 - } - - pub fn get_socks5(&self) -> &Socks5 { - self.socks5.get_socks5() - } - - #[allow(unused)] - pub fn get_socks5_mut(&mut self) -> &mut Socks5 { - self.socks5.get_socks5_mut() - } - - pub fn get_base(&self) -> &BaseClientConfig { - self.socks5.get_base() - } - - pub fn get_base_mut(&mut self) -> &mut BaseClientConfig { - self.socks5.get_base_mut() - } - - pub async fn init( - service_provider: &str, - chosen_gateway_id: &str, - ) -> Result<(Config, KeyManager)> { - log::info!("Initialising..."); - - let service_provider = service_provider.to_owned(); - let chosen_gateway_id = chosen_gateway_id.to_owned(); - - // The client initialization was originally not written for this use case, so there are - // lots of ways it can panic. Until we have proper error handling in the init code for the - // clients we'll catch any panics here by spawning a new runtime in a separate thread. - let (config, keys) = std::thread::spawn(move || { - tokio::runtime::Runtime::new() - .expect("Failed to create tokio runtime") - .block_on( - async move { init_socks5_config(service_provider, chosen_gateway_id).await }, - ) - }) - .join() - .map_err(|_| BackendError::InitializationPanic)??; - - log::info!("Configuration saved 🚀"); - Ok((config, keys)) - } -} - -pub async fn init_socks5_config( - provider_address: String, - chosen_gateway_id: String, -) -> Result<(Config, KeyManager)> { - log::trace!("Initialising client..."); - let mut config = Config::new(SOCKS5_CONFIG_ID, &provider_address); - - if let Ok(raw_validators) = std::env::var(nym_config_common::defaults::var_names::NYM_API) { - config - .get_base_mut() - .set_custom_nym_apis(nym_config_common::parse_urls(&raw_validators)); - } - - let nym_api_endpoints = config.get_base().get_nym_api_endpoints(); - - let chosen_gateway_id = identity::PublicKey::from_base58_string(chosen_gateway_id) - .map_err(|_| BackendError::UnableToParseGateway)?; - - let mut key_manager = nym_client_core::init::new_client_keys(); - - // Setup gateway and register a new key each time - let gateway = nym_client_core::init::register_with_gateway::( - &mut key_manager, - nym_api_endpoints, - Some(chosen_gateway_id), - false, - ) - .await?; - - config.get_base_mut().set_gateway_endpoint(gateway); - - print_saved_config(&config); - - let address = *key_manager.identity_keypair().public_key(); - log::info!("The address of this client is: {}", address); - - Ok((config, key_manager)) -} - -fn print_saved_config(config: &Config) { - log::info!( - "Saved configuration file to {:?}", - config.get_config().get_config_file_save_location() - ); - log::info!("Gateway id: {}", config.get_base().get_gateway_id()); - log::info!("Gateway owner: {}", config.get_base().get_gateway_owner()); - log::info!( - "Gateway listener: {}", - config.get_base().get_gateway_listener() - ); - log::info!( - "Service provider address: {}", - config.get_socks5().get_provider_mix_address() - ); - log::info!( - "Service provider port: {}", - config.get_socks5().get_listening_port() - ); - log::info!("Client configuration completed."); -} diff --git a/nym-connect/mobile/src-tauri/src/error.rs b/nym-connect/mobile/src-tauri/src/error.rs deleted file mode 100644 index 58f2252ed1..0000000000 --- a/nym-connect/mobile/src-tauri/src/error.rs +++ /dev/null @@ -1,86 +0,0 @@ -use nym_client_core::error::ClientCoreError; -use serde::{Serialize, Serializer}; -use thiserror::Error; - -#[allow(unused)] -#[derive(Error, Debug)] -pub enum BackendError { - #[error("{source}")] - ReqwestError { - #[from] - source: reqwest::Error, - }, - #[error("I/O error: {source}")] - IoError { - #[from] - source: std::io::Error, - }, - #[error("string formatting error: {source}")] - FmtError { - #[from] - source: std::fmt::Error, - }, - #[error("tauri error: {source}")] - TauriError { - #[from] - source: tauri::Error, - }, - #[error("{source}")] - TauriApiError { - #[from] - source: tauri::api::Error, - }, - #[error("{source}")] - SerdeJsonError { - #[from] - source: serde_json::Error, - }, - #[error("{source}")] - ClientCoreError { - #[from] - source: ClientCoreError, - }, - - #[error("could not send disconnect signal to the SOCKS5 client")] - CoundNotSendDisconnectSignal, - #[error("no service provider set")] - NoServiceProviderSet, - #[error("no gateway provider set")] - NoGatewaySet, - #[error("initialization failed with a panic")] - InitializationPanic, - #[error("could not get config id before gateway is set")] - CouldNotGetIdWithoutGateway, - #[error("could not initialize without gateway set")] - CouldNotInitWithoutGateway, - #[error("could not initialize without service provider set")] - CouldNotInitWithoutServiceProvider, - #[error("could not get file name")] - CouldNotGetFilename, - #[error("could not get config file location")] - CouldNotGetConfigFilename, - #[error("could not load existing gateway configuration")] - CouldNotLoadExistingGatewayConfiguration(std::io::Error), - #[error("unable to open a new window")] - NewWindowError, - #[error("unable to parse the specified gateway")] - UnableToParseGateway, - - #[error("HTTP get request failed: {status_code}")] - RequestFail { - url: reqwest::Url, - status_code: reqwest::StatusCode, - }, -} - -impl Serialize for BackendError { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: Serializer, - { - serializer.collect_str(self) - } -} - -// Local crate level Result alias -pub(crate) type Result = std::result::Result; diff --git a/nym-connect/mobile/src-tauri/src/events.rs b/nym-connect/mobile/src-tauri/src/events.rs deleted file mode 100644 index fb9186913f..0000000000 --- a/nym-connect/mobile/src-tauri/src/events.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::sync::Arc; - -use nym_client_core::error::ClientCoreStatusMessage; -use nym_task::manager::TaskStatus; -use tauri::async_runtime::RwLock; - -use crate::{ - state::{GatewayConnectivity, State}, - tasks, -}; - -#[derive(Clone, serde::Serialize)] -struct Payload { - title: String, - message: String, -} - -impl Payload { - fn new(title: String, message: String) -> Self { - Self { title, message } - } -} - -pub fn emit_event(event: &str, title: &str, msg: &str, window: &tauri::Window) { - if let Err(err) = window.emit(event, Payload::new(title.into(), msg.into())) { - log::error!("Failed to emit tauri event: {err}"); - } -} - -pub fn emit_status_event(event: &str, msg: &T, window: &tauri::Window) { - if let Err(err) = window.emit(event, Payload::new("SOCKS5 update".into(), msg.to_string())) { - log::error!("Failed to emit tauri event: {err}"); - } -} - -pub async fn handle_task_status( - task_status: &TaskStatus, - state: &Arc>, - window: &tauri::Window, -) { - match task_status { - TaskStatus::Ready => { - { - let mut state_w = state.write().await; - state_w.mark_connected(window); - } - - emit_status_event("socks5-connected-event", task_status, window); - tasks::start_connection_check(state.clone(), window.clone()); - } - } -} - -pub async fn handle_client_status_message( - client_status_message: &ClientCoreStatusMessage, - state: &Arc>, - window: &tauri::Window, -) { - // TODO: use this instead once we change on the frontend too - let _event_name = match client_status_message { - ClientCoreStatusMessage::GatewayIsSlow | ClientCoreStatusMessage::GatewayIsVerySlow => { - "socks5-gateway-status" - } - }; - - if let Ok(connectivity) = GatewayConnectivity::try_from(client_status_message) { - state.write().await.set_gateway_connectivity(connectivity); - } - - emit_status_event("socks5-status-event", client_status_message, window); -} diff --git a/nym-connect/mobile/src-tauri/src/lib.rs b/nym-connect/mobile/src-tauri/src/lib.rs deleted file mode 100644 index 6be3d9950d..0000000000 --- a/nym-connect/mobile/src-tauri/src/lib.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Remove me after we've tidied up -#![allow(dead_code)] -#![allow(unused_imports)] - -use nym_config_common::defaults::setup_env; -use std::sync::Arc; -use tauri::{App, Manager}; -use tokio::sync::RwLock; - -pub mod config; -mod error; -mod events; -mod logging; -mod models; -pub mod operations; -mod state; -mod tasks; - -pub use state::State; - -#[cfg(mobile)] -mod mobile; -#[cfg(mobile)] -pub use mobile::*; - -pub type SetupHook = Box Result<(), Box> + Send>; - -#[derive(Default)] -pub struct AppBuilder { - setup: Option, -} - -impl AppBuilder { - pub fn new() -> Self { - Self::default() - } - - #[must_use] - pub fn setup(mut self, setup: F) -> Self - where - F: FnOnce(&mut App) -> Result<(), Box> + Send + 'static, - { - self.setup.replace(Box::new(setup)); - self - } - - pub fn run(self) { - 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}"); - } - - let setup = self.setup; - tauri::Builder::default() - .manage(Arc::new(RwLock::new(State::default()))) - .invoke_handler(tauri::generate_handler![ - crate::config::get_config_file_location, - crate::config::get_config_id, - crate::operations::connection::connect::get_gateway, - crate::operations::connection::connect::get_service_provider, - crate::operations::connection::connect::set_gateway, - crate::operations::connection::connect::set_service_provider, - crate::operations::connection::connect::start_connecting, - crate::operations::connection::disconnect::start_disconnecting, - crate::operations::connection::status::get_connection_health_check_status, - crate::operations::connection::status::get_connection_status, - 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::export::export_keys, - ]) - .setup(move |app| { - if let Some(setup) = setup { - logging::setup_logging(app.app_handle())?; - (setup)(app)?; - } - Ok(()) - }) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); - } -} diff --git a/nym-connect/mobile/src-tauri/src/logging.rs b/nym-connect/mobile/src-tauri/src/logging.rs deleted file mode 100644 index 3a6ee89e62..0000000000 --- a/nym-connect/mobile/src-tauri/src/logging.rs +++ /dev/null @@ -1,147 +0,0 @@ -use fern::colors::{Color, ColoredLevelConfig}; -use serde::Serialize; -use serde_repr::{Deserialize_repr, Serialize_repr}; -use std::str::FromStr; -use tauri::Manager; -use time::{format_description, OffsetDateTime}; - -fn formatted_time() -> String { - // if we fail to obtain local time according to local offset, fallback to utc - let _now = OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc()); - - // if we're running it in the unit test, use the hardcoded value - #[cfg(test)] - let _now = OffsetDateTime::from_unix_timestamp(1666666666).unwrap(); - - // the unwraps are fine as we know this description is correct - // note: the reason for this very particular format is a very simple one - // it's what we've always been using since we copied it from the example, - // so feel free to update it to whatever - let format = - format_description::parse("[[[year]-[month]-[day]][[[hour]:[minute]:[second]]").unwrap(); - _now.format(&format).unwrap() -} - -pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerError> { - let colors = ColoredLevelConfig::new() - .trace(Color::Magenta) - .debug(Color::Blue) - .info(Color::Green) - .warn(Color::Yellow) - .error(Color::Red); - let base_config = fern::Dispatch::new() - .level(global_level()) - .filter_lowlevel_external_components() - .show_operations(); - - let stdout_config = fern::Dispatch::new() - .format(move |out, message, record| { - out.finish(format_args!( - "{} {:5} {} > {}", - formatted_time(), - colors.color(record.level()), - record.target(), - message, - )) - }) - .chain(std::io::stdout()); - - let tauri_event_config = fern::Dispatch::new() - .format(move |out, message, record| { - out.finish(format_args!( - "{}[{}] {}", - formatted_time(), - record.target(), - message, - )) - }) - .chain(fern::Output::call(move |record| { - let msg = LogMessage { - message: record.args().to_string(), - level: record.level().into(), - }; - app_handle.emit_all("log://log", msg).unwrap(); - })); - - base_config - .chain(stdout_config) - .chain(tauri_event_config) - .apply() -} - -trait FernExt { - fn show_operations(self) -> Self; - fn filter_lowlevel_external_components(self) -> Self; -} - -impl FernExt for fern::Dispatch { - fn show_operations(self) -> Self { - if ::std::env::var("RUST_TRACE_OPERATIONS").is_ok() { - self.level_for("nym_connect::operations", log::LevelFilter::Trace) - } else { - self - } - } - - 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) - .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("rustls", log::LevelFilter::Warn) - .level_for("tokio_util", log::LevelFilter::Warn) - } -} - -fn global_level() -> log::LevelFilter { - if let Ok(s) = ::std::env::var("RUST_LOG") { - log::LevelFilter::from_str(&s).unwrap_or(log::LevelFilter::Info) - } else { - log::LevelFilter::Info - } -} - -#[derive(Debug, Serialize, Clone)] -struct LogMessage { - message: String, - level: LogLevel, -} - -// Serialize to u16 instead of strings. -#[derive(Debug, Clone, Deserialize_repr, Serialize_repr)] -#[repr(u16)] -enum LogLevel { - Trace = 1, - Debug, - Info, - Warn, - Error, -} - -impl From for LogLevel { - fn from(level: log::Level) -> Self { - match level { - log::Level::Trace => LogLevel::Trace, - log::Level::Debug => LogLevel::Debug, - log::Level::Info => LogLevel::Info, - log::Level::Warn => LogLevel::Warn, - log::Level::Error => LogLevel::Error, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn log_formatting() { - let expected_chrono_formated = "[2022-10-25][02:57:46]".to_string(); - let new_time_based = formatted_time(); - assert_eq!(new_time_based, expected_chrono_formated) - } -} diff --git a/nym-connect/mobile/src-tauri/src/main.rs b/nym-connect/mobile/src-tauri/src/main.rs deleted file mode 100644 index 48484ea1dd..0000000000 --- a/nym-connect/mobile/src-tauri/src/main.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![cfg_attr( - all(not(debug_assertions), target_os = "windows"), - windows_subsystem = "windows" -)] - -use nym_connect::AppBuilder; - -fn main() { - AppBuilder::new().run(); -} diff --git a/nym-connect/mobile/src-tauri/src/mobile.rs b/nym-connect/mobile/src-tauri/src/mobile.rs deleted file mode 100644 index d231976e68..0000000000 --- a/nym-connect/mobile/src-tauri/src/mobile.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[tauri::mobile_entry_point] -fn main() { - super::AppBuilder::new().run(); -} diff --git a/nym-connect/mobile/src-tauri/src/models/mod.rs b/nym-connect/mobile/src-tauri/src/models/mod.rs deleted file mode 100644 index 70b408dc7b..0000000000 --- a/nym-connect/mobile/src-tauri/src/models/mod.rs +++ /dev/null @@ -1,116 +0,0 @@ -use core::fmt; - -use serde::{Deserialize, Serialize}; - -use crate::state::GatewayConnectivity; - -#[cfg_attr(test, derive(ts_rs::TS))] -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] -pub struct ConnectResult { - pub address: String, -} - -#[cfg_attr(test, derive(ts_rs::TS))] -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] -pub struct DisconnectResult { - pub success: bool, -} - -#[cfg_attr(test, derive(ts_rs::TS))] -#[cfg_attr(test, ts(rename_all = "lowercase"))] -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug, Default)] -#[serde(rename_all = "camelCase")] -pub enum ConnectionStatusKind { - #[default] - Disconnected, - Disconnecting, - Connected, - Connecting, -} - -#[cfg_attr(test, derive(ts_rs::TS))] -#[cfg_attr(test, ts(rename_all = "lowercase"))] -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] -#[serde(rename_all = "camelCase")] -pub enum GatewayConnectionStatusKind { - Good, - Bad, - VeryBad, -} - -impl From for GatewayConnectionStatusKind { - fn from(conn: GatewayConnectivity) -> Self { - match conn { - GatewayConnectivity::Good => GatewayConnectionStatusKind::Good, - GatewayConnectivity::Bad { .. } => GatewayConnectionStatusKind::Bad, - GatewayConnectivity::VeryBad { .. } => GatewayConnectionStatusKind::VeryBad, - } - } -} - -#[cfg_attr(test, derive(ts_rs::TS))] -#[cfg_attr(test, ts(rename_all = "lowercase"))] -#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Default)] -pub enum ConnectivityTestResult { - #[default] - NotAvailable, - Success, - Fail, -} - -impl fmt::Display for ConnectionStatusKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ConnectionStatusKind::Disconnected => write!(f, "Disconnected"), - ConnectionStatusKind::Disconnecting => write!(f, "Disconnecting"), - ConnectionStatusKind::Connected => write!(f, "Connected"), - ConnectionStatusKind::Connecting => write!(f, "Connecting"), - } - } -} - -pub const APP_EVENT_CONNECTION_STATUS_CHANGED: &str = "app:connection-status-changed"; - -#[cfg_attr(test, derive(ts_rs::TS))] -#[derive(Clone, Serialize)] -pub struct AppEventConnectionStatusChangedPayload { - pub status: ConnectionStatusKind, -} - -#[cfg_attr(test, derive(ts_rs::TS))] -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DirectoryService { - pub id: String, - pub description: String, - pub items: Vec, -} - -#[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, - pub routing_score: f32, -} - -#[cfg_attr(test, derive(ts_rs::TS))] -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DirectoryServiceProvider { - pub id: String, - pub description: String, - /// 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)] -pub struct PagedResult { - pub page: u32, - pub size: u32, - pub total: i32, - pub items: Vec, -} diff --git a/nym-connect/mobile/src-tauri/src/operations/connection/connect.rs b/nym-connect/mobile/src-tauri/src/operations/connection/connect.rs deleted file mode 100644 index 1d83748698..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/connection/connect.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::{ - error::{BackendError, Result}, - models::ConnectResult, - tasks, State, -}; -use std::sync::Arc; -use tokio::sync::RwLock; - -#[tauri::command] -pub async fn start_connecting( - state: tauri::State<'_, Arc>>, - 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? - }; - - // Setup task for checking status - let state = state.inner().clone(); - tasks::start_disconnect_listener(state.clone(), window.clone(), exit_status_receiver); - tasks::start_status_listener(state, window.clone(), msg_receiver); - - Ok(ConnectResult { - address: "PLACEHOLDER".to_string(), - }) -} - -#[tauri::command] -pub async fn get_service_provider(state: tauri::State<'_, Arc>>) -> Result { - let guard = state.read().await; - guard - .get_service_provider() - .clone() - .ok_or(BackendError::NoServiceProviderSet) -} - -#[tauri::command] -pub async fn set_service_provider( - service_provider: Option, - state: tauri::State<'_, Arc>>, -) -> Result<()> { - log::trace!("Setting service_provider: {:?}", &service_provider); - let mut guard = state.write().await; - guard.set_service_provider(service_provider); - Ok(()) -} - -#[tauri::command] -pub async fn get_gateway(state: tauri::State<'_, Arc>>) -> Result { - let guard = state.read().await; - guard - .get_gateway() - .clone() - .ok_or(BackendError::NoGatewaySet) -} - -#[tauri::command] -pub async fn set_gateway( - gateway: Option, - state: tauri::State<'_, Arc>>, -) -> Result<()> { - log::trace!("Setting gateway: {:?}", &gateway); - let mut guard = state.write().await; - guard.set_gateway(gateway); - Ok(()) -} diff --git a/nym-connect/mobile/src-tauri/src/operations/connection/disconnect.rs b/nym-connect/mobile/src-tauri/src/operations/connection/disconnect.rs deleted file mode 100644 index b75bf49410..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/connection/disconnect.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::error::Result; -use crate::models::ConnectResult; -use crate::State; -use std::sync::Arc; -use tokio::sync::RwLock; - -#[tauri::command] -pub async fn start_disconnecting( - state: tauri::State<'_, Arc>>, - window: tauri::Window, -) -> Result { - log::trace!("Start disconnecting"); - let mut guard = state.write().await; - - guard.start_disconnecting(&window).await?; - - Ok(ConnectResult { - address: "PLACEHOLDER".to_string(), - }) -} diff --git a/nym-connect/mobile/src-tauri/src/operations/connection/health_check.rs b/nym-connect/mobile/src-tauri/src/operations/connection/health_check.rs deleted file mode 100644 index 3f401933a1..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/connection/health_check.rs +++ /dev/null @@ -1,26 +0,0 @@ -use serde::{Deserialize, Serialize}; - -static HEALTH_CHECK_URL: &str = "https://nymtech.net/.wellknown/connect/healthcheck.json"; - -#[derive(Serialize, Deserialize, Debug)] -struct ConnectionSuccess { - status: String, -} - -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!"); - true - } - Ok(res) => { - log::error!("Healthcheck failed with status: {}", res.status); - false - } - Err(err) => { - log::error!("Healthcheck failed: {err}"); - false - } - } -} diff --git a/nym-connect/mobile/src-tauri/src/operations/connection/mod.rs b/nym-connect/mobile/src-tauri/src/operations/connection/mod.rs deleted file mode 100644 index 171d07cd83..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/connection/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod connect; -pub mod disconnect; -pub mod health_check; -pub mod status; diff --git a/nym-connect/mobile/src-tauri/src/operations/connection/status.rs b/nym-connect/mobile/src-tauri/src/operations/connection/status.rs deleted file mode 100644 index 7710524fb5..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/connection/status.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::error::Result; -use crate::tasks; -use std::sync::Arc; - -use tokio::sync::RwLock; - -use crate::models::{ConnectionStatusKind, ConnectivityTestResult, GatewayConnectionStatusKind}; -use crate::state::State; - -#[tauri::command] -pub async fn get_connection_status( - state: tauri::State<'_, Arc>>, -) -> Result { - let state = state.read().await; - Ok(state.get_status()) -} - -#[tauri::command] -pub async fn get_gateway_connection_status( - state: tauri::State<'_, Arc>>, -) -> Result { - let mut state_w = state.write().await; - let gateway_connectivity = state_w.get_gateway_connectivity(); - Ok(gateway_connectivity.into()) -} - -#[tauri::command] -pub async fn get_connection_health_check_status( - state: tauri::State<'_, Arc>>, -) -> Result { - let state = state.read().await; - Ok(state.get_connectivity_test_result()) -} - -// Start a connection check task. This should return with an event within one minute, and update -// the state. -// Trying to run multiple concurrent connection checks probably works but is not supported. -#[tauri::command] -pub fn start_connection_health_check_task( - state: tauri::State<'_, Arc>>, - window: tauri::Window, -) { - tasks::start_connection_check(state.inner().clone(), window); -} diff --git a/nym-connect/mobile/src-tauri/src/operations/directory/mod.rs b/nym-connect/mobile/src-tauri/src/operations/directory/mod.rs deleted file mode 100644 index 0e58035274..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/directory/mod.rs +++ /dev/null @@ -1,92 +0,0 @@ -use itertools::Itertools; - -use crate::error::Result; -use crate::models::{ - DirectoryService, DirectoryServiceProvider, HarbourMasterService, PagedResult, -}; -use nym_api_requests::models::GatewayBondAnnotated; -use nym_contracts_common::types::Percent; - -static SERVICE_PROVIDER_WELLKNOWN_URL: &str = - "https://nymtech.net/.wellknown/connect/service-providers.json"; - -static HARBOUR_MASTER_URL: &str = "https://harbourmaster.nymtech.net/v1/services/?size=100"; - -static GATEWAYS_DETAILED_URL: &str = - "https://validator.nymtech.net/api/v1/status/gateways/detailed"; - -#[tauri::command] -pub async fn get_services() -> Result> { - log::trace!("Fetching services"); - let services_res = reqwest::get(SERVICE_PROVIDER_WELLKNOWN_URL) - .await? - .json::>() - .await?; - log::trace!("Received: {:#?}", services_res); - - log::trace!("Fetching gateways"); - let gateway_res = reqwest::get(GATEWAYS_DETAILED_URL) - .await? - .json::>() - .await?; - log::trace!("Received: {:#?}", gateway_res); - - // TODO: get paged - log::trace!("Fetching active services"); - let active_services = reqwest::get(HARBOUR_MASTER_URL) - .await? - .json::>() - .await?; - log::trace!("Active: {:#?}", active_services); - - let mut filtered: Vec = vec![]; - - for service in &services_res { - let items: _ = service - .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.id, items.len()); - filtered.push(DirectoryService { - id: service.id.clone(), - description: service.description.clone(), - items, - }) - } - - let perf_threshold = Percent::from_percentage_value(90).unwrap(); - - // Use only services that are active AND have a performance of >= 90% - let services_with_good_performance: Vec = filtered - .iter_mut() - .fold(vec![], |mut acc, sp| { - acc.append(&mut sp.items); - acc - }) - .into_iter() - .filter(|sp| { - gateway_res.iter().any(|gateway| { - gateway.gateway_bond.gateway.identity_key == sp.gateway - && gateway.performance >= perf_threshold - }) - }) - .collect(); - - Ok(services_with_good_performance) -} - -#[tauri::command] -pub async fn get_gateways_detailed() -> Result> { - let res = reqwest::get(GATEWAYS_DETAILED_URL) - .await? - .json::>() - .await?; - Ok(res) -} diff --git a/nym-connect/mobile/src-tauri/src/operations/export.rs b/nym-connect/mobile/src-tauri/src/operations/export.rs deleted file mode 100644 index dea876fdd8..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/export.rs +++ /dev/null @@ -1,91 +0,0 @@ -use std::{ffi::OsStr, fs, sync::Arc}; -use tokio::sync::RwLock; - -use crate::{ - error::{BackendError, Result}, - state::State, -}; -use nym_client_core::client::key_manager::KeyManager; -use nym_client_core::config::persistence::keys_paths::ClientKeysPaths; -use nym_crypto::asymmetric::identity; - -pub async fn get_identity_key( - state: &tauri::State<'_, Arc>>, -) -> Result> { - let config = { - let state = state.read().await; - state.load_socks5_config()? - }; - - let paths = ClientKeysPaths::new_from_config(config.get_base()); - let key_manager = KeyManager::load_keys(&paths)?; - let identity_keypair = key_manager.identity_keypair(); - - Ok(identity_keypair) -} - -/// Export the gateway keys as a JSON string blob -#[tauri::command] -pub async fn export_keys(state: tauri::State<'_, Arc>>) -> Result { - let config = { - let state = state.read().await; - state.load_socks5_config()? - }; - - // Get key paths - let ack_key_file = config.get_base().get_ack_key_file(); - let gateway_shared_key_file = config.get_base().get_gateway_shared_key_file(); - - let pub_id_key_file = config.get_base().get_public_identity_key_file(); - let priv_id_key_file = config.get_base().get_private_identity_key_file(); - - let pub_enc_key_file = config.get_base().get_public_encryption_key_file(); - let priv_enc_key_file = config.get_base().get_private_encryption_key_file(); - - // Read file contents - let ack_key = fs::read_to_string(ack_key_file.clone())?; - let gateway_shared_key = fs::read_to_string(gateway_shared_key_file.clone())?; - - let pub_id_key = fs::read_to_string(pub_id_key_file.clone())?; - let priv_id_key = fs::read_to_string(priv_id_key_file.clone())?; - - let pub_enc_key = fs::read_to_string(pub_enc_key_file.clone())?; - let priv_enc_key = fs::read_to_string(priv_enc_key_file.clone())?; - - let ack_key_file = ack_key_file - .file_name() - .map(OsStr::to_string_lossy) - .ok_or(BackendError::CouldNotGetFilename)?; - let gateway_shared_key_file = gateway_shared_key_file - .file_name() - .map(OsStr::to_string_lossy) - .ok_or(BackendError::CouldNotGetFilename)?; - let pub_id_key_file = pub_id_key_file - .file_name() - .map(OsStr::to_string_lossy) - .ok_or(BackendError::CouldNotGetFilename)?; - let priv_id_key_file = priv_id_key_file - .file_name() - .map(OsStr::to_string_lossy) - .ok_or(BackendError::CouldNotGetFilename)?; - let pub_enc_key_file = pub_enc_key_file - .file_name() - .map(OsStr::to_string_lossy) - .ok_or(BackendError::CouldNotGetFilename)?; - let priv_enc_key_file = priv_enc_key_file - .file_name() - .map(OsStr::to_string_lossy) - .ok_or(BackendError::CouldNotGetFilename)?; - - // Format and return as json - let json = serde_json::json!({ - ack_key_file: ack_key, - gateway_shared_key_file: gateway_shared_key, - pub_id_key_file: pub_id_key, - priv_id_key_file: priv_id_key, - pub_enc_key_file: pub_enc_key, - priv_enc_key_file: priv_enc_key, - }); - - Ok(serde_json::to_string_pretty(&json)?) -} diff --git a/nym-connect/mobile/src-tauri/src/operations/help/log.rs b/nym-connect/mobile/src-tauri/src/operations/help/log.rs deleted file mode 100644 index 5b31ea023e..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/help/log.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::error::BackendError; -use tauri::Manager; - -#[cfg(desktop)] -#[tauri::command] -pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), BackendError> { - if let Some(current_log_window) = app_handle.windows().get("log") { - log::info!("Closing log window..."); - if let Err(e) = current_log_window.close() { - log::error!("Unable to close log window: {:?}", e); - } - return Ok(()); - } - - log::info!("Creating log window..."); - match tauri::WindowBuilder::new(&app_handle, "log", tauri::WindowUrl::App("log.html".into())) - .title("Nym Connect Logs") - .build() - { - Ok(window) => { - if let Err(e) = window.set_focus() { - log::error!("Unable to focus log window: {:?}", e); - } - Ok(()) - } - Err(e) => { - log::error!("Unable to create log window: {:?}", e); - Err(BackendError::NewWindowError) - } - } -} diff --git a/nym-connect/mobile/src-tauri/src/operations/help/mod.rs b/nym-connect/mobile/src-tauri/src/operations/help/mod.rs deleted file mode 100644 index 52dafe6d0f..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/help/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod log; -pub mod storage; diff --git a/nym-connect/mobile/src-tauri/src/operations/help/storage.rs b/nym-connect/mobile/src-tauri/src/operations/help/storage.rs deleted file mode 100644 index ab47ffa529..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/help/storage.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::error::BackendError; -use serde::Serialize; -use tauri::Manager; - -#[derive(Debug, Serialize, Clone)] -struct ClearStorageEvent { - kind: String, -} - -#[tauri::command] -pub fn help_clear_storage(app_handle: tauri::AppHandle) -> Result<(), BackendError> { - log::info!("Sending event to clear local storage..."); - - let event = ClearStorageEvent { - kind: "local_storage".to_string(), - }; - app_handle.emit_all("help://clear-storage", event)?; - - Ok(()) -} diff --git a/nym-connect/mobile/src-tauri/src/operations/http.rs b/nym-connect/mobile/src-tauri/src/operations/http.rs deleted file mode 100644 index 5cfe3e1f53..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/http.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::error::{BackendError, Result}; -use serde::de::DeserializeOwned; -use tap::TapFallible; - -pub async fn socks5_get(url: U) -> Result -where - U: reqwest::IntoUrl + std::fmt::Display, - T: DeserializeOwned, -{ - log::info!(">>> GET {url}"); - let proxy = reqwest::Proxy::all("socks5h://127.0.0.1:1080")?; - let client = reqwest::Client::builder() - .proxy(proxy) - .timeout(std::time::Duration::from_secs(20)) - .build()?; - - let resp = client.get(url).send().await.tap_err(|err| { - log::error!("<<< Request send error: {err}"); - })?; - - if resp.status().is_client_error() || resp.status().is_server_error() { - log::error!("<<< {}", resp.status()); - return Err(BackendError::RequestFail { - url: resp.url().clone(), - status_code: resp.status(), - }); - } - - let response_body = resp.text().await.tap_err(|err| { - log::error!("<<< Request error: {err}"); - })?; - log::info!("<<< {response_body}"); - - Ok(serde_json::from_str(&response_body).tap_err(|err| { - log::error!("<<< JSON parsing error: {err}"); - })?) -} diff --git a/nym-connect/mobile/src-tauri/src/operations/mod.rs b/nym-connect/mobile/src-tauri/src/operations/mod.rs deleted file mode 100644 index 7e8f09657b..0000000000 --- a/nym-connect/mobile/src-tauri/src/operations/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod connection; -pub mod directory; -pub mod export; -pub mod help; -pub mod http; diff --git a/nym-connect/mobile/src-tauri/src/state.rs b/nym-connect/mobile/src-tauri/src/state.rs deleted file mode 100644 index 36ec356779..0000000000 --- a/nym-connect/mobile/src-tauri/src/state.rs +++ /dev/null @@ -1,262 +0,0 @@ -use std::time::Duration; - -use ::nym_config_common::NymConfig; -use futures::SinkExt; -use nym_client_core::client::key_manager::KeyManager; -use nym_client_core::error::ClientCoreStatusMessage; -use tap::TapFallible; -use tauri::Manager; - -use nym_socks5_client_core::{ - config::Config as Socks5Config, Socks5ControlMessage, Socks5ControlMessageSender, -}; -use tokio::time::Instant; - -use crate::{ - config::{self, socks5_config_id_appended_with, Config}, - error::{BackendError, Result}, - models::{ - AppEventConnectionStatusChangedPayload, ConnectionStatusKind, ConnectivityTestResult, - APP_EVENT_CONNECTION_STATUS_CHANGED, - }, - tasks::{self, ExitStatusReceiver}, -}; - -// The client will emit messages if the connection to the gateway is poor (or the gateway can't -// keep up with the messages we are sendind). If no messages about this has been received for a -// certain duration then we assume it's all good. -const GATEWAY_CONNECTIVITY_TIMEOUT_SECS: u64 = 20; - -#[derive(Clone, Copy, Default)] -pub enum GatewayConnectivity { - #[default] - Good, - Bad { - when: Instant, - }, - VeryBad { - when: Instant, - }, -} - -impl TryFrom<&ClientCoreStatusMessage> for GatewayConnectivity { - type Error = BackendError; - - fn try_from(value: &ClientCoreStatusMessage) -> Result { - let conn = match value { - ClientCoreStatusMessage::GatewayIsSlow => GatewayConnectivity::Bad { - when: Instant::now(), - }, - ClientCoreStatusMessage::GatewayIsVerySlow => GatewayConnectivity::VeryBad { - when: Instant::now(), - }, - }; - Ok(conn) - } -} - -#[derive(Default)] -pub struct State { - /// The current connection status - status: ConnectionStatusKind, - - /// The service provider - service_provider: Option, - - /// The gateway used. Note that this is also used to create the configuration id - gateway: Option, - - /// Channel that is used to send command messages to the SOCKS5 client, such as to disconnect - socks5_client_sender: Option, - - /// The client will periodically report connectivity to the gateway it's connected to. Unless - /// we get a status message from the client we assume it's good. - gateway_connectivity: GatewayConnectivity, - - /// 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, -} - -impl State { - pub fn new() -> Self { - State { - status: ConnectionStatusKind::Disconnected, - service_provider: None, - gateway: None, - socks5_client_sender: None, - gateway_connectivity: GatewayConnectivity::Good, - connectivity_test_result: ConnectivityTestResult::NotAvailable, - } - } - - pub fn get_gateway_connectivity(&mut self) -> GatewayConnectivity { - self.gateway_connectivity = match self.gateway_connectivity { - c @ (GatewayConnectivity::Bad { when } | GatewayConnectivity::VeryBad { when }) => { - if Instant::now() > when + Duration::from_secs(GATEWAY_CONNECTIVITY_TIMEOUT_SECS) { - GatewayConnectivity::Good - } else { - c - } - } - current => current, - }; - self.gateway_connectivity - } - - pub fn set_gateway_connectivity(&mut self, gateway_connectivity: GatewayConnectivity) { - self.gateway_connectivity = gateway_connectivity - } - - pub fn get_connectivity_test_result(&self) -> ConnectivityTestResult { - self.connectivity_test_result - } - - pub fn set_connectivity_test_result( - &mut self, - connectivity_test_result: ConnectivityTestResult, - ) { - self.connectivity_test_result = connectivity_test_result; - } - - pub fn get_status(&self) -> ConnectionStatusKind { - self.status.clone() - } - - fn set_state(&mut self, status: ConnectionStatusKind, window: &tauri::Window) { - log::info!("{status}"); - self.status = status.clone(); - window - .emit_all( - APP_EVENT_CONNECTION_STATUS_CHANGED, - AppEventConnectionStatusChangedPayload { status }, - ) - .tap_err(|err| log::warn!("{err}")) - .ok(); - } - - pub fn get_service_provider(&self) -> &Option { - &self.service_provider - } - - pub fn set_service_provider(&mut self, provider: Option) { - self.service_provider = provider; - } - - pub fn get_gateway(&self) -> &Option { - &self.gateway - } - - pub fn set_gateway(&mut self, gateway: Option) { - self.gateway = gateway; - } - - /// The effective config id is the static config id appended with the id of the gateway - pub fn get_config_id(&self) -> Result { - self.get_gateway() - .as_ref() - .ok_or(BackendError::CouldNotGetIdWithoutGateway) - .and_then(|gateway_id| socks5_config_id_appended_with(gateway_id)) - } - - pub fn load_socks5_config(&self) -> Result { - let id = self.get_config_id()?; - let config = Socks5Config::load_from_file(&id) - .tap_err(|_| log::warn!("Failed to load configuration file"))?; - Ok(config) - } - - /// Start connecting by first creating a config file, followed by starting a thread running the - /// SOCKS5 client. - pub async fn start_connecting( - &mut self, - window: &tauri::Window, - ) -> Result<(nym_task::StatusReceiver, ExitStatusReceiver)> { - self.set_state(ConnectionStatusKind::Connecting, window); - - let res = self.init_config().await; - match res { - Ok(_) => {} - Err(e) => { - log::error!("Failed to initialize: {e}"); - - // Wait a little to give the user some rudimentary feedback that the click actually - // registered. - tokio::time::sleep(Duration::from_secs(1)).await; - self.set_state(ConnectionStatusKind::Disconnected, window); - return Err(e); - } - }; - let (config, keys) = res.unwrap(); - - // Kick off the main task and get the channel for controlling it - self.start_nym_socks5_client(config, keys) - } - - /// Create a configuration file - async fn init_config(&self) -> Result<(Config, KeyManager)> { - let service_provider = self - .get_service_provider() - .as_ref() - .ok_or(BackendError::CouldNotInitWithoutServiceProvider)?; - let gateway = self - .get_gateway() - .as_ref() - .ok_or(BackendError::CouldNotInitWithoutGateway)?; - log::trace!(" service_provider: {:?}", service_provider); - log::trace!(" gateway: {:?}", gateway); - - config::Config::init(service_provider, gateway).await - } - - /// Spawn a new thread running the SOCKS5 client - fn start_nym_socks5_client( - &mut self, - config: Config, - keys: KeyManager, - ) -> Result<(nym_task::StatusReceiver, ExitStatusReceiver)> { - let id = self.get_config_id()?; - let (control_tx, msg_rx, exit_status_rx, used_gateway) = - tasks::start_nym_socks5_client(&id, config, keys)?; - self.socks5_client_sender = Some(control_tx); - self.gateway = Some(used_gateway.gateway_id); - Ok((msg_rx, exit_status_rx)) - } - - /// Once the SOCKS5 client is operational, the status listener would call this - pub fn mark_connected(&mut self, window: &tauri::Window) { - log::trace!("state::mark_connected"); - self.set_state(ConnectionStatusKind::Connected, window); - } - - /// Disconnect by sending a message to the SOCKS5 client thread. Once it has finished and is - /// disconnected, the disconnect handler will mark it as disconnected. - pub async fn start_disconnecting(&mut self, window: &tauri::Window) -> Result<()> { - log::trace!("state::start_disconnecting"); - self.set_state(ConnectionStatusKind::Disconnecting, window); - - // Send shutdown message - match self.socks5_client_sender { - Some(ref mut sender) => sender - .send(Socks5ControlMessage::Stop) - .await - .map_err(|err| { - log::warn!("Failed trying to send disconnect signal: {err}"); - BackendError::CoundNotSendDisconnectSignal - }), - None => { - log::warn!( - "Trying to disconnect without being able to talk to the SOCKS5 client, \ - is it running?" - ); - Err(BackendError::CoundNotSendDisconnectSignal) - } - } - } - - /// Once the SOCKS5 client has stopped, this should be called by the disconnect handler to mark - /// the state as disconnected. - pub fn mark_disconnected(&mut self, window: &tauri::Window) { - self.set_state(ConnectionStatusKind::Disconnected, window); - } -} diff --git a/nym-connect/mobile/src-tauri/src/tasks.rs b/nym-connect/mobile/src-tauri/src/tasks.rs deleted file mode 100644 index cb00d6f9c9..0000000000 --- a/nym-connect/mobile/src-tauri/src/tasks.rs +++ /dev/null @@ -1,205 +0,0 @@ -use futures::{channel::mpsc, StreamExt}; -use nym_client_core::{ - client::key_manager::KeyManager, - config::{ClientCoreConfigTrait, GatewayEndpointConfig}, - error::ClientCoreStatusMessage, -}; -use nym_task::manager::TaskStatus; -use std::sync::Arc; -use tap::TapFallible; -use tokio::sync::RwLock; - -use nym_config_common::NymConfig; -use nym_socks5_client_core::NymClient as Socks5NymClient; -use nym_socks5_client_core::{config::Config as Socks5Config, Socks5ControlMessageSender}; - -use crate::{ - config::Config, - error::Result, - events::{self, emit_event, emit_status_event}, - models::{ConnectionStatusKind, ConnectivityTestResult}, - operations, - state::State, -}; - -pub type ExitStatusReceiver = futures::channel::oneshot::Receiver; - -/// Status messages sent by the SOCKS5 client task to the main tauri task. -#[derive(Debug)] -pub enum Socks5ExitStatusMessage { - /// The SOCKS5 task successfully stopped - Stopped, - /// The SOCKS5 task failed to start - Failed(Box), -} - -/// The main SOCKS5 client task. It loads the configuration from file determined by the `id`. -pub fn start_nym_socks5_client( - id: &str, - config: Config, - keys: KeyManager, -) -> Result<( - Socks5ControlMessageSender, - nym_task::StatusReceiver, - ExitStatusReceiver, - GatewayEndpointConfig, -)> { - log::info!("Loading config from file: {id}"); - let used_gateway = config.get_base().get_gateway_endpoint().clone(); - - let socks5_client = Socks5NymClient::new_with_keys(config.socks5, Some(keys)); - log::info!("Starting socks5 client"); - - // Channel to send control messages to the socks5 client - let (socks5_ctrl_tx, socks5_ctrl_rx) = mpsc::unbounded(); - - // Channel to send status update messages from the background socks5 task to the frontend. - let (socks5_status_tx, socks5_status_rx) = mpsc::channel(128); - - // Channel to signal back to the main task when the socks5 client finishes, and why - let (socks5_exit_tx, socks5_exit_rx) = futures::channel::oneshot::channel(); - - // Spawn a separate runtime for the socks5 client so we can forcefully terminate. - // Once we can gracefully shutdown the socks5 client we can get rid of this. - // The status channel is used to both get the state of the task, and if it's closed, to check - // for panic. - std::thread::spawn(|| { - let result = tokio::runtime::Runtime::new() - .expect("Failed to create runtime for SOCKS5 client") - .block_on(async move { - socks5_client - .run_and_listen(socks5_ctrl_rx, socks5_status_tx) - .await - }); - - if let Err(err) = result { - log::error!("SOCKS5 proxy failed: {err}"); - socks5_exit_tx - .send(Socks5ExitStatusMessage::Failed(err)) - .expect("Failed to send status message back to main task"); - return; - } - - log::info!("SOCKS5 task finished"); - socks5_exit_tx - .send(Socks5ExitStatusMessage::Stopped) - .expect("Failed to send status message back to main task"); - }); - - Ok(( - socks5_ctrl_tx, - socks5_status_rx, - socks5_exit_rx, - used_gateway, - )) -} - -pub fn start_connection_check(state: Arc>, window: tauri::Window) { - log::debug!("Starting connection check handler"); - tokio::spawn(async move { - if state.read().await.get_status() != ConnectionStatusKind::Connected { - log::error!("SOCKS5 connection status check failed: not connected"); - return; - } - - log::info!("Running connection health check"); - if operations::connection::health_check::run_health_check().await { - state - .write() - .await - .set_connectivity_test_result(ConnectivityTestResult::Success); - emit_event( - "socks5-connection-success-event", - "SOCKS5 success", - "SOCKS5 connection health check successful", - &window, - ); - } else if state.read().await.get_status() == ConnectionStatusKind::Connected { - state - .write() - .await - .set_connectivity_test_result(ConnectivityTestResult::Fail); - log::error!("SOCKS5 connection health check failed"); - emit_event( - "socks5-connection-fail-event", - "SOCKS5 error", - "SOCKS5 connection health check failed", - &window, - ); - } else { - log::debug!("SOCKS5 connection status check cancelled: not connected"); - } - - log::debug!("Connection check handler exiting"); - }); -} - -/// The status listener listens for non-exit status messages from the background socks5 proxy task. -pub fn start_status_listener( - state: Arc>, - window: tauri::Window, - mut msg_receiver: nym_task::StatusReceiver, -) { - log::info!("Starting status listener"); - - tokio::spawn(async move { - while let Some(msg) = msg_receiver.next().await { - log::info!("SOCKS5 proxy sent status message: {}", msg); - - if let Some(task_status) = msg.downcast_ref::() { - events::handle_task_status(task_status, &state, &window).await; - } else if let Some(client_status_message) = - msg.downcast_ref::() - { - events::handle_client_status_message(client_status_message, &state, &window).await; - } else { - emit_status_event("socks5-status-event", &msg, &window); - } - } - log::info!("Status listener exiting"); - }); -} - -/// The disconnect listener listens to the channel setup between the socks5 proxy task and the main -/// tauri task. Primarily it listens for shutdown messages, and updates the state accordingly. -pub fn start_disconnect_listener( - state: Arc>, - window: tauri::Window, - exit_status_receiver: ExitStatusReceiver, -) { - log::trace!("Starting disconnect listener"); - tokio::spawn(async move { - match exit_status_receiver.await { - Ok(Socks5ExitStatusMessage::Stopped) => { - log::info!("SOCKS5 task reported it has finished"); - emit_event( - "socks5-event", - "SOCKS5 finished", - "SOCKS5 task reported it has finished", - &window, - ); - } - Ok(Socks5ExitStatusMessage::Failed(err)) => { - log::info!("SOCKS5 task reported error: {err}"); - emit_event( - "socks5-event", - "SOCKS5 error", - &format!("SOCKS5 failed: {err}"), - &window, - ); - } - Err(_) => { - log::info!("SOCKS5 task appears to have stopped abruptly"); - emit_event( - "socks5-event", - "SOCKS5 error", - "SOCKS5 stopped abruptly. Please try reconnecting.", - &window, - ); - } - } - - let mut state_w = state.write().await; - state_w.mark_disconnected(&window); - }); -} diff --git a/nym-connect/mobile/src-tauri/tauri.conf.json b/nym-connect/mobile/src-tauri/tauri.conf.json deleted file mode 100644 index c968ee8367..0000000000 --- a/nym-connect/mobile/src-tauri/tauri.conf.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "package": { - "productName": "nym-connect", - "version": "1.1.12" - }, - "build": { - "distDir": "../dist", - "devPath": "http://localhost:9000", - "beforeDevCommand": "", - "beforeBuildCommand": "" - }, - "tauri": { - "bundle": { - "active": true, - "targets": "all", - "identifier": "net.nymtech.connect", - "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", - "category": "Business", - "shortDescription": "Browse the internet privately using the Nym Mixnet", - "longDescription": "", - "deb": { - "depends": [] - }, - "macOS": { - "frameworks": [], - "minimumSystemVersion": "", - "exceptionDomain": "", - "signingIdentity": null, - "entitlements": null - }, - "windows": { - "certificateThumbprint": null, - "digestAlgorithm": "sha256", - "timestampUrl": "" - } - }, - "updater": { - "active": false - }, - "allowlist": { - "shell": { - "open": true - }, - "clipboard": { - "writeText": true - }, - "notification": { - "all": true - } - }, - "windows": [ - { - "title": "NymConnect", - "width": 240, - "height": 635, - "resizable": false, - "decorations": false, - "transparent": true - } - ], - "security": { - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'" - } - } -} diff --git a/nym-connect/mobile/src/assets/help-step-four.png b/nym-connect/mobile/src/assets/help-step-four.png deleted file mode 100644 index 5507962892..0000000000 Binary files a/nym-connect/mobile/src/assets/help-step-four.png and /dev/null differ diff --git a/nym-connect/mobile/src/assets/help-step-one-legacy.png b/nym-connect/mobile/src/assets/help-step-one-legacy.png deleted file mode 100644 index 5e91a29cff..0000000000 Binary files a/nym-connect/mobile/src/assets/help-step-one-legacy.png and /dev/null differ diff --git a/nym-connect/mobile/src/assets/help-step-one.png b/nym-connect/mobile/src/assets/help-step-one.png deleted file mode 100644 index 482e752286..0000000000 Binary files a/nym-connect/mobile/src/assets/help-step-one.png and /dev/null differ diff --git a/nym-connect/mobile/src/assets/help-step-three.png b/nym-connect/mobile/src/assets/help-step-three.png deleted file mode 100644 index 932b6c1171..0000000000 Binary files a/nym-connect/mobile/src/assets/help-step-three.png and /dev/null differ diff --git a/nym-connect/mobile/src/assets/help-step-two-legacy.png b/nym-connect/mobile/src/assets/help-step-two-legacy.png deleted file mode 100644 index f2910c98cf..0000000000 Binary files a/nym-connect/mobile/src/assets/help-step-two-legacy.png and /dev/null differ diff --git a/nym-connect/mobile/src/assets/help-step-two.png b/nym-connect/mobile/src/assets/help-step-two.png deleted file mode 100644 index c6f4429d74..0000000000 Binary files a/nym-connect/mobile/src/assets/help-step-two.png and /dev/null differ diff --git a/nym-connect/mobile/src/components/AppVersion.tsx b/nym-connect/mobile/src/components/AppVersion.tsx deleted file mode 100644 index 4ced230efa..0000000000 --- a/nym-connect/mobile/src/components/AppVersion.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { Box } from '@mui/material'; -import { useClientContext } from 'src/context/main'; - -export const AppVersion = () => { - const { appVersion } = useClientContext(); - - return ( - - - Version {appVersion} - - - ); -}; diff --git a/nym-connect/mobile/src/components/AppWindowFrame.tsx b/nym-connect/mobile/src/components/AppWindowFrame.tsx deleted file mode 100644 index 90e01fffa7..0000000000 --- a/nym-connect/mobile/src/components/AppWindowFrame.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import { Box } from '@mui/material'; -import { useLocation } from 'react-router-dom'; -import { useClientContext } from 'src/context/main'; -import { CustomTitleBar } from './CustomTitleBar'; - -export const AppWindowFrame: FCWithChildren = ({ children }) => { - const location = useLocation(); - const { userDefinedGateway, setUserDefinedGateway, userDefinedSPAddress, setUserDefinedSPAddress } = - useClientContext(); - - // defined functions to be used when moving away from pages - const onBack = () => { - switch (location.pathname) { - case '/menu/settings/gateway': - return () => { - // when the user moves away from the settings page and the gateway is not valid - // set isActive to false - if (!userDefinedGateway?.gateway) { - setUserDefinedGateway((current) => ({ ...current, isActive: false })); - } - }; - case '/menu/settings/service-provider': - return () => { - // when the user moves away from the settings page and the sp is not valid - // set isActive to false - if (!userDefinedSPAddress?.address) { - setUserDefinedSPAddress((current) => ({ ...current, isActive: false })); - } - }; - default: - return undefined; - } - }; - - return ( - - - {children} - - ); -}; diff --git a/nym-connect/mobile/src/components/ConnectionButton.tsx b/nym-connect/mobile/src/components/ConnectionButton.tsx deleted file mode 100644 index 1dd72f78df..0000000000 --- a/nym-connect/mobile/src/components/ConnectionButton.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import React from 'react'; -import { ConnectionStatusKind } from '../types'; - -const getStatusFillColor = (status: ConnectionStatusKind, hover: boolean, isError: boolean): string => { - if (isError && hover) { - return '#21D072'; - } - if (isError) { - return '#40475C'; - } - - switch (status) { - case 'disconnected': - if (hover) { - return '#FFFF33'; - } - return '#FFE600'; - case 'connecting': - case 'disconnecting': - return '#FFE600'; - default: - // connected - if (hover) { - return '#E43E3E'; - } - return '#21D072'; - } -}; - -const getStatusText = (status: ConnectionStatusKind, hover: boolean): string => { - switch (status) { - case 'disconnected': - return 'Connect'; - case 'connecting': - return 'Connecting'; - case 'disconnecting': - return 'Connected'; - default: - // connected - if (hover) { - return 'Disconnect'; - } - return 'Connected'; - } -}; - -export const ConnectionButton: FCWithChildren<{ - status: ConnectionStatusKind; - disabled?: boolean; - busy?: boolean; - isError?: boolean; - onClick?: (status: ConnectionStatusKind) => void; -}> = ({ status, disabled, isError, onClick }) => { - const [hover, setHover] = React.useState(false); - - const handleClick = React.useCallback(() => { - if (disabled === true) { - return; - } - if (onClick) { - onClick(status); - } - }, [status, disabled]); - - const statusText = getStatusText(status, hover); - const statusTextColor = isError ? '#40475C' : '#FFF'; - const statusFillColor = getStatusFillColor(status, hover, Boolean(isError)); - - return ( - - !disabled && setHover(true)} - onMouseLeave={() => !disabled && setHover(false)} - > - - - - - - - - - - {status === 'connected' && hover ? ( - - ) : ( - - )} - - {statusText} - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/nym-connect/mobile/src/components/ConnectionStats.tsx b/nym-connect/mobile/src/components/ConnectionStats.tsx deleted file mode 100644 index f17833e66d..0000000000 --- a/nym-connect/mobile/src/components/ConnectionStats.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; -import { Box, Typography } from '@mui/material'; -import prettyBytes from 'pretty-bytes'; - -export interface ConnectionStatsItem { - label: string; - rateBytesPerSecond: number; - totalBytes: number; -} - -const FONT_SIZE = '14px'; - -export const ConnectionStats: FCWithChildren<{ - stats: ConnectionStatsItem[]; -}> = ({ stats }) => ( - -
- {stats.map((stat) => ( - - {stat.label} - - ))} -
-
- {stats.map((stat) => ( - - {formatRate(stat.rateBytesPerSecond)} - - ))} -
-
- {stats.map((stat) => ( - - {formatTotal(stat.totalBytes)} - - ))} -
-
-); - -export function formatRate(bytesPerSecond: number): string { - return `${prettyBytes(bytesPerSecond)}/s`; -} - -export function formatTotal(totalBytes: number): string { - return prettyBytes(totalBytes); -} diff --git a/nym-connect/mobile/src/components/ConnectionStatus.tsx b/nym-connect/mobile/src/components/ConnectionStatus.tsx deleted file mode 100644 index 4ee49c5e14..0000000000 --- a/nym-connect/mobile/src/components/ConnectionStatus.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import React, { useState } from 'react'; -import { Box, CircularProgress, 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 { GatwayWarningInfo, ServiceProviderInfo } from './TooltipInfo'; -import { InfoModal } from './InfoModal'; - -const FONT_SIZE = '14px'; -const FONT_STYLE = 'normal'; - -const ConnectionStatusContent: FCWithChildren<{ - status: ConnectionStatusKind; - serviceProvider?: ServiceProvider; - gatewayError: boolean; -}> = ({ status, serviceProvider, gatewayError }) => { - const [showSpInfo, setShowSpInfo] = useState(false); - const [showGatewayWarn, setShowGatewayWarn] = useState(false); - - if (gatewayError) { - return ( - <> - {serviceProvider && ( - setShowGatewayWarn(false)} - > - - - )} - setShowGatewayWarn(true)} - > - - - Gateway has issues - - - - ); - } - switch (status) { - case 'connected': - return ( - <> - {serviceProvider && ( - setShowSpInfo(false)}> - - - )} - setShowSpInfo(true)} - justifyContent="center" - sx={{ cursor: 'pointer' }} - > - - - Connected to Nym Mixnet - - - - ); - case 'disconnected': - return ( - - Connect to the mixnet - - ); - case 'disconnecting': - return ( - - - - Disconnecting... - - - ); - case 'connecting': - return ( - - - - Connecting... - - - ); - - default: - return null; - } -}; - -export const ConnectionStatus: FCWithChildren<{ - status: ConnectionStatusKind; - gatewayPerformance?: GatewayPerformance; - connectedSince?: DateTime; - serviceProvider?: ServiceProvider; -}> = ({ status, serviceProvider, gatewayPerformance }) => { - const color = status === 'connected' || status === 'disconnecting' ? '#21D072' : 'white'; - - return ( - - - - ); -}; diff --git a/nym-connect/mobile/src/components/ConntectionTimer.tsx b/nym-connect/mobile/src/components/ConntectionTimer.tsx deleted file mode 100644 index c7b341ecfe..0000000000 --- a/nym-connect/mobile/src/components/ConntectionTimer.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React, { useEffect } from 'react'; -import { Stack, Typography } from '@mui/material'; -import { DateTime } from 'luxon'; - -export const ConnectionTimer = ({ connectedSince }: { connectedSince?: DateTime }) => { - const [duration, setDuration] = React.useState(); - useEffect(() => { - const intervalId = setInterval(() => { - if (connectedSince) { - setDuration(DateTime.now().diff(connectedSince).toFormat('hh:mm:ss')); - } - }, 500); - return () => { - clearInterval(intervalId); - }; - }, [connectedSince]); - - return ( - - - Connection time - - - {duration || '00:00:00'} - - - ); -}; diff --git a/nym-connect/mobile/src/components/CopyToClipboard.tsx b/nym-connect/mobile/src/components/CopyToClipboard.tsx deleted file mode 100644 index 7a48662190..0000000000 --- a/nym-connect/mobile/src/components/CopyToClipboard.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Button, IconButton, Tooltip } from '@mui/material'; -import { Check, ContentCopy } from '@mui/icons-material'; -import { clipboard } from '@tauri-apps/api'; - -export const CopyToClipboard = ({ - text = '', - light, - iconButton, -}: { - text?: string; - light?: boolean; - iconButton?: boolean; -}) => { - const [copied, setCopied] = useState(false); - - const handleCopy = async (textToCopy: string) => { - try { - if (clipboard) { - await clipboard.writeText(textToCopy); - } else { - await navigator.clipboard.writeText(textToCopy); - } - setCopied(true); - } catch (e) { - console.log(`failed to copy: ${e}`); - } - }; - - useEffect(() => { - let timer: NodeJS.Timeout; - if (copied) { - timer = setTimeout(() => { - setCopied(false); - }, 2000); - } - return () => clearTimeout(timer); - }, [copied]); - - if (iconButton) - return ( - - handleCopy(text)} - size="small" - sx={{ - color: (theme) => (light ? theme.palette.common.white : theme.palette.nym.background.dark), - }} - > - {!copied ? : } - - - ); - - return ( - - ); -}; diff --git a/nym-connect/mobile/src/components/CustomTitleBar.tsx b/nym-connect/mobile/src/components/CustomTitleBar.tsx deleted file mode 100644 index 9fb13c7cec..0000000000 --- a/nym-connect/mobile/src/components/CustomTitleBar.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react'; -import { ArrowBack, Menu } from '@mui/icons-material'; -import { Box, IconButton, Typography } from '@mui/material'; -// TODO since the structure refactor for NC this import fails -// import { NymWordmark } from '@nymproject/react/logo/NymWordmark'; -import { useNavigate } from 'react-router-dom'; - -const customTitleBarStyles = { - titlebar: { - background: '#1D2125', - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - padding: '16px', - paddingBottom: '0px', - borderTopLeftRadius: '12px', - borderTopRightRadius: '12px', - }, -}; - -const CustomButton = ({ Icon, onClick }: { Icon: React.JSXElementConstructor; onClick: () => void }) => ( - - - -); - -const MenuIcon = () => { - const navigate = useNavigate(); - return navigate('/menu')} />; -}; - -const ArrowBackIcon = ({ onBack }: { onBack?: () => void }) => { - const navigate = useNavigate(); - const handleBack = () => { - onBack?.(); - navigate(-1); - }; - return ; -}; - -const getTitle = (path: string) => { - if (path.includes('settings')) return 'Settings'; - if (path !== '/') { - const title = path.split('/').slice(-1); - return ( - - {title} - - ); - } - - // TODO return ; - return ( - - NYM - - ); -}; - -export const CustomTitleBar = ({ path = '/', onBack }: { path?: string; onBack?: () => void }) => ( - - {/* set width to keep logo centered */} - {path === '/' ? : } - {getTitle(path)} - - -); diff --git a/nym-connect/mobile/src/components/Error.tsx b/nym-connect/mobile/src/components/Error.tsx deleted file mode 100644 index f582536b6b..0000000000 --- a/nym-connect/mobile/src/components/Error.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import { FallbackProps } from 'react-error-boundary'; -import { Alert, AlertTitle, Button } from '@mui/material'; - -export const ErrorFallback = ({ error, resetErrorBoundary }: FallbackProps) => ( -
- - {error.name} - {error.message} - - - Stack trace - {error.stack} - - -
-); diff --git a/nym-connect/mobile/src/components/ErrorFallback.tsx b/nym-connect/mobile/src/components/ErrorFallback.tsx deleted file mode 100644 index f582536b6b..0000000000 --- a/nym-connect/mobile/src/components/ErrorFallback.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import { FallbackProps } from 'react-error-boundary'; -import { Alert, AlertTitle, Button } from '@mui/material'; - -export const ErrorFallback = ({ error, resetErrorBoundary }: FallbackProps) => ( -
- - {error.name} - {error.message} - - - Stack trace - {error.stack} - - -
-); diff --git a/nym-connect/mobile/src/components/ExperimentalWarning.tsx b/nym-connect/mobile/src/components/ExperimentalWarning.tsx deleted file mode 100644 index 59595d389e..0000000000 --- a/nym-connect/mobile/src/components/ExperimentalWarning.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { Box, Typography } from '@mui/material'; - -export const ExperimentalWarning = () => ( - - - This is experimental software. - - - Do not rely on it for strong anonymity (yet). - - -); diff --git a/nym-connect/mobile/src/components/Growth/content/assets/matrix.webp b/nym-connect/mobile/src/components/Growth/content/assets/matrix.webp deleted file mode 100644 index 2b1517166c..0000000000 Binary files a/nym-connect/mobile/src/components/Growth/content/assets/matrix.webp and /dev/null differ diff --git a/nym-connect/mobile/src/components/HelpPage.tsx b/nym-connect/mobile/src/components/HelpPage.tsx deleted file mode 100644 index 3ec3e3e940..0000000000 --- a/nym-connect/mobile/src/components/HelpPage.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import { Stack, Typography } from '@mui/material'; -import { HelpPageActions } from './HelpPageActions'; -import { HelpImage } from './HelpPageImage'; -import { StepIndicator } from './HelpPageStepIndicator'; - -export const HelpPage = ({ - step, - totalSteps, - description, - img, - onNext, - onPrev, -}: { - step: number; - totalSteps: number; - description: string; - img: any; - onNext?: () => void; - onPrev?: () => void; -}) => ( - - - - - How to connect guide {step}/{totalSteps} - - - {description} - - - - - -); diff --git a/nym-connect/mobile/src/components/HelpPageActions.tsx b/nym-connect/mobile/src/components/HelpPageActions.tsx deleted file mode 100644 index 13cba78fd2..0000000000 --- a/nym-connect/mobile/src/components/HelpPageActions.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { ArrowBack, ArrowForward } from '@mui/icons-material'; -import { Box, Button } from '@mui/material'; -import React from 'react'; - -export const HelpPageActions = ({ onNext, onPrev }: { onNext?: () => void; onPrev?: () => void }) => ( - - {onPrev ? ( - - ) : ( -
- )} - {onNext && ( - - )} - -); diff --git a/nym-connect/mobile/src/components/HelpPageImage.tsx b/nym-connect/mobile/src/components/HelpPageImage.tsx deleted file mode 100644 index 69fa38ca31..0000000000 --- a/nym-connect/mobile/src/components/HelpPageImage.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -export const HelpImage = ({ img, imageDescription }: { img: string; imageDescription: string }) => ( - {imageDescription} -); diff --git a/nym-connect/mobile/src/components/HelpPageStepIndicator.tsx b/nym-connect/mobile/src/components/HelpPageStepIndicator.tsx deleted file mode 100644 index fc108425ab..0000000000 --- a/nym-connect/mobile/src/components/HelpPageStepIndicator.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import { Box, Stack } from '@mui/material'; - -const Step = ({ highlight }: { highlight: boolean }) => ( - -); - -export const StepIndicator = ({ step }: { step: number }) => ( - - - = 2} /> - = 3} /> - -); diff --git a/nym-connect/mobile/src/components/InfoModal.tsx b/nym-connect/mobile/src/components/InfoModal.tsx deleted file mode 100644 index 07f98769e5..0000000000 --- a/nym-connect/mobile/src/components/InfoModal.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Close, ErrorOutline } from '@mui/icons-material'; -import { Box, IconButton, Modal, Theme, Typography } from '@mui/material'; -import React from 'react'; - -const styles = { - position: 'absolute', - top: '50%', - left: '50%', - transform: 'translate(-50%, -50%)', - width: 200, - bgcolor: '#292E34', - p: 1.5, - borderRadius: 0.5, - height: 'fit-content', - border: (theme: Theme) => `1px solid ${theme.palette.grey[700]}`, -}; - -const ModalTitle = ({ title, withCloseIcon }: { title: string; withCloseIcon: boolean }) => ( - - - - {title} - - -); - -const ModalBody = ({ description, children }: { description: string; children?: React.ReactElement }) => ( - - {children} - - {description} - - -); - -export const InfoModal = ({ - title, - description, - show, - children, - Action, - onClose, -}: { - title: string; - description: string; - show: boolean; - children?: React.ReactElement; - Action?: React.ReactNode; - onClose?: () => void; -}) => ( - - - {onClose && ( - - - - - - )} - - {children} - {Action && ( - - {Action} - - )} - - -); diff --git a/nym-connect/mobile/src/components/IpAddressAndPort.tsx b/nym-connect/mobile/src/components/IpAddressAndPort.tsx deleted file mode 100644 index 4ecf356d18..0000000000 --- a/nym-connect/mobile/src/components/IpAddressAndPort.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Box, Typography } from '@mui/material'; -import React from 'react'; -import { styled } from '@mui/system'; - -const IpAddressAndPortContainer = styled('div')({ - '.hoverAddressCopy:hover': { - cursor: 'pointer', - textDecoration: 'underline', - textDecorationColor: '#FB6E4E', - textDecorationThickness: '2px', - textUnderlineOffset: '4px', - }, -}); - -export const IpAddressAndPort: FCWithChildren<{ - label: string; - ipAddress: string; - port: number; -}> = ({ label, ipAddress, port }) => { - const [ipAddressCopied, setIpAddressCopied] = React.useState(false); - const [portCopied, setPortCopied] = React.useState(false); - - React.useEffect(() => { - if (ipAddressCopied) { - setTimeout(() => setIpAddressCopied(false), 2000); - } - }, [ipAddressCopied]); - - React.useEffect(() => { - if (portCopied) { - setTimeout(() => setPortCopied(false), 2000); - } - }, [portCopied]); - - return ( - - - - {label} - - - Port - - - - - {ipAddress} - - - {port} - - - - ); -}; diff --git a/nym-connect/mobile/src/components/IpAddressAndPortModal.tsx b/nym-connect/mobile/src/components/IpAddressAndPortModal.tsx deleted file mode 100644 index fcb0f1dfaa..0000000000 --- a/nym-connect/mobile/src/components/IpAddressAndPortModal.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; -import { Box, Typography } from '@mui/material'; -import { InfoModal } from './InfoModal'; -import { CopyToClipboard } from './CopyToClipboard'; - -const FONT_SIZE = '12px'; -const FONT_COLOR = 'grey.400'; - -export const IpAddressAndPortModal = ({ - show, - ipAddress, - port, - onClose, -}: { - show: boolean; - ipAddress: string; - port: number; - onClose: () => void; -}) => ( - - - - Paste below values in the proxy settings of your app - - - Socks5 address - - - {ipAddress} - - - - - Port - - - {port} - - - - -); diff --git a/nym-connect/mobile/src/components/NeedHelp.tsx b/nym-connect/mobile/src/components/NeedHelp.tsx deleted file mode 100644 index 12d9587a5f..0000000000 --- a/nym-connect/mobile/src/components/NeedHelp.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { Box, Button, Typography } from '@mui/material'; -import HelpOutlineIcon from '@mui/icons-material/HelpOutline'; - -const HELP_URL = 'https://docs.nymtech.net'; - -export const NeedHelp: FCWithChildren = () => ( - - - -); diff --git a/nym-connect/mobile/src/components/PowerButton/PowerButton.tsx b/nym-connect/mobile/src/components/PowerButton/PowerButton.tsx deleted file mode 100644 index bc399e70b9..0000000000 --- a/nym-connect/mobile/src/components/PowerButton/PowerButton.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import React, { useCallback } from 'react'; -import { ConnectionStatusKind } from 'src/types'; -import './power-button.css'; - -const getStatusFillColor = (status: ConnectionStatusKind, hover: boolean, isError: boolean): string => { - if (isError && hover) { - return '#21D072'; - } - if (isError) { - return '#40475C'; - } - - switch (status) { - case 'disconnected': - if (hover) { - return '#FFF'; - } - return '#BBB'; - case 'connecting': - return '#FFF'; - case 'disconnecting': - return '#FFF'; - default: - // connected - if (hover) { - return '#E43E3E'; - } - return '#21D072'; - } -}; - -export const PowerButton: FCWithChildren<{ - onClick?: (status: ConnectionStatusKind) => void; - isError?: boolean; - disabled?: boolean; - status: ConnectionStatusKind; - busy?: boolean; -}> = ({ onClick, disabled, status, isError }) => { - const [hover, setHover] = React.useState(false); - - const handleClick = () => { - if (disabled === true) { - return; - } - if (onClick) { - onClick(status); - } - }; - - const statusFillColor = getStatusFillColor(status, hover, Boolean(isError)); - - const getClassName = useCallback(() => { - if (hover) { - switch (status) { - case 'disconnected': - return 'expand'; - default: - return 'contract'; - } - } - if (!hover) { - switch (status) { - case 'connected': - return 'expand'; - default: - return 'contract'; - } - } - return 'contract'; - }, [status, hover]); - - const buttonPulse = () => { - if (status === 'connecting' || status === 'disconnecting') return 'pulse'; - return undefined; - }; - - let statusText: string; - switch (status) { - case 'connected': - statusText = 'stop'; - break; - case 'disconnected': - statusText = 'start'; - break; - default: - statusText = ''; - } - - return ( - !disabled && setHover(true)} - onMouseLeave={() => !disabled && setHover(false)} - > - - - - - - - - - - - - - - - - - - - - - - - {statusText.toUpperCase()} - - - - - - - - - - ); -}; diff --git a/nym-connect/mobile/src/components/PowerButton/power-button.css b/nym-connect/mobile/src/components/PowerButton/power-button.css deleted file mode 100644 index b73d01ca32..0000000000 --- a/nym-connect/mobile/src/components/PowerButton/power-button.css +++ /dev/null @@ -1,72 +0,0 @@ -#ring-expand { - animation-name: rings-expand; - animation-duration: 0.4s; - animation-timing-function: ease-in-out; - animation-play-state: paused; -} - -#ring-one.expand { - opacity: 0.3; - transition: opacity 0.1s ease-in-out; -} - -#ring-two.expand { - opacity: 0.2; - transition: opacity 0.2s ease-in-out; -} - -#ring-three.expand { - opacity: 0.1; - transition: opacity 0.3s ease-in-out; -} - -#ring-four.expand { - opacity: 0.05; - transition: opacity 0.4s ease-in-out; -} - -#ring-one.contract { - opacity: 0; - transition: opacity 0.4s; - transition-delay: 0.1s; -} - -#ring-two.contract { - opacity: 0; - transition: opacity 0.3s; - transition-delay: 0.1s; -} - -#ring-three.contract { - opacity: 0; - transition: opacity 0.1s; - transition-delay: 0.1s; -} - -#ring-four.contract { - opacity: 0; - transition: opacity 0.1s; - transition-delay: 0.1s; -} - -circle, -path, text { - transition: stroke 0.5s, fill 0.5s; -} - -.pulse { - animation-name: pulse; - animation-duration: 0.5s; - animation-iteration-count: infinite; - animation-direction: alternate; -} - -@keyframes pulse { - from { - opacity: 1; - } - - to { - opacity: 0.3; - } -} diff --git a/nym-connect/mobile/src/components/ServiceProviderPopup.tsx b/nym-connect/mobile/src/components/ServiceProviderPopup.tsx deleted file mode 100644 index 660fee4134..0000000000 --- a/nym-connect/mobile/src/components/ServiceProviderPopup.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { Autocomplete, Box, Dialog, DialogProps, TextField, Typography } from '@mui/material'; -import { Service, ServiceProvider, Services } from '../types/directory'; - -export const ServiceProviderPopup: FCWithChildren< - DialogProps & { services: Services; onServiceProviderChanged: (sp?: ServiceProvider, s?: Service) => void } -> = ({ services, onServiceProviderChanged, ...dialogProps }) => { - const options = services.flatMap((s) => - s.items.map((sp) => ({ id: `${s.id}-${sp.id}`, title: sp.description, service: s, sp })), - ); - return ( - - - // filterOptions.filter((o) => o.title.toLowerCase().includes(inputValue.toLowerCase())) - // } - options={options} - groupBy={(option) => option.service.description} - getOptionLabel={(option) => option.title} - onChange={(event, value) => onServiceProviderChanged(value?.sp, value?.service)} - isOptionEqualToValue={(option, value) => option.id.toLowerCase() === value?.id.toLowerCase()} - renderOption={(props, option) => ( - img': { mr: 2, flexShrink: 0 } }} {...props} fontSize="small"> - - {option.id} - -

{option.title}

-
- )} - renderInput={(params) => } - /> -
- ); -}; diff --git a/nym-connect/mobile/src/components/ServiceProviderSelector.stories.tsx b/nym-connect/mobile/src/components/ServiceProviderSelector.stories.tsx deleted file mode 100644 index c7df76ea4a..0000000000 --- a/nym-connect/mobile/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/mobile/src/components/ServiceProviderSelector.tsx b/nym-connect/mobile/src/components/ServiceProviderSelector.tsx deleted file mode 100644 index 741465dac4..0000000000 --- a/nym-connect/mobile/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/mobile/src/components/ServiceSelector.stories.tsx b/nym-connect/mobile/src/components/ServiceSelector.stories.tsx deleted file mode 100644 index 56be7c19ee..0000000000 --- a/nym-connect/mobile/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/mobile/src/components/ServiceSelector.tsx b/nym-connect/mobile/src/components/ServiceSelector.tsx deleted file mode 100644 index 5eb8361d3c..0000000000 --- a/nym-connect/mobile/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/mobile/src/components/TooltipInfo.tsx b/nym-connect/mobile/src/components/TooltipInfo.tsx deleted file mode 100644 index 64b718d174..0000000000 --- a/nym-connect/mobile/src/components/TooltipInfo.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { Divider, Stack, Typography } from '@mui/material'; -import { ServiceProvider } from 'src/types/directory'; - -export const ServiceProviderInfo = ({ serviceProvider }: { serviceProvider: ServiceProvider }) => ( - - - Connection info - - - - Gateway {serviceProvider.gateway} - - - - Service provider {serviceProvider.address.slice(0, 35)}... - - -); - -export const GatwayWarningInfo = () => ( - - - Connection issue - - - Try disconnecting and connecting again - -); diff --git a/nym-connect/mobile/src/config.ts b/nym-connect/mobile/src/config.ts deleted file mode 100644 index f405ceac15..0000000000 --- a/nym-connect/mobile/src/config.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const config = { - DOCS_BASE_URL: 'https://nymtech.net/docs', -}; diff --git a/nym-connect/mobile/src/context/main.tsx b/nym-connect/mobile/src/context/main.tsx deleted file mode 100644 index b999b957a2..0000000000 --- a/nym-connect/mobile/src/context/main.tsx +++ /dev/null @@ -1,211 +0,0 @@ -import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; -import { DateTime } from 'luxon'; -import { invoke } from '@tauri-apps/api'; -import { Error } from 'src/types/error'; -import { getVersion } from '@tauri-apps/api/app'; -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 { ConnectionStatsItem } from '../components/ConnectionStats'; -import { ServiceProvider } from '../types/directory'; - -const FORAGE_GATEWAY_KEY = 'nym-connect-user-gateway'; -const FORAGE_SP_KEY = 'nym-connect-user-sp'; - -type ModeType = 'light' | 'dark'; - -export type TClientContext = { - mode: ModeType; - appVersion?: string; - connectionStatus: ConnectionStatusKind; - connectionStats?: ConnectionStatsItem[]; - connectedSince?: DateTime; - error?: Error; - gatewayPerformance: GatewayPerformance; - selectedProvider?: ServiceProvider; - showInfoModal: boolean; - userDefinedGateway?: UserDefinedGateway; - userDefinedSPAddress: UserDefinedSPAddress; - serviceProviders?: ServiceProvider[]; - setMode: (mode: ModeType) => void; - clearError: () => void; - setConnectionStatus: (connectionStatus: ConnectionStatusKind) => void; - setConnectionStats: (connectionStats: ConnectionStatsItem[] | undefined) => void; - setConnectedSince: (connectedSince: DateTime | undefined) => void; - setShowInfoModal: (show: boolean) => void; - setSerivceProvider: () => void; - startConnecting: () => Promise; - startDisconnecting: () => Promise; - setUserDefinedGateway: React.Dispatch>; - setUserDefinedSPAddress: React.Dispatch>; -}; - -export const ClientContext = createContext({} as TClientContext); - -export const ClientContextProvider: FCWithChildren = ({ children }) => { - const [mode, setMode] = useState('dark'); - const [connectionStatus, setConnectionStatus] = useState(ConnectionStatusKind.connected); - const [connectionStats, setConnectionStats] = useState(); - const [connectedSince, setConnectedSince] = useState(); - const [selectedProvider, setSelectedProvider] = React.useState(); - const [serviceProviders, setServiceProviders] = React.useState(); - const [error, setError] = useState(); - const [appVersion, setAppVersion] = useState(); - const [gatewayPerformance, setGatewayPerformance] = useState('Good'); - const [showInfoModal, setShowInfoModal] = useState(false); - const [userDefinedGateway, setUserDefinedGateway] = useState({ - isActive: false, - gateway: undefined, - }); - const [userDefinedSPAddress, setUserDefinedSPAddress] = useState({ - isActive: false, - address: undefined, - }); - - const getAppVersion = async () => { - const version = await getVersion(); - return version; - }; - - useEffect(() => { - setItemInStorage({ key: FORAGE_GATEWAY_KEY, value: userDefinedGateway }); - }, [userDefinedGateway]); - - useEffect(() => { - setItemInStorage({ key: FORAGE_SP_KEY, value: userDefinedSPAddress }); - }, [userDefinedSPAddress]); - - const initialiseApp = async () => { - const services = await invoke('get_services'); - 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[]); - - if (storedUserDefinedGateway) setUserDefinedGateway(storedUserDefinedGateway); - if (storedUserDefinedSP) setUserDefinedSPAddress(storedUserDefinedSP); - }; - - useEvents({ - onError: (e) => setError(e), - onGatewayPerformanceChange: (performance) => setGatewayPerformance(performance), - onStatusChange: (status) => setConnectionStatus(status), - }); - - useEffect(() => { - initialiseApp(); - }, []); - - useEffect(() => { - // when mounting, load the connection state (needed for the Growth window, that checks the connection state) - (async () => { - const currentStatus: ConnectionStatusKind = await invoke('get_connection_status'); - setConnectionStatus(currentStatus); - })(); - }, []); - - const startConnecting = useCallback(async () => { - try { - await invoke('start_connecting'); - } catch (e) { - setError({ title: 'Could not connect', message: e as string }); - console.log(e); - } - }, []); - - const startDisconnecting = useCallback(async () => { - try { - await invoke('start_disconnecting'); - } catch (e) { - console.log(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 () => { - if (serviceProviders) { - const randomServiceProvider = getRandomSPFromList(serviceProviders); - const withUserDefinitions = await buildServiceProvider(randomServiceProvider); - await setServiceProvider(withUserDefinitions); - setSelectedProvider(withUserDefinitions); - } - return undefined; - }; - - const clearError = () => setError(undefined); - - const contextValue = useMemo( - () => ({ - mode, - appVersion, - setMode, - error, - clearError, - connectionStatus, - setConnectionStatus, - connectionStats, - showInfoModal, - setConnectionStats, - selectedProvider, - serviceProviders, - connectedSince, - setConnectedSince, - setSerivceProvider, - startConnecting, - startDisconnecting, - gatewayPerformance, - setShowInfoModal, - userDefinedSPAddress, - userDefinedGateway, - setUserDefinedGateway, - setUserDefinedSPAddress, - }), - [ - mode, - appVersion, - error, - showInfoModal, - serviceProviders, - connectedSince, - connectionStatus, - connectionStats, - connectedSince, - gatewayPerformance, - selectedProvider, - userDefinedGateway, - userDefinedSPAddress, - ], - ); - - return {children}; -}; - -export const useClientContext = () => useContext(ClientContext); diff --git a/nym-connect/mobile/src/context/mocks/main.tsx b/nym-connect/mobile/src/context/mocks/main.tsx deleted file mode 100644 index 78028b03f8..0000000000 --- a/nym-connect/mobile/src/context/mocks/main.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useMemo } from 'react'; -import { ConnectionStatusKind } from 'src/types'; -import { ClientContext, TClientContext } from '../main'; - -const mockValues: TClientContext = { - appVersion: 'v1.x.x', - mode: 'dark', - connectionStatus: ConnectionStatusKind.disconnected, - selectedProvider: { id: '1', description: 'Keybase service provider', gateway: 'abc123', address: '123abc' }, - gatewayPerformance: 'Good', - showInfoModal: false, - userDefinedGateway: { isActive: false, gateway: '' }, - userDefinedSPAddress: { isActive: false, address: '' }, - setShowInfoModal: () => {}, - setMode: () => {}, - clearError: () => {}, - setConnectedSince: () => {}, - setConnectionStats: () => {}, - setConnectionStatus: () => {}, - startConnecting: async () => {}, - startDisconnecting: async () => {}, - setSerivceProvider: () => {}, - setUserDefinedGateway: () => {}, - setUserDefinedSPAddress: () => {}, -}; - -export const MockProvider: FCWithChildren<{ - children?: React.ReactNode; - connectionStatus?: ConnectionStatusKind; -}> = ({ connectionStatus = ConnectionStatusKind.disconnected, children }) => { - const value = useMemo(() => ({ ...mockValues, connectionStatus }), [connectionStatus]); - return {children}; -}; diff --git a/nym-connect/mobile/src/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf b/nym-connect/mobile/src/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf deleted file mode 100644 index 0fea34ba24..0000000000 Binary files a/nym-connect/mobile/src/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf and /dev/null differ diff --git a/nym-connect/mobile/src/fonts/OpenSans-VariableFont_wdth,wght.ttf b/nym-connect/mobile/src/fonts/OpenSans-VariableFont_wdth,wght.ttf deleted file mode 100644 index 51dd3c3be2..0000000000 Binary files a/nym-connect/mobile/src/fonts/OpenSans-VariableFont_wdth,wght.ttf and /dev/null differ diff --git a/nym-connect/mobile/src/fonts/fonts.css b/nym-connect/mobile/src/fonts/fonts.css deleted file mode 100644 index bf875e952d..0000000000 --- a/nym-connect/mobile/src/fonts/fonts.css +++ /dev/null @@ -1,6 +0,0 @@ -@font-face { - font-family: 'Open Sans'; - src: url('./OpenSans-VariableFont_wdth,wght.ttf') format('truetype-variations'), - url('./OpenSans-Italic-VariableFont_wdth,wght.ttf') format('truetype-variations'); - font-weight: 100 1000; -} \ No newline at end of file diff --git a/nym-connect/mobile/src/hooks/events.ts b/nym-connect/mobile/src/hooks/events.ts deleted file mode 100644 index 4b2c4fcc55..0000000000 --- a/nym-connect/mobile/src/hooks/events.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { useEffect, useRef } from 'react'; -import { listen, UnlistenFn } from '@tauri-apps/api/event'; -import { ConnectionStatusKind, GatewayPerformance } from 'src/types'; -import { Error } from 'src/types/error'; -import { TauriEvent } from 'src/types/event'; - -const TAURI_EVENT_STATUS_CHANGED = 'app:connection-status-changed'; - -export const useEvents = ({ - onError, - onStatusChange, - onGatewayPerformanceChange, -}: { - onError: (error: Error) => void; - onStatusChange: (status: ConnectionStatusKind) => void; - onGatewayPerformanceChange: (status: GatewayPerformance) => void; -}) => { - const timerId = useRef(); - - useEffect(() => { - const unlisten: UnlistenFn[] = []; - - // TODO: fix typings - listen(TAURI_EVENT_STATUS_CHANGED, (event) => { - const { status } = event.payload as any; - console.log(TAURI_EVENT_STATUS_CHANGED, { status, event }); - onStatusChange(status); - }) - .then((result) => { - unlisten.push(result); - }) - .catch((e) => console.log(e)); - - listen('socks5-event', (e: TauriEvent) => { - console.log(e); - onError(e.payload); - }).then((result) => { - unlisten.push(result); - }); - - listen('socks5-status-event', (e: TauriEvent) => { - if (e.payload.message.includes('slow')) { - onGatewayPerformanceChange('Poor'); - - if (timerId?.current) { - clearTimeout(timerId.current); - } - - timerId.current = setTimeout(() => { - onGatewayPerformanceChange('Good'); - }, 10000); - } - }).then((result) => { - unlisten.push(result); - }); - - listen('socks5-connection-fail-event', (e: TauriEvent) => { - onError({ title: 'Connection failed', message: `${e.payload.message} - Please disconnect and reconnect.` }); - onGatewayPerformanceChange('Poor'); - }).then((result) => { - unlisten.push(result); - }); - - return () => { - unlisten.forEach((unsubscribe) => unsubscribe()); - }; - }, []); -}; diff --git a/nym-connect/mobile/src/index.tsx b/nym-connect/mobile/src/index.tsx deleted file mode 100644 index 996825445d..0000000000 --- a/nym-connect/mobile/src/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import { createRoot } from 'react-dom/client'; -import { ErrorBoundary } from 'react-error-boundary'; -import { BrowserRouter as Router } from 'react-router-dom'; -import { GlobalStyles } from '@mui/material'; -import { ClientContextProvider } from './context/main'; -import { ErrorFallback } from './components/Error'; -import { NymMixnetTheme } from './theme'; -import { AppWindowFrame } from './components/AppWindowFrame'; -import { AppRoutes } from './routes'; - -const elem = document.getElementById('root'); - -if (elem) { - const root = createRoot(elem); - root.render( - - - - - - - - - - - - , - ); -} diff --git a/nym-connect/mobile/src/layouts/ConnectionLayout.tsx b/nym-connect/mobile/src/layouts/ConnectionLayout.tsx deleted file mode 100644 index 300a971e15..0000000000 --- a/nym-connect/mobile/src/layouts/ConnectionLayout.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Box, Stack } from '@mui/material'; -import React from 'react'; - -export const ConnectionLayout = ({ - TopContent, - ConnectButton, - BottomContent, -}: { - TopContent: React.ReactNode; - ConnectButton: React.ReactNode; - BottomContent: React.ReactNode; -}) => ( - - - {TopContent} - - - {ConnectButton} - - - {BottomContent} - - -); diff --git a/nym-connect/mobile/src/pages/connection/Connected.tsx b/nym-connect/mobile/src/pages/connection/Connected.tsx deleted file mode 100644 index d1f59e55eb..0000000000 --- a/nym-connect/mobile/src/pages/connection/Connected.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react'; -import { Box, Stack } from '@mui/material'; -import { DateTime } from 'luxon'; -import { IpAddressAndPortModal } from 'src/components/IpAddressAndPortModal'; -import { ConnectionTimer } from 'src/components/ConntectionTimer'; -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 { ExperimentalWarning } from 'src/components/ExperimentalWarning'; -import { ConnectionLayout } from 'src/layouts/ConnectionLayout'; -import { PowerButton } from 'src/components/PowerButton/PowerButton'; -import { Error } from 'src/types/error'; -import { InfoModal } from 'src/components/InfoModal'; - -export const Connected: FCWithChildren<{ - error?: Error; - status: ConnectionStatusKind; - showInfoModal: boolean; - gatewayPerformance: GatewayPerformance; - stats: ConnectionStatsItem[]; - ipAddress: string; - port: number; - connectedSince?: DateTime; - busy?: boolean; - isError?: boolean; - serviceProvider?: ServiceProvider; - clearError: () => void; - onConnectClick: (status: ConnectionStatusKind) => void; - closeInfoModal: () => void; -}> = ({ - error, - status, - showInfoModal, - gatewayPerformance, - ipAddress, - port, - connectedSince, - busy, - isError, - serviceProvider, - clearError, - onConnectClick, - closeInfoModal, -}) => ( - <> - {error && } - - - - -
- } - ConnectButton={ - - } - BottomContent={ - - - - - - - - - } - /> - -); diff --git a/nym-connect/mobile/src/pages/connection/Disconnected.tsx b/nym-connect/mobile/src/pages/connection/Disconnected.tsx deleted file mode 100644 index d3937fea24..0000000000 --- a/nym-connect/mobile/src/pages/connection/Disconnected.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import { Stack, Typography } from '@mui/material'; -import { ConnectionStatus } from 'src/components/ConnectionStatus'; -import { ConnectionTimer } from 'src/components/ConntectionTimer'; -import { InfoModal } from 'src/components/InfoModal'; -import { Error } from 'src/types/error'; -import { ExperimentalWarning } from 'src/components/ExperimentalWarning'; -import { ServiceProvider, Services } from 'src/types/directory'; -import { ConnectionStatusKind } from 'src/types'; -import { PowerButton } from 'src/components/PowerButton/PowerButton'; -import { Box } from '@mui/system'; -import { ConnectionLayout } from 'src/layouts/ConnectionLayout'; - -export const Disconnected: FCWithChildren<{ - error?: Error; - status: ConnectionStatusKind; - services?: Services; - busy?: boolean; - isError?: boolean; - serviceProvider?: ServiceProvider; - clearError: () => void; - onConnectClick: (status: ConnectionStatusKind) => void; -}> = ({ status, error, onConnectClick, clearError }) => ( - <> - {error && } - - - - - } - ConnectButton={} - BottomContent={ - - - - You are not protected - - - - - - - } - /> - -); diff --git a/nym-connect/mobile/src/pages/connection/index.tsx b/nym-connect/mobile/src/pages/connection/index.tsx deleted file mode 100644 index 5288517207..0000000000 --- a/nym-connect/mobile/src/pages/connection/index.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import React from 'react'; -import { Stack } from '@mui/material'; -import { forage } from '@tauri-apps/tauri-forage'; -import { DateTime } from 'luxon'; -import { useClientContext } from 'src/context/main'; -import { useTauriEvents } from 'src/utils'; -import { Connected } from './Connected'; -import { Disconnected } from './Disconnected'; - -export const ConnectionPage = () => { - const context = useClientContext(); - const [busy, setBusy] = React.useState(); - - useTauriEvents('help://clear-storage', (_event) => { - console.log('About to clear local storage...'); - // clear local storage - try { - forage.clear()(); - console.log('Local storage cleared'); - } catch (e) { - console.error('Failed to clear local storage', e); - } - }); - - const handleConnectClick = async () => { - const currentStatus = context.connectionStatus; - if (currentStatus === 'connected' || currentStatus === 'disconnected') { - setBusy(true); - // eslint-disable-next-line default-case - switch (currentStatus) { - case 'disconnected': - await context.setSerivceProvider(); - await context.startConnecting(); - context.setConnectedSince(DateTime.now()); - context.setShowInfoModal(true); - break; - case 'connected': - await context.startDisconnecting(); - context.setConnectedSince(undefined); - break; - } - setBusy(false); - } - }; - - const closeInfoModal = () => context.setShowInfoModal(false); - - if (context.connectionStatus === 'connected') - return ( - - - - ); - - return ( - - - - ); -}; diff --git a/nym-connect/mobile/src/pages/menu/Apps.tsx b/nym-connect/mobile/src/pages/menu/Apps.tsx deleted file mode 100644 index e9b9ee71c6..0000000000 --- a/nym-connect/mobile/src/pages/menu/Apps.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { Divider, Typography } from '@mui/material'; -import { Box } from '@mui/system'; - -const appsSchema = { - messagingApps: ['Telegram', 'Keybase'], - wallets: ['Blockstream', 'Electrum'], -}; - -export const CompatibleApps = () => ( - - - Supported apps - - - Messaging apps - - - - {appsSchema.messagingApps.map((app) => ( - - {app} - - ))} - - - - Wallets - - - - {appsSchema.wallets.map((wallet) => ( - - {wallet} - - ))} - - -); diff --git a/nym-connect/mobile/src/pages/menu/Guide.tsx b/nym-connect/mobile/src/pages/menu/Guide.tsx deleted file mode 100644 index 2323d68a82..0000000000 --- a/nym-connect/mobile/src/pages/menu/Guide.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { useState } from 'react'; -import { HelpPage } from 'src/components/HelpPage'; -import Image1 from '../../assets/help-step-one.png'; -import Image2 from '../../assets/help-step-two.png'; -import Image4 from '../../assets/help-step-four.png'; - -export const HelpGuide = () => { - const [step, setStep] = useState(1); - const TOTAL_STEPS = 3; - - if (step === 1) - return ( - setStep(2)} - /> - ); - - if (step === 2) - return ( - setStep(1)} - onNext={() => setStep(3)} - /> - ); - - if (step === 3) - return ( - setStep(2)} - /> - ); - - return null; -}; diff --git a/nym-connect/mobile/src/pages/menu/index.tsx b/nym-connect/mobile/src/pages/menu/index.tsx deleted file mode 100644 index 47840d6668..0000000000 --- a/nym-connect/mobile/src/pages/menu/index.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import { Apps, HelpOutline, Settings } from '@mui/icons-material'; -import { Stack, Link, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography } from '@mui/material'; -import { Link as RouterLink } from 'react-router-dom'; -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: 'Settings', icon: Settings, path: 'settings' }, -]; - -export const Menu = () => ( - - - {menuSchema.map((item) => ( - - - - - - {' '} - - - {item.title} - - - - - - ))} - - - -); diff --git a/nym-connect/mobile/src/pages/menu/settings/GatewaySettings.tsx b/nym-connect/mobile/src/pages/menu/settings/GatewaySettings.tsx deleted file mode 100644 index c3fdc9808d..0000000000 --- a/nym-connect/mobile/src/pages/menu/settings/GatewaySettings.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { ChangeEvent, useState } from 'react'; -import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField'; -import { Box, FormControl, FormControlLabel, FormHelperText, Stack, Switch, Typography } from '@mui/material'; -import { useClientContext } from 'src/context/main'; -import { ConnectionStatusKind } from 'src/types'; -import { AppVersion } from 'src/components/AppVersion'; - -export const GatewaySettings = () => { - const { userDefinedGateway, setUserDefinedGateway, connectionStatus } = useClientContext(); - const [gatewayKey, setGatewayKey] = useState(userDefinedGateway?.gateway); - - const handleIsValidGatewayKey = (isValid: boolean) => { - let gateway: string | undefined; - - if (isValid) { - gateway = gatewayKey; - } - - setUserDefinedGateway((current) => ({ ...current, gateway })); - }; - - const handleChange = (e: ChangeEvent) => { - setUserDefinedGateway((current) => ({ ...current, isActive: e.target.checked })); - }; - - return ( - - - - - Select your Gateway - - - Use a gateway of your choice - - - - } - label={userDefinedGateway?.isActive ? 'On' : 'Off'} - /> - {connectionStatus === ConnectionStatusKind.connected && ( - This setting is disabled during an active connection - )} - {userDefinedGateway?.isActive && ( - - )} - - - - - To find a gateway go to{' '} - - explorer.nymtech.net/network-components/gateways - - - - - - - ); -}; diff --git a/nym-connect/mobile/src/pages/menu/settings/ServiceProviderSettings.tsx b/nym-connect/mobile/src/pages/menu/settings/ServiceProviderSettings.tsx deleted file mode 100644 index 61a87debb1..0000000000 --- a/nym-connect/mobile/src/pages/menu/settings/ServiceProviderSettings.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { ChangeEvent } from 'react'; -import { - Autocomplete, - Box, - FormControl, - FormControlLabel, - FormHelperText, - Stack, - Switch, - TextField, - Typography, -} from '@mui/material'; -import { AppVersion } from 'src/components/AppVersion'; -import { ConnectionStatusKind } from 'src/types'; -import { useClientContext } from 'src/context/main'; - -export const ServiceProviderSettings = () => { - const { connectionStatus, serviceProviders, userDefinedSPAddress, setUserDefinedSPAddress } = useClientContext(); - - const toggleOnOff = (e: ChangeEvent) => { - setUserDefinedSPAddress((current) => ({ ...current, isActive: e.target.checked })); - }; - - const handleSelectFromList = (value: string | null) => { - setUserDefinedSPAddress((current) => ({ ...current, address: value ?? undefined })); - }; - - const getSPDescription = (spAddress: string) => { - const match = serviceProviders?.find((sp) => sp.address === spAddress); - - if (match) return match.description; - - return 'N/A'; - }; - - const validateInput = (value: string) => { - setUserDefinedSPAddress((current) => ({ ...current, address: !value.length ? undefined : value })); - }; - - return ( - - - - - Select your Service Provider - - - Pick a service provider from the list or enter your own - - - - } - label={userDefinedSPAddress.isActive ? 'On' : 'Off'} - /> - {connectionStatus === ConnectionStatusKind.connected && ( - This setting is disabled during an active connection - )} - {userDefinedSPAddress.isActive && serviceProviders && ( - sp.address)} - freeSolo - value={userDefinedSPAddress.address || ''} - onChange={(e, value) => handleSelectFromList(value)} - size="small" - renderInput={(params) => ( - validateInput(e.target.value)} - /> - )} - ListboxProps={{ style: { background: 'unset', fontSize: '14px' } }} - /> - )} - - {userDefinedSPAddress.address && userDefinedSPAddress.isActive && ( - - Name of Service Provider - - {getSPDescription(userDefinedSPAddress.address)} - - - )} - - - - - ); -}; diff --git a/nym-connect/mobile/src/routes/index.tsx b/nym-connect/mobile/src/routes/index.tsx deleted file mode 100644 index dd3eec678e..0000000000 --- a/nym-connect/mobile/src/routes/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { Routes, Route } from 'react-router-dom'; -import { ConnectionPage } from 'src/pages/connection'; -import { Menu } from 'src/pages/menu'; -import { CompatibleApps } from 'src/pages/menu/Apps'; -import { HelpGuide } from 'src/pages/menu/Guide'; -import { SettingsMenu } from 'src/pages/menu/settings'; -import { GatewaySettings } from 'src/pages/menu/settings/GatewaySettings'; -import { ServiceProviderSettings } from 'src/pages/menu/settings/ServiceProviderSettings'; - -export const AppRoutes = () => ( - - } /> - - } /> - } /> - } /> - - } /> - } /> - } /> - - - -); diff --git a/nym-connect/mobile/src/stories/AppFlow.stories.tsx b/nym-connect/mobile/src/stories/AppFlow.stories.tsx deleted file mode 100644 index c00cb2aa9e..0000000000 --- a/nym-connect/mobile/src/stories/AppFlow.stories.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Box } from '@mui/material'; -import { DateTime } from 'luxon'; -import { Disconnected } from 'src/pages/connection/Disconnected'; -import { Connected } from 'src/pages/connection/Connected'; -import { ConnectionStatusKind } from 'src/types'; -import { AppWindowFrame } from '../components/AppWindowFrame'; -import { useClientContext } from '../context/main'; -import { Services } from '../types/directory'; - -export default { - title: 'App/Flow', - component: AppWindowFrame, -} as ComponentMeta; - -const width = 240; -const height = 575; - -export const Mock: ComponentStory = () => { - const context = useClientContext(); - const [busy, setBusy] = React.useState(); - const services: Services = [ - { - id: 'keybase', - description: 'Keybase', - items: [ - { - id: 'nym-keybase', - description: 'Nym Keybase Service Provider', - address: '1234.5678', - gateway: 'abcedf', - }, - ], - }, - ]; - const handleConnectClick = React.useCallback(() => { - const oldStatus = context.connectionStatus; - if (oldStatus === 'connected' || oldStatus === 'disconnected') { - setBusy(true); - - // eslint-disable-next-line default-case - switch (oldStatus) { - case 'disconnected': - context.setConnectionStatus(ConnectionStatusKind.connecting); - break; - case 'connected': - context.setConnectionStatus(ConnectionStatusKind.disconnecting); - break; - } - - setTimeout(() => { - // eslint-disable-next-line default-case - switch (oldStatus) { - case 'disconnected': - context.setConnectedSince(DateTime.now()); - context.setConnectionStatus(ConnectionStatusKind.connected); - break; - case 'connected': - context.setConnectionStatus(ConnectionStatusKind.disconnected); - break; - } - setBusy(false); - }, 5000); - } - }, [context.connectionStatus]); - - if ( - context.connectionStatus === ConnectionStatusKind.disconnected || - context.connectionStatus === ConnectionStatusKind.connecting - ) { - return ( - - - {}} - /> - - - ); - } - - return ( - - {}} - gatewayPerformance="Good" - showInfoModal={false} - closeInfoModal={() => undefined} - status={context.connectionStatus} - busy={busy} - onConnectClick={handleConnectClick} - ipAddress="127.0.0.1" - port={1080} - connectedSince={context.connectedSince} - serviceProvider={services[0].items[0]} - stats={[ - { - label: 'in:', - totalBytes: 1024, - rateBytesPerSecond: 1024 * 1024 * 1024 + 10, - }, - { - label: 'out:', - totalBytes: 1024 * 1024 * 1024 * 1024 * 20, - rateBytesPerSecond: 1024 * 1024 + 10, - }, - ]} - /> - - ); -}; diff --git a/nym-connect/mobile/src/stories/AppWindowFrame.stories.tsx b/nym-connect/mobile/src/stories/AppWindowFrame.stories.tsx deleted file mode 100644 index e8fa9a8b6b..0000000000 --- a/nym-connect/mobile/src/stories/AppWindowFrame.stories.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Box } from '@mui/material'; -import { AppWindowFrame } from '../components/AppWindowFrame'; - -export default { - title: 'App/AppWindowFrame', - component: AppWindowFrame, -} as ComponentMeta; - -export const Default: ComponentStory = () => ( - - - Culpa deserunt cupidatat culpa nisi aute dolore nisi deserunt cillum consequat elit. Nostrud id occaecat - consectetur consectetur excepteur labore consectetur. Laboris tempor consequat qui exercitation adipisicing sunt - cupidatat est. Officia dolore qui eu dolor velit ex ea qui laborum. Mollit ut est sit irure elit ad ut deserunt. - - -); diff --git a/nym-connect/mobile/src/stories/ConnectedLayout.stories.tsx b/nym-connect/mobile/src/stories/ConnectedLayout.stories.tsx deleted file mode 100644 index 426252dc63..0000000000 --- a/nym-connect/mobile/src/stories/ConnectedLayout.stories.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Box } from '@mui/material'; -import { DateTime } from 'luxon'; -import { Connected } from 'src/pages/connection/Connected'; -import { ConnectionStatusKind } from 'src/types'; - -const onClick = () => undefined; - -export default { - title: 'Layouts/ConnectedLayout', - component: Connected, -} as ComponentMeta; - -export const Default: ComponentStory = () => ( - - {}} - gatewayPerformance="Good" - showInfoModal={false} - closeInfoModal={() => undefined} - status={ConnectionStatusKind.connected} - connectedSince={DateTime.now()} - ipAddress="127.0.0.1" - serviceProvider={{ id: 'service 1', description: 'good services', address: 'abc123', gateway: '8910xyz' }} - port={1080} - stats={[ - { - label: 'in:', - totalBytes: 1024, - rateBytesPerSecond: 1024 * 1024 * 1024 + 10, - }, - { - label: 'out:', - totalBytes: 1024 * 1024 * 1024 * 1024 * 20, - rateBytesPerSecond: 1024 * 1024 + 10, - }, - ]} - onConnectClick={onClick} - /> - -); diff --git a/nym-connect/mobile/src/stories/ConnectionButton.stories.tsx b/nym-connect/mobile/src/stories/ConnectionButton.stories.tsx deleted file mode 100644 index 6fcf7530ad..0000000000 --- a/nym-connect/mobile/src/stories/ConnectionButton.stories.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { ConnectionStatusKind } from 'src/types'; -import { ConnectionButton } from '../components/ConnectionButton'; - -export default { - title: 'Components/ConnectionButton', - component: ConnectionButton, -} as ComponentMeta; - -export const Disconnected: ComponentStory = () => ( - -); - -export const Connecting: ComponentStory = () => ( - -); - -export const Connected: ComponentStory = () => ( - -); - -export const Disconnecting: ComponentStory = () => ( - -); diff --git a/nym-connect/mobile/src/stories/ConnectionStats.stories.tsx b/nym-connect/mobile/src/stories/ConnectionStats.stories.tsx deleted file mode 100644 index 7faea66c16..0000000000 --- a/nym-connect/mobile/src/stories/ConnectionStats.stories.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { ConnectionStats } from '../components/ConnectionStats'; - -export default { - title: 'Components/ConnectionStats', - component: ConnectionStats, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -// 👇 Each story then reuses that template -export const Default = Template.bind({}); -Default.args = { - stats: [ - { - label: 'in:', - totalBytes: 1024, - rateBytesPerSecond: 1024 * 1024 * 1024 + 10, - }, - { - label: 'out:', - totalBytes: 1024 * 1024 * 1024 * 1024 * 20, - rateBytesPerSecond: 1024 * 1024 + 10, - }, - ], -}; diff --git a/nym-connect/mobile/src/stories/ConnectionStatus.stories.tsx b/nym-connect/mobile/src/stories/ConnectionStatus.stories.tsx deleted file mode 100644 index d98e262374..0000000000 --- a/nym-connect/mobile/src/stories/ConnectionStatus.stories.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { DateTime } from 'luxon'; -import { ConnectionStatus } from '../components/ConnectionStatus'; -import { ConnectionStatusKind } from '../types'; - -export default { - title: 'Components/ConnectionStatus', - component: ConnectionStatus, -} as ComponentMeta; - -export const Disconnected: ComponentStory = () => ( - -); - -export const Connecting: ComponentStory = () => ( - -); - -export const Connected: ComponentStory = () => ( - -); - -export const Disconnecting: ComponentStory = () => ( - -); diff --git a/nym-connect/mobile/src/stories/DefaultLayout.stories.tsx b/nym-connect/mobile/src/stories/DefaultLayout.stories.tsx deleted file mode 100644 index 4d1b869a10..0000000000 --- a/nym-connect/mobile/src/stories/DefaultLayout.stories.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Box } from '@mui/material'; -import { Disconnected } from 'src/pages/connection/Disconnected'; -import { ConnectionStatusKind } from '../types'; - -export default { - title: 'Layouts/DefaultLayout', - component: Disconnected, -} as ComponentMeta; - -const onClick = () => undefined; - -export const Default: ComponentStory = () => ( - - {}} - error={undefined} - onConnectClick={onClick} - /> - -); - -export const WithServices: ComponentStory = () => ( - - {}} - error={undefined} - onConnectClick={onClick} - /> - -); diff --git a/nym-connect/mobile/src/stories/Introduction.stories.mdx b/nym-connect/mobile/src/stories/Introduction.stories.mdx deleted file mode 100644 index eb1d51fc24..0000000000 --- a/nym-connect/mobile/src/stories/Introduction.stories.mdx +++ /dev/null @@ -1,7 +0,0 @@ -import { Meta } from '@storybook/addon-docs'; - - - -# nym-connect - -Components for connecting to the Nym mixnet. diff --git a/nym-connect/mobile/src/stories/IpAddressAndPort.stories.tsx b/nym-connect/mobile/src/stories/IpAddressAndPort.stories.tsx deleted file mode 100644 index 943b3a40b4..0000000000 --- a/nym-connect/mobile/src/stories/IpAddressAndPort.stories.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { IpAddressAndPort } from '../components/IpAddressAndPort'; - -export default { - title: 'Components/IpAddressAndPort', - component: IpAddressAndPort, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ; - -// 👇 Each story then reuses that template -export const Default = Template.bind({}); -Default.args = { label: 'Socks5 address', ipAddress: '127.0.0.1', port: 1080 }; diff --git a/nym-connect/mobile/src/stories/PowerButton.stories.tsx b/nym-connect/mobile/src/stories/PowerButton.stories.tsx deleted file mode 100644 index 6cb2896849..0000000000 --- a/nym-connect/mobile/src/stories/PowerButton.stories.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { PowerButton } from 'src/components/PowerButton/PowerButton'; -import { ConnectionStatusKind } from 'src/types'; - -export default { - title: 'Components/PowerButton', - component: PowerButton, -} as ComponentMeta; - -export const Disconnected: ComponentStory = () => ( - -); - -export const Connecting: ComponentStory = () => ( - -); - -export const Connected: ComponentStory = () => ( - -); - -export const Disconnecting: ComponentStory = () => ( - -); - -export const Disabled: ComponentStory = () => ( - -); diff --git a/nym-connect/mobile/src/theme/index.tsx b/nym-connect/mobile/src/theme/index.tsx deleted file mode 100644 index d307fe46ee..0000000000 --- a/nym-connect/mobile/src/theme/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { createTheme, ThemeProvider } from '@mui/material/styles'; -import { CssBaseline } from '@mui/material'; -import { getDesignTokens } from './theme'; -// eslint-disable-next-line import/no-relative-packages -import '../../../../assets/fonts/non-variable/fonts.css'; - -/** - * Provides the theme for Nym Connect by reacting to the light/dark mode choice stored in the app context. - */ -export const NymMixnetTheme: FCWithChildren<{ mode: 'light' | 'dark' }> = ({ children, mode }) => { - const theme = React.useMemo(() => createTheme(getDesignTokens(mode)), [mode]); - return ( - - - {children} - - ); -}; - -export const NymShipyardTheme: FCWithChildren<{ mode?: 'light' | 'dark' }> = ({ children, mode = 'dark' }) => { - const theme = React.useMemo(() => createTheme(getDesignTokens(mode, true)), [mode]); - return ( - - - {children} - - ); -}; diff --git a/nym-connect/mobile/src/theme/mui-theme.d.ts b/nym-connect/mobile/src/theme/mui-theme.d.ts deleted file mode 100644 index 76db4b1a99..0000000000 --- a/nym-connect/mobile/src/theme/mui-theme.d.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* eslint-disable no-shadow,@typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-interface */ -import { Theme, ThemeOptions, Palette, PaletteOptions } from '@mui/material/styles'; -import { PaletteMode } from '@mui/material'; - -/** - * If you are unfamiliar with Material UI theming, please read the following first: - * - https://mui.com/customization/theming/ - * - https://mui.com/customization/palette/ - * - https://mui.com/customization/dark-mode/#dark-mode-with-custom-palette - * - * This file adds typings to the theme using Typescript's module augmentation. - * - * Read the following if you are unfamiliar with module augmentation and declaration merging. Then - * look at the recommendations from Material UI docs for implementation: - * - https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation - * - https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces - * - https://mui.com/customization/palette/#adding-new-colors - * - * - * IMPORTANT: - * - * The type augmentation must match MUI's definitions. So, notice the use of `interface` rather than - * `type Foo = { ... }` - this is necessary to merge the definitions. - */ - -declare module '@mui/material/styles' { - /** - * This interface defines a palette used across Nym for branding - */ - interface NymPalette { - highlight: string; - cta: string; - success: string; - warning: string; - info: string; - fee: string; - background: { light: string; dark: string }; - text: { - light: string; - dark: string; - }; - shipyard: string; - } - - interface NymPaletteVariant { - mode: PaletteMode; - background: { - main: string; - paper: string; - }; - text: { - main: string; - }; - topNav: { - background: string; - }; - shipyard: { - main: string; - }; - } - - /** - * A palette definition only for the Nym Mixnet that extends the Nym palette - */ - interface NymMixnetPalette { - nymMixnet: {}; - } - - interface NymPaletteAndNymMixnetPalette { - nym: NymPalette & NymMixnetPalette; - } - - type NymPaletteAndNymMixnetPaletteOptions = Partial; - - /** - * Add anything not palette related to the theme here - */ - interface NymTheme {} - - /** - * This augments the definitions of the MUI Theme with the Nym theme, as well as - * a partial `ThemeOptions` type used by `createTheme` - * - * IMPORTANT: only add extensions to the interfaces above, do not modify the lines below - */ - interface Theme extends NymTheme {} - interface ThemeOptions extends Partial {} - interface Palette extends NymPaletteAndNymMixnetPalette {} - interface PaletteOptions extends NymPaletteAndNymMixnetPaletteOptions {} -} diff --git a/nym-connect/mobile/src/theme/theme.tsx b/nym-connect/mobile/src/theme/theme.tsx deleted file mode 100644 index 23c8ff8b57..0000000000 --- a/nym-connect/mobile/src/theme/theme.tsx +++ /dev/null @@ -1,284 +0,0 @@ -import { PaletteMode } from '@mui/material'; -import { - createTheme, - NymMixnetPalette, - NymPalette, - NymPaletteVariant, - PaletteOptions, - ThemeOptions, -} from '@mui/material/styles'; - -//----------------------------------------------------------------------------------------------- -// Nym palette type definitions -// - -/** - * The Nym palette. - * - * IMPORTANT: do not export this constant, always use the MUI `useTheme` hook to get the correct - * colours for dark/light mode. - */ -const nymPalette: NymPalette = { - /** emphasises important elements */ - highlight: '#21D072', - cta: '#FB6E4E', - success: '#21D073', - info: '#60D7EF', - warning: '#FFE600', - fee: '#967FF0', - background: { light: '#F4F6F8', dark: '#1D2125' }, - text: { - light: '#F2F2F2', - dark: '#1D2125', - }, - shipyard: '#817FFA', -}; - -const darkMode: NymPaletteVariant = { - mode: 'dark', - background: { - main: '#1D2125', - paper: '#242C3D', - }, - text: { - main: '#F2F2F2', - }, - topNav: { - background: '#111826', - }, - shipyard: { - main: '#817FFA', - }, -}; - -const lightMode: NymPaletteVariant = { - mode: 'light', - background: { - main: '#F2F2F2', - paper: '#FFFFFF', - }, - text: { - main: '#1D2125', - }, - topNav: { - background: '#111826', - }, - shipyard: { - main: '#817FFA', - }, -}; - -/** - * Nym palette specific to the Nym Mixnode - * - * IMPORTANT: do not export this constant, always use the MUI `useTheme` hook to get the correct - * colours for dark/light mode. - */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const nymMixnetPalette = (variant: NymPaletteVariant): NymMixnetPalette => ({ - nymMixnet: {}, -}); - -//----------------------------------------------------------------------------------------------- -// Nym palettes for light and dark mode -// - -/** - * Map a Nym palette variant onto the MUI palette - */ -const variantToMUIPalette = (variant: NymPaletteVariant): PaletteOptions => ({ - text: { - primary: variant.text.main, - }, - primary: { - main: nymPalette.highlight, - contrastText: '#fff', - }, - secondary: { - main: variant.mode === 'dark' ? nymPalette.background.light : nymPalette.background.dark, - }, - success: { - main: nymPalette.success, - }, - info: { - main: nymPalette.info, - }, - warning: { - main: nymPalette.warning, - }, - background: { - default: variant.background.main, - paper: variant.background.paper, - }, -}); - -/** - * Map a Nym palette variant onto the MUI palette for Shipyard - */ -const variantShipyardToMUIPalette = (variant: NymPaletteVariant): PaletteOptions => ({ - text: { - primary: variant.text.main, - }, - primary: { - main: nymPalette.shipyard, - contrastText: '#fff', - }, - secondary: { - main: variant.mode === 'dark' ? nymPalette.background.light : nymPalette.background.dark, - }, - success: { - main: nymPalette.success, - }, - info: { - main: nymPalette.info, - }, - warning: { - main: nymPalette.warning, - }, - background: { - default: variant.background.main, - paper: variant.background.paper, - }, -}); - -/** - * Returns the Network Explorer palette for light mode. - */ -const createLightModePalette = (): PaletteOptions => ({ - nym: { - ...nymPalette, - ...nymMixnetPalette(lightMode), - }, - ...variantToMUIPalette(lightMode), -}); - -/** - * Returns the Network Explorer palette for dark mode. - */ -const createDarkModePalette = (): PaletteOptions => ({ - nym: { - ...nymPalette, - ...nymMixnetPalette(darkMode), - }, - ...variantToMUIPalette(darkMode), -}); - -/** - * Returns the Shipyard palette for dark mode. - */ -const createShipyardDarkModePalette = (): PaletteOptions => ({ - nym: { - ...nymPalette, - ...nymMixnetPalette(darkMode), - }, - ...variantShipyardToMUIPalette(darkMode), -}); - -/** - * IMPORANT: if you need to get the default MUI theme, use the following - * - * import { createTheme as systemCreateTheme } from '@mui/system'; - * - * // get the MUI system defaults for light mode - * const systemTheme = systemCreateTheme({ palette: { mode: 'light' } }); - * - * - * return { - * // change `primary` to default MUI `success` - * primary: { - * main: systemTheme.palette.success.main, - * }, - * nym: { - * ...nymPalette, - * ...nymMixnetPalette, - * }, - * }; - */ - -//----------------------------------------------------------------------------------------------- -// Nym theme overrides -// - -/** - * Gets the theme options to be passed to `createTheme`. - * - * Based on pattern from https://mui.com/customization/dark-mode/#dark-mode-with-custom-palette. - * - * @param mode The theme mode: 'light' or 'dark' - */ -export const getDesignTokens = (mode: PaletteMode, isShipyard: boolean = false): ThemeOptions => { - let overrides; - if (isShipyard) { - overrides = createShipyardDarkModePalette(); - } else { - overrides = mode === 'light' ? createLightModePalette() : createDarkModePalette(); - } - - // create the palette from user's choice of light or dark mode - const { palette } = createTheme({ - palette: { - mode, - ...overrides, - }, - }); - - // then customise theme and components - return { - palette, - typography: { - fontFamily: [ - 'Open Sans', - 'sans-serif', - 'BlinkMacSystemFont', - 'Roboto', - 'Oxygen', - 'Ubuntu', - 'Helvetica Neue', - ].join(','), - fontSize: 16, - fontWeightRegular: 500, - button: { - textTransform: 'none', - fontWeight: '600', - }, - }, - shape: { - borderRadius: 8, - }, - transitions: { - duration: { - shortest: 150, - shorter: 200, - short: 250, - standard: 300, - complex: 375, - enteringScreen: 225, - leavingScreen: 195, - }, - easing: { - easeIn: 'cubic-bezier(0.4, 0, 1, 1)', - }, - }, - components: { - MuiButton: { - styleOverrides: { - sizeLarge: { - height: 55, - }, - }, - }, - MuiStepIcon: { - styleOverrides: { - root: { - '&.Mui-completed': { - color: nymPalette.success, - }, - '&.Mui-active': { - color: nymPalette.background.dark, - }, - }, - }, - }, - }, - }; -}; diff --git a/nym-connect/mobile/src/types/connection.ts b/nym-connect/mobile/src/types/connection.ts deleted file mode 100644 index 8fb5234477..0000000000 --- a/nym-connect/mobile/src/types/connection.ts +++ /dev/null @@ -1,8 +0,0 @@ -export enum ConnectionStatusKind { - disconnected = 'disconnected', - disconnecting = 'disconnecting', - connected = 'connected', - connecting = 'connecting', -} - -export type GatewayPerformance = 'Good' | 'Poor' | 'VeryPoor'; diff --git a/nym-connect/mobile/src/types/directory.ts b/nym-connect/mobile/src/types/directory.ts deleted file mode 100644 index 55395192a1..0000000000 --- a/nym-connect/mobile/src/types/directory.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface ServiceProvider { - id: string; - description: string; - address: string; - gateway: string; -} - -export interface Service { - id: string; - description: string; - items: ServiceProvider[]; -} - -export type Services = Service[]; diff --git a/nym-connect/mobile/src/types/error.ts b/nym-connect/mobile/src/types/error.ts deleted file mode 100644 index 599b959c39..0000000000 --- a/nym-connect/mobile/src/types/error.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type Error = { - title: string; - message: string; -}; diff --git a/nym-connect/mobile/src/types/event.ts b/nym-connect/mobile/src/types/event.ts deleted file mode 100644 index 66d1e514a6..0000000000 --- a/nym-connect/mobile/src/types/event.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type TauriEvent = { - payload: { - title: string; - message: string; - }; -}; diff --git a/nym-connect/mobile/src/types/gateway.ts b/nym-connect/mobile/src/types/gateway.ts deleted file mode 100644 index 735a3cdb03..0000000000 --- a/nym-connect/mobile/src/types/gateway.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface UserDefinedGateway { - isActive: boolean; - gateway?: string; -} diff --git a/nym-connect/mobile/src/types/index.ts b/nym-connect/mobile/src/types/index.ts deleted file mode 100644 index 6bc150d410..0000000000 --- a/nym-connect/mobile/src/types/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './rust'; -export * from './connection'; diff --git a/nym-connect/mobile/src/types/rust/index.ts b/nym-connect/mobile/src/types/rust/index.ts deleted file mode 100644 index f8efcfe235..0000000000 --- a/nym-connect/mobile/src/types/rust/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './stateparams'; diff --git a/nym-connect/mobile/src/types/rust/stateparams.ts b/nym-connect/mobile/src/types/rust/stateparams.ts deleted file mode 100644 index b7d7032420..0000000000 --- a/nym-connect/mobile/src/types/rust/stateparams.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-disable camelcase */ -export interface TauriContractStateParams { - minimum_mixnode_pledge: string; - minimum_gateway_pledge: string; - mixnode_rewarded_set_size: number; - mixnode_active_set_size: number; -} diff --git a/nym-connect/mobile/src/types/service-provider.ts b/nym-connect/mobile/src/types/service-provider.ts deleted file mode 100644 index ed370e2755..0000000000 --- a/nym-connect/mobile/src/types/service-provider.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface UserDefinedGateway { - isActive: boolean; - gateway?: string; -} - -export interface UserDefinedSPAddress { - isActive: boolean; - address?: string; -} diff --git a/nym-connect/mobile/src/types/storage.ts b/nym-connect/mobile/src/types/storage.ts deleted file mode 100644 index ca792b65d9..0000000000 --- a/nym-connect/mobile/src/types/storage.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface StorageKeyValue { - key: string; - value: T; -} diff --git a/nym-connect/mobile/src/typings/FC.d.ts b/nym-connect/mobile/src/typings/FC.d.ts deleted file mode 100644 index 08ebdfa298..0000000000 --- a/nym-connect/mobile/src/typings/FC.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare type FCWithChildren

= React.FC>; diff --git a/nym-connect/mobile/src/typings/jpeg.d.ts b/nym-connect/mobile/src/typings/jpeg.d.ts deleted file mode 100644 index af2ed72913..0000000000 --- a/nym-connect/mobile/src/typings/jpeg.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -declare module '*.jpeg' { - const value: any; - export default value; -} - -declare module '*.jpg' { - const value: any; - export default value; -} diff --git a/nym-connect/mobile/src/typings/json.d.ts b/nym-connect/mobile/src/typings/json.d.ts deleted file mode 100644 index b72dd46ee5..0000000000 --- a/nym-connect/mobile/src/typings/json.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.json' { - const content: any; - export default content; -} diff --git a/nym-connect/mobile/src/typings/png.d.ts b/nym-connect/mobile/src/typings/png.d.ts deleted file mode 100644 index dd84df40a4..0000000000 --- a/nym-connect/mobile/src/typings/png.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.png' { - const content: any; - export default content; -} diff --git a/nym-connect/mobile/src/typings/svg.d.ts b/nym-connect/mobile/src/typings/svg.d.ts deleted file mode 100644 index 091d25e210..0000000000 --- a/nym-connect/mobile/src/typings/svg.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.svg' { - const content: any; - export default content; -} diff --git a/nym-connect/mobile/src/typings/webp.d.ts b/nym-connect/mobile/src/typings/webp.d.ts deleted file mode 100644 index d594a17f53..0000000000 --- a/nym-connect/mobile/src/typings/webp.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.webp' { - const value: any; - export default value; -} diff --git a/nym-connect/mobile/src/typings/yaml.d.ts b/nym-connect/mobile/src/typings/yaml.d.ts deleted file mode 100644 index 4c36d21ec0..0000000000 --- a/nym-connect/mobile/src/typings/yaml.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -declare module '*.yml' { - const content: { [key: string]: any }; - export default content; -} - -declare module '*.yaml' { - const content: { [key: string]: any }; - export default content; -} diff --git a/nym-connect/mobile/src/utils/index.ts b/nym-connect/mobile/src/utils/index.ts deleted file mode 100644 index 04b415c026..0000000000 --- a/nym-connect/mobile/src/utils/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { useEffect, useRef } from 'react'; -import { EventName, listen, UnlistenFn, EventCallback } from '@tauri-apps/api/event'; -import { invoke } from '@tauri-apps/api'; -import { forage } from '@tauri-apps/tauri-forage'; -import { StorageKeyValue } from 'src/types/storage'; - -export const useTauriEvents = (event: EventName, handler: EventCallback) => { - const unlisten = useRef(); - - // list for events to clear local storage - useEffect(() => { - listen(event, handler).then((fn) => { - unlisten.current = fn; - }); - - return () => { - if (unlisten.current) { - unlisten.current(); - } - }; - }, []); -}; - -export const toggleLogViewer = async () => { - await invoke('help_log_toggle_window'); -}; - -export async function setItemInStorage({ key, value }: StorageKeyValue) { - try { - await forage.setItem({ - key, - value, - } as any)(); - } catch (e) { - console.warn(e); - } - return undefined; -} - -export const getItemFromStorage = async ({ key }: Pick, 'key'>) => { - try { - const gatewayFromStorage = await forage.getItem({ key })(); - return gatewayFromStorage; - } catch (e) { - console.warn(e); - } - return undefined; -}; diff --git a/nym-connect/mobile/tsconfig.eslint.json b/nym-connect/mobile/tsconfig.eslint.json deleted file mode 100644 index 836be28a14..0000000000 --- a/nym-connect/mobile/tsconfig.eslint.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "jsx": "react-jsx", - "noEmit": true - }, - "include": [ - ".storybook/*.js", - "webpack.*.js", - "src/**/*.js", - "src/**/*.jsx", - "src/**/*.ts", - "src/**/*.tsx", - "src/**/*.stories.*", - ], - "exclude": [ - "node_modules", - "dist" - ] -} diff --git a/nym-connect/mobile/tsconfig.json b/nym-connect/mobile/tsconfig.json deleted file mode 100644 index 585e7203d8..0000000000 --- a/nym-connect/mobile/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "noFallthroughCasesInSwitch": true, - "skipLibCheck": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": false, - "jsx": "react-jsx", - "sourceMap": true, - "baseUrl": ".", - "paths": { - "@assets/*": ["../assets/*"] - } - }, - "exclude": ["node_modules", "dist", "jest.config.js", "webpack.config.js", "webpack.prod.js", "webpack.common.js", "target", "src-tauri"] -} diff --git a/nym-connect/mobile/webpack.common.js b/nym-connect/mobile/webpack.common.js deleted file mode 100644 index c835404dda..0000000000 --- a/nym-connect/mobile/webpack.common.js +++ /dev/null @@ -1,55 +0,0 @@ -const path = require('path'); -const { mergeWithRules } = require('webpack-merge'); -const { webpackCommon } = require('@nymproject/webpack'); - -const entry = { - app: path.resolve(__dirname, 'src/index.tsx'), -}; - -module.exports = mergeWithRules({ - module: { - rules: { - test: 'match', - use: 'replace', - }, - }, -})( - webpackCommon(__dirname, [ - { filename: 'index.html', chunks: ['app'], template: path.resolve(__dirname, 'public/index.html') }, - ]), - { - module: { - rules: [ - { - test: /\.mdx?$/, - use: [ - { - loader: '@mdx-js/loader', - /** @type {import('@mdx-js/loader').Options} */ - options: {}, - }, - ], - }, - { - test: /\.ya?ml$/, - type: 'asset/resource', - use: [ - { - loader: 'yaml-loader', - options: { - asJSON: true, - }, - }, - ], - }, - ], - }, - entry, - output: { - clean: true, - path: path.resolve(__dirname, 'dist'), - filename: '[name].bundle.js', - publicPath: '/', - }, - }, -); diff --git a/nym-connect/mobile/webpack.dev.js b/nym-connect/mobile/webpack.dev.js deleted file mode 100644 index 2358b4de20..0000000000 --- a/nym-connect/mobile/webpack.dev.js +++ /dev/null @@ -1,78 +0,0 @@ -const { mergeWithRules } = require('webpack-merge'); -const webpack = require('webpack'); -const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); -const ReactRefreshTypeScript = require('react-refresh-typescript'); -const commonConfig = require('./webpack.common'); - -const config = mergeWithRules({ - module: { - rules: { - test: 'match', - use: 'replace', - }, - }, -})(commonConfig, { - mode: 'development', - devtool: 'inline-source-map', - module: { - rules: [ - { - test: /\.tsx?$/, - use: 'ts-loader', - exclude: /node_modules/, - options: { - getCustomTransformers: () => ({ - before: [ReactRefreshTypeScript()], - }), - // `ts-loader` does not work with HMR unless `transpileOnly` is used. - // If you need type checking, `ForkTsCheckerWebpackPlugin` is an alternative. - transpileOnly: true, - }, - }, - ], - }, - plugins: [ - new ReactRefreshWebpackPlugin(), - - // this can be included automatically by the dev server, however build mode fails if missing - new webpack.HotModuleReplacementPlugin(), - ], - - // recommended for faster rebuild - optimization: { - runtimeChunk: true, - removeAvailableModules: false, - removeEmptyChunks: false, - splitChunks: false, - }, - - cache: { - type: 'filesystem', - buildDependencies: { - // restart on config change - config: ['./webpack.dev.js'], - }, - }, - - devServer: { - port: 9000, - compress: true, - historyApiFallback: true, - // disable this because on android it makes reloading infinity loop - hot: false, - host: 'local-ipv4', - allowedHosts: 'all', - client: { - overlay: false, - }, - }, -}); - -// Remove WebpackFavicons plugin as it makes FDroid build more -// difficult to configure since webpack-favicons depends on sharp, -// which depends on system library libvips -// As we are building for mobile, this is useless anyway -// TODO do not base deletion on index -config.plugins.splice(2, 1); - -module.exports = config; diff --git a/nym-connect/mobile/webpack.prod.js b/nym-connect/mobile/webpack.prod.js deleted file mode 100644 index df059d4082..0000000000 --- a/nym-connect/mobile/webpack.prod.js +++ /dev/null @@ -1,24 +0,0 @@ -const path = require('path'); -const { default: merge } = require('webpack-merge'); -const common = require('./webpack.common'); - -const entry = { - app: path.resolve(__dirname, 'src/index.tsx'), -}; - -const config = merge(common, { - mode: 'production', - node: { - __dirname: false, - }, - entry, -}); - -// Remove WebpackFavicons plugin as it makes FDroid build more -// difficult to configure since webpack-favicons depends on sharp, -// which depends on system library libvips -// As we are building for mobile, this is useless anyway -// TODO do not base deletion on index -config.plugins.splice(2, 1); - -module.exports = config; diff --git a/nym-connect/native/android/README.md b/nym-connect/native/android/README.md index b2a92f2454..c0092aedd5 100644 --- a/nym-connect/native/android/README.md +++ b/nym-connect/native/android/README.md @@ -37,7 +37,7 @@ RELEASE=true ./build-android.sh aarch64 x86_64 The shared library for each ABIs will be automatically moved into `app/src/main/jniLibs/*` directories. -### APK build (from terminal) +### APK/AAB build (from terminal) This project is setup with multiple [product flavors](app/build.gradle) to build for specific architectures.\ @@ -48,12 +48,18 @@ Supported archs: - x86_64 - x86 -For example to build for _arm64_ in _release_ mode use +For example to build an APK for _arm64_ in _release_ mode use ```shell ./gradlew :app:assembleArm64Release ``` +Instead of building an APK, to build an app bundle (`.aab`) run + +```shell +./gradlew :app:bundleArm64Release +``` + **NOTE**: you likely want _arch64_ (`arm64` & `x86_64`) for APK distribution To build a _universal_ APK which includes all ABI use diff --git a/nym-connect/native/android/app/build.gradle b/nym-connect/native/android/app/build.gradle deleted file mode 100644 index 72ba890287..0000000000 --- a/nym-connect/native/android/app/build.gradle +++ /dev/null @@ -1,127 +0,0 @@ -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' -} - -android { - namespace 'net.nymtech.nyms5' - compileSdk 33 - - defaultConfig { - applicationId "net.nymtech.nyms5" - minSdk 24 - targetSdk 33 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - vectorDrawables { - useSupportLibrary true - } - } - - buildTypes { - release { - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - - flavorDimensions "abi" - productFlavors { - universal { - dimension "abi" - ndk { - abiFilters "arm64-v8a", "armeabi-v7a", "x86_64", "x86" - } - } - arch64 { - dimension "abi" - ndk { - abiFilters "arm64-v8a", "x86_64" - } - } - arm64 { - dimension "abi" - ndk { - abiFilters "arm64-v8a" - } - } - arm { - dimension "abi" - ndk { - abiFilters "armeabi-v7a" - } - } - x86_64 { - dimension "abi" - ndk { - abiFilters "x86_64" - } - } - x86 { - dimension "abi" - ndk { - abiFilters "x86" - } - } - } - -// splits { -// abi { -// enable true -// reset() -// include "x86_64", "arm64-v8a" -// universalApk true -// } -// } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } - buildFeatures { - compose true - } - composeOptions { - kotlinCompilerExtensionVersion '1.4.6' - } - packagingOptions { - resources { - excludes += '/META-INF/{AL2.0,LGPL2.1}' - } - } -} - -dependencies { - implementation 'androidx.core:core-ktx:1.10.1' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' - implementation 'androidx.activity:activity-compose:1.7.2' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' - implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0' - implementation platform('androidx.compose:compose-bom:2022.10.00') - implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1' - implementation 'androidx.navigation:navigation-compose:2.6.0' - implementation 'androidx.compose.runtime:runtime-livedata' - implementation 'androidx.compose.ui:ui' - implementation 'androidx.compose.ui:ui-graphics' - implementation 'androidx.compose.ui:ui-tooling-preview' - implementation 'androidx.compose.material3:material3' - implementation 'androidx.work:work-runtime-ktx:2.8.1' - implementation 'androidx.datastore:datastore-preferences:1.0.0' - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - androidTestImplementation platform('androidx.compose:compose-bom:2022.10.00') - 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:2.3.1' - implementation 'io.sentry:sentry-android:6.24.0' -} diff --git a/nym-connect/native/android/app/build.gradle.kts b/nym-connect/native/android/app/build.gradle.kts new file mode 100644 index 0000000000..b1c912729f --- /dev/null +++ b/nym-connect/native/android/app/build.gradle.kts @@ -0,0 +1,149 @@ +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" +} + +android { + namespace = "net.nymtech.nyms5" + compileSdk = 33 + + defaultConfig { + applicationId = "net.nymtech.nyms5" + minSdk = 24 + targetSdk = 33 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary = true + } + } + + bundle { + language { + enableSplit = true + } + density { + enableSplit = true + } + abi { + enableSplit = true + } + } + + buildTypes { + release { + isMinifyEnabled = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + flavorDimensions += "abi" + productFlavors { + create("universal") { + dimension = "abi" + ndk { + abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86") + } + } + create("arch64") { + dimension = "abi" + ndk { + abiFilters += listOf("arm64-v8a", "x86_64") + } + } + create("arm64") { + dimension = "abi" + ndk { + abiFilters += "arm64-v8a" + } + } + create("arm") { + dimension = "abi" + ndk { + abiFilters += "armeabi-v7a" + } + } + create("x86_64") { + dimension = "abi" + ndk { + abiFilters += "x86_64" + } + } + create("x86") { + dimension = "abi" + ndk { + abiFilters += "x86" + } + } + } + +// splits { +// abi { +// enable true +// reset() +// include "x86_64", "arm64-v8a" +// universalApk true +// } +// } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.4.6" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } +} + +sentry { + // TODO disable auto upload of mapping files for now to ease FDroid submission + // (avoiding to have to provide a sentry auth token during compile time) + autoUploadProguardMapping.set(false) +} + +dependencies { + + implementation("androidx.core:core-ktx:1.10.1") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") + implementation("androidx.activity:activity-compose:1.7.2") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") + implementation(platform("androidx.compose:compose-bom:2022.10.00")) + implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1") + implementation("androidx.navigation:navigation-compose:2.6.0") + implementation("androidx.compose.runtime:runtime-livedata") + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.ui:ui-graphics") + implementation("androidx.compose.ui:ui-tooling-preview") + implementation("androidx.compose.material3:material3") + implementation("androidx.work:work-runtime-ktx:2.8.1") + implementation("androidx.datastore:datastore-preferences:1.0.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.3") + androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") + androidTestImplementation(platform("androidx.compose:compose-bom:2022.10.00")) + 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: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/App.kt b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/App.kt index 105270644c..6da2011ffc 100644 --- a/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/App.kt +++ b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/App.kt @@ -19,7 +19,6 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.runBlocking - val Context.dataStore: DataStore by preferencesDataStore(name = "settings") val monitoringKey = booleanPreferencesKey("monitoring") diff --git a/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/MainActivity.kt b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/MainActivity.kt index 3530d6db81..962e2c3760 100644 --- a/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/MainActivity.kt +++ b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/MainActivity.kt @@ -1,71 +1,23 @@ package net.nymtech.nyms5 +import android.Manifest.permission.POST_NOTIFICATIONS +import android.content.pm.PackageManager import android.os.Build import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material.icons.filled.Menu -import androidx.compose.material3.CenterAlignedTopAppBar -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.LinearProgressIndicator -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Surface -import androidx.compose.material3.Switch -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import kotlinx.coroutines.launch import net.nymtech.nyms5.ui.theme.NymTheme -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.Alignment -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalClipboardManager -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.font.FontStyle -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.edit -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController import androidx.work.WorkInfo import androidx.work.WorkManager -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.map +import net.nymtech.nyms5.ui.composables.HomeScreen class MainActivity : ComponentActivity() { private val tag = "MainActivity" @@ -77,11 +29,24 @@ class MainActivity : ComponentActivity() { ) } + private val requestPermissionLauncher = + registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if (isGranted) { + Log.d(tag, "permission POST_NOTIFICATIONS has been granted") + } else { + Log.d(tag, "permission POST_NOTIFICATIONS has NOT been granted") + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d(tag, "____onCreate") Log.i(tag, "device SDK [${Build.VERSION.SDK_INT}]") + checkPermission() + // observe proxy work progress WorkManager.getInstance(applicationContext) .getWorkInfoByIdLiveData(ProxyWorker.workId) @@ -139,241 +104,22 @@ class MainActivity : ComponentActivity() { super.onStart() viewModel.checkStateSync() } -} -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun HomeScreen( - proxyState: MainViewModel.ProxyState, - monitoring: Boolean, - dataStore: DataStore, - onSwitch: (value: Boolean) -> Unit, -) { - val navController = rememberNavController() - var expanded by remember { mutableStateOf(false) } - val scope = rememberCoroutineScope() - - Scaffold(topBar = { - CenterAlignedTopAppBar( - title = { - Text(stringResource(R.string.app_name)) - }, - navigationIcon = { - val navBackStackEntry by navController.currentBackStackEntryAsState() - val currentRoute = navBackStackEntry?.destination?.route - - if (currentRoute === "proxy") { - IconButton(onClick = { expanded = true }) { - Icon( - imageVector = Icons.Filled.Menu, - contentDescription = "Main menu" - ) - } - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false } - ) { - DropdownMenuItem(onClick = { - navController.navigate("monitoring") { - popUpTo("proxy") - } - expanded = false - }, text = { - Text("Error reporting") - }) - } - } else { - IconButton(onClick = { - navController.navigate("proxy") { - popUpTo("proxy") - } - }) { - Icon( - imageVector = Icons.Filled.ArrowBack, - contentDescription = "Back home" - ) - } + private fun checkPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // POST_NOTIFICATIONS is needed for the notification displayed + // when socks5 proxy client is running + when (checkSelfPermission(POST_NOTIFICATIONS)) { + PackageManager.PERMISSION_GRANTED -> { + Log.d(tag, "check permission POST_NOTIFICATIONS: granted") } - }, - ) - }) { contentPadding -> - NavHost( - navController = navController, - startDestination = "proxy", - modifier = Modifier.padding(contentPadding) - ) { - composable("proxy") { - S5ClientSwitch( - connected = proxyState.connected, - loading = proxyState.loading, - onSwitch = onSwitch - ) - } - composable("monitoring") { - Monitoring(initialValue = monitoring) { - scope.launch(Dispatchers.IO) { - dataStore.edit { settings -> settings[monitoringKey] = it } - } + + else -> { + Log.d(tag, "check permission POST_NOTIFICATIONS: not granted") + requestPermissionLauncher.launch(POST_NOTIFICATIONS) } } } } } -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun S5ClientSwitch( - connected: Boolean, - loading: Boolean, - onSwitch: (value: Boolean) -> Unit, - modifier: Modifier = Modifier -) { - val clipboardManager = LocalClipboardManager.current - val host = stringResource(R.string.proxy_host) - val port = stringResource(R.string.proxy_port) - if (loading) { - Row { - LinearProgressIndicator( - modifier = modifier.fillMaxWidth(), - color = MaterialTheme.colorScheme.secondary - ) - } - } else { - Spacer(modifier = modifier.height(2.dp)) - } - Column(modifier = modifier.padding(16.dp)) { - Row( - modifier = modifier.padding(16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Text("Nym proxy") - Spacer(modifier = modifier.width(14.dp)) - Switch(checked = connected, enabled = !loading, onCheckedChange = { - onSwitch(!connected) - }, modifier = Modifier.testTag("switch_connect")) - } - if (connected && !loading) { - Column(modifier = modifier.padding(16.dp)) { - Text( - color = Color.Green, - fontStyle = FontStyle.Italic, - text = stringResource(R.string.connected_text) - ) - Spacer(modifier = modifier.height(10.dp)) - Row( - verticalAlignment = Alignment.CenterVertically - ) { - Surface( - onClick = { clipboardManager.setText(AnnotatedString(host)) }, - shape = RoundedCornerShape(6.dp), - ) { - Row( - modifier = modifier.padding(8.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text(host) - Spacer(modifier = modifier.width(8.dp)) - Icon( - painter = painterResource(R.drawable.copy_24), - contentDescription = "copy to clipboard" - ) - } - } - Spacer(modifier = modifier.width(12.dp)) - Surface( - onClick = { clipboardManager.setText(AnnotatedString(port)) }, - shape = RoundedCornerShape(6.dp), - ) { - Row( - modifier = modifier.padding(8.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Text(port) - Spacer(modifier = modifier.width(8.dp)) - Icon( - painter = painterResource(R.drawable.copy_24), - contentDescription = "copy to clipboard" - ) - } - } - } - } - } - } -} - -@Composable -fun Monitoring( - modifier: Modifier = Modifier, - initialValue: Boolean, - onSwitch: (value: Boolean) -> Unit, -) { - var monitoring by remember { mutableStateOf(initialValue) } - - Column( - modifier = modifier - .padding(16.dp) - .verticalScroll(rememberScrollState()) - ) { - Row( - verticalAlignment = Alignment.CenterVertically - ) { - Text("Enable error reporting") - Spacer(modifier = modifier.width(16.dp)) - Switch(checked = monitoring, onCheckedChange = { - monitoring = it - onSwitch(it) - }) - } - Spacer(modifier = modifier.height(18.dp)) - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - painter = painterResource(R.drawable.warning_24), - contentDescription = "copy to clipboard", - tint = Color.Yellow - ) - Spacer(modifier = modifier.width(16.dp)) - Text(stringResource(R.string.monitoring_desc_2), color = Color.Yellow) - } - Spacer(modifier = modifier.height(18.dp)) - Text(stringResource(R.string.monitoring_desc_1)) - } -} - -@Preview -@Composable -fun PreviewSocks5Client() { - val tag = "UI" - var connected by rememberSaveable { mutableStateOf(false) } - var loading by rememberSaveable { mutableStateOf(false) } - NymTheme { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - S5ClientSwitch(connected, loading, { - when { - it -> Log.d(tag, "switch ON") - else -> Log.d(tag, "switch OFF") - } - connected = it - loading = false - }) - } - } -} - -@Preview -@Composable -fun PreviewMonitoring() { - NymTheme { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - Monitoring(initialValue = false) { - Log.d("Monitoring", "switch $it") - } - } - } -} diff --git a/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/composables/HomeScreen.kt b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/composables/HomeScreen.kt new file mode 100644 index 0000000000..898c7613fe --- /dev/null +++ b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/composables/HomeScreen.kt @@ -0,0 +1,113 @@ +package net.nymtech.nyms5.ui.composables + +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import net.nymtech.nyms5.MainViewModel +import net.nymtech.nyms5.R +import net.nymtech.nyms5.monitoringKey + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HomeScreen( + proxyState: MainViewModel.ProxyState, + monitoring: Boolean, + dataStore: DataStore, + onSwitch: (value: Boolean) -> Unit, +) { + val navController = rememberNavController() + var expanded by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + Scaffold(topBar = { + CenterAlignedTopAppBar( + title = { + Text(stringResource(R.string.app_name)) + }, + navigationIcon = { + val navBackStackEntry by navController.currentBackStackEntryAsState() + val currentRoute = navBackStackEntry?.destination?.route + + if (currentRoute === "proxy") { + IconButton(onClick = { expanded = true }) { + Icon( + imageVector = Icons.Filled.Menu, + contentDescription = "Main menu" + ) + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem(onClick = { + navController.navigate("monitoring") { + popUpTo("proxy") + } + expanded = false + }, text = { + Text("Error reporting") + }) + } + } else { + IconButton(onClick = { + navController.navigate("proxy") { + popUpTo("proxy") + } + }) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Back home" + ) + } + } + }, + ) + }) { contentPadding -> + NavHost( + navController = navController, + startDestination = "proxy", + modifier = Modifier.padding(contentPadding) + ) { + composable("proxy") { + S5ClientSwitch( + connected = proxyState.connected, + loading = proxyState.loading, + onSwitch = onSwitch + ) + } + composable("monitoring") { + Monitoring(initialValue = monitoring) { + scope.launch(Dispatchers.IO) { + dataStore.edit { settings -> settings[monitoringKey] = it } + } + } + } + } + } +} \ No newline at end of file diff --git a/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/composables/Monitoring.kt b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/composables/Monitoring.kt new file mode 100644 index 0000000000..4195647891 --- /dev/null +++ b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/composables/Monitoring.kt @@ -0,0 +1,93 @@ +package net.nymtech.nyms5.ui.composables + +import android.util.Log +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import net.nymtech.nyms5.R +import net.nymtech.nyms5.ui.theme.NymTheme +import net.nymtech.nyms5.ui.theme.darkYellow +import net.nymtech.nyms5.ui.theme.lightYellow + +@Composable +fun Monitoring( + modifier: Modifier = Modifier, + initialValue: Boolean, + onSwitch: (value: Boolean) -> Unit, +) { + var monitoring by remember { mutableStateOf(initialValue) } + val yellowColor = when (isSystemInDarkTheme()) { + true -> darkYellow + false -> lightYellow + } + + Column( + modifier = modifier + .padding(16.dp) + .verticalScroll(rememberScrollState()) + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text("Enable error reporting") + Spacer(modifier = modifier.width(16.dp)) + Switch(checked = monitoring, onCheckedChange = { + monitoring = it + onSwitch(it) + }) + } + Spacer(modifier = modifier.height(18.dp)) + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + painter = painterResource(R.drawable.warning_24), + contentDescription = "copy to clipboard", + tint = yellowColor + ) + Spacer(modifier = modifier.width(16.dp)) + Text( + stringResource(R.string.monitoring_desc_2), + color = yellowColor + ) + } + Spacer(modifier = modifier.height(18.dp)) + Text(stringResource(R.string.monitoring_desc_1)) + } +} + +@Preview +@Composable +fun PreviewMonitoring() { + NymTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + Monitoring(initialValue = false) { + Log.d("Monitoring", "switch $it") + } + } + } +} \ No newline at end of file diff --git a/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/composables/S5ClientSwitch.kt b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/composables/S5ClientSwitch.kt new file mode 100644 index 0000000000..3361e6fc8a --- /dev/null +++ b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/composables/S5ClientSwitch.kt @@ -0,0 +1,142 @@ +package net.nymtech.nyms5.ui.composables + +import android.util.Log +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import net.nymtech.nyms5.R +import net.nymtech.nyms5.ui.theme.NymTheme + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun S5ClientSwitch( + connected: Boolean, + loading: Boolean, + onSwitch: (value: Boolean) -> Unit, + modifier: Modifier = Modifier +) { + val clipboardManager = LocalClipboardManager.current + val host = stringResource(R.string.proxy_host) + val port = stringResource(R.string.proxy_port) + if (loading) { + Row { + LinearProgressIndicator( + modifier = modifier.fillMaxWidth(), + color = MaterialTheme.colorScheme.secondary + ) + } + } else { + Spacer(modifier = modifier.height(2.dp)) + } + Column(modifier = modifier.padding(16.dp)) { + Row( + modifier = modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text("Nym proxy") + Spacer(modifier = modifier.width(14.dp)) + Switch(checked = connected, enabled = !loading, onCheckedChange = { + onSwitch(!connected) + }, modifier = Modifier.testTag("switch_connect")) + } + if (connected && !loading) { + Column(modifier = modifier.padding(16.dp)) { + Text( + color = Color.Green, + fontStyle = FontStyle.Italic, + text = stringResource(R.string.connected_text) + ) + Spacer(modifier = modifier.height(10.dp)) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Surface( + onClick = { clipboardManager.setText(AnnotatedString(host)) }, + shape = RoundedCornerShape(6.dp), + ) { + Row( + modifier = modifier.padding(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text(host) + Spacer(modifier = modifier.width(8.dp)) + Icon( + painter = painterResource(R.drawable.copy_24), + contentDescription = "copy to clipboard" + ) + } + } + Spacer(modifier = modifier.width(12.dp)) + Surface( + onClick = { clipboardManager.setText(AnnotatedString(port)) }, + shape = RoundedCornerShape(6.dp), + ) { + Row( + modifier = modifier.padding(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text(port) + Spacer(modifier = modifier.width(8.dp)) + Icon( + painter = painterResource(R.drawable.copy_24), + contentDescription = "copy to clipboard" + ) + } + } + } + } + } + } +} + +@Preview +@Composable +fun PreviewSocks5Client() { + val tag = "UI" + var connected by rememberSaveable { mutableStateOf(false) } + var loading by rememberSaveable { mutableStateOf(false) } + NymTheme { + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + S5ClientSwitch(connected, loading, { + when { + it -> Log.d(tag, "switch ON") + else -> Log.d(tag, "switch OFF") + } + connected = it + loading = false + }) + } + } +} \ No newline at end of file diff --git a/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/theme/Color.kt b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/theme/Color.kt index 1667a76208..85daf1ecf4 100644 --- a/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/theme/Color.kt +++ b/nym-connect/native/android/app/src/main/java/net/nymtech/nyms5/ui/theme/Color.kt @@ -8,4 +8,12 @@ val Pink80 = Color(0xFFEFB8C8) val Purple40 = Color(0xFF6650a4) val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file +val Pink40 = Color(0xFF7D5260) + +// Custom colors +// generated from https://m3.material.io/theme-builder#/custom +val yellow = Color(0xFFD3B919) +val lightYellow = Color(0xFF6D5E00) +val lightOnYellow = Color(0xFFFFFFFF) +val darkYellow = Color(0xFFE1C62A) +val darkOnYellow = Color(0xFF393000) diff --git a/nym-connect/native/android/build.gradle b/nym-connect/native/android/build.gradle deleted file mode 100644 index fd3d478923..0000000000 --- a/nym-connect/native/android/build.gradle +++ /dev/null @@ -1,6 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -plugins { - id 'com.android.application' version '8.0.2' apply false - id 'com.android.library' version '8.0.2' apply false - id 'org.jetbrains.kotlin.android' version '1.8.20' apply false -} \ No newline at end of file diff --git a/nym-connect/native/android/build.gradle.kts b/nym-connect/native/android/build.gradle.kts new file mode 100644 index 0000000000..6cb6871c9e --- /dev/null +++ b/nym-connect/native/android/build.gradle.kts @@ -0,0 +1,6 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id("com.android.application") version "8.1.0" apply false + id("com.android.library") version "8.1.0" apply false + id("org.jetbrains.kotlin.android") version "1.8.20" apply false +} \ No newline at end of file diff --git a/nym-connect/native/android/settings.gradle b/nym-connect/native/android/settings.gradle.kts similarity index 94% rename from nym-connect/native/android/settings.gradle rename to nym-connect/native/android/settings.gradle.kts index 0bf8eaecd3..267005beeb 100644 --- a/nym-connect/native/android/settings.gradle +++ b/nym-connect/native/android/settings.gradle.kts @@ -12,5 +12,7 @@ dependencyResolutionManagement { mavenCentral() } } + rootProject.name = "nyms5" -include ':app' +include(":app") + \ No newline at end of file diff --git a/nym-wallet/CHANGELOG.md b/nym-wallet/CHANGELOG.md index f0d62aec2d..d0b6d0aa6f 100644 --- a/nym-wallet/CHANGELOG.md +++ b/nym-wallet/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] +## [1.2.7] (2023-08-17) +- release due to schema changes in the contract + ## [v1.2.6] (2023-07-18) - [wallet] bugfix: don't send funds for pledge decrease simulation ([#3676]) diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index 2da872c4fc..3cb32da98f 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/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" @@ -39,9 +48,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher 0.4.4", @@ -50,12 +59,12 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" dependencies = [ "aead", - "aes 0.8.2", + "aes 0.8.3", "cipher 0.4.4", "ctr 0.9.2", "ghash", @@ -75,9 +84,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" dependencies = [ "memchr", ] @@ -98,43 +107,87 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.69" +name = "anstream" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anyhow" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "argon2" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95c2fcf79ad1932ac6269a738109997a83c227c09b75842ae564dc8ede6a861c" +checksum = "b2e554a8638bdc1e4eae9984845306cc95f8a9208ba8d49c3859fd958b46774d" dependencies = [ "base64ct", "blake2 0.10.6", + "cpufeatures", "password-hash", ] [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "async-trait" -version = "0.1.64" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] @@ -144,7 +197,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" dependencies = [ "atk-sys", - "bitflags", + "bitflags 1.3.2", "glib", "libc", ] @@ -158,7 +211,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -194,6 +247,21 @@ 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", + "object", + "rustc-demangle", +] + [[package]] name = "base16ct" version = "0.1.1" @@ -214,15 +282,15 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" -version = "1.0.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bip32" @@ -237,7 +305,7 @@ dependencies = [ "pbkdf2", "rand_core 0.6.4", "ripemd", - "sha2 0.10.6", + "sha2 0.10.7", "subtle 2.4.1", "zeroize", ] @@ -268,6 +336,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "bitvec" version = "1.0.1" @@ -301,20 +375,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "blake3" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "digest 0.10.7", -] - [[package]] name = "block" version = "0.1.6" @@ -332,9 +392,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array 0.14.7", ] @@ -353,6 +413,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "brotli" version = "3.3.4" @@ -386,14 +452,14 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" dependencies = [ - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] name = "bstr" -version = "1.2.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f0778972c64420fdedc63f09919c8a88bda7b25135357fd25a5d9f3257e832" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", "serde", @@ -401,9 +467,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byte-tools" @@ -413,9 +479,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" [[package]] name = "byteorder" @@ -438,7 +504,7 @@ version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-sys-rs", "glib", "libc", @@ -453,7 +519,7 @@ checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" dependencies = [ "glib-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -468,11 +534,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -502,11 +569,12 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.11.0" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" +checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" dependencies = [ "smallvec", + "target-lexicon", ] [[package]] @@ -525,30 +593,6 @@ dependencies = [ "keystream", ] -[[package]] -name = "chacha20" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" -dependencies = [ - "cfg-if", - "cipher 0.4.4", - "cpufeatures", -] - -[[package]] -name = "chacha20poly1305" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" -dependencies = [ - "aead", - "chacha20", - "cipher 0.4.4", - "poly1305", - "zeroize", -] - [[package]] name = "cipher" version = "0.3.0" @@ -566,38 +610,45 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", - "zeroize", ] [[package]] name = "clap" -version = "4.1.4" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" dependencies = [ - "bitflags", + "clap_builder", "clap_derive", - "clap_lex", - "is-terminal", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", "strsim", - "termcolor", ] [[package]] name = "clap_complete" -version = "4.1.4" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501ff0a401473ea1d4c3b125ff95506b62c5bc5768d818634195fbb7c4ad5ff4" +checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce" dependencies = [ "clap", ] [[package]] name = "clap_complete_fig" -version = "4.1.1" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63a06158a72dbb088f864887b4409fd22600ba379f3cee3040f842234cc5c2d0" +checksum = "99fee1d30a51305a6c2ed3fc5709be3c8af626c9c958e04dd9ae94e27bcbce9f" dependencies = [ "clap", "clap_complete", @@ -605,25 +656,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.1.0" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck 0.4.1", - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] name = "clap_lex" -version = "0.3.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" -dependencies = [ - "os_str_bytes", -] +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "cocoa" @@ -631,7 +678,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "cocoa-foundation", "core-foundation", @@ -643,11 +690,11 @@ dependencies = [ [[package]] name = "cocoa-foundation" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "core-foundation", "core-graphics-types", @@ -663,25 +710,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] -name = "colored" -version = "1.9.3" +name = "colorchoice" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "colored" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f741c91823341bebf717d4c71bda820630ce065443b58bd1b7451af008355" dependencies = [ - "atty", + "is-terminal", "lazy_static", "winapi", ] [[package]] name = "colored" -version = "2.0.0" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ - "atty", + "is-terminal", "lazy_static", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -696,15 +749,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" - -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "convert_case" @@ -724,9 +771,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core-graphics" @@ -734,7 +781,7 @@ version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-graphics-types", "foreign-types", @@ -743,13 +790,12 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", - "foreign-types", "libc", ] @@ -787,9 +833,9 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.2.5" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75836a10cb9654c54e77ee56da94d592923092a10b369cdb0dbd56acefc16340" +checksum = "7e272708a9745dad8b591ef8a718571512130f2b39b33e3d7a27c558e3069394" dependencies = [ "digest 0.10.7", "ed25519-zebra", @@ -800,18 +846,18 @@ dependencies = [ [[package]] name = "cosmwasm-derive" -version = "1.2.5" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9f7f0e51bfc7295f7b2664fe8513c966428642aa765dad8a74acdab5e0c773" +checksum = "296db6a3caca5283425ae0cf347f4e46999ba3f6620dbea8939a0e00347831ce" dependencies = [ - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f00b363610218eea83f24bbab09e1a7c3920b79f068334fdfcc62f6129ef9fc" +checksum = "bb6b2fb76758ef59cddc77f2e2ae91c22f77da49037e9f182e9c2833f0e959b1" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -822,22 +868,23 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae38f909b2822d32b275c9e2db9728497aa33ffe67dd463bc67c6a3b7092785c" +checksum = "2bfa39422f0d9f1c9a6fd3711573258495314dfa3aae738ea825ecd9964bc659" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "cosmwasm-std" -version = "1.2.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49b85345e811c8e80ec55d0d091e4fcb4f00f97ab058f9be5f614c444a730cb" +checksum = "1f6dc2ee23313add5ecacc3ccac217b9967ad9d2d11bd56e5da6aa65a9da6138" dependencies = [ "base64 0.13.1", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -845,17 +892,16 @@ dependencies = [ "hex", "schemars", "serde", - "serde-json-wasm 0.5.1", - "sha2 0.10.6", + "serde-json-wasm", + "sha2 0.10.7", "thiserror", - "uint", ] [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -871,53 +917,23 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", ] -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset 0.9.0", - "scopeguard", -] - [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.9" @@ -987,17 +1003,17 @@ dependencies = [ "proc-macro2", "quote", "smallvec", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "cssparser-macros" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfae75de57f2b2e85e8768c3ea840fd159c8f33e2b6522c7835b7abac81be16e" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] @@ -1016,7 +1032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1037,12 +1053,6 @@ dependencies = [ "cipher 0.4.4", ] -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -1071,9 +1081,9 @@ dependencies = [ [[package]] name = "cw-controllers" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91440ce8ec4f0642798bc8c8cb6b9b53c1926c6dadaf0eed267a5145cd529071" +checksum = "d5d8edce4b78785f36413f67387e4be7d0cb7d032b5d4164bcc024f9c3f3f2ea" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1086,9 +1096,9 @@ dependencies = [ [[package]] name = "cw-storage-plus" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a5083c258acd68386734f428a5a171b29f7d733151ae83090c6fcc9417ffa" +checksum = "3f0e92a069d62067f3472c62e30adedb4cab1754725c0f2a682b3128d2bf3c79" dependencies = [ "cosmwasm-std", "schemars", @@ -1105,29 +1115,30 @@ dependencies = [ "cosmwasm-std", "cw2", "schemars", - "semver 1.0.16", + "semver 1.0.18", "serde", "thiserror", ] [[package]] name = "cw2" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb70cee2cf0b4a8ff7253e6bc6647107905e8eb37208f87d54f67810faa62f8" +checksum = "29ac2dc7a55ad64173ca1e0a46697c31b7a5c51342f55a1e84a724da4eb99908" dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", "schemars", "serde", + "thiserror", ] [[package]] name = "cw20" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91666da6c7b40c8dd5ff94df655a28114efc10c79b70b4d06f13c31e37d60609" +checksum = "011c45920f8200bd5d32d4fe52502506f64f2f75651ab408054d4cfc75ca3a9b" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1138,9 +1149,9 @@ dependencies = [ [[package]] name = "cw3" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fe0b587008aa221cd2a2579a21990a28c4347dc53ad43167c68ad765f5b6efa" +checksum = "171af3d9127de6805a7dd819fb070c7d2f6c3ea85f4193f42cef259f0a7f33d5" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1153,9 +1164,9 @@ dependencies = [ [[package]] name = "cw4" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c236e0bae02ce97e89235a681dd0e07d099524b369f1ef908d704db3e6b049b" +checksum = "a398696307efadaaa2d0850076f865fa706c959d493cb4203314f72be6b77a64" dependencies = [ "cosmwasm-schema", "cosmwasm-std", @@ -1185,7 +1196,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1196,7 +1207,7 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1211,14 +1222,20 @@ dependencies = [ [[package]] name = "der" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", "zeroize", ] +[[package]] +name = "deranged" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" + [[package]] name = "derivative" version = "2.2.0" @@ -1227,7 +1244,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1239,8 +1256,8 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.0", - "syn 1.0.107", + "rustc_version", + "syn 1.0.109", ] [[package]] @@ -1267,7 +1284,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle 2.4.1", @@ -1343,36 +1360,36 @@ checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[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" -version = "0.4.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] name = "dtoa-short" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde03329ae10e79ede66c9ce4dc930aa8599043b0743008548680f25b91502d6" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" dependencies = [ "dtoa", ] [[package]] name = "dunce" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dyn-clone" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "ecdsa" @@ -1392,7 +1409,7 @@ version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ - "der 0.7.7", + "der 0.7.8", "digest 0.10.7", "elliptic-curve 0.13.5", "rfc6979 0.4.0", @@ -1411,9 +1428,9 @@ dependencies = [ [[package]] name = "ed25519" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb04eee5d9d907f29e80ee6b0e78f7e2c82342c63e3580d8c4f69d9d5aad963" +checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" dependencies = [ "pkcs8 0.10.2", "signature 2.1.0", @@ -1453,7 +1470,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ "curve25519-dalek", - "hashbrown", + "hashbrown 0.12.3", "hex", "rand_core 0.6.4", "serde", @@ -1463,9 +1480,9 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" @@ -1532,13 +1549,13 @@ dependencies = [ [[package]] name = "enum-iterator-derive" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "828de45d0ca18782232dfb8f3ea9cc428e8ced380eb26a520baaacfc70de39ce" +checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] @@ -1555,14 +1572,20 @@ dependencies = [ ] [[package]] -name = "errno" -version = "0.2.8" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1587,20 +1610,26 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.8.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" dependencies = [ - "instant", + "simd-adler32", ] [[package]] name = "fern" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a" +checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" dependencies = [ - "colored 1.9.3", + "colored 1.9.4", "log", ] @@ -1636,31 +1665,31 @@ dependencies = [ [[package]] name = "field-offset" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset 0.6.5", - "rustc_version 0.3.3", + "memoffset", + "rustc_version", ] [[package]] name = "filetime" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys 0.45.0", + "redox_syscall 0.3.5", + "windows-sys 0.48.0", ] [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "miniz_oxide", @@ -1699,9 +1728,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -1730,9 +1759,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -1745,9 +1774,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -1755,15 +1784,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -1772,38 +1801,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -1832,7 +1861,7 @@ version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -1848,7 +1877,7 @@ version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", @@ -1865,7 +1894,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -1882,7 +1911,7 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -1894,21 +1923,21 @@ dependencies = [ "gdk-sys", "glib-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", "x11", ] [[package]] name = "generator" -version = "0.7.2" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266041a359dfa931b370ef684cceb84b166beb14f7f0421f4a6a3d0c446d12e" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" dependencies = [ "cc", "libc", "log", "rustversion", - "windows 0.39.0", + "windows 0.48.0", ] [[package]] @@ -1966,7 +1995,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -1979,13 +2008,19 @@ dependencies = [ "polyval", ] +[[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", @@ -2005,7 +2040,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", "winapi", ] @@ -2015,7 +2050,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "libgit2-sys", "log", @@ -2028,7 +2063,7 @@ version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-executor", @@ -2044,9 +2079,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.15.11" +version = "0.15.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64" +checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" dependencies = [ "anyhow", "heck 0.4.1", @@ -2054,7 +2089,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -2064,7 +2099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" dependencies = [ "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -2075,9 +2110,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" dependencies = [ "aho-corasick", "bstr", @@ -2094,7 +2129,7 @@ checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" dependencies = [ "glib-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -2138,7 +2173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" dependencies = [ "atk", - "bitflags", + "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -2169,28 +2204,28 @@ dependencies = [ "gobject-sys", "libc", "pango-sys", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] name = "gtk3-macros" -version = "0.15.4" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f518afe90c23fba585b2d7697856f9e6a7bbc62f65588035e66f6afb01a2e9" +checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" dependencies = [ "anyhow", "proc-macro-crate", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "h2" -version = "0.3.15" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -2198,7 +2233,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -2228,6 +2263,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "headers" version = "0.3.8" @@ -2235,7 +2276,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" dependencies = [ "base64 0.13.1", - "bitflags", + "bitflags 1.3.2", "bytes", "headers-core", "http", @@ -2279,18 +2320,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -2344,18 +2376,18 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", - "itoa 1.0.5", + "itoa 1.0.9", ] [[package]] @@ -2383,9 +2415,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -2414,9 +2446,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -2427,9 +2459,9 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.5", + "itoa 1.0.9", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -2504,11 +2536,10 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.2.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -2532,9 +2563,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.5" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" dependencies = [ "bytemuck", "byteorder", @@ -2551,15 +2582,25 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", "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" @@ -2587,32 +2628,21 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "io-lifetimes" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" -dependencies = [ - "libc", - "windows-sys 0.45.0", -] - [[package]] name = "ipnet" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "is-terminal" -version = "0.4.3" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", + "hermit-abi 0.3.2", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2632,9 +2662,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "javascriptcore-rs" @@ -2642,7 +2672,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "glib", "javascriptcore-rs-sys", ] @@ -2681,18 +2711,18 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -2717,7 +2747,7 @@ dependencies = [ "cfg-if", "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -2730,7 +2760,7 @@ dependencies = [ "ecdsa 0.16.8", "elliptic-curve 0.13.5", "once_cell", - "sha2 0.10.6", + "sha2 0.10.7", "signature 2.1.0", ] @@ -2760,9 +2790,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.146" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libgit2-sys" @@ -2778,15 +2808,15 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "libc", @@ -2805,9 +2835,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "lioness" @@ -2823,9 +2853,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -2833,11 +2863,10 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" dependencies = [ - "cfg-if", "serde", ] @@ -2891,7 +2920,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -2906,15 +2935,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.0" @@ -2926,9 +2946,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minisign-verify" @@ -2938,23 +2958,23 @@ checksum = "933dca44d65cdd53b355d0b73d380a2ff5da71f87f036053188bf1eab6a19881" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", + "simd-adler32", ] [[package]] name = "mio" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -2981,7 +3001,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" dependencies = [ - "bitflags", + "bitflags 1.3.2", "jni-sys", "ndk-sys", "num_enum", @@ -3015,15 +3035,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -[[package]] -name = "nom8" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" -dependencies = [ - "memchr", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3042,7 +3053,7 @@ checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -3068,9 +3079,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", "libm", @@ -3078,33 +3089,33 @@ 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 0.3.2", "libc", ] [[package]] name = "num_enum" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -3169,22 +3180,20 @@ dependencies = [ name = "nym-coconut-bandwidth-contract-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "nym-multisig-contract-common", - "schemars", - "serde", ] [[package]] name = "nym-coconut-dkg-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-utils", "nym-contracts-common", "nym-multisig-contract-common", - "schemars", - "serde", ] [[package]] @@ -3207,7 +3216,7 @@ dependencies = [ "log", "nym-network-defaults", "serde", - "toml 0.7.4", + "toml 0.7.6", "url", ] @@ -3216,6 +3225,7 @@ name = "nym-contracts-common" version = "0.5.0" dependencies = [ "bs58 0.4.0", + "cosmwasm-schema", "cosmwasm-std", "schemars", "serde", @@ -3258,6 +3268,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nym-ephemera-common" +version = "0.1.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cw-utils", + "nym-contracts-common", +] + [[package]] name = "nym-group-contract-common" version = "0.1.0" @@ -3274,13 +3294,14 @@ name = "nym-mixnet-contract-common" version = "0.6.0" dependencies = [ "bs58 0.4.0", + "cosmwasm-schema", "cosmwasm-std", "humantime-serde", "log", "nym-contracts-common", "schemars", "serde", - "serde-json-wasm 0.4.1", + "serde-json-wasm", "serde_repr", "thiserror", "time", @@ -3306,11 +3327,11 @@ dependencies = [ name = "nym-name-service-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-utils", "nym-contracts-common", - "schemars", "serde", "thiserror", ] @@ -3323,28 +3344,12 @@ dependencies = [ "dotenvy", "hex-literal", "once_cell", + "schemars", "serde", "thiserror", "url", ] -[[package]] -name = "nym-outfox" -version = "0.1.0" -dependencies = [ - "blake3", - "chacha20", - "chacha20poly1305", - "curve25519-dalek", - "getrandom 0.2.10", - "log", - "rand 0.7.3", - "rayon", - "sphinx-packet", - "thiserror", - "zeroize", -] - [[package]] name = "nym-pemstore" version = "0.3.0" @@ -3356,12 +3361,11 @@ dependencies = [ name = "nym-service-provider-directory-common" version = "0.1.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "cw-controllers", "cw-utils", "nym-contracts-common", - "schemars", - "serde", "thiserror", ] @@ -3369,7 +3373,6 @@ dependencies = [ name = "nym-sphinx-types" version = "0.2.0" dependencies = [ - "nym-outfox", "sphinx-packet", "thiserror", ] @@ -3402,7 +3405,6 @@ dependencies = [ "nym-config", "nym-mixnet-contract-common", "nym-validator-client", - "nym-vesting-contract", "nym-vesting-contract-common", "reqwest", "schemars", @@ -3421,9 +3423,12 @@ dependencies = [ "async-trait", "base64 0.13.1", "bip39", - "colored 2.0.0", + "colored 2.0.4", "cosmrs", "cosmwasm-std", + "cw-controllers", + "cw-utils", + "cw2", "cw3", "cw4", "eyre", @@ -3437,13 +3442,13 @@ dependencies = [ "nym-coconut-interface", "nym-config", "nym-contracts-common", + "nym-ephemera-common", "nym-group-contract-common", "nym-mixnet-contract-common", "nym-multisig-contract-common", "nym-name-service-common", "nym-network-defaults", "nym-service-provider-directory-common", - "nym-vesting-contract", "nym-vesting-contract-common", "openssl", "prost", @@ -3455,36 +3460,20 @@ dependencies = [ "thiserror", "tokio", "url", + "wasmtimer", "zeroize", ] -[[package]] -name = "nym-vesting-contract" -version = "1.4.0" -dependencies = [ - "cosmwasm-derive", - "cosmwasm-std", - "cw-storage-plus", - "cw2", - "nym-contracts-common", - "nym-mixnet-contract-common", - "nym-vesting-contract-common", - "schemars", - "semver 1.0.16", - "serde", - "thiserror", - "vergen", -] - [[package]] name = "nym-vesting-contract-common" version = "0.7.0" dependencies = [ + "cosmwasm-schema", "cosmwasm-std", "nym-contracts-common", "nym-mixnet-contract-common", - "schemars", "serde", + "thiserror", ] [[package]] @@ -3514,7 +3503,6 @@ dependencies = [ "nym-network-defaults", "nym-types", "nym-validator-client", - "nym-vesting-contract", "nym-vesting-contract-common", "serde", "serde_json", @@ -3524,13 +3512,13 @@ dependencies = [ [[package]] name = "nym_wallet" -version = "1.2.6" +version = "1.2.7" dependencies = [ "async-trait", "base64 0.13.1", "bip39", "cfg-if", - "colored 2.0.0", + "colored 2.0.4", "cosmrs", "cosmwasm-std", "dirs 4.0.0", @@ -3549,7 +3537,6 @@ dependencies = [ "nym-store-cipher", "nym-types", "nym-validator-client", - "nym-vesting-contract", "nym-vesting-contract-common", "nym-wallet-types", "once_cell", @@ -3614,10 +3601,19 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.17.0" +name = "object" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -3643,11 +3639,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.45" +version = "0.10.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -3658,13 +3654,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] @@ -3675,20 +3671,19 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.26.0+1.1.1u" +version = "111.27.0+1.1.1v" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37" +checksum = "06e8f197c82d7511c5b014030c9b1efeda40d7d5f99d23b4ceed3524a5e63f02" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.80" +version = "0.9.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" dependencies = [ - "autocfg", "cc", "libc", "openssl-src", @@ -3702,12 +3697,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - [[package]] name = "overload" version = "0.1.1" @@ -3729,7 +3718,7 @@ version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", "glib", "libc", "once_cell", @@ -3745,7 +3734,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -3760,15 +3749,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.45.0", + "windows-targets", ] [[package]] @@ -3784,9 +3773,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pathdiff" @@ -3844,15 +3833,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.5.5" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" +checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" dependencies = [ "thiserror", "ucd-trie", @@ -3860,9 +3849,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.5" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac3922aac69a40733080f53c1ce7f91dcf57e1a5f6c52f421fadec7fbdc4b69" +checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" dependencies = [ "pest", "pest_generator", @@ -3870,26 +3859,26 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.5" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06646e185566b5961b4058dd107e0a7f56e77c3f484549fb119867773c0f202" +checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] name = "pest_meta" -version = "2.5.5" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f60b2ba541577e2a0c307c8f39d1439108120eb7903adeb6497fa880c59616" +checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" dependencies = [ "once_cell", "pest", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -3955,7 +3944,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -3969,7 +3958,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -3992,29 +3981,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -4038,24 +4027,24 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.7", + "der 0.7.8", "spki 0.7.2", ] [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plist" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5329b8f106a176ab0dce4aae5da86bfcb139bb74fb00882859e03745011f3635" +checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" dependencies = [ - "base64 0.13.1", - "indexmap", + "base64 0.21.2", + "indexmap 1.9.3", "line-wrap", "quick-xml", "serde", @@ -4064,32 +4053,22 @@ dependencies = [ [[package]] name = "png" -version = "0.17.7" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", + "fdeflate", "flate2", "miniz_oxide", ] -[[package]] -name = "poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" -dependencies = [ - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash", -] - [[package]] name = "polyval" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" dependencies = [ "cfg-if", "cpufeatures", @@ -4121,12 +4100,12 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit 0.18.1", + "toml_edit", ] [[package]] @@ -4138,7 +4117,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "version_check", ] @@ -4161,9 +4140,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -4188,7 +4167,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -4214,18 +4193,18 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.26.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -4329,34 +4308,9 @@ dependencies = [ [[package]] name = "raw-window-handle" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a" -dependencies = [ - "cty", -] - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "redox_syscall" @@ -4364,7 +4318,16 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -4374,19 +4337,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.10", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-automata 0.3.6", + "regex-syntax 0.7.4", ] [[package]] @@ -4395,31 +4359,39 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-syntax" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", "bytes", "encoding_rs", "futures-core", @@ -4520,13 +4492,10 @@ dependencies = [ ] [[package]] -name = "rustc_version" -version = "0.3.3" +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc_version" @@ -4534,21 +4503,20 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver 1.0.18", ] [[package]] name = "rustix" -version = "0.36.8" +version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags", + "bitflags 2.4.0", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -4578,15 +4546,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "safemem" @@ -4605,21 +4573,21 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] name = "schemars" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" +checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" dependencies = [ "dyn-clone", - "indexmap", + "indexmap 1.9.3", "schemars_derive", "serde", "serde_json", @@ -4627,14 +4595,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" +checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -4645,9 +4613,9 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -4680,7 +4648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", - "der 0.7.7", + "der 0.7.8", "generic-array 0.14.7", "pkcs8 0.10.2", "subtle 2.4.1", @@ -4689,11 +4657,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -4702,9 +4670,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -4716,7 +4684,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cssparser", "derive_more", "fxhash", @@ -4741,9 +4709,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" dependencies = [ "serde", ] @@ -4759,49 +4727,40 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.160" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] name = "serde-json-wasm" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" -dependencies = [ - "serde", -] - -[[package]] -name = "serde-json-wasm" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +checksum = "a15bee9b04dd165c3f4e142628982ddde884c2022a89e8ddf99c4829bf2c3a58" dependencies = [ "serde", ] [[package]] name = "serde_bytes" -version = "0.11.9" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.28", ] [[package]] @@ -4812,36 +4771,36 @@ checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ - "itoa 1.0.5", + "itoa 1.0.9", "ryu", "serde", ] [[package]] name = "serde_repr" -version = "0.1.10" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[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", ] @@ -4853,7 +4812,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.5", + "itoa 1.0.9", "ryu", "serde", ] @@ -4877,7 +4836,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -4899,7 +4858,7 @@ checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -4938,9 +4897,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -4985,6 +4944,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "siphasher" version = "0.3.10" @@ -4993,36 +4958,46 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "soup2" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gio", "glib", "libc", @@ -5036,7 +5011,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gio-sys", "glib-sys", "gobject-sys", @@ -5091,7 +5066,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", - "der 0.7.7", + "der 0.7.8", ] [[package]] @@ -5109,17 +5084,11 @@ dependencies = [ "loom", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "string_cache" -version = "0.8.4" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", @@ -5166,7 +5135,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -5198,9 +5167,9 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -5209,27 +5178,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", - "unicode-xid", -] - [[package]] name = "system-deps" version = "5.0.0" @@ -5245,14 +5202,14 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.0.3" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" +checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" dependencies = [ - "cfg-expr 0.11.0", + "cfg-expr 0.15.4", "heck 0.4.1", "pkg-config", - "toml 0.5.11", + "toml 0.7.6", "version-compare 0.1.1", ] @@ -5262,7 +5219,7 @@ version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac8e6399427c8494f9849b58694754d7cc741293348a6836b6c8d2c5aa82d8e6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "cc", "cocoa", @@ -5296,7 +5253,7 @@ dependencies = [ "scopeguard", "serde", "unicode-segmentation", - "uuid 1.3.0", + "uuid 1.4.1", "windows 0.39.0", "windows-implement", "x11-dl", @@ -5310,15 +5267,21 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" dependencies = [ "filetime", "libc", "xattr", ] +[[package]] +name = "target-lexicon" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" + [[package]] name = "tauri" version = "1.2.3" @@ -5349,7 +5312,7 @@ dependencies = [ "raw-window-handle", "regex", "rfd", - "semver 1.0.16", + "semver 1.0.18", "serde", "serde_json", "serde_repr", @@ -5365,7 +5328,7 @@ dependencies = [ "time", "tokio", "url", - "uuid 1.3.0", + "uuid 1.4.1", "webkit2gtk", "webview2-com", "windows 0.39.0", @@ -5382,7 +5345,7 @@ dependencies = [ "cargo_toml", "heck 0.4.1", "json-patch", - "semver 1.0.16", + "semver 1.0.18", "serde_json", "tauri-utils", "winres", @@ -5403,14 +5366,14 @@ dependencies = [ "proc-macro2", "quote", "regex", - "semver 1.0.16", + "semver 1.0.18", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "tauri-utils", "thiserror", "time", - "uuid 1.3.0", + "uuid 1.4.1", "walkdir", ] @@ -5423,7 +5386,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", "tauri-codegen", "tauri-utils", ] @@ -5443,7 +5406,7 @@ dependencies = [ "serde_json", "tauri-utils", "thiserror", - "uuid 1.3.0", + "uuid 1.4.1", "webview2-com", "windows 0.39.0", ] @@ -5461,7 +5424,7 @@ dependencies = [ "raw-window-handle", "tauri-runtime", "tauri-utils", - "uuid 1.3.0", + "uuid 1.4.1", "webkit2gtk", "webview2-com", "windows 0.39.0", @@ -5486,7 +5449,7 @@ dependencies = [ "phf 0.10.1", "proc-macro2", "quote", - "semver 1.0.16", + "semver 1.0.18", "serde", "serde_json", "serde_with", @@ -5498,16 +5461,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" dependencies = [ "cfg-if", "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.48.0", ] [[package]] @@ -5518,7 +5480,7 @@ checksum = "3f0a7d05cf78524782337f8edd55cbc578d159a16ad4affe2135c92f7dbac7f0" dependencies = [ "bytes", "digest 0.10.7", - "ed25519 2.2.1", + "ed25519 2.2.2", "ed25519-consensus", "flex-error", "futures", @@ -5532,7 +5494,7 @@ dependencies = [ "serde_bytes", "serde_json", "serde_repr", - "sha2 0.10.6", + "sha2 0.10.7", "signature 2.1.0", "subtle 2.4.1", "subtle-encoding", @@ -5590,7 +5552,7 @@ dependencies = [ "hyper-rustls", "peg", "pin-project", - "semver 1.0.16", + "semver 1.0.18", "serde", "serde_bytes", "serde_json", @@ -5636,22 +5598,22 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] @@ -5666,11 +5628,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" dependencies = [ - "itoa 1.0.5", + "deranged", + "itoa 1.0.9", "libc", "num_threads", "serde", @@ -5680,15 +5643,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.6" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" dependencies = [ "time-core", ] @@ -5710,33 +5673,32 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "40de3a2ba249dcb097e01be5e67a5ff53cf250397715a071a81543e8a832a920" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.3", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] @@ -5762,9 +5724,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -5785,52 +5747,35 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ "serde", "serde_spanned", - "toml_datetime 0.6.2", - "toml_edit 0.19.10", + "toml_datetime", + "toml_edit", ] [[package]] name = "toml_datetime" -version = "0.5.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" - -[[package]] -name = "toml_datetime" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.18.1" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "indexmap", - "nom8", - "toml_datetime 0.5.1", -] - -[[package]] -name = "toml_edit" -version = "0.19.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" -dependencies = [ - "indexmap", + "indexmap 2.0.0", "serde", "serde_spanned", - "toml_datetime 0.6.2", + "toml_datetime", "winnow", ] @@ -5854,20 +5799,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -5886,9 +5831,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", @@ -5919,9 +5864,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "ts-rs" -version = "6.2.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4added4070a4fdf9df03457206cd2e4b12417c8560a2954d91ffcbe60177a56a" +checksum = "e1ff1f8c90369bc172200013ac17ae86e7b5def580687df4e6127883454ff2b0" dependencies = [ "thiserror", "ts-rs-macros", @@ -5929,14 +5874,14 @@ dependencies = [ [[package]] name = "ts-rs-macros" -version = "6.2.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f807fdb3151fee75df7485b901a89624358cd07a67a8fb1a5831bf5a07681ff" +checksum = "a6f41cc0aeb7a4a55730188e147d3795a7349b501f8334697fd37629b896cdc2" dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", "termcolor", ] @@ -5948,33 +5893,21 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -5991,17 +5924,11 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "universal-hash" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", "subtle 2.4.1", @@ -6015,9 +5942,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -6031,6 +5958,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "0.8.2" @@ -6039,9 +5972,9 @@ checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" [[package]] name = "uuid" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "getrandom 0.2.10", ] @@ -6069,7 +6002,7 @@ dependencies = [ "enum-iterator", "getset", "git2", - "rustc_version 0.4.0", + "rustc_version", "rustversion", "thiserror", "time", @@ -6095,22 +6028,20 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -6128,9 +6059,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -6138,24 +6069,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -6165,9 +6096,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6175,28 +6106,42 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 2.0.28", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasmtimer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f656cd8858a5164932d8a90f936700860976ec21eb00e0fe2aa8cab13f6b4cf" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -6208,7 +6153,7 @@ version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk", "gdk-sys", @@ -6233,7 +6178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" dependencies = [ "atk-sys", - "bitflags", + "bitflags 1.3.2", "cairo-sys-rs", "gdk-pixbuf-sys", "gdk-sys", @@ -6246,7 +6191,7 @@ dependencies = [ "pango-sys", "pkg-config", "soup2-sys", - "system-deps 6.0.3", + "system-deps 6.1.1", ] [[package]] @@ -6288,7 +6233,7 @@ checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn 1.0.109", ] [[package]] @@ -6364,6 +6309,15 @@ dependencies = [ "windows_x86_64_msvc 0.39.0", ] +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-bindgen" version = "0.39.0" @@ -6380,7 +6334,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" dependencies = [ - "syn 1.0.107", + "syn 1.0.109", "windows-tokens", ] @@ -6396,22 +6350,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.1", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -6420,29 +6365,14 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm 0.48.0", "windows_aarch64_msvc 0.48.0", @@ -6461,9 +6391,9 @@ checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" @@ -6485,9 +6415,9 @@ checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" @@ -6509,9 +6439,9 @@ checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" @@ -6533,9 +6463,9 @@ checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" @@ -6557,9 +6487,9 @@ checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" @@ -6569,9 +6499,9 @@ checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" @@ -6593,9 +6523,9 @@ checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" @@ -6605,9 +6535,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d" dependencies = [ "memchr", ] @@ -6656,7 +6586,7 @@ dependencies = [ "once_cell", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "soup2", "tao", "thiserror", @@ -6711,9 +6641,9 @@ dependencies = [ [[package]] name = "xattr" -version = "0.2.3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +checksum = "f4686009f71ff3e5c4dbcf1a282d0a44db3f021ba69350cd42086b3e5f1c6985" dependencies = [ "libc", ] @@ -6730,21 +6660,20 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.3.3" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", - "synstructure", + "syn 2.0.28", ] [[package]] name = "zip" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "byteorder", "crc32fast", diff --git a/nym-wallet/nym-wallet-types/Cargo.toml b/nym-wallet/nym-wallet-types/Cargo.toml index 63a4aa5daf..ca422a9832 100644 --- a/nym-wallet/nym-wallet-types/Cargo.toml +++ b/nym-wallet/nym-wallet-types/Cargo.toml @@ -9,9 +9,9 @@ hex-literal = "0.3.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" strum = { version = "0.23", features = ["derive"] } -ts-rs = "6.1.2" +ts-rs = "7.0.0" -cosmwasm-std = "1.2.0" +cosmwasm-std = "1.3.0" cosmrs = "=0.14.0" nym-config = { path = "../../common/config" } @@ -19,8 +19,6 @@ 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" } 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" } nym-types = { path = "../../common/types" } diff --git a/nym-wallet/nym-wallet-types/src/network/qa.rs b/nym-wallet/nym-wallet-types/src/network/qa.rs index 1d298de8dd..605cc63fdd 100644 --- a/nym-wallet/nym-wallet-types/src/network/qa.rs +++ b/nym-wallet/nym-wallet-types/src/network/qa.rs @@ -3,6 +3,8 @@ use nym_network_defaults::{ChainDetails, DenomDetails, NymContracts, ValidatorDe // -- Chain details -- +pub(crate) const NETWORK_NAME: &str = "qa"; + pub(crate) const BECH32_PREFIX: &str = "n"; pub(crate) const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6); pub(crate) const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6); @@ -13,8 +15,6 @@ pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "n10qt8wg0n7z740ssvf3urmvgtjhxpyp74hxqvqt7z226gykuus7eq5u9pvq"; pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "n1vguuxez2h5ekltfj9gjd62fs5k4rl2zy5hfrncasykzw08rezpfstk9xtk"; -pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = - "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0"; pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = "n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw"; pub(crate) const GROUP_CONTRACT_ADDRESS: &str = @@ -23,6 +23,8 @@ pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "n1p54qvfde6mpnqvz3dnpa78x2qyyr5k4sgw9qr97mxjgklc5gze9sv6t964"; pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = "n1xqkp8x4gqwjnhemtemc5dqhwll6w6rrgpywvhka7sh8vz8swul9sp3lv3w"; +pub(crate) const EPHEMERA_CONTRACT_ADDRESS: &str = + "n1xqkp8x4gqwjnhemtemc5dqhwll6w6rrgpywvhka7sh8vz8swul9sp3lv3w"; pub(crate) const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str = "n1nhdr07kmjns2x8dnp53tdk4qxreze8zdxj6xucyvkdj9tta73rjqa96wps"; pub(crate) const NAME_SERVICE_CONTRACT_ADDRESS: &str = @@ -37,8 +39,11 @@ pub(crate) fn validators() -> Vec { )] } +pub(crate) const EXPLORER_API: &str = "https://qa-explorer.qa.nymte.ch/api/"; + pub(crate) fn network_details() -> nym_network_defaults::NymNetworkDetails { nym_network_defaults::NymNetworkDetails { + network_name: NETWORK_NAME.into(), chain_details: ChainDetails { bech32_account_prefix: BECH32_PREFIX.to_string(), mix_denom: MIX_DENOM.into(), @@ -48,17 +53,18 @@ pub(crate) fn network_details() -> nym_network_defaults::NymNetworkDetails { contracts: NymContracts { mixnet_contract_address: parse_optional_str(MIXNET_CONTRACT_ADDRESS), vesting_contract_address: parse_optional_str(VESTING_CONTRACT_ADDRESS), - bandwidth_claim_contract_address: parse_optional_str(BANDWIDTH_CLAIM_CONTRACT_ADDRESS), coconut_bandwidth_contract_address: parse_optional_str( COCONUT_BANDWIDTH_CONTRACT_ADDRESS, ), group_contract_address: parse_optional_str(GROUP_CONTRACT_ADDRESS), multisig_contract_address: parse_optional_str(MULTISIG_CONTRACT_ADDRESS), coconut_dkg_contract_address: parse_optional_str(COCONUT_DKG_CONTRACT_ADDRESS), + ephemera_contract_address: parse_optional_str(EPHEMERA_CONTRACT_ADDRESS), service_provider_directory_contract_address: parse_optional_str( SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS, ), name_service_contract_address: parse_optional_str(NAME_SERVICE_CONTRACT_ADDRESS), }, + explorer_api: parse_optional_str(EXPLORER_API), } } diff --git a/nym-wallet/nym-wallet-types/src/network/sandbox.rs b/nym-wallet/nym-wallet-types/src/network/sandbox.rs index 169c058c3c..ace2ff7663 100644 --- a/nym-wallet/nym-wallet-types/src/network/sandbox.rs +++ b/nym-wallet/nym-wallet-types/src/network/sandbox.rs @@ -3,33 +3,43 @@ use nym_network_defaults::{ChainDetails, DenomDetails, NymContracts, ValidatorDe // -- Chain details -- -pub(crate) const BECH32_PREFIX: &str = "nymt"; -pub(crate) const MIX_DENOM: DenomDetails = DenomDetails::new("unymt", "nymt", 6); -pub(crate) const STAKE_DENOM: DenomDetails = DenomDetails::new("unyxt", "nyxt", 6); +pub(crate) const NETWORK_NAME: &str = "sandbox"; + +pub(crate) const BECH32_PREFIX: &str = "n"; +pub(crate) const MIX_DENOM: DenomDetails = DenomDetails::new("unym", "nym", 6); +pub(crate) const STAKE_DENOM: DenomDetails = DenomDetails::new("unyx", "nyx", 6); // -- Contract addresses -- -pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j"; -pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt14ejqjyq8um4p3xfqj74yld5waqljf88fn549lh"; -pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = - "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv"; +pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = + "n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav"; +pub(crate) const VESTING_CONTRACT_ADDRESS: &str = + "n1unyuj8qnmygvzuex3dwmg9yzt9alhvyeat0uu0jedg2wj33efl5qackslz"; pub(crate) const COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str = - "nymt1nz0r0au8aj6dc00wmm3ufy4g4k86rjzlgq608r"; -pub(crate) const GROUP_CONTRACT_ADDRESS: &str = "nymt1k8re7jwz6rnnwrktnejdwkwnncte7ek7kk6fvg"; -pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = "nymt1k8re7jwz6rnnwrktnejdwkwnncte7ek7kk6fvg"; -pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = "nymt1k8re7jwz6rnnwrktnejdwkwnncte7ek7kk6fvg"; + "n16a32stm6kknhq5cc8rx77elr66pygf2hfszw7wvpq746x3uffylqkjar4l"; +pub(crate) const GROUP_CONTRACT_ADDRESS: &str = + "n1pd7kfgvr5tpcv0xnlv46c4jsq9jg2r799xxrcwqdm4l2jhq2pjwqrmz5ju"; +pub(crate) const MULTISIG_CONTRACT_ADDRESS: &str = + "n14ph4e660eyqz0j36zlkaey4zgzexm5twkmjlqaequxr2cjm9eprqsmad6k"; +pub(crate) const COCONUT_DKG_CONTRACT_ADDRESS: &str = + "n1ahg0erc2fs6xx3j5m8sfx3ryuzdjh6kf6qm9plsf865fltekyrfsesac6a"; +pub(crate) const EPHEMERA_CONTRACT_ADDRESS: &str = + "n1ahg0erc2fs6xx3j5m8sfx3ryuzdjh6kf6qm9plsf865fltekyrfsesac6a"; // -- Constructor functions -- pub(crate) fn validators() -> Vec { vec![ValidatorDetails::new( - "https://sandbox-validator.nymtech.net", - Some("https://sandbox-validator.nymtech.net/api"), + "https://sandbox-validator1.nymtech.net", + Some("https://sandbox-nym-api1.nymtech.net/api"), )] } +pub(crate) const EXPLORER_API: &str = "https://sandbox-explorer.nymtech.net/api/"; + pub(crate) fn network_details() -> nym_network_defaults::NymNetworkDetails { nym_network_defaults::NymNetworkDetails { + network_name: NETWORK_NAME.into(), chain_details: ChainDetails { bech32_account_prefix: BECH32_PREFIX.to_string(), mix_denom: MIX_DENOM.into(), @@ -39,15 +49,16 @@ pub(crate) fn network_details() -> nym_network_defaults::NymNetworkDetails { contracts: NymContracts { mixnet_contract_address: parse_optional_str(MIXNET_CONTRACT_ADDRESS), vesting_contract_address: parse_optional_str(VESTING_CONTRACT_ADDRESS), - bandwidth_claim_contract_address: parse_optional_str(BANDWIDTH_CLAIM_CONTRACT_ADDRESS), coconut_bandwidth_contract_address: parse_optional_str( COCONUT_BANDWIDTH_CONTRACT_ADDRESS, ), group_contract_address: parse_optional_str(GROUP_CONTRACT_ADDRESS), multisig_contract_address: parse_optional_str(MULTISIG_CONTRACT_ADDRESS), coconut_dkg_contract_address: parse_optional_str(COCONUT_DKG_CONTRACT_ADDRESS), + ephemera_contract_address: parse_optional_str(EPHEMERA_CONTRACT_ADDRESS), service_provider_directory_contract_address: None, name_service_contract_address: None, }, + explorer_api: parse_optional_str(EXPLORER_API), } } diff --git a/nym-wallet/package.json b/nym-wallet/package.json index e93b887fcf..e91c500678 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -1,6 +1,6 @@ { "name": "@nymproject/nym-wallet-app", - "version": "1.2.6", + "version": "1.2.7", "main": "index.js", "license": "MIT", "scripts": { diff --git a/nym-wallet/src-tauri/Cargo.toml b/nym-wallet/src-tauri/Cargo.toml index 19b5f0e3c9..d6b9c1392a 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.6" +version = "1.2.7" description = "Nym Native Wallet" authors = ["Nym Technologies SA"] license = "" @@ -32,7 +32,7 @@ itertools = "0.10" log = { version = "0.4", features = ["serde"] } once_cell = "1.7.2" pretty_env_logger = "0.4" -reqwest = {version = "0.11.9", features = ["json"] } +reqwest = {version = "0.11.18", features = ["json"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_repr = "0.1" @@ -48,20 +48,16 @@ k256 = { version = "0.13", features = ["ecdsa", "sha256"] } base64 = "0.13" zeroize = { version = "1.5", features = ["zeroize_derive", "serde"] } -cosmwasm-std = "1.2.0" +cosmwasm-std = "1.3.0" cosmrs = "=0.14.0" -nym-validator-client = { path = "../../common/client-libs/validator-client", features = [ - "signing", "http-client" -] } +nym-validator-client = { path = "../../common/client-libs/validator-client" } nym-crypto = { path = "../../common/crypto", features = ["asymmetric"] } nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common" } 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" } nym-coconut-interface = { path = "../../common/coconut-interface" } -# Used for Type conversion, can be extracted but its a lot of work -nym-vesting-contract = { path = "../../contracts/vesting" } nym-types = { path = "../../common/types" } nym-wallet-types = { path = "../nym-wallet-types" } nym-store-cipher = { path = "../../common/store-cipher", features = ["json"] } @@ -70,7 +66,7 @@ nym-store-cipher = { path = "../../common/store-cipher", features = ["json"] } nym-crypto = { path = "../../common/crypto", features = ["rand"] } rand_chacha = "0.2" tempfile = "3.3.0" -ts-rs = "6.1.2" +ts-rs = "7.0.0" [features] default = ["custom-protocol"] diff --git a/nym-wallet/src-tauri/src/config/mod.rs b/nym-wallet/src-tauri/src/config/mod.rs index 24e8a7d03a..bdb369ade6 100644 --- a/nym-wallet/src-tauri/src/config/mod.rs +++ b/nym-wallet/src-tauri/src/config/mod.rs @@ -237,15 +237,6 @@ impl Config { .expect("Wrong format for vesting contract address") } - pub fn get_bandwidth_claim_contract_address(&self, network: WalletNetwork) -> CosmosAccountId { - self.base - .networks - .bandwidth_claim_contract_address(&network) - .expect("No bandwidth claim contract address found in config") - .parse() - .expect("Wrong format for bandwidth claim contract address") - } - pub fn set_default_nyxd_urls(&mut self, urls: &HashMap) { for (network, url) in urls { self.set_default_nyxd_url(url.to_owned(), network); @@ -499,12 +490,6 @@ impl SupportedNetworks { .map(|network_details| network_details.vesting_contract_address.as_str()) } - fn bandwidth_claim_contract_address(&self, network: &WalletNetwork) -> Option<&str> { - self.networks - .get(network) - .map(|network_details| network_details.bandwidth_claim_contract_address.as_str()) - } - fn validators(&self, network: &WalletNetwork) -> impl Iterator { self.networks .get(network) @@ -522,7 +507,6 @@ struct NetworkDetails { stake_denom: DenomDetailsOwned, mixnet_contract_address: String, vesting_contract_address: String, - bandwidth_claim_contract_address: String, statistics_service_url: String, validators: Vec, } @@ -543,10 +527,6 @@ impl From for NetworkDetails { .contracts .vesting_contract_address .unwrap_or_default(), - bandwidth_claim_contract_address: details - .contracts - .bandwidth_claim_contract_address - .unwrap_or_default(), statistics_service_url: "".to_string(), validators: details.endpoints, } diff --git a/nym-wallet/src-tauri/src/error.rs b/nym-wallet/src-tauri/src/error.rs index 3d9374c22a..7f542d257c 100644 --- a/nym-wallet/src-tauri/src/error.rs +++ b/nym-wallet/src-tauri/src/error.rs @@ -196,11 +196,6 @@ impl From for BackendError { impl From for BackendError { fn from(e: ValidatorClientError) -> Self { - match e { - ValidatorClientError::NymAPIError { source } => source.into(), - ValidatorClientError::MalformedUrlProvided(e) => e.into(), - ValidatorClientError::NyxdError(e) => e.into(), - ValidatorClientError::NoAPIUrlAvailable => TypesError::NoNymApiUrlConfigured.into(), - } + TypesError::from(e).into() } } diff --git a/nym-wallet/src-tauri/src/operations/helpers.rs b/nym-wallet/src-tauri/src/operations/helpers.rs index 57fcb5d1bb..35dacbb190 100644 --- a/nym-wallet/src-tauri/src/operations/helpers.rs +++ b/nym-wallet/src-tauri/src/operations/helpers.rs @@ -12,10 +12,10 @@ use nym_mixnet_contract_common::{ construct_mixnode_bonding_sign_payload, Gateway, GatewayBondingPayload, MixNode, MixNodeCostParams, SignableGatewayBondingMsg, SignableMixNodeBondingMsg, }; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, NymContractsProvider}; use nym_validator_client::nyxd::error::NyxdError; -use nym_validator_client::nyxd::traits::MixnetQueryClient; -use nym_validator_client::nyxd::{Coin, DirectSigningNyxdClient}; -use nym_validator_client::Client; +use nym_validator_client::nyxd::Coin; +use nym_validator_client::DirectSigningHttpRpcValidatorClient; // define this as a separate trait for mocking purposes #[async_trait] @@ -26,15 +26,20 @@ pub(crate) trait AddressAndNonceProvider { } #[async_trait] -impl AddressAndNonceProvider for Client { +impl AddressAndNonceProvider for DirectSigningHttpRpcValidatorClient { async fn get_signing_nonce(&self) -> Result { - self.nyxd.get_signing_nonce(self.nyxd.address()).await + self.nyxd.get_signing_nonce(&self.nyxd.address()).await } fn vesting_contract_address(&self) -> Addr { // 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.nyxd.vesting_contract_address().as_ref()) + Addr::unchecked( + self.nyxd + .vesting_contract_address() + .expect("unknown vesting contract address") + .as_ref(), + ) } fn cw_address(&self) -> Addr { diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index a15732e5ee..4060b1e810 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -9,9 +9,10 @@ use cosmrs::bip32::DerivationPath; use itertools::Itertools; use nym_config::defaults::{NymNetworkDetails, COSMOS_DERIVATION_PATH}; use nym_types::account::{Account, AccountEntry, Balance}; +use nym_validator_client::nyxd::CosmWasmClient; use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet; use nym_validator_client::signing::AccountData; -use nym_validator_client::{nyxd::DirectSigningNyxdClient, Client}; +use nym_validator_client::DirectSigningHttpRpcValidatorClient; use nym_wallet_types::network::Network as WalletNetwork; use std::collections::HashMap; use strum::IntoEnumIterator; @@ -35,7 +36,7 @@ pub async fn get_balance(state: tauri::State<'_, WalletState>) -> Result { @@ -251,7 +252,7 @@ fn create_clients( default_api_urls: &HashMap, config: &Config, mnemonic: &Mnemonic, -) -> Result)>, BackendError> { +) -> Result, BackendError> { let mut clients = Vec::new(); for network in WalletNetwork::iter() { let nyxd_url = if let Some(url) = config.get_selected_validator_nyxd_url(network) { @@ -282,18 +283,13 @@ fn create_clients( let network_details = NymNetworkDetails::from(network) .clone() .with_mixnet_contract(Some(config.get_mixnet_contract_address(network).as_ref())) - .with_vesting_contract(Some(config.get_vesting_contract_address(network).as_ref())) - .with_bandwidth_claim_contract(Some( - config - .get_bandwidth_claim_contract_address(network) - .as_ref(), - )); + .with_vesting_contract(Some(config.get_vesting_contract_address(network).as_ref())); let config = nym_validator_client::Config::try_from_nym_network_details(&network_details)? - .with_urls(nyxd_url, api_url); + .with_urls(nyxd_url, api_url) + .with_simulated_gas_multiplier(CUSTOM_SIMULATED_GAS_MULTIPLIER); - let mut client = nym_validator_client::Client::new_signing(config, mnemonic.clone())?; - client.set_nyxd_simulated_gas_multiplier(CUSTOM_SIMULATED_GAS_MULTIPLIER); + let client = nym_validator_client::Client::new_signing(config, mnemonic.clone())?; clients.push((network, client)); } Ok(clients) diff --git a/nym-wallet/src-tauri/src/operations/mixnet/admin.rs b/nym-wallet/src-tauri/src/operations/mixnet/admin.rs index d8824e41d8..2670ff360c 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/admin.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/admin.rs @@ -5,7 +5,7 @@ use crate::error::BackendError; use crate::state::WalletState; use nym_mixnet_contract_common::ContractStateParams; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; use nym_validator_client::nyxd::Fee; use nym_wallet_types::admin::TauriContractStateParams; diff --git a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs index 4acd1aecfc..896b62175a 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/bond.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/bond.rs @@ -14,7 +14,7 @@ use nym_types::currency::DecCoin; use nym_types::gateway::GatewayBond; use nym_types::mixnode::{MixNodeCostParams, MixNodeDetails}; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, MixnetSigningClient}; use nym_validator_client::nyxd::Fee; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; @@ -337,7 +337,10 @@ pub async fn get_mixnode_avg_uptime( log::info!(">>> Get mixnode bond details"); let guard = state.read().await; let client = guard.current_client()?; - let res = client.nyxd.get_owned_mixnode(client.nyxd.address()).await?; + let res = client + .nyxd + .get_owned_mixnode(&client.nyxd.address()) + .await?; match res.mixnode_details { Some(details) => { @@ -363,7 +366,10 @@ pub async fn mixnode_bond_details( log::info!(">>> Get mixnode bond details"); let guard = state.read().await; let client = guard.current_client()?; - let res = client.nyxd.get_owned_mixnode(client.nyxd.address()).await?; + let res = client + .nyxd + .get_owned_mixnode(&client.nyxd.address()) + .await?; let details = res .mixnode_details .map(|details| { @@ -391,7 +397,10 @@ pub async fn gateway_bond_details( log::info!(">>> Get gateway bond details"); let guard = state.read().await; let client = guard.current_client()?; - let bond = client.nyxd.get_owned_gateway(client.nyxd.address()).await?; + let bond = client + .nyxd + .get_owned_gateway(&client.nyxd.address()) + .await?; let res = bond .gateway .map(|bond| { diff --git a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs index 8a4a82e158..18bd72551f 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs @@ -13,7 +13,9 @@ use nym_types::deprecated::{ use nym_types::mixnode::MixNodeCostParams; use nym_types::pending_events::PendingEpochEvent; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{ + MixnetQueryClient, MixnetSigningClient, NymContractsProvider, PagedMixnetQueryClient, +}; use nym_validator_client::nyxd::Fee; #[tauri::command] @@ -25,7 +27,7 @@ pub async fn get_pending_delegation_events( let reg = guard.registered_coins()?; let client = guard.current_client()?; - let events = client.get_all_nyxd_pending_epoch_events().await?; + let events = client.nyxd.get_all_pending_epoch_events().await?; let converted = events .into_iter() .map(|e| PendingEpochEvent::try_from_mixnet_contract(e, reg)) @@ -156,10 +158,13 @@ pub async fn get_all_mix_delegations( let address = client.nyxd.address(); let network = guard.current_network(); let base_mix_denom = network.base_mix_denom().to_string(); - let vesting_contract = client.nyxd.vesting_contract_address(); + let vesting_contract = client + .nyxd + .vesting_contract_address() + .expect("vesting contract address is not available"); log::info!(" >>> Get delegations"); - let delegations = client.get_all_delegator_delegations(address).await?; + let delegations = client.nyxd.get_all_delegator_delegations(&address).await?; log::info!(" <<< {} delegations", delegations.len()); let pending_events_for_account = get_pending_delegation_events(state.clone()).await?; @@ -185,7 +190,11 @@ pub async fn get_all_mix_delegations( d.amount ); - let mixnode = client.get_mixnode_details(d.mix_id).await?.mixnode_details; + let mixnode = client + .nyxd + .get_mixnode_details(d.mix_id) + .await? + .mixnode_details; let accumulated_by_operator = mixnode .as_ref() @@ -214,7 +223,7 @@ pub async fn get_all_mix_delegations( log::trace!(" >>> Get accumulated rewards: address = {}", address); let pending_reward = client .nyxd - .get_pending_delegator_reward(address, d.mix_id, d.proxy.clone()) + .get_pending_delegator_reward(&address, d.mix_id, d.proxy.clone()) .await?; let accumulated_rewards = match &pending_reward.amount_earned { diff --git a/nym-wallet/src-tauri/src/operations/mixnet/interval.rs b/nym-wallet/src-tauri/src/operations/mixnet/interval.rs index e18fa1e44a..05151d8a53 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/interval.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/interval.rs @@ -5,7 +5,7 @@ use crate::error::BackendError; use crate::nyxd_client; use crate::state::WalletState; use nym_types::pending_events::{PendingEpochEvent, PendingIntervalEvent}; -use nym_validator_client::nyxd::traits::MixnetQueryClient; +use nym_validator_client::nyxd::contract_traits::{MixnetQueryClient, PagedMixnetQueryClient}; use nym_wallet_types::interval::Interval; #[tauri::command] @@ -26,7 +26,7 @@ pub async fn get_pending_epoch_events( let guard = state.read().await; let reg = guard.registered_coins()?; let client = guard.current_client()?; - let res = client.get_all_nyxd_pending_epoch_events().await?; + let res = client.nyxd.get_all_pending_epoch_events().await?; log::info!("<<< got = {:?} events", res.len()); @@ -46,7 +46,7 @@ pub async fn get_pending_interval_events( let guard = state.read().await; let reg = guard.registered_coins()?; let client = guard.current_client()?; - let res = client.get_all_nyxd_pending_interval_events().await?; + let res = client.nyxd.get_all_pending_interval_events().await?; log::info!("<<< got = {:?} events", res.len()); diff --git a/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs b/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs index 62ef534d02..266295c6e1 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/rewards.rs @@ -3,7 +3,9 @@ use crate::state::WalletState; use crate::vesting::rewards::vesting_claim_delegator_reward; use nym_mixnet_contract_common::{MixId, RewardingParams}; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::traits::{MixnetQueryClient, MixnetSigningClient}; +use nym_validator_client::nyxd::contract_traits::{ + MixnetQueryClient, MixnetSigningClient, NymContractsProvider, PagedMixnetQueryClient, +}; use nym_validator_client::nyxd::Fee; #[tauri::command] @@ -64,13 +66,21 @@ pub async fn claim_locked_and_unlocked_delegator_reward( log::trace!(">>> Get delegations: mix_id = {}", mix_id); let address = client.nyxd.address(); - let delegations = client.get_all_delegator_delegations(address).await?; + let delegations = client.nyxd.get_all_delegator_delegations(&address).await?; log::trace!("<<< {} delegations", delegations.len()); - let vesting_contract = client.nyxd.vesting_contract_address().to_string(); - let liquid_delegation = client.get_delegation_details(mix_id, address, None).await?; + let vesting_contract = client + .nyxd + .vesting_contract_address() + .expect("vesting contract address is not available") + .to_string(); + let liquid_delegation = client + .nyxd + .get_delegation_details(mix_id, &address, None) + .await?; let vesting_delegation = client - .get_delegation_details(mix_id, address, Some(vesting_contract)) + .nyxd + .get_delegation_details(mix_id, &address, Some(vesting_contract)) .await?; drop(guard); diff --git a/nym-wallet/src-tauri/src/operations/signatures/sign.rs b/nym-wallet/src-tauri/src/operations/signatures/sign.rs index 24da5a0ca1..c494bc5b4f 100644 --- a/nym-wallet/src-tauri/src/operations/signatures/sign.rs +++ b/nym-wallet/src-tauri/src/operations/signatures/sign.rs @@ -4,6 +4,7 @@ use cosmrs::crypto::secp256k1::{Signature, VerifyingKey}; use cosmrs::crypto::PublicKey; use cosmrs::AccountId; use k256::ecdsa::signature::Verifier; +use nym_validator_client::nyxd::CosmWasmClient; use nym_validator_client::signing::signer::OfflineSigner; use serde::Serialize; use serde_json::json; @@ -23,15 +24,16 @@ pub async fn sign( ) -> Result { let guard = state.read().await; let client = guard.current_client()?; - let wallet = client.nyxd.signer(); - let derived_accounts = wallet.try_derive_accounts()?; + let derived_accounts = client.nyxd.get_accounts()?; let account = derived_accounts.first().ok_or_else(|| { log::error!(">>> Unable to derive account"); BackendError::SignatureError("unable to derive account".to_string()) })?; log::info!("<<< Signing message"); - let signature = wallet.sign_raw_with_account(account, message.as_bytes())?; + let signature = client + .nyxd + .sign_raw_with_account(account, message.as_bytes())?; let output = SignatureOutputJson { account_id: account.address().to_string(), public_key: account.public_key(), @@ -49,14 +51,10 @@ async fn get_pubkey_from_account_address( log::info!("Getting public key for address {} from chain...", address); let guard = state.read().await; let client = guard.current_client()?; - let account = client - .nyxd - .get_account_details(address) - .await? - .ok_or_else(|| { - log::error!("No account associated with address {}", address); - BackendError::SignatureError(format!("No account associated with address {address}")) - })?; + let account = client.nyxd.get_account(address).await?.ok_or_else(|| { + log::error!("No account associated with address {}", address); + BackendError::SignatureError(format!("No account associated with address {address}")) + })?; let base_account = account.try_get_base_account()?; base_account.pubkey.ok_or_else(|| { @@ -116,7 +114,7 @@ pub async fn verify( // get public key from current account address let guard = state.read().await; let client = guard.current_client()?; - let address = client.nyxd.address(); + let address = &client.nyxd.address(); get_pubkey_from_account_address(address, &state).await? } }; diff --git a/nym-wallet/src-tauri/src/operations/simulate/admin.rs b/nym-wallet/src-tauri/src/operations/simulate/admin.rs index f47b44e454..1884c7472e 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/admin.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/admin.rs @@ -5,6 +5,7 @@ use crate::error::BackendError; use crate::operations::simulate::FeeDetails; use crate::WalletState; use nym_mixnet_contract_common::{ContractStateParams, ExecuteMsg}; +use nym_validator_client::nyxd::contract_traits::NymContractsProvider; use nym_wallet_types::admin::TauriContractStateParams; #[tauri::command] @@ -18,7 +19,10 @@ pub async fn simulate_update_contract_settings( params.try_convert_to_mixnet_contract_params(reg)?; let client = guard.current_client()?; - let mixnet_contract = client.nyxd.mixnet_contract_address(); + let mixnet_contract = client + .nyxd + .mixnet_contract_address() + .expect("mixnet contract address is not available"); let msg = client.nyxd.wrap_contract_execute_message( mixnet_contract, diff --git a/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs index 332f0eeb43..11d360ef71 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/mixnet.rs @@ -11,6 +11,7 @@ use nym_mixnet_contract_common::{ExecuteMsg, Gateway, MixId, MixNode}; use nym_mixnet_contract_common::{GatewayConfigUpdate, MixNodeConfigUpdate}; use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; +use nym_validator_client::nyxd::contract_traits::NymContractsProvider; async fn simulate_mixnet_operation( msg: ExecuteMsg, @@ -28,7 +29,10 @@ async fn simulate_mixnet_operation( }; let client = guard.current_client()?; - let mixnet_contract = client.nyxd.mixnet_contract_address(); + let mixnet_contract = client + .nyxd + .mixnet_contract_address() + .expect("mixnet contract address is not available"); let msg = client .nyxd diff --git a/nym-wallet/src-tauri/src/operations/simulate/vesting.rs b/nym-wallet/src-tauri/src/operations/simulate/vesting.rs index 79e40ec792..b225aa9ff3 100644 --- a/nym-wallet/src-tauri/src/operations/simulate/vesting.rs +++ b/nym-wallet/src-tauri/src/operations/simulate/vesting.rs @@ -11,6 +11,7 @@ use nym_mixnet_contract_common::{Gateway, MixId, MixNode}; use nym_mixnet_contract_common::{GatewayConfigUpdate, MixNodeConfigUpdate}; use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; +use nym_validator_client::nyxd::contract_traits::NymContractsProvider; use nym_vesting_contract_common::ExecuteMsg; async fn simulate_vesting_operation( @@ -29,7 +30,10 @@ async fn simulate_vesting_operation( }; let client = guard.current_client()?; - let vesting_contract = client.nyxd.vesting_contract_address(); + let vesting_contract = client + .nyxd + .vesting_contract_address() + .expect("vesting contract address is not available"); let msg = client .nyxd diff --git a/nym-wallet/src-tauri/src/operations/vesting/bond.rs b/nym-wallet/src-tauri/src/operations/vesting/bond.rs index f2077e5694..8e47953109 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/bond.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/bond.rs @@ -10,7 +10,7 @@ use nym_mixnet_contract_common::{GatewayConfigUpdate, MixNodeConfigUpdate}; use nym_types::currency::DecCoin; use nym_types::mixnode::MixNodeCostParams; use nym_types::transaction::TransactionExecuteResult; -use nym_validator_client::nyxd::{traits::VestingSigningClient, Fee}; +use nym_validator_client::nyxd::{contract_traits::VestingSigningClient, Fee}; use std::cmp::Ordering; #[tauri::command] diff --git a/nym-wallet/src-tauri/src/operations/vesting/delegate.rs b/nym-wallet/src-tauri/src/operations/vesting/delegate.rs index ff7c33710f..64ac3ed736 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::{traits::VestingSigningClient, Fee}; +use nym_validator_client::nyxd::{contract_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 a24e26d001..f3377d5ed5 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::traits::VestingQueryClient; +use nym_validator_client::nyxd::contract_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 dbedb302e3..d990b2c1c5 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/rewards.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/rewards.rs @@ -5,7 +5,7 @@ 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::contract_traits::VestingSigningClient; use nym_validator_client::nyxd::Fee; #[tauri::command] diff --git a/nym-wallet/src-tauri/src/state.rs b/nym-wallet/src-tauri/src/state.rs index bfc2d85cb6..5fd4485e18 100644 --- a/nym-wallet/src-tauri/src/state.rs +++ b/nym-wallet/src-tauri/src/state.rs @@ -8,10 +8,8 @@ use log::warn; use nym_types::currency::{DecCoin, Denom, RegisteredCoins}; use nym_types::fees::FeeDetails; use nym_validator_client::nyxd::cosmwasm_client::types::SimulateResponse; -use nym_validator_client::nyxd::{ - AccountId as CosmosAccountId, Coin, DirectSigningNyxdClient, Fee, -}; -use nym_validator_client::Client; +use nym_validator_client::nyxd::{AccountId as CosmosAccountId, Coin, Fee, SigningCosmWasmClient}; +use nym_validator_client::DirectSigningHttpRpcValidatorClient; use nym_wallet_types::network::Network; use nym_wallet_types::network_config; use once_cell::sync::Lazy; @@ -67,7 +65,7 @@ impl WalletState { #[derive(Default)] pub struct WalletStateInner { config: config::Config, - signing_clients: HashMap>, + signing_clients: HashMap, current_network: Network, // All the accounts the we get from decrypting the wallet. We hold on to these for being able to @@ -181,7 +179,7 @@ impl WalletStateInner { // this MUST succeed as we just used it before let client = self.current_client()?; let gas_price = client.nyxd.gas_price().clone(); - let gas_adjustment = client.nyxd.gas_adjustment(); + let gas_adjustment = client.nyxd.simulated_gas_multiplier(); let res = SimulateResult::new(simulate_response.gas_info, gas_price, gas_adjustment); @@ -196,7 +194,7 @@ impl WalletStateInner { pub fn client( &self, network: Network, - ) -> Result<&Client, BackendError> { + ) -> Result<&DirectSigningHttpRpcValidatorClient, BackendError> { self.signing_clients .get(&network) .ok_or(BackendError::ClientNotInitialized) @@ -205,13 +203,13 @@ impl WalletStateInner { pub fn client_mut( &mut self, network: Network, - ) -> Result<&mut Client, BackendError> { + ) -> Result<&mut DirectSigningHttpRpcValidatorClient, BackendError> { self.signing_clients .get_mut(&network) .ok_or(BackendError::ClientNotInitialized) } - pub fn current_client(&self) -> Result<&Client, BackendError> { + pub fn current_client(&self) -> Result<&DirectSigningHttpRpcValidatorClient, BackendError> { self.signing_clients .get(&self.current_network) .ok_or(BackendError::ClientNotInitialized) @@ -220,7 +218,7 @@ impl WalletStateInner { #[allow(unused)] pub fn current_client_mut( &mut self, - ) -> Result<&mut Client, BackendError> { + ) -> Result<&mut DirectSigningHttpRpcValidatorClient, BackendError> { self.signing_clients .get_mut(&self.current_network) .ok_or(BackendError::ClientNotInitialized) @@ -240,7 +238,7 @@ impl WalletStateInner { Ok(self.config.save_to_files()?) } - pub fn add_client(&mut self, network: Network, client: Client) { + pub fn add_client(&mut self, network: Network, client: DirectSigningHttpRpcValidatorClient) { self.signing_clients.insert(network, client); } diff --git a/nym-wallet/src-tauri/src/utils.rs b/nym-wallet/src-tauri/src/utils.rs index e5aed4e092..2a340cc5dd 100644 --- a/nym-wallet/src-tauri/src/utils.rs +++ b/nym-wallet/src-tauri/src/utils.rs @@ -8,7 +8,7 @@ use cosmwasm_std::Decimal; 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::contract_traits::MixnetQueryClient; use nym_wallet_types::app::AppEnv; fn get_env_as_option(key: &str) -> Option { @@ -30,7 +30,7 @@ pub fn get_env() -> AppEnv { #[tauri::command] pub async fn owns_mixnode(state: tauri::State<'_, WalletState>) -> Result { Ok(nyxd_client!(state) - .get_owned_mixnode(nyxd_client!(state).address()) + .get_owned_mixnode(&nyxd_client!(state).address()) .await? .mixnode_details .is_some()) @@ -39,7 +39,7 @@ pub async fn owns_mixnode(state: tauri::State<'_, WalletState>) -> Result) -> Result { Ok(nyxd_client!(state) - .get_owned_gateway(nyxd_client!(state).address()) + .get_owned_gateway(&nyxd_client!(state).address()) .await? .gateway .is_some()) @@ -53,7 +53,9 @@ pub async fn try_convert_pubkey_to_mix_id( let res = nyxd_client!(state) .get_mixnode_details_by_identity(mix_identity) .await?; - Ok(res.map(|mixnode_details| mixnode_details.mix_id())) + Ok(res + .mixnode_details + .map(|mixnode_details| mixnode_details.mix_id())) } #[tauri::command] diff --git a/nym-wallet/src-tauri/tauri.conf.json b/nym-wallet/src-tauri/tauri.conf.json index 2986866ccb..2660030fb3 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.6" + "version": "1.2.7" }, "build": { "distDir": "../dist", diff --git a/nym-wallet/src/components/Bonding/forms/gatewayValidationSchema.ts b/nym-wallet/src/components/Bonding/forms/gatewayValidationSchema.ts index b36ff2d095..6935b6e623 100644 --- a/nym-wallet/src/components/Bonding/forms/gatewayValidationSchema.ts +++ b/nym-wallet/src/components/Bonding/forms/gatewayValidationSchema.ts @@ -28,7 +28,7 @@ export const gatewayValidationSchema = Yup.object().shape({ location: Yup.string() .required('A location is required') - .test('valid-location', 'A valid version is required', (locationValueTest) => + .test('valid-location', 'A valid location is required', (locationValueTest) => locationValueTest ? validateLocation(locationValueTest) : false, ), diff --git a/nym-wallet/src/components/Bonding/utils.ts b/nym-wallet/src/components/Bonding/utils.ts index d3285215f6..6e7b05a651 100644 --- a/nym-wallet/src/components/Bonding/utils.ts +++ b/nym-wallet/src/components/Bonding/utils.ts @@ -4,12 +4,13 @@ import { toPercentFloatString } from '../../utils'; export function mixnodeToTauri(data: MixnodeData): MixNode { return { - ...data, mix_port: data.mixPort, http_api_port: data.httpApiPort, verloc_port: data.verlocPort, sphinx_key: data.sphinxKey, identity_key: data.identityKey, + version: data.version, + host: data.host, }; } @@ -25,7 +26,6 @@ export function costParamsToTauri(data: MixnodeAmount): MixNodeCostParams { export function gatewayToTauri(data: GatewayData): Gateway { return { - ...data, host: data.host, version: data.version, mix_port: data.mixPort, diff --git a/nym-wallet/webdriver/yarn.lock b/nym-wallet/webdriver/yarn.lock index 0c01bfb5cc..3e139e91a3 100644 --- a/nym-wallet/webdriver/yarn.lock +++ b/nym-wallet/webdriver/yarn.lock @@ -2024,10 +2024,10 @@ pkg-dir@4.2.0: dependencies: find-up "^4.0.0" -prettier@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" - integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== +prettier@^2.8.7: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== pretty-format@^27.3.1: version "27.3.1" @@ -2259,9 +2259,9 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== "semver@2 || 3 || 4 || 5": - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== serialize-error@^8.0.0: version "8.1.0" diff --git a/package.json b/package.json index 523db5363f..a967dafdee 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ "ts-packages/*", "nym-wallet", "nym-connect/**", - "nym-browser-extension", "explorer", "types", "clients/validator" @@ -29,12 +28,11 @@ "tsc": "lerna run tsc --stream", "types:lint:fix": "lerna run lint:fix --scope @nymproject/types --scope @nymproject/nym-wallet-app", "audit:fix": "npm_config_yes=true npx yarn-audit-fix -- --dry-run", - "preinstall": "yarn install:copy-placeholders && yarn install:copy-storage-placeholders", - "install:copy-placeholders": "cp scripts/build/yarn/wasm-placeholder/package.json sdk/typescript/packages/nym-client-wasm", - "install:copy-storage-placeholders": "mkdir -p nym-browser-extension/storage/pkg && cp scripts/build/yarn/storage-placeholder/package.json nym-browser-extension/storage/pkg" + "preinstall": "yarn install:copy-placeholders", + "install:copy-placeholders": "cp scripts/build/yarn/wasm-placeholder/package.json sdk/typescript/packages/nym-client-wasm" }, "devDependencies": { "lerna": "^6.6.2", "npm-run-all": "^4.1.5" } -} +} \ No newline at end of file diff --git a/sdk/lib/socks5-listener/Cargo.lock b/sdk/lib/socks5-listener/Cargo.lock deleted file mode 100644 index 3fc8b34151..0000000000 --- a/sdk/lib/socks5-listener/Cargo.lock +++ /dev/null @@ -1,5331 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array 0.14.7", -] - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "ctr 0.8.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aes" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" -dependencies = [ - "cfg-if", - "cipher 0.4.4", - "cpufeatures", -] - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.9", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" -dependencies = [ - "memchr", -] - -[[package]] -name = "android_log-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f0fc03f560e1aebde41c2398b691cb98b5ea5996a6184a7a67bbbb77448969" - -[[package]] -name = "android_logger" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa490e751f3878eb9accb9f18988eca52c2337ce000a8bf31ef50d4c723ca9e" -dependencies = [ - "android_log-sys", - "env_logger 0.10.0", - "log", - "once_cell", -] - -[[package]] -name = "anstream" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is-terminal", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" - -[[package]] -name = "anstyle-parse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys 0.48.0", -] - -[[package]] -name = "anstyle-wincon" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" -dependencies = [ - "anstyle", - "windows-sys 0.48.0", -] - -[[package]] -name = "anyhow" -version = "1.0.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "async-trait" -version = "0.1.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "atoi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atoi" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bip32" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873faa4363bfc54c36a48321da034c92a0645a363eed34d948683ffc1706e37f" -dependencies = [ - "bs58", - "hmac 0.11.0", - "k256", - "once_cell", - "pbkdf2", - "rand_core 0.6.4", - "ripemd160", - "sha2 0.9.9", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "bip39" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" -dependencies = [ - "bitcoin_hashes", - "rand 0.8.5", - "rand_core 0.6.4", - "serde", - "unicode-normalization", - "zeroize", -] - -[[package]] -name = "bitcoin_hashes" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" -dependencies = [ - "byte-tools", - "crypto-mac 0.7.0", - "digest 0.8.1", - "opaque-debug 0.2.3", -] - -[[package]] -name = "blake3" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "digest 0.10.6", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "block-padding", - "generic-array 0.14.7", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54757888b09a69be70b5ec303e382a74227392086ba808cb01eeca29233a2397" -dependencies = [ - "digest 0.9.0", - "ff 0.10.1", - "group 0.10.0", - "pairing 0.20.0", - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[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 0.11.1", - "group 0.11.0", - "pairing 0.21.0", - "rand_core 0.6.4", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" -dependencies = [ - "sha2 0.9.9", -] - -[[package]] -name = "bumpalo" -version = "3.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chacha" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862" -dependencies = [ - "byteorder", - "keystream", -] - -[[package]] -name = "chacha20" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" -dependencies = [ - "cfg-if", - "cipher 0.4.4", - "cpufeatures", -] - -[[package]] -name = "chacha20poly1305" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" -dependencies = [ - "aead", - "chacha20", - "cipher 0.4.4", - "poly1305", - "zeroize", -] - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", - "zeroize", -] - -[[package]] -name = "clap" -version = "4.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" -dependencies = [ - "clap_builder", - "clap_derive", - "once_cell", -] - -[[package]] -name = "clap_builder" -version = "4.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" -dependencies = [ - "anstream", - "anstyle", - "bitflags", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_complete" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36774babb166352bb4f7b9cb16f781ffa3439d2a8f12cd31bea85a38c888fea3" -dependencies = [ - "clap", -] - -[[package]] -name = "clap_complete_fig" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3af28956330989baa428ed4d3471b853715d445c62de21b67292e22cf8a41fa" -dependencies = [ - "clap", - "clap_complete", -] - -[[package]] -name = "clap_derive" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "clap_lex" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "colored" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" -dependencies = [ - "atty", - "lazy_static", - "winapi", -] - -[[package]] -name = "combine" -version = "4.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "const-oid" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" - -[[package]] -name = "constant_time_eq" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b" - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "cosmos-sdk-proto" -version = "0.12.3" -source = "git+https://github.com/neacsu/cosmos-rust?branch=neacsu/feegrant_support#f63ded63ec13e753ebe8bdafe9dc503df265d67d" -dependencies = [ - "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" -dependencies = [ - "bip32", - "cosmos-sdk-proto", - "ecdsa", - "eyre", - "getrandom 0.2.9", - "k256", - "prost", - "prost-types", - "rand_core 0.6.4", - "serde", - "serde_json", - "subtle-encoding", - "tendermint", - "tendermint-rpc", - "thiserror", -] - -[[package]] -name = "cosmwasm-crypto" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970d1d705862179b5d7a233ae36f02f21c4ec1b8075fe60c77fc5b43471331fa" -dependencies = [ - "digest 0.9.0", - "ed25519-zebra", - "k256", - "rand_core 0.6.4", - "thiserror", -] - -[[package]] -name = "cosmwasm-derive" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b36e527620a2a3e00e46b6e731ab6c9b68d11069c986f7d7be8eba79ef081a4" -dependencies = [ - "syn 1.0.109", -] - -[[package]] -name = "cosmwasm-std" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875994993c2082a6fcd406937bf0fca21c349e4a624f3810253a14fa83a3a195" -dependencies = [ - "base64 0.13.1", - "cosmwasm-crypto", - "cosmwasm-derive", - "forward_ref", - "schemars", - "serde", - "serde-json-wasm", - "thiserror", - "uint", -] - -[[package]] -name = "cpufeatures" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" -dependencies = [ - "crc-catalog 1.1.1", -] - -[[package]] -name = "crc" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" -dependencies = [ - "crc-catalog 2.2.0", -] - -[[package]] -name = "crc-catalog" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" - -[[package]] -name = "crc-catalog" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -dependencies = [ - "generic-array 0.12.4", - "subtle 1.0.0", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array 0.14.7", - "subtle 2.4.1", -] - -[[package]] -name = "ct-logs" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8" -dependencies = [ - "sct 0.6.1", -] - -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ctr" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" -dependencies = [ - "cipher 0.3.0", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher 0.4.4", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "cw-storage-plus" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648b1507290bbc03a8d88463d7cd9b04b1fa0155e5eef366c4fa052b9caaac7a" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "cw-utils" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "cw2" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "serde", -] - -[[package]] -name = "cw3" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b" -dependencies = [ - "cosmwasm-std", - "cw-utils", - "schemars", - "serde", -] - -[[package]] -name = "cw4" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd" -dependencies = [ - "cosmwasm-std", - "cw-storage-plus", - "schemars", - "serde", -] - -[[package]] -name = "dashmap" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" -dependencies = [ - "cfg-if", - "hashbrown 0.12.3", - "lock_api", - "once_cell", - "parking_lot_core 0.9.7", -] - -[[package]] -name = "der" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" -dependencies = [ - "const-oid", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", - "subtle 2.4.1", -] - -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys 0.3.7", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "dyn-clone" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" - -[[package]] -name = "ecdsa" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d69ae62e0ce582d56380743515fefaf1a8c70cec685d9677636d7e30ae9dc9" -dependencies = [ - "der", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand 0.7.3", - "serde", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "ed25519-zebra" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" -dependencies = [ - "curve25519-dalek", - "hashbrown 0.12.3", - "hex", - "rand_core 0.6.4", - "serde", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "elliptic-curve" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" -dependencies = [ - "base16ct", - "crypto-bigint", - "der", - "ff 0.11.1", - "generic-array 0.14.7", - "group 0.11.0", - "rand_core 0.6.4", - "sec1", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "encoding_rs" -version = "0.8.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enum-iterator" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a0ac4aeb3a18f92eaf09c6bb9b3ac30ff61ca95514fc58cbead1c9a6bf5401" -dependencies = [ - "enum-iterator-derive", -] - -[[package]] -name = "enum-iterator-derive" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime 1.3.0", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "env_logger" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "ext-trait" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d772df1c1a777963712fb68e014235e80863d6a91a85c4e06ba2d16243a310e5" -dependencies = [ - "ext-trait-proc_macros", -] - -[[package]] -name = "ext-trait-proc_macros" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab7934152eaf26aa5aa9f7371408ad5af4c31357073c9e84c3b9d7f11ad639a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "extension-traits" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a296e5a895621edf9fa8329c83aa1cb69a964643e36cf54d8d7a69b789089537" -dependencies = [ - "ext-trait", -] - -[[package]] -name = "eyre" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "ff" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f" -dependencies = [ - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[package]] -name = "ff" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924" -dependencies = [ - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "flex-error" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" -dependencies = [ - "eyre", - "paste", -] - -[[package]] -name = "flume" -version = "0.10.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" -dependencies = [ - "futures-core", - "futures-sink", - "pin-project", - "spin 0.9.8", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "forward_ref" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-intrusive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "serde", - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getset" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ghost" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77ac7b51b8e6313251737fcef4b1c01a2ea102bde68415b62c0ee9268fec357" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "git2" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" -dependencies = [ - "bitflags", - "libc", - "libgit2-sys", - "log", - "url", -] - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "group" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912" -dependencies = [ - "byteorder", - "ff 0.10.1", - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[package]] -name = "group" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89" -dependencies = [ - "byteorder", - "ff 0.11.1", - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[package]] -name = "h2" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "handlebars" -version = "3.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3" -dependencies = [ - "log", - "pest", - "pest_derive", - "quick-error 2.0.1", - "serde", - "serde_json", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashlink" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf" -dependencies = [ - "hashbrown 0.11.2", -] - -[[package]] -name = "hashlink" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" -dependencies = [ - "hashbrown 0.12.3", -] - -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64 0.13.1", - "bitflags", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - -[[package]] -name = "hkdf" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" -dependencies = [ - "digest 0.9.0", - "hmac 0.11.0", -] - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac 0.11.1", - "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.6", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error 1.2.3", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "humantime-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" -dependencies = [ - "humantime 2.1.0", - "serde", -] - -[[package]] -name = "hyper" -version = "0.14.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-proxy" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca815a891b24fdfb243fa3239c86154392b0953ee584aa1a2a1f66d20cbe75cc" -dependencies = [ - "bytes", - "futures", - "headers", - "http", - "hyper", - "hyper-rustls", - "rustls-native-certs", - "tokio", - "tokio-rustls 0.22.0", - "tower-service", - "webpki 0.21.4", -] - -[[package]] -name = "hyper-rustls" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" -dependencies = [ - "ct-logs", - "futures-util", - "hyper", - "log", - "rustls 0.19.1", - "rustls-native-certs", - "tokio", - "tokio-rustls 0.22.0", - "webpki 0.21.4", - "webpki-roots 0.21.1", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "input_buffer" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413" -dependencies = [ - "bytes", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "inventory" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb5160c60ba1e809707918ee329adb99d222888155835c6feedba19f6c3fd4" -dependencies = [ - "ctor", - "ghost", - "inventory-impl", -] - -[[package]] -name = "inventory-impl" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e41b53715c6f0c4be49510bb82dee2c1e51c8586d885abe65396e82ed518548" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "ipnet" -version = "2.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" - -[[package]] -name = "is-terminal" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sec1", - "sha2 0.9.9", - "sha3", -] - -[[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "keystream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.144" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" - -[[package]] -name = "libgit2-sys" -version = "0.13.5+1.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e5ea06c26926f1002dd553fded6cfcdc9784c1f60feeb58368b4d9b07b6dba" -dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", -] - -[[package]] -name = "libm" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" - -[[package]] -name = "libsqlite3-sys" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" - -[[package]] -name = "lioness" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9" -dependencies = [ - "arrayref", - "blake2", - "chacha", - "keystream", -] - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "macro_rules_attribute" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea1bd89c00ca5cf70d924bdaa5baea9eabf4709489ce9e71f2f559ed04edd862" -dependencies = [ - "macro_rules_attribute-proc_macro", - "paste", -] - -[[package]] -name = "macro_rules_attribute-proc_macro" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dd856d451cc0da70e2ef2ce95a18e39a93b7558bedf10201ad28503f918568" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "nym-api-requests" -version = "0.1.0" -dependencies = [ - "bs58", - "cosmrs", - "cosmwasm-std", - "getset", - "nym-coconut-interface", - "nym-mixnet-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "nym-bandwidth-controller" -version = "0.1.0" -dependencies = [ - "bip39", - "nym-coconut-interface", - "nym-credential-storage", - "nym-credentials", - "nym-crypto", - "nym-network-defaults", - "nym-validator-client", - "rand 0.7.3", - "thiserror", - "url", -] - -[[package]] -name = "nym-bin-common" -version = "0.5.0" -dependencies = [ - "atty", - "clap", - "clap_complete", - "clap_complete_fig", - "log", - "pretty_env_logger", - "semver 0.11.0", - "serde", - "vergen", -] - -[[package]] -name = "nym-client-core" -version = "1.1.14" -dependencies = [ - "async-trait", - "dashmap", - "dirs 4.0.0", - "futures", - "gloo-timers", - "humantime-serde", - "log", - "nym-bandwidth-controller", - "nym-config", - "nym-credential-storage", - "nym-crypto", - "nym-gateway-client", - "nym-gateway-requests", - "nym-network-defaults", - "nym-nonexhaustive-delayqueue", - "nym-pemstore", - "nym-sphinx", - "nym-task", - "nym-topology", - "nym-validator-client", - "rand 0.7.3", - "serde", - "serde_json", - "sqlx 0.6.3", - "tap", - "thiserror", - "time", - "tokio", - "tokio-stream", - "tokio-tungstenite", - "tungstenite", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-timer", - "wasm-utils", - "zeroize", -] - -[[package]] -name = "nym-coconut" -version = "0.5.0" -dependencies = [ - "bls12_381 0.6.0", - "bs58", - "digest 0.9.0", - "ff 0.11.1", - "getrandom 0.2.9", - "group 0.11.0", - "itertools", - "nym-dkg", - "nym-pemstore", - "rand 0.8.5", - "serde", - "serde_derive", - "sha2 0.9.9", - "thiserror", -] - -[[package]] -name = "nym-coconut-bandwidth-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "nym-multisig-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "nym-coconut-dkg-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "cw-utils", - "nym-contracts-common", - "nym-multisig-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "nym-coconut-interface" -version = "0.1.0" -dependencies = [ - "bs58", - "getset", - "nym-coconut", - "serde", - "thiserror", -] - -[[package]] -name = "nym-config" -version = "0.1.0" -dependencies = [ - "dirs 5.0.1", - "handlebars", - "log", - "nym-network-defaults", - "serde", - "toml 0.7.4", - "url", -] - -[[package]] -name = "nym-contracts-common" -version = "0.4.0" -dependencies = [ - "bs58", - "cosmwasm-std", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "nym-credential-storage" -version = "0.1.0" -dependencies = [ - "async-trait", - "log", - "sqlx 0.5.13", - "thiserror", - "tokio", -] - -[[package]] -name = "nym-credentials" -version = "0.1.0" -dependencies = [ - "bls12_381 0.5.0", - "cosmrs", - "nym-api-requests", - "nym-coconut-interface", - "nym-crypto", - "nym-validator-client", - "thiserror", -] - -[[package]] -name = "nym-crypto" -version = "0.3.0" -dependencies = [ - "aes 0.8.2", - "blake3", - "bs58", - "cipher 0.4.4", - "ctr 0.9.2", - "digest 0.10.6", - "ed25519-dalek", - "generic-array 0.14.7", - "hkdf 0.12.3", - "hmac 0.12.1", - "nym-pemstore", - "nym-sphinx-types", - "rand 0.7.3", - "subtle-encoding", - "thiserror", - "x25519-dalek", - "zeroize", -] - -[[package]] -name = "nym-dkg" -version = "0.1.0" -dependencies = [ - "bitvec", - "bls12_381 0.6.0", - "bs58", - "ff 0.11.1", - "group 0.11.0", - "lazy_static", - "nym-pemstore", - "rand 0.8.5", - "rand_chacha 0.3.1", - "rand_core 0.6.4", - "serde", - "serde_derive", - "sha2 0.9.9", - "thiserror", - "zeroize", -] - -[[package]] -name = "nym-gateway-client" -version = "0.1.0" -dependencies = [ - "futures", - "getrandom 0.2.9", - "log", - "nym-bandwidth-controller", - "nym-coconut-interface", - "nym-credential-storage", - "nym-crypto", - "nym-gateway-requests", - "nym-network-defaults", - "nym-pemstore", - "nym-sphinx", - "nym-task", - "nym-validator-client", - "rand 0.7.3", - "serde", - "thiserror", - "tokio", - "tokio-stream", - "tokio-tungstenite", - "tungstenite", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-timer", - "wasm-utils", -] - -[[package]] -name = "nym-gateway-requests" -version = "0.1.0" -dependencies = [ - "bs58", - "futures", - "generic-array 0.14.7", - "log", - "nym-coconut-interface", - "nym-credentials", - "nym-crypto", - "nym-pemstore", - "nym-sphinx", - "rand 0.7.3", - "serde", - "serde_json", - "thiserror", - "tungstenite", - "zeroize", -] - -[[package]] -name = "nym-group-contract-common" -version = "0.1.0" -dependencies = [ - "cw4", - "schemars", - "serde", -] - -[[package]] -name = "nym-mixnet-contract-common" -version = "0.5.0" -dependencies = [ - "bs58", - "cosmwasm-std", - "humantime-serde", - "log", - "nym-contracts-common", - "schemars", - "serde", - "serde-json-wasm", - "serde_repr", - "thiserror", - "time", -] - -[[package]] -name = "nym-multisig-contract-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "cw-utils", - "cw3", - "cw4", - "schemars", - "serde", - "thiserror", -] - -[[package]] -name = "nym-name-service-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "nym-network-defaults" -version = "0.1.0" -dependencies = [ - "cfg-if", - "dotenvy", - "hex-literal", - "once_cell", - "serde", - "thiserror", - "url", -] - -[[package]] -name = "nym-nonexhaustive-delayqueue" -version = "0.1.0" -dependencies = [ - "futures-core", - "slab", - "tokio", - "tokio-stream", - "tokio-util", - "wasm-timer", -] - -[[package]] -name = "nym-ordered-buffer" -version = "0.1.0" -dependencies = [ - "log", - "thiserror", -] - -[[package]] -name = "nym-outfox" -version = "0.1.0" -dependencies = [ - "blake3", - "chacha20", - "chacha20poly1305", - "curve25519-dalek", - "getrandom 0.2.9", - "rayon", - "sphinx-packet 0.1.0 (git+https://github.com/nymtech/sphinx.git)", - "thiserror", - "zeroize", -] - -[[package]] -name = "nym-pemstore" -version = "0.2.0" -dependencies = [ - "pem", -] - -[[package]] -name = "nym-service-provider-directory-common" -version = "0.1.0" -dependencies = [ - "cosmwasm-std", - "schemars", - "serde", -] - -[[package]] -name = "nym-service-providers-common" -version = "0.1.0" -dependencies = [ - "async-trait", - "log", - "nym-bin-common", - "nym-sphinx-anonymous-replies", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "nym-socks5-client-core" -version = "0.1.0" -dependencies = [ - "dirs 4.0.0", - "futures", - "log", - "nym-bandwidth-controller", - "nym-client-core", - "nym-config", - "nym-credential-storage", - "nym-network-defaults", - "nym-service-providers-common", - "nym-socks5-proxy-helpers", - "nym-socks5-requests", - "nym-sphinx", - "nym-task", - "nym-validator-client", - "pin-project", - "rand 0.7.3", - "serde", - "tap", - "thiserror", - "tokio", -] - -[[package]] -name = "nym-socks5-listener" -version = "0.1.0" -dependencies = [ - "android_logger", - "anyhow", - "futures", - "jni", - "lazy_static", - "log", - "nym-client-core", - "nym-config", - "nym-credential-storage", - "nym-crypto", - "nym-socks5-client-core", - "openssl", - "rand 0.7.3", - "safer-ffi", - "tokio", -] - -[[package]] -name = "nym-socks5-proxy-helpers" -version = "0.1.0" -dependencies = [ - "bytes", - "futures", - "log", - "nym-ordered-buffer", - "nym-socks5-requests", - "nym-task", - "tokio", - "tokio-util", -] - -[[package]] -name = "nym-socks5-requests" -version = "0.1.0" -dependencies = [ - "nym-service-providers-common", - "nym-sphinx-addressing", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "nym-sphinx" -version = "0.1.0" -dependencies = [ - "log", - "nym-crypto", - "nym-outfox", - "nym-sphinx-acknowledgements", - "nym-sphinx-addressing", - "nym-sphinx-anonymous-replies", - "nym-sphinx-chunking", - "nym-sphinx-cover", - "nym-sphinx-forwarding", - "nym-sphinx-framing", - "nym-sphinx-params", - "nym-sphinx-routing", - "nym-sphinx-types", - "nym-topology", - "rand 0.7.3", - "rand_distr", - "thiserror", - "tokio", -] - -[[package]] -name = "nym-sphinx-acknowledgements" -version = "0.1.0" -dependencies = [ - "nym-crypto", - "nym-pemstore", - "nym-sphinx-addressing", - "nym-sphinx-params", - "nym-sphinx-types", - "nym-topology", - "rand 0.7.3", - "thiserror", - "zeroize", -] - -[[package]] -name = "nym-sphinx-addressing" -version = "0.1.0" -dependencies = [ - "nym-crypto", - "nym-sphinx-types", - "serde", - "thiserror", -] - -[[package]] -name = "nym-sphinx-anonymous-replies" -version = "0.1.0" -dependencies = [ - "bs58", - "nym-crypto", - "nym-sphinx-addressing", - "nym-sphinx-params", - "nym-sphinx-types", - "nym-topology", - "rand 0.7.3", - "serde", - "thiserror", - "wasm-bindgen", -] - -[[package]] -name = "nym-sphinx-chunking" -version = "0.1.0" -dependencies = [ - "log", - "nym-sphinx-addressing", - "nym-sphinx-params", - "nym-sphinx-types", - "rand 0.7.3", - "thiserror", -] - -[[package]] -name = "nym-sphinx-cover" -version = "0.1.0" -dependencies = [ - "nym-crypto", - "nym-sphinx-acknowledgements", - "nym-sphinx-addressing", - "nym-sphinx-chunking", - "nym-sphinx-forwarding", - "nym-sphinx-params", - "nym-sphinx-types", - "nym-topology", - "rand 0.7.3", - "thiserror", -] - -[[package]] -name = "nym-sphinx-forwarding" -version = "0.1.0" -dependencies = [ - "nym-outfox", - "nym-sphinx-addressing", - "nym-sphinx-params", - "nym-sphinx-types", -] - -[[package]] -name = "nym-sphinx-framing" -version = "0.1.0" -dependencies = [ - "bytes", - "nym-sphinx-params", - "nym-sphinx-types", - "thiserror", - "tokio-util", -] - -[[package]] -name = "nym-sphinx-params" -version = "0.1.0" -dependencies = [ - "nym-crypto", - "nym-sphinx-types", - "serde", - "thiserror", -] - -[[package]] -name = "nym-sphinx-routing" -version = "0.1.0" -dependencies = [ - "nym-sphinx-addressing", - "nym-sphinx-types", - "thiserror", -] - -[[package]] -name = "nym-sphinx-types" -version = "0.2.0" -dependencies = [ - "sphinx-packet 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nym-task" -version = "0.1.0" -dependencies = [ - "futures", - "log", - "thiserror", - "tokio", - "wasm-bindgen", - "wasm-bindgen-futures", -] - -[[package]] -name = "nym-topology" -version = "0.1.0" -dependencies = [ - "async-trait", - "bs58", - "log", - "nym-bin-common", - "nym-crypto", - "nym-mixnet-contract-common", - "nym-sphinx-addressing", - "nym-sphinx-routing", - "nym-sphinx-types", - "rand 0.7.3", - "thiserror", -] - -[[package]] -name = "nym-validator-client" -version = "0.1.0" -dependencies = [ - "async-trait", - "base64 0.13.1", - "bip39", - "colored", - "cosmrs", - "cosmwasm-std", - "cw3", - "cw4", - "eyre", - "flate2", - "futures", - "itertools", - "log", - "nym-api-requests", - "nym-coconut-bandwidth-contract-common", - "nym-coconut-dkg-common", - "nym-coconut-interface", - "nym-config", - "nym-contracts-common", - "nym-group-contract-common", - "nym-mixnet-contract-common", - "nym-multisig-contract-common", - "nym-name-service-common", - "nym-network-defaults", - "nym-service-provider-directory-common", - "nym-vesting-contract", - "nym-vesting-contract-common", - "prost", - "reqwest", - "serde", - "serde_json", - "sha2 0.9.9", - "thiserror", - "tokio", - "url", - "zeroize", -] - -[[package]] -name = "nym-vesting-contract" -version = "1.4.0" -dependencies = [ - "cosmwasm-derive", - "cosmwasm-std", - "cw-storage-plus", - "cw2", - "nym-contracts-common", - "nym-mixnet-contract-common", - "nym-vesting-contract-common", - "schemars", - "semver 1.0.17", - "serde", - "thiserror", - "vergen", -] - -[[package]] -name = "nym-vesting-contract-common" -version = "0.6.0" -dependencies = [ - "cosmwasm-std", - "nym-contracts-common", - "nym-mixnet-contract-common", - "schemars", - "serde", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl" -version = "0.10.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-src" -version = "111.25.3+1.1.1t" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924757a6a226bf60da5f7dd0311a34d2b52283dd82ddeb103208ddc66362f80c" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" -dependencies = [ - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "pairing" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de9d09263c9966e8196fe0380c9dbbc7ea114b5cf371ba29004bc1f9c6db7f3" -dependencies = [ - "group 0.10.0", -] - -[[package]] -name = "pairing" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2e415e349a3006dd7d9482cdab1c980a845bed1377777d768cb693a44540b42" -dependencies = [ - "group 0.11.0", -] - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "paste" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" - -[[package]] -name = "pbkdf2" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" -dependencies = [ - "crypto-mac 0.11.1", -] - -[[package]] -name = "peg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a" -dependencies = [ - "peg-macros", - "peg-runtime", -] - -[[package]] -name = "peg-macros" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" -dependencies = [ - "peg-runtime", - "proc-macro2", - "quote", -] - -[[package]] -name = "peg-runtime" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088" - -[[package]] -name = "pem" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" -dependencies = [ - "base64 0.13.1", - "once_cell", - "regex", -] - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pest" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" -dependencies = [ - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "pest_meta" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" -dependencies = [ - "once_cell", - "pest", - "sha2 0.10.6", -] - -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" -dependencies = [ - "der", - "spki", - "zeroize", -] - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" -dependencies = [ - "cpufeatures", - "opaque-debug 0.3.0", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "pretty_env_logger" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" -dependencies = [ - "env_logger 0.7.1", - "log", -] - -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e" -dependencies = [ - "bytes", - "prost-derive", -] - -[[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", -] - -[[package]] -name = "prost-types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" -dependencies = [ - "bytes", - "prost", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quote" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.9", -] - -[[package]] -name = "rand_distr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9532ada3929fb8b2e9dbe28d1e06c9b2cc65813f074fcb6bd5fbefeff9d56" -dependencies = [ - "num-traits", - "rand 0.7.3", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom 0.2.9", - "redox_syscall 0.2.16", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" - -[[package]] -name = "reqwest" -version = "0.11.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" -dependencies = [ - "base64 0.21.0", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "rfc6979" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ef608575f6392792f9ecf7890c00086591d29a83910939d430753f7c050525" -dependencies = [ - "crypto-bigint", - "hmac 0.11.0", - "zeroize", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "ripemd160" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver 1.0.17", -] - -[[package]] -name = "rustix" -version = "0.37.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64 0.13.1", - "log", - "ring", - "sct 0.6.1", - "webpki 0.21.4", -] - -[[package]] -name = "rustls" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct 0.7.0", - "webpki 0.22.0", -] - -[[package]] -name = "rustls-native-certs" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" -dependencies = [ - "openssl-probe", - "rustls 0.19.1", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" -dependencies = [ - "base64 0.21.0", -] - -[[package]] -name = "rustversion" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" - -[[package]] -name = "ryu" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" - -[[package]] -name = "safer-ffi" -version = "0.1.0-rc1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7270b08a2af50909969cfc28a239cfdda9ab1b3df74749b265c46956183e5bf7" -dependencies = [ - "inventory", - "libc", - "macro_rules_attribute", - "paste", - "safer_ffi-proc_macros", - "scopeguard", - "uninit", - "unwind_safe", - "with_builtin_macros", -] - -[[package]] -name = "safer_ffi-proc_macros" -version = "0.1.0-rc1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1438fb328c52f5148ea02a317603efd802f2cd6eb9e721adfd51b94bb55d9ddb" -dependencies = [ - "macro_rules_attribute", - "prettyplease", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" -dependencies = [ - "windows-sys 0.42.0", -] - -[[package]] -name = "schemars" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" -dependencies = [ - "dyn-clone", - "indexmap", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 1.0.109", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sec1" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" -dependencies = [ - "der", - "generic-array 0.14.7", - "pkcs8", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.163" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-json-wasm" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479b4dbc401ca13ee8ce902851b834893251404c4f3c65370a49e047a6be09a5" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.163" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "serde_json" -version = "1.0.96" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "serde_spanned" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.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", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" -dependencies = [ - "digest 0.9.0", - "rand_core 0.6.4", -] - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "sphinx-packet" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc43eda802856ee82a7555c7b75ceb9e07451741c7a2f5f23d036020e01189d4" -dependencies = [ - "aes 0.7.5", - "arrayref", - "blake2", - "bs58", - "byteorder", - "chacha", - "curve25519-dalek", - "digest 0.9.0", - "hkdf 0.11.0", - "hmac 0.11.0", - "lioness", - "log", - "rand 0.7.3", - "rand_distr", - "sha2 0.9.9", - "subtle 2.4.1", -] - -[[package]] -name = "sphinx-packet" -version = "0.1.0" -source = "git+https://github.com/nymtech/sphinx.git#ca107d94360cdf8bbfbdb12fe5320ed74f80e40c" -dependencies = [ - "aes 0.7.5", - "arrayref", - "blake2", - "bs58", - "byteorder", - "chacha", - "curve25519-dalek", - "digest 0.9.0", - "hkdf 0.11.0", - "hmac 0.11.0", - "lioness", - "log", - "rand 0.7.3", - "rand_distr", - "sha2 0.9.9", - "subtle 2.4.1", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "sqlformat" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4" -dependencies = [ - "itertools", - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlformat" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" -dependencies = [ - "itertools", - "nom", - "unicode_categories", -] - -[[package]] -name = "sqlx" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b" -dependencies = [ - "sqlx-core 0.5.13", - "sqlx-macros 0.5.13", -] - -[[package]] -name = "sqlx" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" -dependencies = [ - "sqlx-core 0.6.3", - "sqlx-macros 0.6.3", -] - -[[package]] -name = "sqlx-core" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5" -dependencies = [ - "ahash", - "atoi 0.4.0", - "bitflags", - "byteorder", - "bytes", - "crc 2.1.0", - "crossbeam-queue", - "either", - "event-listener", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "hashlink 0.7.0", - "hex", - "indexmap", - "itoa", - "libc", - "libsqlite3-sys", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rustls 0.19.1", - "sha2 0.10.6", - "smallvec", - "sqlformat 0.1.8", - "sqlx-rt 0.5.13", - "stringprep", - "thiserror", - "tokio-stream", - "url", - "webpki 0.21.4", - "webpki-roots 0.21.1", -] - -[[package]] -name = "sqlx-core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" -dependencies = [ - "ahash", - "atoi 1.0.0", - "bitflags", - "byteorder", - "bytes", - "crc 3.0.1", - "crossbeam-queue", - "dotenvy", - "either", - "event-listener", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "hashlink 0.8.1", - "hex", - "indexmap", - "itoa", - "libc", - "libsqlite3-sys", - "log", - "memchr", - "once_cell", - "paste", - "percent-encoding", - "rustls 0.20.8", - "rustls-pemfile", - "sha2 0.10.6", - "smallvec", - "sqlformat 0.2.1", - "sqlx-rt 0.6.3", - "stringprep", - "thiserror", - "tokio-stream", - "url", - "webpki-roots 0.22.6", -] - -[[package]] -name = "sqlx-macros" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1" -dependencies = [ - "dotenv", - "either", - "heck", - "once_cell", - "proc-macro2", - "quote", - "sha2 0.10.6", - "sqlx-core 0.5.13", - "sqlx-rt 0.5.13", - "syn 1.0.109", - "url", -] - -[[package]] -name = "sqlx-macros" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" -dependencies = [ - "dotenvy", - "either", - "heck", - "once_cell", - "proc-macro2", - "quote", - "sha2 0.10.6", - "sqlx-core 0.6.3", - "sqlx-rt 0.6.3", - "syn 1.0.109", - "url", -] - -[[package]] -name = "sqlx-rt" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae" -dependencies = [ - "once_cell", - "tokio", - "tokio-rustls 0.22.0", -] - -[[package]] -name = "sqlx-rt" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" -dependencies = [ - "once_cell", - "tokio", - "tokio-rustls 0.23.4", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stringprep" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "subtle-encoding" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" -dependencies = [ - "zeroize", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", -] - -[[package]] -name = "tendermint" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca881fa4dedd2b46334f13be7fbc8cc1549ba4be5a833fe4e73d1a1baaf7949" -dependencies = [ - "async-trait", - "bytes", - "ed25519", - "ed25519-dalek", - "flex-error", - "futures", - "k256", - "num-traits", - "once_cell", - "prost", - "prost-types", - "ripemd160", - "serde", - "serde_bytes", - "serde_json", - "serde_repr", - "sha2 0.9.9", - "signature", - "subtle 2.4.1", - "subtle-encoding", - "tendermint-proto", - "time", - "zeroize", -] - -[[package]] -name = "tendermint-config" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c56ee93f4e9b7e7daba86d171f44572e91b741084384d0ae00df7991873dfd" -dependencies = [ - "flex-error", - "serde", - "serde_json", - "tendermint", - "toml 0.5.11", - "url", -] - -[[package]] -name = "tendermint-proto" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71f925d74903f4abbdc4af0110635a307b3cb05b175fdff4a7247c14a4d0874" -dependencies = [ - "bytes", - "flex-error", - "num-derive", - "num-traits", - "prost", - "prost-types", - "serde", - "serde_bytes", - "subtle-encoding", - "time", -] - -[[package]] -name = "tendermint-rpc" -version = "0.23.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63f57ee05a1e927887191c76d1b139de9fa40c180b9f8727ee44377242a6" -dependencies = [ - "async-trait", - "bytes", - "flex-error", - "futures", - "getrandom 0.2.9", - "http", - "hyper", - "hyper-proxy", - "hyper-rustls", - "peg", - "pin-project", - "serde", - "serde_bytes", - "serde_json", - "subtle-encoding", - "tendermint", - "tendermint-config", - "tendermint-proto", - "thiserror", - "time", - "tokio", - "tracing", - "url", - "uuid", - "walkdir", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "time" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" -dependencies = [ - "itoa", - "js-sys", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" - -[[package]] -name = "time-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" -dependencies = [ - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" -dependencies = [ - "autocfg", - "bytes", - "libc", - "mio", - "num_cpus", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls 0.19.1", - "tokio", - "webpki 0.21.4", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.8", - "tokio", - "webpki 0.22.0", -] - -[[package]] -name = "tokio-stream" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e96bb520beab540ab664bd5a9cfeaa1fcd846fa68c830b42e2c8963071251d2" -dependencies = [ - "futures-util", - "log", - "pin-project", - "tokio", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "slab", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tungstenite" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093" -dependencies = [ - "base64 0.13.1", - "byteorder", - "bytes", - "http", - "httparse", - "input_buffer", - "log", - "rand 0.8.5", - "sha-1", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "uninit" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e130f2ed46ca5d8ec13c7ff95836827f92f5f5f37fd2b2bf16f33c408d98bb6" -dependencies = [ - "extension-traits", -] - -[[package]] -name = "universal-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" -dependencies = [ - "crypto-common", - "subtle 2.4.1", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "unwind_safe" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0976c77def3f1f75c4ef892a292c31c0bbe9e3d0702c63044d7c76db298171a3" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vergen" -version = "7.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447f9238a4553957277b3ee09d80babeae0811f1b3baefb093de1c0448437a37" -dependencies = [ - "anyhow", - "cfg-if", - "enum-iterator", - "getset", - "git2", - "rustc_version", - "rustversion", - "thiserror", - "time", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "walkdir" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 1.0.109", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "git+https://github.com/mmsinclair/wasm-timer?rev=b9d1a54ad514c2f230a026afe0dde341e98cd7b6#b9d1a54ad514c2f230a026afe0dde341e98cd7b6" -dependencies = [ - "futures", - "js-sys", - "parking_lot", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasm-utils" -version = "0.1.0" -dependencies = [ - "futures", - "getrandom 0.2.9", - "js-sys", - "tungstenite", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" -dependencies = [ - "webpki 0.21.4", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki 0.22.0", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "winnow" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "with_builtin_macros" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a59d55032495429b87f9d69954c6c8602e4d3f3e0a747a12dea6b0b23de685da" -dependencies = [ - "with_builtin_macros-proc_macros", -] - -[[package]] -name = "with_builtin_macros-proc_macros" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15bd7679c15e22924f53aee34d4e448c45b674feb6129689af88593e129f8f42" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x25519-dalek" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" -dependencies = [ - "curve25519-dalek", - "rand_core 0.5.1", - "zeroize", -] - -[[package]] -name = "zeroize" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] diff --git a/sdk/lib/socks5-listener/Cargo.toml b/sdk/lib/socks5-listener/Cargo.toml index 793b9bd7dc..fe2ff08134 100644 --- a/sdk/lib/socks5-listener/Cargo.toml +++ b/sdk/lib/socks5-listener/Cargo.toml @@ -18,7 +18,7 @@ codegen-units = 1 [dependencies] anyhow = { workspace = true } -futures = "0.3" +futures = { workspace = true } lazy_static = "1.4.0" nym-bin-common = { path = "../../../common/bin-common"} nym-client-core = { path = "../../../common/client-core", default-features = false } @@ -26,7 +26,7 @@ nym-config-common = { path = "../../../common/config", package = "nym-config" } nym-credential-storage = { path = "../../../common/credential-storage" } nym-crypto = { path = "../../../common/crypto" } nym-socks5-client-core = { path = "../../../common/socks5-client-core", default-features = false } -openssl = { version = "0.10", features = ["vendored"] } +openssl = { version = "^0.10.55", features = ["vendored"] } serde = { workspace = true } tokio = { workspace = true, features = ["sync", "time"] } log = "0.4.17" diff --git a/sdk/lib/socks5-listener/src/lib.rs b/sdk/lib/socks5-listener/src/lib.rs index 1a3e22ca6c..98dac2755d 100644 --- a/sdk/lib/socks5-listener/src/lib.rs +++ b/sdk/lib/socks5-listener/src/lib.rs @@ -58,7 +58,7 @@ async fn is_shutdown_handle_set() -> bool { fn set_default_env() { if !ENV_SET.swap(true, Ordering::SeqCst) { - setup_env(None); + setup_env::<&str>(None); } } diff --git a/sdk/rust/nym-sdk/Cargo.toml b/sdk/rust/nym-sdk/Cargo.toml index 7aaced99aa..6986e0ef15 100644 --- a/sdk/rust/nym-sdk/Cargo.toml +++ b/sdk/rust/nym-sdk/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +async-trait = { workspace = true } bip39 = { workspace = true } nym-client-core = { path = "../../../common/client-core", features = ["fs-surb-storage"]} nym-crypto = { path = "../../../common/crypto" } @@ -13,6 +14,7 @@ nym-gateway-requests = { path = "../../../gateway/gateway-requests" } nym-bandwidth-controller = { path = "../../../common/bandwidth-controller" } nym-credentials = { path = "../../../common/credentials" } nym-credential-storage = { path = "../../../common/credential-storage" } +nym-credential-utils = { path = "../../../common/credential-utils" } nym-network-defaults = { path = "../../../common/network-defaults" } nym-sphinx = { path = "../../../common/nymsphinx" } nym-task = { path = "../../../common/task" } @@ -20,18 +22,29 @@ nym-topology = { path = "../../../common/topology" } nym-socks5-client-core = { path = "../../../common/socks5-client-core" } nym-validator-client = { path = "../../../common/client-libs/validator-client", features = ["http-client"] } -futures = "0.3" +futures = { workspace = true } log = { workspace = true } rand = { version = "0.7.3" } tap = "1.0.1" -thiserror = "1.0.38" -url = "2.2" +thiserror = { workspace = true } +url = { workspace = true } toml = "0.5.10" [dev-dependencies] +anyhow = { workspace = true } dotenvy = { workspace = true } pretty_env_logger = "0.4.0" -reqwest = { version = "0.11", features = ["json", "socks"] } +reqwest = { workspace = true, features = ["json", "socks"] } thiserror = { workspace = true } -tokio = { version = "1", features = ["full"] } +tokio = { workspace = true, features = ["full"] } nym-bin-common = { path = "../../../common/bin-common" } + +# extra dependencies for libp2p examples +libp2p = { git = "https://github.com/ChainSafe/rust-libp2p.git", rev = "e3440d25681df380c9f0f8cfdcfd5ecc0a4f2fb6", features = [ "identify", "macros", "ping", "tokio", "tcp", "dns", "websocket", "noise", "mplex", "yamux", "gossipsub" ]} +tokio-stream = "0.1.12" +tokio-util = { version = "0.7", features = ["codec"] } +parking_lot = "0.12" +hex = "0.4" + +[features] +libp2p-vanilla = [] \ No newline at end of file diff --git a/sdk/rust/nym-sdk/examples/bandwidth.rs b/sdk/rust/nym-sdk/examples/bandwidth.rs index fbe100f037..377dd48527 100644 --- a/sdk/rust/nym-sdk/examples/bandwidth.rs +++ b/sdk/rust/nym-sdk/examples/bandwidth.rs @@ -1,42 +1,43 @@ +use futures::StreamExt; +use nym_network_defaults::setup_env; use nym_sdk::mixnet; +use nym_sdk::mixnet::MixnetMessageSender; #[tokio::main] -async fn main() { +async fn main() -> anyhow::Result<()> { nym_bin_common::logging::setup_logging(); // right now, only sandbox has coconut setup // this should be run from the `sdk/rust/nym-sdk` directory - dotenvy::from_path("../../../envs/sandbox.env").unwrap(); + setup_env(Some("../../../envs/sandbox.env")); let sandbox_network = mixnet::NymNetworkDetails::new_from_env(); + let mnemonic = String::from("my super secret mnemonic"); let mixnet_client = mixnet::MixnetClientBuilder::new_ephemeral() .network_details(sandbox_network) .enable_credentials_mode() .build() - .await - .unwrap(); + .await?; - let bandwidth_client = mixnet_client - .create_bandwidth_client(String::from("very secret mnemonic")) - .unwrap(); + let bandwidth_client = mixnet_client.create_bandwidth_client(mnemonic)?; // Get a bandwidth credential worth 1000000 unym for the mixnet_client - bandwidth_client.acquire(1000000).await.unwrap(); + bandwidth_client.acquire(1000000).await?; // Connect using paid bandwidth credential - let mut client = mixnet_client.connect_to_mixnet().await.unwrap(); + let mut client = mixnet_client.connect_to_mixnet().await?; let our_address = client.nym_address(); // Send a message throughout the mixnet to ourselves - client.send_str(*our_address, "hello there").await; + client + .send_plain_message(*our_address, "hello there") + .await?; println!("Waiting for message"); - if let Some(received) = client.wait_for_messages().await { - for r in received { - println!("Received: {}", String::from_utf8_lossy(&r.message)); - } - } + let received = client.next().await.unwrap(); + println!("Received: {}", String::from_utf8_lossy(&received.message)); client.disconnect().await; + Ok(()) } diff --git a/sdk/rust/nym-sdk/examples/builder.rs b/sdk/rust/nym-sdk/examples/builder.rs index af6beec5f8..6ab50bc92c 100644 --- a/sdk/rust/nym-sdk/examples/builder.rs +++ b/sdk/rust/nym-sdk/examples/builder.rs @@ -1,4 +1,5 @@ use nym_sdk::mixnet; +use nym_sdk::mixnet::MixnetMessageSender; #[tokio::main] async fn main() { @@ -19,7 +20,10 @@ async fn main() { println!("Our client nym address is: {our_address}"); // Send a message through the mixnet to ourselves - client.send_str(*our_address, "hello there").await; + client + .send_plain_message(*our_address, "hello there") + .await + .unwrap(); println!("Waiting for message"); if let Some(received) = client.wait_for_messages().await { diff --git a/sdk/rust/nym-sdk/examples/builder_with_storage.rs b/sdk/rust/nym-sdk/examples/builder_with_storage.rs index ca7a732e5b..900b2d323f 100644 --- a/sdk/rust/nym-sdk/examples/builder_with_storage.rs +++ b/sdk/rust/nym-sdk/examples/builder_with_storage.rs @@ -1,4 +1,5 @@ use nym_sdk::mixnet; +use nym_sdk::mixnet::MixnetMessageSender; use std::path::PathBuf; #[tokio::main] @@ -26,7 +27,10 @@ async fn main() { println!("Our client nym address is: {our_address}"); // Send a message throught the mixnet to ourselves - client.send_str(*our_address, "hello there").await; + client + .send_plain_message(*our_address, "hello there") + .await + .unwrap(); println!("Waiting for message"); if let Some(received) = client.wait_for_messages().await { diff --git a/sdk/rust/nym-sdk/examples/custom_topology_provider.rs b/sdk/rust/nym-sdk/examples/custom_topology_provider.rs index 2ddf9d8ea0..7232f5ac58 100644 --- a/sdk/rust/nym-sdk/examples/custom_topology_provider.rs +++ b/sdk/rust/nym-sdk/examples/custom_topology_provider.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use nym_sdk::mixnet; +use nym_sdk::mixnet::MixnetMessageSender; use nym_topology::provider_trait::{async_trait, TopologyProvider}; use nym_topology::{nym_topology_from_detailed, NymTopology}; use url::Url; @@ -69,7 +70,10 @@ async fn main() { println!("Our client nym address is: {our_address}"); // Send a message through the mixnet to ourselves - client.send_str(*our_address, "hello there").await; + client + .send_plain_message(*our_address, "hello there") + .await + .unwrap(); println!("Waiting for message (ctrl-c to exit)"); client diff --git a/sdk/rust/nym-sdk/examples/libp2p_chat/README.md b/sdk/rust/nym-sdk/examples/libp2p_chat/README.md new file mode 100644 index 0000000000..0287329769 --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_chat/README.md @@ -0,0 +1,64 @@ +# rust-libp2p-nym + +This repo contains an example implementation of a libp2p transport using the Nym mixnet. It relies on the ChainSafe's fork of libp2p: https://github.com/ChainSafe/rust-libp2p + +## Requirements + +- Rust 1.68.2 +- `Protoc` protobuf compiler. On Debian/Ubuntu distributed via `apt` as `protobuf-compiler` & on Arch/Manjaro via AUR as `[python-protobuf-compiler](https://aur.archlinux.org/packages/python-protobuf-compiler)`. + +## Usage + +To instantiate a libp2p swarm using the transport: + +```rust +use libp2p::core::{muxing::StreamMuxerBox, transport::Transport}; +use libp2p::swarm::{keep_alive::Behaviour, SwarmBuilder}; +use libp2p::{identity, PeerId}; +use nym_sdk::mixnet::MixnetClient; +use rust_libp2p_nym::transport::NymTransport; +use rust_libp2p_nym::test_utils::create_nym_client; +use std::error::Error; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let local_key = identity::Keypair::generate_ed25519(); + let local_peer_id = PeerId::from(local_key.public()); + info!("Local peer id: {local_peer_id:?}"); + + let nym_id = rand::random::().to_string(); + let nym_client = MixnetClient::connect_new().await.unwrap(); + let transport = NymTransport::new(nym_client, local_key.clone()).await?; + let _swarm = SwarmBuilder::with_tokio_executor( + transport + .map(|a, _| (a.0, StreamMuxerBox::new(a.1))) + .boxed(), + Behaviour::default(), + local_peer_id, + ) + .build(); + Ok(()) +} +``` + +## Chat example + +To run the libp2p chat example, run the following in one terminal: +```bash +cargo run --example libp2p_chat +# Local peer id: PeerId("12D3KooWLukBu6q2FerWPFhFFhiYaJkhn2sBmceh9UCaXe6hJf5D") +# Listening on "/nym/FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve" +``` + +In another terminal, run ping again, passing the Nym multiaddress printed previously: +```bash +cargo run --example libp2p_chat -- /nym/FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve +# Local peer id: PeerId("12D3KooWNsuRwG6DHnFJCDR8B3zdvja6xLcfnbtKCsQWJ8eppyWC") +# Dialed /nym/FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve +# Listening on "/nym/2oiRW5C9ivyF3Bo3Gpm4H9EqSKH7A6GpcrRRwVSDVUQ9.EajgCnhzimsP6KskUwKcEj8VFCmHR78s2J6FHWcZ4etR@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf" +``` + +You should see that the nodes connected and sent messages to each other: +```bash +# 2023-08-10T14:06:28.116Z INFO libp2p_chat > Got message: 'hello world' with id: 37393732353836333838333537303637303237 from peer: 12D3KooWB6k8ZGDF44N4FMRhgVBNihwk1wMYSumosxiZq9pUTbAz +``` diff --git a/sdk/rust/nym-sdk/examples/libp2p_chat/main.rs b/sdk/rust/nym-sdk/examples/libp2p_chat/main.rs new file mode 100644 index 0000000000..04e1ecca57 --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_chat/main.rs @@ -0,0 +1,171 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! A basic chat application with logs demonstrating libp2p and the gossipsub protocol +//! combined with mDNS for the discovery of peers to gossip with. +//! +//! Using two terminal windows, start two instances, typing the following in each: +//! +//! ```sh +//! cargo run +//! ``` +//! +//! Mutual mDNS discovery may take a few seconds. When each peer does discover the other +//! it will print a message like: +//! +//! ```sh +//! mDNS discovered a new peer: {peerId} +//! ``` +//! +//! Type a message and hit return: the message is sent and printed in the other terminal. +//! Close with Ctrl-c. +//! +//! You can open more terminal windows and add more peers using the same line above. +//! +//! Once an additional peer is mDNS discovered it can participate in the conversation +//! and all peers will receive messages sent from it. +//! +//! If a participant exits (Control-C or otherwise) the other peers will receive an mDNS expired +//! event and remove the expired peer from the list of known peers. + +use crate::rust_libp2p_nym::transport::NymTransport; +use futures::{prelude::*, select}; +use libp2p::Multiaddr; +use libp2p::{ + core::muxing::StreamMuxerBox, + gossipsub, identity, + swarm::NetworkBehaviour, + swarm::{SwarmBuilder, SwarmEvent}, + PeerId, Transport, +}; +use log::{error, info, LevelFilter}; +use nym_sdk::mixnet::MixnetClient; +use std::collections::hash_map::DefaultHasher; +use std::error::Error; +use std::hash::{Hash, Hasher}; +use std::time::Duration; +use tokio::io; +use tokio_util::codec; + +#[path = "../libp2p_shared/lib.rs"] +mod rust_libp2p_nym; + +// We create a custom network behaviour that uses Gossipsub +#[derive(NetworkBehaviour)] +struct Behaviour { + gossipsub: gossipsub::Behaviour, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + pretty_env_logger::formatted_timed_builder() + .filter_level(LevelFilter::Warn) + .filter(Some("libp2p_chat"), LevelFilter::Info) + .init(); + + // Create a random PeerId + let id_keys = identity::Keypair::generate_ed25519(); + let local_peer_id = PeerId::from(id_keys.public()); + info!("Local peer id: {local_peer_id}"); + + // To content-address message, we can take the hash of message and use it as an ID. + let message_id_fn = |message: &gossipsub::Message| { + let mut s = DefaultHasher::new(); + message.data.hash(&mut s); + gossipsub::MessageId::from(s.finish().to_string()) + }; + + // Set a custom gossipsub configuration + let gossipsub_config = gossipsub::ConfigBuilder::default() + .heartbeat_interval(Duration::from_secs(10)) // This is set to aid debugging by not cluttering the log space + .validation_mode(gossipsub::ValidationMode::Strict) // This sets the kind of message validation. The default is Strict (enforce message signing) + .message_id_fn(message_id_fn) // content-address messages. No two messages of the same content will be propagated. + .build() + .expect("Valid config"); + + // build a gossipsub network behaviour + let mut gossipsub = gossipsub::Behaviour::new( + gossipsub::MessageAuthenticity::Signed(id_keys), + gossipsub_config, + ) + .expect("Correct configuration"); + // Create a Gossipsub topic + let topic = gossipsub::IdentTopic::new("test-net"); + // subscribes to our topic + gossipsub.subscribe(&topic)?; + + let client = MixnetClient::connect_new().await.unwrap(); + info!("client address: {}", client.nym_address()); + + let local_key = identity::Keypair::generate_ed25519(); + let local_peer_id = PeerId::from(local_key.public()); + info!("Local peer id: {local_peer_id:?}"); + + let transport = NymTransport::new(client, local_key).await?; + + let mut swarm = SwarmBuilder::with_tokio_executor( + transport + .map(|a, _| (a.0, StreamMuxerBox::new(a.1))) + .boxed(), + Behaviour { gossipsub }, + local_peer_id, + ) + .build(); + + if let Some(addr) = std::env::args().nth(1) { + let remote: Multiaddr = addr.parse()?; + swarm.dial(remote)?; + info!("Dialed {addr}") + } + + // Read full lines from stdin + let mut stdin = codec::FramedRead::new(io::stdin(), codec::LinesCodec::new()).fuse(); + + info!("Enter messages via STDIN and they will be sent to connected peers using Gossipsub"); + + // Kick it off + loop { + select! { + line = stdin.select_next_some() => { + if let Err(e) = swarm + .behaviour_mut().gossipsub + .publish(topic.clone(), line.expect("Stdin not to close").as_bytes()) { + error!("Publish error: {e:?}"); + } + }, + event = swarm.select_next_some() => { + match event { + SwarmEvent::Behaviour(BehaviourEvent::Gossipsub(gossipsub::Event::Message { + propagation_source: peer_id, + message_id: id, + message, + })) => info!( + "Got message: '{}' with id: {id} from peer: {peer_id}", + String::from_utf8_lossy(&message.data), + ), + SwarmEvent::NewListenAddr { address, .. } => { + info!("Local node is listening on {address}"); + } + other => {info!("other event: {:?}", other)} + } + } + } + } +} diff --git a/sdk/rust/nym-sdk/examples/libp2p_ping/README.md b/sdk/rust/nym-sdk/examples/libp2p_ping/README.md new file mode 100644 index 0000000000..2218793e89 --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_ping/README.md @@ -0,0 +1,79 @@ +# rust-libp2p-nym + +This repo contains an example implementation of a libp2p transport using the Nym mixnet. It relies on the ChainSafe's fork of libp2p: https://github.com/ChainSafe/rust-libp2p + +## Requirements + +- Rust 1.68.2 +- `Protoc` protobuf compiler. On Debian/Ubuntu distributed via `apt` as `protobuf-compiler` & on Arch/Manjaro via AUR as `[python-protobuf-compiler](https://aur.archlinux.org/packages/python-protobuf-compiler)`. + +## Usage + +To instantiate a libp2p swarm using the transport: + +```rust +use libp2p::core::{muxing::StreamMuxerBox, transport::Transport}; +use libp2p::swarm::{keep_alive::Behaviour, SwarmBuilder}; +use libp2p::{identity, PeerId}; +use nym_sdk::mixnet::MixnetClient; +use rust_libp2p_nym::transport::NymTransport; +use rust_libp2p_nym::test_utils::create_nym_client; +use std::error::Error; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let local_key = identity::Keypair::generate_ed25519(); + let local_peer_id = PeerId::from(local_key.public()); + info!("Local peer id: {local_peer_id:?}"); + + let nym_id = rand::random::().to_string(); + let nym_client = MixnetClient::connect_new().await.unwrap(); + let transport = NymTransport::new(nym_client, local_key.clone()).await?; + let _swarm = SwarmBuilder::with_tokio_executor( + transport + .map(|a, _| (a.0, StreamMuxerBox::new(a.1))) + .boxed(), + Behaviour::default(), + local_peer_id, + ) + .build(); + Ok(()) +} +``` + +## Ping example + +To run the libp2p ping example, run the following in one terminal: +```bash +cargo run --example libp2p_ping +# Local peer id: PeerId("12D3KooWLukBu6q2FerWPFhFFhiYaJkhn2sBmceh9UCaXe6hJf5D") +# Listening on "/nym/FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve" +``` + +In another terminal, run ping again, passing the Nym multiaddress printed previously: +```bash +cargo run --example libp2p_ping -- /nym/FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve +# Local peer id: PeerId("12D3KooWNsuRwG6DHnFJCDR8B3zdvja6xLcfnbtKCsQWJ8eppyWC") +# Dialed /nym/FhtkzizQg2JbZ19kGkRKXdjV2QnFbT5ww88ZAKaD4nkF.7Remi4UVYzn1yL3qYtEcQBGh6tzTYxMdYB4uqyHVc5Z4@62F81C9GrHDRja9WCqozemRFSzFPMecY85MbGwn6efve +# Listening on "/nym/2oiRW5C9ivyF3Bo3Gpm4H9EqSKH7A6GpcrRRwVSDVUQ9.EajgCnhzimsP6KskUwKcEj8VFCmHR78s2J6FHWcZ4etR@Fo4f4SQLdoyoGkFae5TpVhRVoXCF8UiypLVGtGjujVPf" +``` + +You should see that the nodes connected and pinged each other: +```bash +# Mar 30 22:56:36.400 INFO ping: BehaviourEvent: Event { peer: PeerId("12D3KooWGf2oYd6U2nrLzfDrN9zxsjSQjPsMh2oDJPUQ9hiHMNtf"), result: Ok(Ping { rtt: 1.06836675s }) } +``` +```bash +# Mar 30 22:56:35.595 INFO ping: BehaviourEvent: Event { peer: PeerId("12D3KooWMd5ak31DXuZq7x1JuFSR6toA5RDQrPaHrfXEhy7vqqpC"), result: Ok(Pong) } +``` + +In order to run the ping example with vanilla libp2p, which uses tcp, pass the +`--features libp2p-vanilla` flag to the example and follow the instructions on the +rust-libp2p project as usual. + +```bash +RUST_LOG=ping=debug cargo run --example ping --features libp2p-vanilla +``` + +```bash +RUST_LOG=ping=debug cargo run --example ping --features libp2p-vanilla -- "/ip4/127.0.0.1/tcp/$PORT" +``` diff --git a/sdk/rust/nym-sdk/examples/libp2p_ping/main.rs b/sdk/rust/nym-sdk/examples/libp2p_ping/main.rs new file mode 100644 index 0000000000..24ea089c10 --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_ping/main.rs @@ -0,0 +1,142 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Ping example +//! +//! See ../src/tutorial.rs for a step-by-step guide building the example below. +//! +//! In the first terminal window, run: +//! +//! ```sh +//! cargo run --example ping --features=full +//! ``` +//! +//! It will print the PeerId and the listening addresses, e.g. `Listening on +//! "/ip4/0.0.0.0/tcp/24915"` +//! +//! In the second terminal window, start a new instance of the example with: +//! +//! ```sh +//! cargo run --example ping --features=full -- /ip4/127.0.0.1/tcp/24915 +//! ``` +//! +//! The two nodes establish a connection, negotiate the ping protocol +//! and begin pinging each other. + +use libp2p::futures::StreamExt; +use libp2p::ping::Success; +use libp2p::swarm::{keep_alive, NetworkBehaviour, SwarmEvent}; +use libp2p::{identity, ping, Multiaddr, PeerId}; +use log::{debug, info, LevelFilter}; +use nym_sdk::mixnet::MixnetClient; +use std::error::Error; +use std::time::Duration; + +#[path = "../libp2p_shared/lib.rs"] +mod rust_libp2p_nym; + +#[tokio::main] +async fn main() -> Result<(), Box> { + pretty_env_logger::formatted_timed_builder() + .filter_level(LevelFilter::Warn) + .filter(Some("libp2p_ping"), LevelFilter::Debug) + .init(); + + let local_key = identity::Keypair::generate_ed25519(); + let local_peer_id = PeerId::from(local_key.public()); + info!("Local peer id: {local_peer_id:?}"); + + #[cfg(not(feature = "libp2p-vanilla"))] + let mut swarm = { + debug!("Running `ping` example using NymTransport"); + use libp2p::core::{muxing::StreamMuxerBox, transport::Transport}; + use libp2p::swarm::SwarmBuilder; + use rust_libp2p_nym::transport::NymTransport; + + let client = MixnetClient::connect_new().await.unwrap(); + + let transport = NymTransport::new(client, local_key.clone()).await?; + SwarmBuilder::with_tokio_executor( + transport + .map(|a, _| (a.0, StreamMuxerBox::new(a.1))) + .boxed(), + Behaviour::default(), + local_peer_id, + ) + .build() + }; + + #[cfg(feature = "libp2p-vanilla")] + let mut swarm = { + debug!("Running `ping` example using the vanilla libp2p tokio_development_transport"); + let transport = libp2p::tokio_development_transport(local_key)?; + let mut swarm = + libp2p::Swarm::with_tokio_executor(transport, Behaviour::default(), local_peer_id); + swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?; + swarm + }; + + // Dial the peer identified by the multi-address given as the second + // command-line argument, if any. + if let Some(addr) = std::env::args().nth(1) { + let remote: Multiaddr = addr.parse()?; + swarm.dial(remote)?; + info!("Dialed {addr}") + } + + let mut total_ping_rtt: Duration = Duration::from_micros(0); + let mut counter: u128 = 0; + loop { + match swarm.select_next_some().await { + SwarmEvent::NewListenAddr { address, .. } => info!("Listening on {address:?}"), + SwarmEvent::Behaviour(event) => { + // Get the round-trip duration for the pings. + // This value is already captured in the BehaviourEvent::Ping's `Success::Ping` + // field. + debug!("{event:?}"); + if let BehaviourEvent::Ping(ping_event) = event { + let result: Success = ping_event.result?; + match result { + Success::Ping { rtt } => { + counter += 1; + total_ping_rtt += rtt; + let average_ping_rtt = Duration::from_micros( + (total_ping_rtt.as_micros() / counter).try_into().unwrap(), + ); + info!("Ping RTT: {rtt:?} AVERAGE RTT: ({counter} pings): {average_ping_rtt:?}"); + } + Success::Pong => info!("Pong Event"), + } + } + } + _ => {} + } + } +} + +/// Our network behaviour. +/// +/// For illustrative purposes, this includes the [`KeepAlive`](behaviour::KeepAlive) behaviour so a continuous sequence of +/// pings can be observed. +#[derive(NetworkBehaviour, Default)] +struct Behaviour { + keep_alive: keep_alive::Behaviour, + ping: ping::Behaviour, +} diff --git a/sdk/rust/nym-sdk/examples/libp2p_shared/connection.rs b/sdk/rust/nym-sdk/examples/libp2p_shared/connection.rs new file mode 100644 index 0000000000..fd5c41ccd1 --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_shared/connection.rs @@ -0,0 +1,418 @@ +use libp2p::core::{muxing::StreamMuxerEvent, PeerId, StreamMuxer}; +use log::debug; +use nym_sphinx::addressing::clients::Recipient; +use std::{ + collections::{HashMap, HashSet}, + pin::Pin, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, + task::{Context, Poll, Waker}, +}; +use tokio::sync::{ + mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + oneshot, +}; + +use super::error::Error; +use super::message::{ + ConnectionId, Message, OutboundMessage, SubstreamId, SubstreamMessage, SubstreamMessageType, + TransportMessage, +}; +use super::substream::Substream; + +/// Connection represents the result of a connection setup process. +/// It implements `StreamMuxer` and thus has stream multiplexing built in. +#[derive(Debug)] +pub struct Connection { + pub(crate) peer_id: PeerId, + pub(crate) remote_recipient: Recipient, + pub(crate) id: ConnectionId, + + /// receive inbound messages from the `InnerConnection` + pub(crate) inbound_rx: UnboundedReceiver, + + /// substream ID -> outbound pending substream exists + /// the key is deleted when the response is received, or the request times out + pending_substreams: HashSet, + + /// substream ID -> substream's inbound_tx channel + substream_inbound_txs: HashMap>>, + + /// substream ID -> substream's close_tx channel + substream_close_txs: HashMap>, + + /// send messages to the mixnet + /// used for sending `SubstreamMessageType::OpenRequest` messages + /// also passed to each substream so they can write to the mixnet + pub(crate) mixnet_outbound_tx: UnboundedSender, + + /// inbound substream open requests; used in poll_inbound + inbound_open_tx: UnboundedSender, + inbound_open_rx: UnboundedReceiver, + + /// closed substream IDs; used in poll_close + close_tx: UnboundedSender, + close_rx: UnboundedReceiver, + + /// message nonce contains the next nonce that should be used when + /// sending a message over the connection + pub(crate) message_nonce: Arc, + + waker: Option, +} + +impl Connection { + pub(crate) fn new( + peer_id: PeerId, + remote_recipient: Recipient, + id: ConnectionId, + inbound_rx: UnboundedReceiver, + mixnet_outbound_tx: UnboundedSender, + ) -> Self { + let (inbound_open_tx, inbound_open_rx) = unbounded_channel(); + let (close_tx, close_rx) = unbounded_channel(); + + Connection { + peer_id, + remote_recipient, + id, + inbound_rx, + pending_substreams: HashSet::new(), + substream_inbound_txs: HashMap::new(), + substream_close_txs: HashMap::new(), + mixnet_outbound_tx, + inbound_open_tx, + inbound_open_rx, + close_tx, + close_rx, + message_nonce: Arc::new(AtomicU64::new(1)), + waker: None, + } + } + + fn new_outbound_substream(&mut self) -> Result { + let substream_id = SubstreamId::generate(); + let nonce = self.message_nonce.fetch_add(1, Ordering::SeqCst); + + // send the substream open request that requests to open a substream with the given ID + self.mixnet_outbound_tx + .send(OutboundMessage { + recipient: self.remote_recipient, + message: Message::TransportMessage(TransportMessage { + nonce, + id: self.id.clone(), + message: SubstreamMessage { + substream_id: substream_id.clone(), + message_type: SubstreamMessageType::OpenRequest, + }, + }), + }) + .map_err(|e| Error::OutboundSendError(e.to_string()))?; + + // track pending outbound substreams + // TODO we should probably lock this? storing map values should be atomic + let res = self.new_substream(substream_id.clone()); + if res.is_ok() { + self.pending_substreams.insert(substream_id); + } + res + } + + // creates a new substream instance with the given ID. + fn new_substream(&mut self, id: SubstreamId) -> Result { + // check we don't already have a substream with this ID + if self.substream_inbound_txs.get(&id).is_some() { + return Err(Error::SubstreamIdExists(id)); + } + + let (inbound_tx, inbound_rx) = unbounded_channel::>(); + let (close_tx, close_rx) = oneshot::channel::<()>(); + self.substream_inbound_txs.insert(id.clone(), inbound_tx); + self.substream_close_txs.insert(id.clone(), close_tx); + + if let Some(waker) = self.waker.take() { + waker.wake(); + } + + Ok(Substream::new( + self.remote_recipient, + self.id.clone(), + id, + inbound_rx, + self.mixnet_outbound_tx.clone(), + close_rx, + self.message_nonce.clone(), + )) + } + + fn handle_close(&mut self, substream_id: SubstreamId) -> Result<(), Error> { + if self.substream_inbound_txs.remove(&substream_id).is_none() { + return Err(Error::SubstreamIdDoesNotExist(substream_id)); + } + + // notify substream that it's closed + let close_tx = self.substream_close_txs.remove(&substream_id); + close_tx.unwrap().send(()).unwrap(); + + // notify poll_close that the substream is closed + self.close_tx + .send(substream_id) + .map_err(|e| Error::InboundSendError(e.to_string())) + } +} + +impl StreamMuxer for Connection { + type Substream = Substream; + type Error = Error; + + fn poll_inbound( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + if let Poll::Ready(Some(substream)) = self.inbound_open_rx.poll_recv(cx) { + return Poll::Ready(Ok(substream)); + } + + Poll::Pending + } + + fn poll_outbound( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(self.new_outbound_substream()) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if let Poll::Ready(Some(_)) = self.close_rx.poll_recv(cx) { + return Poll::Ready(Ok(())); + } + + Poll::Pending + } + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + while let Poll::Ready(Some(msg)) = self.inbound_rx.poll_recv(cx) { + match msg.message_type { + SubstreamMessageType::OpenRequest => { + // create a new substream with the given ID + let substream = self.new_substream(msg.substream_id.clone())?; + let nonce = self.message_nonce.fetch_add(1, Ordering::SeqCst); + + // send the response to the remote peer + self.mixnet_outbound_tx + .send(OutboundMessage { + recipient: self.remote_recipient, + message: Message::TransportMessage(TransportMessage { + nonce, + id: self.id.clone(), + message: SubstreamMessage { + substream_id: msg.substream_id.clone(), + message_type: SubstreamMessageType::OpenResponse, + }, + }), + }) + .map_err(|e| Error::OutboundSendError(e.to_string()))?; + debug!("wrote OpenResponse for substream: {:?}", &msg.substream_id); + + // send the substream to our own channel to be returned in poll_inbound + self.inbound_open_tx + .send(substream) + .map_err(|e| Error::InboundSendError(e.to_string()))?; + + debug!("new inbound substream: {:?}", &msg.substream_id); + } + SubstreamMessageType::OpenResponse => { + if !self.pending_substreams.remove(&msg.substream_id) { + debug!( + "SubstreamMessageType::OpenResponse no substream pending for ID: {:?}", + &msg.substream_id + ); + } + } + SubstreamMessageType::Close => { + self.handle_close(msg.substream_id)?; + } + SubstreamMessageType::Data(data) => { + debug!("SubstreamMessageType::Data: {:?}", &data); + let inbound_tx = self + .substream_inbound_txs + .get_mut(&msg.substream_id) + .expect("must have a substream channel for substream"); + + // NOTE: this ignores channel closed errors, which is fine because the substream + // might have been closed/dropped + inbound_tx.send(data).ok(); + } + } + } + + self.waker = Some(cx.waker().clone()); + Poll::Pending + } +} + +/// PendingConnection represents a connection that's been initiated, but not completed. +pub(crate) struct PendingConnection { + pub(crate) remote_recipient: Recipient, + pub(crate) connection_tx: oneshot::Sender, +} + +impl PendingConnection { + pub(crate) fn new( + remote_recipient: Recipient, + connection_tx: oneshot::Sender, + ) -> Self { + PendingConnection { + remote_recipient, + connection_tx, + } + } +} + +#[cfg(test)] +mod test { + use super::super::message::InboundMessage; + use super::super::mixnet::initialize_mixnet; + use super::*; + use futures::future::poll_fn; + use futures::{AsyncReadExt, AsyncWriteExt, FutureExt}; + use nym_sdk::mixnet::MixnetClient; + + async fn inbound_receive_and_send( + connection_id: ConnectionId, + mixnet_inbound_rx: &mut UnboundedReceiver, + inbound_tx: &UnboundedSender, + expected_nonce: u64, + ) { + let recv_msg = mixnet_inbound_rx.recv().await.unwrap(); + match recv_msg.0 { + Message::TransportMessage(TransportMessage { + nonce, + id, + message: msg, + }) => { + assert_eq!(nonce, expected_nonce); + assert_eq!(id, connection_id); + inbound_tx.send(msg).unwrap(); + } + _ => panic!("unexpected message"), + } + } + + #[tokio::test] + async fn test_connection_stream_muxer() { + let client = MixnetClient::connect_new().await.unwrap(); + let (sender_address, mut sender_mixnet_inbound_rx, sender_outbound_tx) = + initialize_mixnet(client, None).await.unwrap(); + + let client2 = MixnetClient::connect_new().await.unwrap(); + + let (recipient_address, mut recipient_mixnet_inbound_rx, recipient_outbound_tx) = + initialize_mixnet(client2, None).await.unwrap(); + + let connection_id = ConnectionId::generate(); + + let recipient_peer_id = PeerId::random(); + let sender_peer_id = PeerId::random(); + + // create the connections + let (sender_inbound_tx, sender_inbound_rx) = unbounded_channel::(); + let mut sender_connection = Connection::new( + recipient_peer_id, + recipient_address, + connection_id.clone(), + sender_inbound_rx, + sender_outbound_tx, + ); + let (recipient_inbound_tx, recipient_inbound_rx) = unbounded_channel::(); + let mut recipient_connection = Connection::new( + sender_peer_id, + sender_address, + connection_id.clone(), + recipient_inbound_rx, + recipient_outbound_tx, + ); + + // send the substream OpenRequest to the mixnet + let mut sender_substream = sender_connection.new_outbound_substream().unwrap(); + assert!(sender_connection + .pending_substreams + .contains(&sender_substream.substream_id)); + assert_eq!(sender_connection.message_nonce.load(Ordering::SeqCst), 2); + + // poll the recipient inbound stream; should receive the OpenRequest and create the substream + inbound_receive_and_send( + connection_id.clone(), + &mut recipient_mixnet_inbound_rx, + &recipient_inbound_tx, + 1, + ) + .await; + poll_fn(|cx| Pin::new(&mut recipient_connection).as_mut().poll(cx)).now_or_never(); + assert_eq!(recipient_connection.message_nonce.load(Ordering::SeqCst), 2); + + // poll recipient's poll_inbound to receive the substream + let maybe_recipient_substream = poll_fn(|cx| { + Pin::new(&mut recipient_connection) + .as_mut() + .poll_inbound(cx) + }) + .now_or_never(); + let mut recipient_substream = maybe_recipient_substream.unwrap().unwrap(); + + // poll sender's connection to receive the OpenResponse and send it to the Connection inbound channel + inbound_receive_and_send( + connection_id.clone(), + &mut sender_mixnet_inbound_rx, + &sender_inbound_tx, + 1, + ) + .await; + + // poll sender's poll_outbound to get the substream + poll_fn(|cx| Pin::new(&mut sender_connection).as_mut().poll(cx)).now_or_never(); + assert!(sender_connection.pending_substreams.is_empty()); + + // finally, write message to the substream + let data = b"hello world"; + sender_substream.write_all(data).await.unwrap(); + assert_eq!(sender_connection.message_nonce.load(Ordering::SeqCst), 3); + + // receive message from the mixnet, push to the recipient Connection inbound channel + inbound_receive_and_send( + connection_id.clone(), + &mut recipient_mixnet_inbound_rx, + &recipient_inbound_tx, + 2, + ) + .await; + + // poll the sender's connection to send the msg from the connection inbound channel to the substream's + poll_fn(|cx| Pin::new(&mut sender_connection).as_mut().poll(cx)).now_or_never(); + + // poll the recipient's connection to read the msg from the mixnet and mux it into the substream + poll_fn(|cx| Pin::new(&mut recipient_connection).as_mut().poll(cx)).now_or_never(); + + let mut buf = [0u8; 11]; + let n = recipient_substream.read(&mut buf).await.unwrap(); + assert_eq!(n, 11); + assert_eq!(buf, data[..]); + + // test closing the stream; assert the stream is closed on both sides + sender_substream.close().await.unwrap(); + assert_eq!(sender_connection.message_nonce.load(Ordering::SeqCst), 4); + inbound_receive_and_send( + connection_id.clone(), + &mut recipient_mixnet_inbound_rx, + &recipient_inbound_tx, + 3, + ) + .await; + } +} diff --git a/sdk/rust/nym-sdk/examples/libp2p_shared/error.rs b/sdk/rust/nym-sdk/examples/libp2p_shared/error.rs new file mode 100644 index 0000000000..bf1ebc8175 --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_shared/error.rs @@ -0,0 +1,64 @@ +use libp2p::core::multiaddr; +use nym_sphinx::addressing::clients::RecipientFormattingError; + +use super::message::SubstreamId; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("unimplemented")] + Unimplemented, + #[error("failed to format multiaddress from nym address")] + FailedToFormatMultiaddr(#[from] multiaddr::Error), + #[error("unexpected protocol in multiaddress")] + InvalidProtocolForMultiaddr, + #[error("failed to decode message")] + InvalidMessageBytes, + #[error("no connection found for ConnectionResponse")] + NoConnectionForResponse, + #[error("received ConnectionResponse but connection was already established")] + ConnectionAlreadyEstablished, + #[error("received None recipient in ConnectionRequest")] + NoneRecipientInConnectionRequest, + #[error("cannot handle connection request; already have connection with given ID")] + ConnectionIDExists, + #[error("no connection found for TransportMessage")] + NoConnectionForTransportMessage, + #[error("failed to decode ConnectionMessage; too short")] + ConnectionMessageBytesTooShort, + #[error("failed to decode ConnectionMessage; no recipient")] + ConnectionMessageBytesNoRecipient, + #[error("failed to decode ConnectionMessage; no peer ID")] + ConnectionMessageBytesNoPeerId, + #[error("invalid peer ID bytes")] + InvalidPeerIdBytes, + #[error("invalid recipient bytes")] + InvalidRecipientBytes(#[from] RecipientFormattingError), + #[error("invalid recipient prefix byte")] + InvalidRecipientPrefixByte, + #[error("failed to decode TransportMessage; too short")] + TransportMessageBytesTooShort, + #[error("failed to decode TransportMessage; invalid nonce")] + InvalidNonce, + #[error("invalid substream ID")] + InvalidSubstreamMessageBytes, + #[error("invalid substream message type byte")] + InvalidSubstreamMessageType, + #[error("substrean with given ID already exists")] + SubstreamIdExists(SubstreamId), + #[error("no substream found for given ID")] + SubstreamIdDoesNotExist(SubstreamId), + #[error("recv error: channel closed")] + OneshotRecvError(#[from] tokio::sync::oneshot::error::RecvError), + #[error("recv error: channel closed")] + RecvError, + #[error("outbound send error")] + OutboundSendError(String), + #[error("inbound send error")] + InboundSendError(String), + #[error("failed to send new connection; receiver dropped")] + ConnectionSendError, + #[error("failed to send initial TransportEvent::NewAddress")] + SendErrorTransportEvent, + #[error("dial timed out")] + DialTimeout(#[from] tokio::time::error::Elapsed), +} diff --git a/sdk/rust/nym-sdk/examples/libp2p_shared/lib.rs b/sdk/rust/nym-sdk/examples/libp2p_shared/lib.rs new file mode 100644 index 0000000000..ba33f1d91f --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_shared/lib.rs @@ -0,0 +1,10 @@ +pub(crate) mod connection; +pub mod error; +pub(crate) mod message; +pub(crate) mod mixnet; +pub(crate) mod queue; +pub mod substream; +pub mod transport; + +/// The deafult timeout secs for [`transport::Upgrade`] future. +const DEFAULT_HANDSHAKE_TIMEOUT_SECS: u64 = 5; diff --git a/sdk/rust/nym-sdk/examples/libp2p_shared/message.rs b/sdk/rust/nym-sdk/examples/libp2p_shared/message.rs new file mode 100644 index 0000000000..9dc4c7085b --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_shared/message.rs @@ -0,0 +1,335 @@ +use libp2p::core::PeerId; +use nym_sphinx::addressing::clients::Recipient; +use rand::rngs::OsRng; +use rand::RngCore; +use std::fmt::{Debug, Formatter}; + +use super::error::Error; + +const RECIPIENT_LENGTH: usize = Recipient::LEN; +const CONNECTION_ID_LENGTH: usize = 32; +const SUBSTREAM_ID_LENGTH: usize = 32; + +const NONCE_BYTES_LEN: usize = 8; // length of u64 +const MIN_CONNECTION_MESSAGE_LEN: usize = CONNECTION_ID_LENGTH + NONCE_BYTES_LEN; + +/// ConnectionId is a unique, randomly-generated per-connection ID that's used to +/// identify which connection a message belongs to. +#[derive(Clone, Default, Eq, Hash, PartialEq)] +pub(crate) struct ConnectionId([u8; 32]); + +impl ConnectionId { + pub(crate) fn generate() -> Self { + let mut bytes = [0u8; 32]; + OsRng.fill_bytes(&mut bytes); + ConnectionId(bytes) + } + + fn from_bytes(bytes: &[u8]) -> Self { + let mut id = [0u8; 32]; + id[..].copy_from_slice(&bytes[0..CONNECTION_ID_LENGTH]); + ConnectionId(id) + } +} + +impl Debug for ConnectionId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(self.0)) + } +} + +/// SubstreamId is a unique, randomly-generated per-substream ID that's used to +/// identify which substream a message belongs to. +#[derive(Clone, Default, Eq, Hash, PartialEq)] +pub struct SubstreamId(pub(crate) [u8; 32]); + +impl SubstreamId { + pub(crate) fn generate() -> Self { + let mut bytes = [0u8; 32]; + OsRng.fill_bytes(&mut bytes); + SubstreamId(bytes) + } + + fn from_bytes(bytes: &[u8]) -> Self { + let mut id = [0u8; 32]; + id[..].copy_from_slice(&bytes[0..SUBSTREAM_ID_LENGTH]); + SubstreamId(id) + } +} + +impl Debug for SubstreamId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(self.0)) + } +} + +#[derive(Debug)] +#[allow(clippy::enum_variant_names)] +pub(crate) enum Message { + ConnectionRequest(ConnectionMessage), + ConnectionResponse(ConnectionMessage), + TransportMessage(TransportMessage), +} + +/// ConnectionMessage is exchanged to open a new connection. +#[derive(Debug)] +pub(crate) struct ConnectionMessage { + pub(crate) peer_id: PeerId, + pub(crate) id: ConnectionId, + /// recipient is the sender's Nym address. + /// only required if this is a ConnectionRequest. + pub(crate) recipient: Option, +} + +/// TransportMessage is sent over a connection after establishment. +#[derive(Debug, Clone)] +pub(crate) struct TransportMessage { + /// increments by 1 for every TransportMessage sent over a connection. + /// required for ordering, since Nym does not guarantee ordering. + /// ConnectionMessages do not need nonces, as we know that they will + /// be the first messages sent over a connection. + /// the first TransportMessage sent over a connection will have nonce 1. + pub(crate) nonce: u64, + pub(crate) message: SubstreamMessage, + pub(crate) id: ConnectionId, +} + +impl Message { + fn try_from_bytes(bytes: Vec) -> Result { + if bytes.len() < 2 { + return Err(Error::InvalidMessageBytes); + } + + Ok(match bytes[0] { + 0 => Message::ConnectionRequest(ConnectionMessage::try_from_bytes(&bytes[1..])?), + 1 => Message::ConnectionResponse(ConnectionMessage::try_from_bytes(&bytes[1..])?), + 2 => Message::TransportMessage(TransportMessage::try_from_bytes(&bytes[1..])?), + _ => return Err(Error::InvalidMessageBytes), + }) + } +} + +impl ConnectionMessage { + fn to_bytes(&self) -> Vec { + let mut bytes = self.id.0.to_vec(); + match self.recipient { + Some(recipient) => { + bytes.push(1u8); + bytes.append(&mut recipient.to_bytes().to_vec()); + } + None => bytes.push(0u8), + } + bytes.append(&mut self.peer_id.to_bytes()); + bytes + } + + fn try_from_bytes(bytes: &[u8]) -> Result { + if bytes.len() < CONNECTION_ID_LENGTH + 1 { + return Err(Error::ConnectionMessageBytesTooShort); + } + + let id = ConnectionId::from_bytes(&bytes[0..CONNECTION_ID_LENGTH]); + let recipient = match bytes[CONNECTION_ID_LENGTH] { + 0u8 => None, + 1u8 => { + if bytes.len() < CONNECTION_ID_LENGTH + 1 + RECIPIENT_LENGTH { + return Err(Error::ConnectionMessageBytesNoRecipient); + } + + let mut recipient_bytes = [0u8; RECIPIENT_LENGTH]; + recipient_bytes[..].copy_from_slice( + &bytes[CONNECTION_ID_LENGTH + 1..CONNECTION_ID_LENGTH + 1 + RECIPIENT_LENGTH], + ); + Some( + Recipient::try_from_bytes(recipient_bytes) + .map_err(Error::InvalidRecipientBytes)?, + ) + } + _ => { + return Err(Error::InvalidRecipientPrefixByte); + } + }; + let peer_id = match recipient { + Some(_) => { + if bytes.len() < CONNECTION_ID_LENGTH + RECIPIENT_LENGTH + 2 { + return Err(Error::ConnectionMessageBytesNoPeerId); + } + PeerId::from_bytes(&bytes[CONNECTION_ID_LENGTH + 1 + RECIPIENT_LENGTH..]) + .map_err(|_| Error::InvalidPeerIdBytes)? + } + None => { + if bytes.len() < CONNECTION_ID_LENGTH + 2 { + return Err(Error::ConnectionMessageBytesNoPeerId); + } + PeerId::from_bytes(&bytes[CONNECTION_ID_LENGTH + 1..]) + .map_err(|_| Error::InvalidPeerIdBytes)? + } + }; + Ok(ConnectionMessage { + peer_id, + recipient, + id, + }) + } +} + +impl TransportMessage { + fn to_bytes(&self) -> Vec { + let mut bytes = self.nonce.to_be_bytes().to_vec(); + bytes.extend_from_slice(self.id.0.as_ref()); + bytes.extend_from_slice(&self.message.to_bytes()); + bytes + } + + fn try_from_bytes(bytes: &[u8]) -> Result { + if bytes.len() < MIN_CONNECTION_MESSAGE_LEN + 1 { + return Err(Error::TransportMessageBytesTooShort); + } + + let nonce = u64::from_be_bytes( + bytes[0..NONCE_BYTES_LEN] + .to_vec() + .try_into() + .map_err(|_| Error::InvalidNonce)?, + ); + let id = ConnectionId::from_bytes(&bytes[NONCE_BYTES_LEN..MIN_CONNECTION_MESSAGE_LEN]); + let message = SubstreamMessage::try_from_bytes(&bytes[MIN_CONNECTION_MESSAGE_LEN..])?; + Ok(TransportMessage { nonce, message, id }) + } +} + +impl Ord for TransportMessage { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.nonce.cmp(&other.nonce) + } +} + +impl std::cmp::PartialOrd for TransportMessage { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl std::cmp::Eq for TransportMessage {} + +impl std::cmp::PartialEq for TransportMessage { + fn eq(&self, other: &Self) -> bool { + self.nonce == other.nonce + } +} + +#[derive(Debug, Clone)] +pub(crate) enum SubstreamMessageType { + OpenRequest, + OpenResponse, + Close, + Data(Vec), +} + +impl SubstreamMessageType { + fn to_u8(&self) -> u8 { + match self { + SubstreamMessageType::OpenRequest => 0, + SubstreamMessageType::OpenResponse => 1, + SubstreamMessageType::Close => 2, + SubstreamMessageType::Data(_) => 3, + } + } +} + +/// SubstreamMessage is a message sent over a substream. +#[derive(Debug, Clone)] +pub(crate) struct SubstreamMessage { + pub(crate) substream_id: SubstreamId, + pub(crate) message_type: SubstreamMessageType, +} + +impl SubstreamMessage { + pub(crate) fn new_with_data(substream_id: SubstreamId, message: Vec) -> Self { + SubstreamMessage { + substream_id, + message_type: SubstreamMessageType::Data(message), + } + } + + pub(crate) fn new_close(substream_id: SubstreamId) -> Self { + SubstreamMessage { + substream_id, + message_type: SubstreamMessageType::Close, + } + } + + pub(crate) fn to_bytes(&self) -> Vec { + let mut bytes = self.substream_id.0.clone().to_vec(); + bytes.push(self.message_type.to_u8()); + if let SubstreamMessageType::Data(message) = &self.message_type { + bytes.extend_from_slice(message); + } + bytes + } + + pub(crate) fn try_from_bytes(bytes: &[u8]) -> Result { + if bytes.len() < SUBSTREAM_ID_LENGTH + 1 { + return Err(Error::InvalidSubstreamMessageBytes); + } + + let substream_id = SubstreamId::from_bytes(&bytes[0..SUBSTREAM_ID_LENGTH]); + let message_type = match bytes[SUBSTREAM_ID_LENGTH] { + 0 => SubstreamMessageType::OpenRequest, + 1 => SubstreamMessageType::OpenResponse, + 2 => SubstreamMessageType::Close, + 3 => { + if bytes.len() < SUBSTREAM_ID_LENGTH + 2 { + return Err(Error::InvalidSubstreamMessageBytes); + } + SubstreamMessageType::Data(bytes[SUBSTREAM_ID_LENGTH + 1..].to_vec()) + } + _ => return Err(Error::InvalidSubstreamMessageType), + }; + + Ok(SubstreamMessage { + substream_id, + message_type, + }) + } +} + +impl Message { + pub(crate) fn to_bytes(&self) -> Vec { + match self { + Message::ConnectionRequest(msg) => { + let mut bytes = 0_u8.to_be_bytes().to_vec(); + bytes.append(&mut msg.to_bytes()); + bytes + } + Message::ConnectionResponse(msg) => { + let mut bytes = 1_u8.to_be_bytes().to_vec(); + bytes.append(&mut msg.to_bytes()); + bytes + } + Message::TransportMessage(msg) => { + let mut bytes = 2_u8.to_be_bytes().to_vec(); + bytes.append(&mut msg.to_bytes()); + bytes + } + } + } +} + +/// InboundMessage represents an inbound mixnet message. +pub(crate) struct InboundMessage(pub(crate) Message); + +/// OutboundMessage represents an outbound mixnet message. +#[derive(Debug)] +pub(crate) struct OutboundMessage { + pub(crate) message: Message, + pub(crate) recipient: Recipient, +} + +pub(crate) fn parse_message_data(data: &[u8]) -> Result { + if data.len() < 2 { + return Err(Error::InvalidMessageBytes); + } + let msg = Message::try_from_bytes(data.to_vec())?; + Ok(InboundMessage(msg)) +} diff --git a/sdk/rust/nym-sdk/examples/libp2p_shared/mixnet.rs b/sdk/rust/nym-sdk/examples/libp2p_shared/mixnet.rs new file mode 100644 index 0000000000..f75048203b --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_shared/mixnet.rs @@ -0,0 +1,164 @@ +use futures::{pin_mut, select}; +use futures::{FutureExt, StreamExt}; +use log::debug; +use nym_sdk::mixnet::{IncludedSurbs, MixnetClient, MixnetClientSender, MixnetMessageSender}; +use nym_sphinx::addressing::clients::Recipient; +use nym_sphinx::receiver::ReconstructedMessage; +use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; + +use super::error::Error; +use super::message::*; + +/// initialize_mixnet initializes a read/write connection to a Nym websockets endpoint. +/// It starts a task that listens for inbound messages from the endpoint and writes outbound messages to the endpoint. +pub(crate) async fn initialize_mixnet( + client: MixnetClient, + notify_inbound_tx: Option>, +) -> Result< + ( + Recipient, + UnboundedReceiver, + UnboundedSender, + ), + Error, +> { + let recipient = *client.nym_address(); + + // a channel of inbound messages from the mixnet.. + // the transport reads from (listens) to the inbound_rx. + // TODO: this is probably a DOS vector; we should limit the size of the channel. + let (inbound_tx, inbound_rx) = unbounded_channel::(); + + // a channel of outbound messages to be written to the mixnet. + // the transport writes to outbound_tx. + let (outbound_tx, mut outbound_rx) = unbounded_channel::(); + + let sink = client.split_sender(); + let mut stream = client; + + tokio::task::spawn(async move { + loop { + let t1 = check_inbound(&mut stream, &inbound_tx, ¬ify_inbound_tx).fuse(); + let t2 = check_outbound(&sink, &mut outbound_rx).fuse(); + + pin_mut!(t1, t2); + + select! { + _ = t1 => {}, + _ = t2 => {}, + }; + } + }); + + Ok((recipient, inbound_rx, outbound_tx)) +} + +async fn check_inbound( + client: &mut MixnetClient, + inbound_tx: &UnboundedSender, + notify_inbound_tx: &Option>, +) -> Result<(), Error> { + if let Some(msg) = client.next().await { + if let Some(notify_tx) = notify_inbound_tx { + notify_tx + .send(()) + .map_err(|e| Error::InboundSendError(e.to_string()))?; + } + + handle_inbound(msg, inbound_tx).await?; + } + + Err(Error::Unimplemented) +} + +async fn handle_inbound( + msg: ReconstructedMessage, + inbound_tx: &UnboundedSender, +) -> Result<(), Error> { + let data = parse_message_data(&msg.message)?; + inbound_tx + .send(data) + .map_err(|e| Error::InboundSendError(e.to_string()))?; + Ok(()) +} + +async fn check_outbound( + mixnet_sender: &MixnetClientSender, + outbound_rx: &mut UnboundedReceiver, +) -> Result<(), Error> { + match outbound_rx.recv().await { + Some(message) => { + write_bytes( + mixnet_sender, + message.recipient, + &message.message.to_bytes(), + ) + .await + } + None => Err(Error::RecvError), + } +} + +async fn write_bytes( + mixnet_sender: &MixnetClientSender, + recipient: Recipient, + message: &[u8], +) -> Result<(), Error> { + if let Err(_err) = mixnet_sender + .send_message(recipient, message, IncludedSurbs::ExposeSelfAddress) + .await + { + return Err(Error::Unimplemented); + } + + debug!( + "wrote message to mixnet: recipient: {:?}", + recipient.to_string() + ); + Ok(()) +} + +#[cfg(test)] +mod test { + use super::super::message::{ + self, ConnectionId, Message, SubstreamId, SubstreamMessage, SubstreamMessageType, + TransportMessage, + }; + use super::super::mixnet::initialize_mixnet; + use nym_sdk::mixnet::MixnetClient; + + #[tokio::test] + async fn test_mixnet_poll_inbound_and_outbound() { + let client = MixnetClient::connect_new().await.unwrap(); + let (self_address, mut inbound_rx, outbound_tx) = + initialize_mixnet(client, None).await.unwrap(); + let msg_inner = "hello".as_bytes(); + let substream_id = SubstreamId::generate(); + let msg = Message::TransportMessage(TransportMessage { + nonce: 1, // arbitrary + id: ConnectionId::generate(), + message: SubstreamMessage::new_with_data(substream_id.clone(), msg_inner.to_vec()), + }); + + // send a message to ourselves through the mixnet + let out_msg = message::OutboundMessage { + message: msg, + recipient: self_address, + }; + + outbound_tx.send(out_msg).unwrap(); + + // receive the message from ourselves over the mixnet + let received_msg = inbound_rx.recv().await.unwrap(); + if let Message::TransportMessage(recv_msg) = received_msg.0 { + assert_eq!(substream_id, recv_msg.message.substream_id); + if let SubstreamMessageType::Data(data) = recv_msg.message.message_type { + assert_eq!(msg_inner, data.as_slice()); + } else { + panic!("expected SubstreamMessage::Data") + } + } else { + panic!("expected Message::TransportMessage") + } + } +} diff --git a/sdk/rust/nym-sdk/examples/libp2p_shared/queue.rs b/sdk/rust/nym-sdk/examples/libp2p_shared/queue.rs new file mode 100644 index 0000000000..2b59c82877 --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_shared/queue.rs @@ -0,0 +1,140 @@ +use log::{debug, warn}; +use std::collections::BTreeSet; + +use super::message::TransportMessage; + +/// MessageQueue is a queue of messages, ordered by nonce, that we've +/// received but are not yet able to process because we're waiting for +/// a message with the next expected nonce first. +/// This is required because Nym does not guarantee any sort of message +/// ordering, only delivery. +/// TODO: is there a DOS vector here where a malicious peer sends us +/// messages only with nonce higher than the next expected nonce? +pub(crate) struct MessageQueue { + /// nonce of the next message we expect to receive on the + /// connection. + /// any messages with a nonce greater than this are pushed into + /// the queue. + /// if we get a message with a nonce equal to this, then we + /// immediately handle it in the transport and increment the nonce. + next_expected_nonce: u64, + + /// the actual queue of messages, ordered by nonce. + /// the head of the queue's nonce is always greater + /// than the next expected nonce. + queue: BTreeSet, +} + +impl MessageQueue { + pub(crate) fn new() -> Self { + MessageQueue { + next_expected_nonce: 0, + queue: BTreeSet::new(), + } + } + + pub(crate) fn print_nonces(&self) { + let nonces = self.queue.iter().map(|msg| msg.nonce).collect::>(); + debug!("MessageQueue: {:?}", nonces); + } + + /// sets the next expected nonce to 1, indicating that we've received + /// a ConnectionRequest or ConnectionResponse. + pub(crate) fn set_connection_message_received(&mut self) { + if self.next_expected_nonce != 0 { + panic!("connection message received twice"); + } + + self.next_expected_nonce = self.next_expected_nonce.wrapping_add(1); + } + + /// tries to push a message into the queue. + /// if the message has the next expected nonce, then the message is returned, + /// and should be processed by the caller. + /// in that case, the internal queue's next expected nonce is incremented. + pub(crate) fn try_push(&mut self, msg: TransportMessage) -> Option { + if msg.nonce == self.next_expected_nonce { + self.next_expected_nonce = self.next_expected_nonce.wrapping_add(1); + Some(msg) + } else { + if msg.nonce < self.next_expected_nonce { + // this shouldn't happen normally, only if the other node + // is not following the protocol + warn!("received a message with a nonce that is too low"); + return None; + } + + if !self.queue.insert(msg) { + // this shouldn't happen normally, only if the other node + // is not following the protocol + warn!("received a message with a duplicate nonce"); + return None; + } + + None + } + } + + pub(crate) fn pop(&mut self) -> Option { + let Some(head) = self.queue.first() else { + return None; + }; + + if head.nonce == self.next_expected_nonce { + self.next_expected_nonce = self.next_expected_nonce.wrapping_add(1); + Some(self.queue.pop_first().unwrap()) + } else { + None + } + } +} + +#[cfg(test)] +mod test { + use super::super::message::{ConnectionId, SubstreamId, SubstreamMessage}; + + use super::*; + + impl TransportMessage { + fn new(nonce: u64, message: SubstreamMessage, id: ConnectionId) -> Self { + TransportMessage { nonce, message, id } + } + } + + #[test] + fn test_message_queue() { + let mut queue = MessageQueue::new(); + + let test_substream_message = + SubstreamMessage::new_with_data(SubstreamId::generate(), vec![1, 2, 3]); + let connection_id = ConnectionId::generate(); + + let msg1 = TransportMessage::new(1, test_substream_message.clone(), connection_id.clone()); + let msg2 = TransportMessage::new(2, test_substream_message.clone(), connection_id.clone()); + let msg3 = TransportMessage::new(3, test_substream_message.clone(), connection_id.clone()); + + assert_eq!(queue.try_push(msg1.clone()), None); + assert_eq!(queue.try_push(msg3.clone()), None); + assert_eq!(queue.try_push(msg2.clone()), None); + + assert_eq!(queue.pop(), None); + + // set expected nonce to 1 + queue.set_connection_message_received(); + assert_eq!(queue.pop(), Some(msg1)); + + let msg4 = TransportMessage::new(4, test_substream_message.clone(), connection_id.clone()); + assert_eq!(queue.try_push(msg4.clone()), None); + + assert_eq!(queue.pop(), Some(msg2)); + assert_eq!(queue.pop(), Some(msg3)); + assert_eq!(queue.pop(), Some(msg4)); + assert_eq!(queue.pop(), None); + assert_eq!(queue.next_expected_nonce, 5); + + // should just return the message and increment nonce when message nonce = next expected nonce + let msg5 = TransportMessage::new(5, test_substream_message, connection_id); + assert_eq!(queue.try_push(msg5.clone()), Some(msg5)); + assert_eq!(queue.next_expected_nonce, 6); + } +} diff --git a/sdk/rust/nym-sdk/examples/libp2p_shared/substream.rs b/sdk/rust/nym-sdk/examples/libp2p_shared/substream.rs new file mode 100644 index 0000000000..f7620a835c --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_shared/substream.rs @@ -0,0 +1,422 @@ +use super::message::{ + ConnectionId, Message, OutboundMessage, SubstreamId, SubstreamMessage, TransportMessage, +}; +use futures::{ + io::{Error as IoError, ErrorKind}, + AsyncRead, AsyncWrite, +}; +use log::debug; +use nym_sphinx::addressing::clients::Recipient; +use parking_lot::Mutex; +use std::{ + pin::Pin, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, + task::{Context, Poll}, +}; +use tokio::sync::{ + mpsc::{UnboundedReceiver, UnboundedSender}, + oneshot::Receiver, +}; + +#[derive(Debug)] +pub struct Substream { + remote_recipient: Recipient, + connection_id: ConnectionId, + pub(crate) substream_id: SubstreamId, + + /// inbound messages; inbound_tx is in the corresponding Connection + pub(crate) inbound_rx: UnboundedReceiver>, + + /// outbound messages; go directly to the mixnet + outbound_tx: UnboundedSender, + + /// used to signal when the substream is closed + close_rx: Receiver<()>, + closed: Mutex, + + // buffer of data that's been written to the stream, + // but not yet read by the application. + unread_data: Mutex>, + + message_nonce: Arc, +} + +impl Substream { + pub(crate) fn new( + remote_recipient: Recipient, + connection_id: ConnectionId, + substream_id: SubstreamId, + inbound_rx: UnboundedReceiver>, + outbound_tx: UnboundedSender, + close_rx: Receiver<()>, + message_nonce: Arc, + ) -> Self { + Substream { + remote_recipient, + connection_id, + substream_id, + inbound_rx, + outbound_tx, + close_rx, + closed: Mutex::new(false), + unread_data: Mutex::new(vec![]), + message_nonce, + } + } + + fn check_closed(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Result<(), IoError> { + let closed_err = IoError::new(ErrorKind::Other, "stream closed"); + + // close_rx will return an error if the channel is closed (ie. sender was dropped), + // or if it's empty + let received_closed = self.close_rx.try_recv(); + + let mut closed = self.closed.lock(); + if *closed { + return Err(closed_err); + } + + if received_closed.is_ok() { + *closed = true; + return Err(closed_err); + } + + Ok(()) + } +} + +impl AsyncRead for Substream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + let closed_result = self.as_mut().check_closed(cx); + if let Err(e) = closed_result { + return Poll::Ready(Err(e)); + } + + let inbound_rx_data = self.inbound_rx.poll_recv(cx); + + // first, write any previously unread data to the buf + let mut unread_data = self.unread_data.lock(); + let filled_len = if unread_data.len() > 0 { + let unread_len = unread_data.len(); + let buf_len = buf.len(); + let copy_len = std::cmp::min(unread_len, buf_len); + buf[..copy_len].copy_from_slice(&unread_data[..copy_len]); + *unread_data = unread_data[copy_len..].to_vec(); + copy_len + } else { + 0 + }; + + if let Poll::Ready(Some(data)) = inbound_rx_data { + if filled_len == buf.len() { + // we've filled the buffer, so we'll have to save the rest for later + let mut new = vec![]; + new.extend(unread_data.drain(..)); + new.extend(data.iter()); + *unread_data = new; + return Poll::Ready(Ok(filled_len)); + } + + // otherwise, there's still room in the buffer, so we'll copy the rest of the data + let remaining_len = buf.len() - filled_len; + let data_len = data.len(); + + // we have more data than buffer room remaining, save the extra for later + if remaining_len < data_len { + unread_data.extend_from_slice(&data[remaining_len..]); + } + + let copied = std::cmp::min(remaining_len, data_len); + buf[filled_len..filled_len + copied].copy_from_slice(&data[..copied]); + debug!("poll_read copied {} bytes: data {:?}", copied, buf); + return Poll::Ready(Ok(copied)); + } + + if filled_len > 0 { + debug!("poll_read copied {} bytes: data {:?}", filled_len, buf); + return Poll::Ready(Ok(filled_len)); + } + + Poll::Pending + } +} + +impl AsyncWrite for Substream { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + if let Err(e) = self.as_mut().check_closed(cx) { + return Poll::Ready(Err(e)); + } + + let nonce = self.message_nonce.fetch_add(1, Ordering::SeqCst); + + self.outbound_tx + .send(OutboundMessage { + recipient: self.remote_recipient, + message: Message::TransportMessage(TransportMessage { + nonce, + id: self.connection_id.clone(), + message: SubstreamMessage::new_with_data( + self.substream_id.clone(), + buf.to_vec(), + ), + }), + }) + .map_err(|e| { + IoError::new( + ErrorKind::Other, + format!("poll_write outbound_tx error: {}", e), + ) + })?; + + Poll::Ready(Ok(buf.len())) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if let Err(e) = self.check_closed(cx) { + return Poll::Ready(Err(e)); + } + + Poll::Ready(Ok(())) + } + + fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + let nonce = self.message_nonce.fetch_add(1, Ordering::SeqCst); + + let mut closed = self.closed.lock(); + if *closed { + return Poll::Ready(Err(IoError::new(ErrorKind::Other, "stream closed"))); + } + + *closed = true; + + // send a close message to the mixnet + self.outbound_tx + .send(OutboundMessage { + recipient: self.remote_recipient, + message: Message::TransportMessage(TransportMessage { + nonce, + id: self.connection_id.clone(), + message: SubstreamMessage::new_close(self.substream_id.clone()), + }), + }) + .map_err(|e| { + IoError::new( + ErrorKind::Other, + format!("poll_close outbound_rx error: {}", e), + ) + })?; + + Poll::Ready(Ok(())) + } +} + +#[cfg(test)] +mod test { + use super::super::message::{ + ConnectionId, Message, SubstreamId, SubstreamMessage, TransportMessage, + }; + use super::super::mixnet::initialize_mixnet; + use super::Substream; + use futures::{AsyncReadExt, AsyncWriteExt}; + use nym_sdk::mixnet::MixnetClient; + use nym_sphinx::addressing::clients::Recipient; + use std::sync::atomic::AtomicU64; + use std::sync::Arc; + + #[tokio::test] + async fn test_substream_poll_read_unread_data() { + let (outbound_tx, _) = tokio::sync::mpsc::unbounded_channel(); + let connection_id = ConnectionId::generate(); + let substream_id = SubstreamId::generate(); + + let (inbound_tx, inbound_rx) = tokio::sync::mpsc::unbounded_channel(); + let (_, close_rx) = tokio::sync::oneshot::channel(); + + let mut substream = Substream::new( + Recipient::try_from_base58_string("D1rrpsysCGCYXy9saP8y3kmNpGtJZUXN9SvFoUcqAsM9.9Ssso1ea5NfkbMASdiseDSjTN1fSWda5SgEVjdSN4CvV@GJqd3ZxpXWSNxTfx7B1pPtswpetH4LnJdFeLeuY5KUuN").unwrap(), + connection_id, + substream_id, + inbound_rx, + outbound_tx, + close_rx, + Arc::new(AtomicU64::new(1)), + ); + + // test writing and reading w/ same length data + let data = b"hello".to_vec(); + inbound_tx.send(data.clone()).unwrap(); + let mut buf = [0u8; 5]; + let read_len = substream.read(&mut buf).await.unwrap(); + assert_eq!(read_len, data.len()); + assert_eq!(buf.to_vec(), data); + + // test writing data longer than read buffer + let data = b"nootwashere".to_vec(); + inbound_tx.send(data.clone()).unwrap(); + + let mut buf = [0u8; 4]; + let read_len = substream.read(&mut buf).await.unwrap(); + assert_eq!(read_len, buf.len()); + assert_eq!(buf.to_vec(), b"noot".to_vec()); + + let mut buf = [0u8; 7]; + let read_len = substream.read(&mut buf).await.unwrap(); + assert_eq!(read_len, buf.len()); + assert_eq!(buf.to_vec(), b"washere".to_vec()); + + // test read buffer larger than written data + let data = b"nootwashere".to_vec(); + inbound_tx.send(data.clone()).unwrap(); + let mut buf = [0u8; 16]; + let read_len = substream.read(&mut buf).await.unwrap(); + assert_eq!(read_len, data.len()); + assert_eq!(buf[..data.len()], data); + assert_eq!(buf[data.len()..].to_vec(), vec![0u8; 16 - data.len()]); + + // test writing data longer than read buffer multiple times + let data = b"nootwashere".to_vec(); + inbound_tx.send(data.clone()).unwrap(); + + let mut buf = [0u8; 4]; + let read_len = substream.read(&mut buf).await.unwrap(); + assert_eq!(read_len, buf.len()); + assert_eq!(buf.to_vec(), b"noot".to_vec()); + + let data = b"asdf".to_vec(); + inbound_tx.send(data.clone()).unwrap(); + + let mut buf = [0u8; 4]; + let read_len = substream.read(&mut buf).await.unwrap(); + assert_eq!(read_len, buf.len()); + assert_eq!(buf.to_vec(), b"wash".to_vec()); + + let mut buf = [0u8; 8]; + let read_len = substream.read(&mut buf).await.unwrap(); + assert_eq!(read_len, 7); + assert_eq!(buf[..7], b"ereasdf".to_vec()); + } + + #[tokio::test] + async fn test_substream_read_write() { + let client = MixnetClient::connect_new().await.unwrap(); + let (self_address, mut mixnet_inbound_rx, outbound_tx) = + initialize_mixnet(client, None).await.unwrap(); + + const MSG_INNER: &[u8] = "hello".as_bytes(); + let connection_id = ConnectionId::generate(); + let substream_id = SubstreamId::generate(); + + let (inbound_tx, inbound_rx) = tokio::sync::mpsc::unbounded_channel(); + let (_, close_rx) = tokio::sync::oneshot::channel(); + + let mut substream = Substream::new( + self_address, + connection_id, + substream_id, + inbound_rx, + outbound_tx, + close_rx, + Arc::new(AtomicU64::new(1)), + ); + + // send message to ourselves over the mixnet + substream.write_all(MSG_INNER).await.unwrap(); + + // receive full message over the mixnet + let recv_msg = mixnet_inbound_rx.recv().await.unwrap(); + match recv_msg.0 { + Message::TransportMessage(TransportMessage { + nonce, + id: _, + message: + SubstreamMessage { + substream_id: _, + message_type: msg, + }, + }) => { + assert_eq!(nonce, 1); + match msg { + super::super::message::SubstreamMessageType::Data(data) => { + assert_eq!(data, MSG_INNER); + // send message to substream inbound channel + inbound_tx.send(data).unwrap(); + } + _ => panic!("unexpected message type"), + } + } + _ => panic!("unexpected message"), + } + + // read message from substream + let mut buf = [0u8; MSG_INNER.len()]; + substream.read_exact(&mut buf).await.unwrap(); + assert_eq!(buf, MSG_INNER); + + // close substream + substream.close().await.unwrap(); + + // try to read/write to closed substream; should error + substream.write_all(MSG_INNER).await.unwrap_err(); + substream.read_exact(&mut buf).await.unwrap_err(); + + // assert a close message was sent over the mixnet + let recv_msg = mixnet_inbound_rx.recv().await.unwrap(); + match recv_msg.0 { + Message::TransportMessage(TransportMessage { + nonce: _, + id: _, + message: + SubstreamMessage { + substream_id: _, + message_type: msg, + }, + }) => match msg { + super::super::message::SubstreamMessageType::Close => {} + _ => panic!("unexpected message type"), + }, + _ => panic!("unexpected message: {:?}", recv_msg.0), + } + } + + #[tokio::test] + async fn test_substream_recv_close() { + let client = MixnetClient::connect_new().await.unwrap(); + let (self_address, _, outbound_tx) = initialize_mixnet(client, None).await.unwrap(); + + const MSG_INNER: &[u8] = "hello".as_bytes(); + let connection_id = ConnectionId::generate(); + let substream_id = SubstreamId::generate(); + + let (_, inbound_rx) = tokio::sync::mpsc::unbounded_channel(); + let (close_tx, close_rx) = tokio::sync::oneshot::channel(); + + let mut substream = Substream::new( + self_address, + connection_id, + substream_id, + inbound_rx, + outbound_tx, + close_rx, + Arc::new(AtomicU64::new(1)), + ); + + // close substream + close_tx.send(()).unwrap(); + + // try to read/write to closed substream; should error + substream.write_all(MSG_INNER).await.unwrap_err(); + let mut buf = [0u8; MSG_INNER.len()]; + substream.read_exact(&mut buf).await.unwrap_err(); + } +} diff --git a/sdk/rust/nym-sdk/examples/libp2p_shared/transport.rs b/sdk/rust/nym-sdk/examples/libp2p_shared/transport.rs new file mode 100644 index 0000000000..88142cc37f --- /dev/null +++ b/sdk/rust/nym-sdk/examples/libp2p_shared/transport.rs @@ -0,0 +1,897 @@ +use futures::prelude::*; +use libp2p::core::{ + identity::Keypair, + multiaddr::{Multiaddr, Protocol}, + transport::{ListenerId, TransportError, TransportEvent}, + PeerId, Transport, +}; +use log::debug; +use nym_sdk::mixnet::MixnetClient; +use nym_sphinx::addressing::clients::Recipient; +use std::{ + collections::HashMap, + pin::Pin, + str::FromStr, + task::{Context, Poll, Waker}, +}; +use tokio::{ + sync::{ + mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + oneshot, + }, + time::{timeout, Duration}, +}; +use tokio_stream::wrappers::UnboundedReceiverStream; + +use super::connection::{Connection, PendingConnection}; +use super::error::Error; +use super::message::{ + ConnectionId, ConnectionMessage, InboundMessage, Message, OutboundMessage, SubstreamMessage, + TransportMessage, +}; +use super::mixnet::initialize_mixnet; +use super::queue::MessageQueue; +use super::DEFAULT_HANDSHAKE_TIMEOUT_SECS; + +/// InboundTransportEvent represents an inbound event from the mixnet. +pub enum InboundTransportEvent { + ConnectionRequest(Upgrade), + ConnectionResponse, + TransportMessage, +} + +/// NymTransport implements the Transport trait using the Nym mixnet. +pub struct NymTransport { + /// our Nym address + self_address: Recipient, + pub(crate) listen_addr: Multiaddr, + pub(crate) listener_id: ListenerId, + + /// our libp2p keypair; currently not really used + keypair: Keypair, + + /// established connections -> channel which sends messages received from + /// the mixnet to the corresponding Connection + connections: HashMap>, + + /// outbound pending dials + pending_dials: HashMap, + + /// connection message queues + message_queues: HashMap, + + /// inbound mixnet messages + inbound_stream: UnboundedReceiverStream, + + /// outbound mixnet messages + outbound_tx: UnboundedSender, + + /// inbound messages for Transport.poll() + poll_rx: UnboundedReceiver>, + + /// outbound messages to Transport.poll() + poll_tx: UnboundedSender>, + + waker: Option, + + /// Timeout for the [`Upgrade`] future. + handshake_timeout: Duration, +} + +impl NymTransport { + /// New transport. + pub async fn new(client: MixnetClient, keypair: Keypair) -> Result { + Self::new_maybe_with_notify_inbound(client, keypair, None, None).await + } + + /// New transport with a timeout. + #[allow(dead_code)] + pub async fn new_with_timeout( + client: MixnetClient, + keypair: Keypair, + timeout: Duration, + ) -> Result { + Self::new_maybe_with_notify_inbound(client, keypair, None, Some(timeout)).await + } + + /// Add timeout to transport and return self. + #[allow(dead_code)] + pub fn with_timeout(mut self, timeout: Duration) -> Self { + self.handshake_timeout = timeout; + self + } + + async fn new_maybe_with_notify_inbound( + client: MixnetClient, + keypair: Keypair, + notify_inbound_tx: Option>, + timeout: Option, + ) -> Result { + let (self_address, inbound_rx, outbound_tx) = + initialize_mixnet(client, notify_inbound_tx).await?; + let listen_addr = nym_address_to_multiaddress(self_address)?; + let listener_id = ListenerId::new(); + + let (poll_tx, poll_rx) = unbounded_channel::>(); + + poll_tx + .send(TransportEvent::NewAddress { + listener_id, + listen_addr: listen_addr.clone(), + }) + .map_err(|_| Error::SendErrorTransportEvent)?; + + let inbound_stream = UnboundedReceiverStream::new(inbound_rx); + let handshake_timeout = + timeout.unwrap_or_else(|| Duration::from_secs(DEFAULT_HANDSHAKE_TIMEOUT_SECS)); + + Ok(Self { + self_address, + listen_addr, + listener_id, + keypair, + connections: HashMap::new(), + pending_dials: HashMap::new(), + message_queues: HashMap::new(), + inbound_stream, + outbound_tx, + poll_rx, + poll_tx, + waker: None, + handshake_timeout, + }) + } + + pub(crate) fn peer_id(&self) -> PeerId { + PeerId::from_public_key(&self.keypair.public()) + } + + fn handle_message_queue_on_connection_initiation( + &mut self, + id: &ConnectionId, + ) -> Result<(), Error> { + debug!("handle_message_queue_on_connection_initiation"); + let Some(inbound_tx) = self.connections.get(id) else { + // this should not happen + return Err(Error::NoConnectionForTransportMessage); + }; + + match self.message_queues.get_mut(id) { + Some(queue) => { + // update expected nonce + queue.set_connection_message_received(); + + // push pending inbound some messages in this case + while let Some(msg) = queue.pop() { + debug!( + "popped queued message with nonce {} for connection", + msg.nonce + ); + inbound_tx + .send(msg.message.clone()) + .map_err(|e| Error::InboundSendError(e.to_string()))?; + } + } + None => { + // no queue exists for this connection, create one + let queue = MessageQueue::new(); + self.message_queues.insert(id.clone(), queue); + let queue = self.message_queues.get_mut(id).unwrap(); + queue.set_connection_message_received(); + } + }; + + debug!("returning from handle_message_queue_on_connection_initiation"); + Ok(()) + } + + // handle_connection_response resolves the pending connection corresponding to the response + // (if there is one) into a Connection. + fn handle_connection_response(&mut self, msg: &ConnectionMessage) -> Result<(), Error> { + if self.connections.contains_key(&msg.id) { + return Err(Error::ConnectionAlreadyEstablished); + } + + if let Some(pending_conn) = self.pending_dials.remove(&msg.id) { + // resolve connection and put into pending_conn channel + let (conn, conn_tx) = self.create_connection_types( + msg.peer_id, + pending_conn.remote_recipient, + msg.id.clone(), + ); + + self.connections.insert(msg.id.clone(), conn_tx); + self.handle_message_queue_on_connection_initiation(&msg.id)?; + + pending_conn + .connection_tx + .send(conn) + .map_err(|_| Error::ConnectionSendError)?; + + if let Some(waker) = self.waker.take() { + waker.wake(); + } + + Ok(()) + } else { + Err(Error::NoConnectionForResponse) + } + } + + /// handle_connection_request handles an incoming connection request, sends back a + /// connection response, and finally completes the upgrade into a Connection. + fn handle_connection_request(&mut self, msg: &ConnectionMessage) -> Result { + if msg.recipient.is_none() { + return Err(Error::NoneRecipientInConnectionRequest); + } + + // ensure we don't already have a conn with the same id + if self.connections.get(&msg.id).is_some() { + return Err(Error::ConnectionIDExists); + } + + let (conn, conn_tx) = + self.create_connection_types(msg.peer_id, msg.recipient.unwrap(), msg.id.clone()); + self.connections.insert(msg.id.clone(), conn_tx); + self.handle_message_queue_on_connection_initiation(&msg.id)?; + + let resp = ConnectionMessage { + peer_id: self.peer_id(), + recipient: None, + id: msg.id.clone(), + }; + + self.outbound_tx + .send(OutboundMessage { + message: Message::ConnectionResponse(resp), + recipient: msg.recipient.unwrap(), + }) + .map_err(|e| Error::OutboundSendError(e.to_string()))?; + + if let Some(waker) = self.waker.take() { + waker.wake(); + } + + Ok(conn) + } + + fn handle_transport_message(&mut self, msg: TransportMessage) -> Result<(), Error> { + let queue = match self.message_queues.get_mut(&msg.id) { + Some(queue) => queue, + None => { + // no queue exists for this connection, create one + let queue = MessageQueue::new(); + self.message_queues.insert(msg.id.clone(), queue); + self.message_queues.get_mut(&msg.id).unwrap() + } + }; + + queue.print_nonces(); + + let nonce = msg.nonce; + let Some(msg) = queue.try_push(msg) else { + // don't push the message yet, it's been queued + debug!("message with nonce {} queued for connection", nonce); + return Ok(()); + }; + + let Some(inbound_tx) = self.connections.get(&msg.id) else { + return Err(Error::NoConnectionForTransportMessage); + }; + + // send original message + debug!( + "sending original message with nonce {} for connection", + nonce + ); + inbound_tx + .send(msg.message.clone()) + .map_err(|e| Error::InboundSendError(e.to_string()))?; + + // try to pop queued messages and send them on inbound channel + while let Some(msg) = queue.pop() { + debug!( + "popped queued message with nonce {} for connection", + msg.nonce + ); + inbound_tx + .send(msg.message.clone()) + .map_err(|e| Error::InboundSendError(e.to_string()))?; + } + + if let Some(waker) = self.waker.clone().take() { + waker.wake(); + } + + Ok(()) + } + + fn create_connection_types( + &self, + remote_peer_id: PeerId, + recipient: Recipient, + id: ConnectionId, + ) -> (Connection, UnboundedSender) { + let (inbound_tx, inbound_rx) = unbounded_channel::(); + + // representation of a connection; this contains channels for applications to read/write to. + let conn = Connection::new( + remote_peer_id, + recipient, + id, + inbound_rx, + self.outbound_tx.clone(), + ); + + // inbound_tx is what we write to when receiving messages on the mixnet, + (conn, inbound_tx) + } + + /// handle_inbound handles an inbound message from the mixnet, received via self.inbound_stream. + fn handle_inbound(&mut self, msg: Message) -> Result { + match msg { + Message::ConnectionRequest(inner) => { + debug!("got inbound connection request {:?}", inner); + match self.handle_connection_request(&inner) { + Ok(conn) => { + let (connection_tx, connection_rx) = + oneshot::channel::<(PeerId, Connection)>(); + let upgrade = Upgrade::new(connection_rx); + connection_tx + .send((inner.peer_id, conn)) + .map_err(|_| Error::ConnectionSendError)?; + Ok(InboundTransportEvent::ConnectionRequest(upgrade)) + } + Err(e) => Err(e), + } + } + Message::ConnectionResponse(msg) => { + debug!("got inbound connection response {:?}", msg); + self.handle_connection_response(&msg) + .map(|_| InboundTransportEvent::ConnectionResponse) + } + Message::TransportMessage(msg) => { + debug!("got inbound TransportMessage: {:?}", msg); + self.handle_transport_message(msg) + .map(|_| InboundTransportEvent::TransportMessage) + } + } + } +} + +/// Upgrade represents a transport listener upgrade. +/// Note: we immediately upgrade a connection request to a connection, +/// so this only contains a channel for receiving that connection. +pub struct Upgrade { + connection_tx: oneshot::Receiver<(PeerId, Connection)>, +} + +impl Upgrade { + fn new(connection_tx: oneshot::Receiver<(PeerId, Connection)>) -> Upgrade { + Upgrade { connection_tx } + } +} + +impl Future for Upgrade { + type Output = Result<(PeerId, Connection), Error>; + + // poll checks if the upgrade has turned into a connection yet + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.connection_tx + .poll_unpin(cx) + .map_err(|_| Error::RecvError) + } +} + +impl Transport for NymTransport { + type Output = (PeerId, Connection); + type Error = Error; + type ListenerUpgrade = Upgrade; + type Dial = Pin> + Send>>; + + fn listen_on(&mut self, _: Multiaddr) -> Result> { + // we should only allow listening on the multiaddress containing our Nym address + Ok(self.listener_id) + } + + fn remove_listener(&mut self, id: ListenerId) -> bool { + if self.listener_id != id { + return false; + } + + // TODO: close channels? + self.poll_tx + .send(TransportEvent::ListenerClosed { + listener_id: id, + reason: Ok(()), + }) + .expect("failed to send listener closed event"); + true + } + + fn dial(&mut self, addr: Multiaddr) -> Result> { + debug!("dialing {}", addr); + + let id = ConnectionId::generate(); + + // create remote recipient address + let recipient = multiaddress_to_nym_address(addr).map_err(TransportError::Other)?; + + // create pending conn structs and store + let (connection_tx, connection_rx) = oneshot::channel::(); + + let inner_pending_conn = PendingConnection::new(recipient, connection_tx); + self.pending_dials.insert(id.clone(), inner_pending_conn); + + // put ConnectionRequest message into outbound message channel + let msg = ConnectionMessage { + peer_id: self.peer_id(), + recipient: Some(self.self_address), + id, + }; + + let outbound_tx = self.outbound_tx.clone(); + + let mut waker = self.waker.clone(); + let handshake_timeout = self.handshake_timeout; + Ok(async move { + outbound_tx + .send(OutboundMessage { + message: Message::ConnectionRequest(msg), + recipient, + }) + .map_err(|e| Error::OutboundSendError(e.to_string()))?; + + debug!("sent outbound ConnectionRequest"); + if let Some(waker) = waker.take() { + waker.wake(); + }; + + let conn = timeout(handshake_timeout, connection_rx).await??; + Ok((conn.peer_id, conn)) + } + .boxed()) + } + + // dial_as_listener currently just calls self.dial(). + fn dial_as_listener( + &mut self, + addr: Multiaddr, + ) -> Result> { + self.dial(addr) + } + + fn poll( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + // new addresses + listener close events + if let Poll::Ready(Some(res)) = self.poll_rx.recv().boxed().poll_unpin(cx) { + return Poll::Ready(res); + } + + // check for and handle inbound messages + while let Poll::Ready(Some(msg)) = self.inbound_stream.poll_next_unpin(cx) { + match self.handle_inbound(msg.0) { + Ok(event) => match event { + InboundTransportEvent::ConnectionRequest(upgrade) => { + debug!("InboundTransportEvent::ConnectionRequest"); + return Poll::Ready(TransportEvent::Incoming { + listener_id: self.listener_id, + upgrade, + local_addr: self.listen_addr.clone(), + send_back_addr: self.listen_addr.clone(), + }); + } + InboundTransportEvent::ConnectionResponse => { + debug!("InboundTransportEvent::ConnectionResponse"); + } + InboundTransportEvent::TransportMessage => { + debug!("InboundTransportEvent::TransportMessage"); + } + }, + Err(e) => { + return Poll::Ready(TransportEvent::ListenerError { + listener_id: self.listener_id, + error: e, + }); + } + }; + } + + self.waker = Some(cx.waker().clone()); + Poll::Pending + } + + fn address_translation(&self, _listen: &Multiaddr, _observed: &Multiaddr) -> Option { + None + } +} + +fn nym_address_to_multiaddress(addr: Recipient) -> Result { + Multiaddr::from_str(&format!("/nym/{}", addr)).map_err(Error::FailedToFormatMultiaddr) +} + +fn multiaddress_to_nym_address(multiaddr: Multiaddr) -> Result { + let mut multiaddr = multiaddr; + match multiaddr.pop().unwrap() { + Protocol::Nym(addr) => Recipient::from_str(&addr).map_err(Error::InvalidRecipientBytes), + _ => Err(Error::InvalidProtocolForMultiaddr), + } +} + +#[cfg(test)] +mod test { + use super::super::connection::Connection; + use super::super::error::Error; + use super::super::message::{ + Message, OutboundMessage, SubstreamId, SubstreamMessage, SubstreamMessageType, + TransportMessage, + }; + use super::super::substream::Substream; + use super::{nym_address_to_multiaddress, NymTransport}; + use futures::{future::poll_fn, AsyncReadExt, AsyncWriteExt, FutureExt}; + use libp2p::core::{ + identity::Keypair, + transport::{Transport, TransportEvent}, + Multiaddr, StreamMuxer, + }; + use log::info; + use nym_bin_common::logging::setup_logging; + use nym_sdk::mixnet::MixnetClient; + use std::{pin::Pin, str::FromStr, sync::atomic::Ordering}; + use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; + + impl Connection { + fn write(&self, msg: SubstreamMessage) -> Result<(), Error> { + let nonce = self.message_nonce.fetch_add(1, Ordering::SeqCst); + self.mixnet_outbound_tx + .send(OutboundMessage { + recipient: self.remote_recipient, + message: Message::TransportMessage(TransportMessage { + nonce, + id: self.id.clone(), + message: msg, + }), + }) + .map_err(|e| Error::OutboundSendError(e.to_string()))?; + Ok(()) + } + } + + impl NymTransport { + async fn new_with_notify_inbound( + client: MixnetClient, + notify_inbound_tx: UnboundedSender<()>, + ) -> Result { + let local_key = Keypair::generate_ed25519(); + Self::new_maybe_with_notify_inbound(client, local_key, Some(notify_inbound_tx), None) + .await + } + } + + #[tokio::test] + async fn test_transport_connection() { + setup_logging(); + + let client = MixnetClient::connect_new().await.unwrap(); + let (dialer_notify_inbound_tx, mut dialer_notify_inbound_rx) = unbounded_channel(); + let mut dialer_transport = + NymTransport::new_with_notify_inbound(client, dialer_notify_inbound_tx) + .await + .unwrap(); + + let client2 = MixnetClient::connect_new().await.unwrap(); + let (listener_notify_inbound_tx, mut listener_notify_inbound_rx) = unbounded_channel(); + let mut listener_transport = + NymTransport::new_with_notify_inbound(client2, listener_notify_inbound_tx) + .await + .unwrap(); + let listener_multiaddr = + nym_address_to_multiaddress(listener_transport.self_address).unwrap(); + assert_new_address_event(Pin::new(&mut dialer_transport)).await; + assert_new_address_event(Pin::new(&mut listener_transport)).await; + + // dial the remote peer + let mut dial = dialer_transport.dial(listener_multiaddr).unwrap(); + + // poll the dial to send the connection request message + assert!(poll_fn(|cx| Pin::new(&mut dial).as_mut().poll_unpin(cx)) + .now_or_never() + .is_none()); + listener_notify_inbound_rx.recv().await.unwrap(); + + // should receive the connection request from the mixnet and send the connection response + let res = poll_fn(|cx| Pin::new(&mut listener_transport).as_mut().poll(cx)).await; + let mut upgrade = match res { + TransportEvent::Incoming { + listener_id, + upgrade, + local_addr, + send_back_addr, + } => { + assert_eq!(listener_id, listener_transport.listener_id); + assert_eq!(local_addr, listener_transport.listen_addr); + assert_eq!(send_back_addr, listener_transport.listen_addr); + upgrade + } + _ => panic!("expected TransportEvent::Incoming, got {:?}", res), + }; + dialer_notify_inbound_rx.recv().await.unwrap(); + + // should receive the connection response from the mixnet + assert!( + poll_fn(|cx| Pin::new(&mut dialer_transport).as_mut().poll(cx)) + .now_or_never() + .is_none() + ); + info!("waiting for connections..."); + + // should be able to resolve the connections now + let (_, mut listener_conn) = poll_fn(|cx| Pin::new(&mut upgrade).as_mut().poll_unpin(cx)) + .now_or_never() + .expect("the upgrade should be ready") + .expect("the upgrade should not error"); + let (_, mut dialer_conn) = poll_fn(|cx| Pin::new(&mut dial).as_mut().poll_unpin(cx)) + .now_or_never() + .expect("the upgrade should be ready") + .expect("the upgrade should not error"); + info!("connections established"); + + // write messages from the dialer to the listener and vice versa + send_and_receive_over_conns( + b"hello".to_vec(), + &mut dialer_conn, + &mut listener_conn, + Pin::new(&mut listener_transport), + &mut listener_notify_inbound_rx, + ) + .await; + send_and_receive_over_conns( + b"hi".to_vec(), + &mut dialer_conn, + &mut listener_conn, + Pin::new(&mut listener_transport), + &mut listener_notify_inbound_rx, + ) + .await; + send_and_receive_over_conns( + b"world".to_vec(), + &mut listener_conn, + &mut dialer_conn, + Pin::new(&mut dialer_transport), + &mut dialer_notify_inbound_rx, + ) + .await; + } + + async fn assert_new_address_event(mut transport: Pin<&mut NymTransport>) { + match poll_fn(|cx| transport.as_mut().poll(cx)).await { + TransportEvent::NewAddress { + listener_id, + listen_addr, + } => { + assert_eq!(listener_id, transport.listener_id); + assert_eq!(listen_addr, transport.listen_addr); + } + _ => panic!("expected TransportEvent::NewAddress"), + } + } + + async fn send_and_receive_over_conns( + msg: Vec, + conn1: &mut Connection, + conn2: &mut Connection, + mut transport2: Pin<&mut NymTransport>, + notify_inbound_rx: &mut UnboundedReceiver<()>, + ) { + // send message over conn1 to conn2 + let substream_id = SubstreamId::generate(); + conn1 + .write(SubstreamMessage::new_with_data( + substream_id.clone(), + msg.clone(), + )) + .unwrap(); + notify_inbound_rx.recv().await.unwrap(); + + // poll transport2 to push message from transport to connection + assert!(poll_fn(|cx| transport2.as_mut().poll(cx)) + .now_or_never() + .is_none()); + let substream_msg = conn2.inbound_rx.recv().await.unwrap(); + if let SubstreamMessageType::Data(data) = substream_msg.message_type { + assert_eq!(data, msg); + } else { + panic!("expected data message"); + } + } + + #[tokio::test] + async fn test_transport_substream() { + let client = MixnetClient::connect_new().await.unwrap(); + + let (dialer_notify_inbound_tx, mut dialer_notify_inbound_rx) = unbounded_channel(); + let mut dialer_transport = + NymTransport::new_with_notify_inbound(client, dialer_notify_inbound_tx) + .await + .unwrap(); + + let client2 = MixnetClient::connect_new().await.unwrap(); + + let (listener_notify_inbound_tx, mut listener_notify_inbound_rx) = unbounded_channel(); + let mut listener_transport = + NymTransport::new_with_notify_inbound(client2, listener_notify_inbound_tx) + .await + .unwrap(); + let listener_multiaddr = + nym_address_to_multiaddress(listener_transport.self_address).unwrap(); + assert_new_address_event(Pin::new(&mut dialer_transport)).await; + assert_new_address_event(Pin::new(&mut listener_transport)).await; + + // dial the remote peer + let mut dial = dialer_transport.dial(listener_multiaddr).unwrap(); + + // poll the dial to send the connection request message + assert!(poll_fn(|cx| Pin::new(&mut dial).as_mut().poll_unpin(cx)) + .now_or_never() + .is_none()); + listener_notify_inbound_rx.recv().await.unwrap(); + + // should receive the connection request from the mixnet and send the connection response + let res = poll_fn(|cx| Pin::new(&mut listener_transport).as_mut().poll(cx)).await; + let mut upgrade = match res { + TransportEvent::Incoming { + listener_id, + upgrade, + local_addr, + send_back_addr, + } => { + assert_eq!(listener_id, listener_transport.listener_id); + assert_eq!(local_addr, listener_transport.listen_addr); + assert_eq!(send_back_addr, listener_transport.listen_addr); + upgrade + } + _ => panic!("expected TransportEvent::Incoming, got {:?}", res), + }; + dialer_notify_inbound_rx.recv().await.unwrap(); + + // should receive the connection response from the mixnet + assert!( + poll_fn(|cx| Pin::new(&mut dialer_transport).as_mut().poll(cx)) + .now_or_never() + .is_none() + ); + info!("waiting for connections..."); + + // should be able to resolve the connections now + let (_, mut listener_conn) = poll_fn(|cx| Pin::new(&mut upgrade).as_mut().poll_unpin(cx)) + .now_or_never() + .unwrap() + .unwrap(); + let (_, mut dialer_conn) = poll_fn(|cx| Pin::new(&mut dial).as_mut().poll_unpin(cx)) + .now_or_never() + .unwrap() + .unwrap(); + info!("connections established"); + + // initiate a new substream from the dialer + let mut dialer_substream = + poll_fn(|cx| Pin::new(&mut dialer_conn).as_mut().poll_outbound(cx)) + .await + .unwrap(); + listener_notify_inbound_rx.recv().await.unwrap(); + + // accept the substream on the listener + assert!( + poll_fn(|cx| Pin::new(&mut listener_transport).as_mut().poll(cx)) + .now_or_never() + .is_none() + ); + poll_fn(|cx| Pin::new(&mut listener_conn).as_mut().poll(cx)).now_or_never(); + + // poll recipient's poll_inbound to receive the substream; sends a response to the sender + let mut listener_substream = + poll_fn(|cx| Pin::new(&mut listener_conn).as_mut().poll_inbound(cx)) + .now_or_never() + .unwrap() + .unwrap(); + info!("got listener substream"); + dialer_notify_inbound_rx.recv().await.unwrap(); + + // poll sender to finalize the substream + assert!( + poll_fn(|cx| Pin::new(&mut dialer_transport).as_mut().poll(cx)) + .now_or_never() + .is_none() + ); + poll_fn(|cx| Pin::new(&mut dialer_conn).as_mut().poll(cx)).now_or_never(); + info!("got dialer substream"); + + // write message from dialer to listener + send_and_receive_substream_message( + b"hello world".to_vec(), + Pin::new(&mut dialer_substream), + Pin::new(&mut listener_substream), + Pin::new(&mut listener_transport), + Pin::new(&mut listener_conn), + &mut listener_notify_inbound_rx, + ) + .await; + + // write message from listener to dialer + send_and_receive_substream_message( + b"hello back".to_vec(), + Pin::new(&mut listener_substream), + Pin::new(&mut dialer_substream), + Pin::new(&mut dialer_transport), + Pin::new(&mut dialer_conn), + &mut dialer_notify_inbound_rx, + ) + .await; + + // close the substream from the dialer side + info!("closing dialer substream"); + dialer_substream.close().await.unwrap(); + listener_notify_inbound_rx.recv().await.unwrap(); + info!("dialer substream closed"); + + // assert we can't read or write to either substream + dialer_substream.write_all(b"hello").await.unwrap_err(); + // poll listener transport and conn to receive the substream close message + poll_fn(|cx| Pin::new(&mut listener_transport).as_mut().poll(cx)).now_or_never(); + poll_fn(|cx| Pin::new(&mut listener_conn).as_mut().poll(cx)).now_or_never(); + listener_substream.write_all(b"hello").await.unwrap_err(); + let mut buf = vec![0u8; 5]; + dialer_substream.read(&mut buf).await.unwrap_err(); + listener_substream.read(&mut buf).await.unwrap_err(); + dialer_substream.close().await.unwrap_err(); + listener_substream.close().await.unwrap_err(); + } + + async fn send_and_receive_substream_message( + data: Vec, + mut sender_substream: Pin<&mut Substream>, + mut recipient_substream: Pin<&mut Substream>, + mut recipient_transport: Pin<&mut NymTransport>, + mut recipient_conn: Pin<&mut Connection>, + recipient_notify_inbound_rx: &mut UnboundedReceiver<()>, + ) { + // write message + sender_substream.write_all(&data).await.unwrap(); + recipient_notify_inbound_rx.recv().await.unwrap(); + + // poll recipient for message + poll_fn(|cx| recipient_transport.as_mut().poll(cx)).now_or_never(); + poll_fn(|cx| recipient_conn.as_mut().poll(cx)).now_or_never(); + let mut buf = vec![0u8; data.len()]; + let n = recipient_substream.read(&mut buf).await.unwrap(); + assert_eq!(n, data.len()); + assert_eq!(buf, data[..]); + } + + #[tokio::test] + async fn test_transport_timeout() { + let client = MixnetClient::connect_new().await.unwrap(); + + let (dialer_notify_inbound_tx, _) = unbounded_channel(); + let mut dialer_transport = + NymTransport::new_with_notify_inbound(client, dialer_notify_inbound_tx) + .await + .unwrap() + .with_timeout(std::time::Duration::from_millis(100)); + + // mock a transport that will never resolve the connection. + let empty_addr = Multiaddr::from_str( + "/nym/Hmer6Ndt3PV13YW53HM8ri4NvqqtfDQUQBhzvKqb1dag.2g478dyxtrQXGWc1Mk2VEqdPcWXpz7EhAcjhdAJtVZdA@AnnYnEtBjB2a5sHmeRCnBq43qxyHDf95Bqd7cwQyKNLR" + ) + .expect("unable to parse multiaddress"); + + let dial = dialer_transport.dial(empty_addr).unwrap(); + assert!(dial + .await + .expect_err("should have timed out") + .to_string() + .contains("dial timed out")); + } +} diff --git a/sdk/rust/nym-sdk/examples/manually_handle_storage.rs b/sdk/rust/nym-sdk/examples/manually_handle_storage.rs index 3c5ef4f450..6130af5a25 100644 --- a/sdk/rust/nym-sdk/examples/manually_handle_storage.rs +++ b/sdk/rust/nym-sdk/examples/manually_handle_storage.rs @@ -3,6 +3,7 @@ use nym_client_core::client::base_client::storage::gateway_details::{ }; use nym_sdk::mixnet::{ self, EmptyReplyStorage, EphemeralCredentialStorage, KeyManager, KeyStore, MixnetClientStorage, + MixnetMessageSender, }; use nym_topology::provider_trait::async_trait; @@ -26,7 +27,10 @@ async fn main() { println!("Our client nym address is: {our_address}"); // Send important info up the pipe to a buddy - client.send_str(*our_address, "hello there").await; + client + .send_plain_message(*our_address, "hello there") + .await + .unwrap(); println!("Waiting for message"); if let Some(received) = client.wait_for_messages().await { diff --git a/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs b/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs index 3ef7e2257f..bad1c9651b 100644 --- a/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs +++ b/sdk/rust/nym-sdk/examples/manually_overwrite_topology.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use nym_sdk::mixnet; +use nym_sdk::mixnet::MixnetMessageSender; use nym_topology::mix::Layer; use nym_topology::{mix, NymTopology}; use std::collections::BTreeMap; @@ -81,7 +82,10 @@ async fn main() { println!("Our client nym address is: {our_address}"); // Send a message through the mixnet to ourselves - client.send_str(*our_address, "hello there").await; + client + .send_plain_message(*our_address, "hello there") + .await + .unwrap(); println!("Waiting for message (ctrl-c to exit)"); client diff --git a/sdk/rust/nym-sdk/examples/parallel_sending_and_receiving.rs b/sdk/rust/nym-sdk/examples/parallel_sending_and_receiving.rs new file mode 100644 index 0000000000..ca9b843717 --- /dev/null +++ b/sdk/rust/nym-sdk/examples/parallel_sending_and_receiving.rs @@ -0,0 +1,42 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use futures::StreamExt; +use nym_sdk::mixnet; +use nym_sdk::mixnet::MixnetMessageSender; + +#[tokio::main] +async fn main() { + nym_bin_common::logging::setup_logging(); + + // Passing no config makes the client fire up an ephemeral session and figure stuff out on its own + let mut client = mixnet::MixnetClient::connect_new().await.unwrap(); + + // Be able to get our client address + let our_address = *client.nym_address(); + println!("Our client nym address is: {our_address}"); + + let sender = client.split_sender(); + + // receiving task + let receiving_task_handle = tokio::spawn(async move { + if let Some(received) = client.next().await { + println!("Received: {}", String::from_utf8_lossy(&received.message)); + } + + client.disconnect().await; + }); + + // sending task + let sending_task_handle = tokio::spawn(async move { + sender + .send_plain_message(our_address, "hello from a different task!") + .await + .unwrap(); + }); + + // wait for both tasks to be done + println!("waiting for shutdown"); + sending_task_handle.await.unwrap(); + receiving_task_handle.await.unwrap(); +} diff --git a/sdk/rust/nym-sdk/examples/simple.rs b/sdk/rust/nym-sdk/examples/simple.rs index f2325e6048..14720ba24c 100644 --- a/sdk/rust/nym-sdk/examples/simple.rs +++ b/sdk/rust/nym-sdk/examples/simple.rs @@ -1,4 +1,5 @@ use nym_sdk::mixnet; +use nym_sdk::mixnet::MixnetMessageSender; #[tokio::main] async fn main() { @@ -12,7 +13,10 @@ async fn main() { println!("Our client nym address is: {our_address}"); // Send a message throught the mixnet to ourselves - client.send_str(*our_address, "hello there").await; + client + .send_plain_message(*our_address, "hello there") + .await + .unwrap(); println!("Waiting for message (ctrl-c to exit)"); client diff --git a/sdk/rust/nym-sdk/examples/surb-reply.rs b/sdk/rust/nym-sdk/examples/surb-reply.rs index 38c7e8257a..abe955b2b9 100644 --- a/sdk/rust/nym-sdk/examples/surb-reply.rs +++ b/sdk/rust/nym-sdk/examples/surb-reply.rs @@ -1,5 +1,6 @@ use nym_sdk::mixnet::{ - AnonymousSenderTag, MixnetClientBuilder, ReconstructedMessage, StoragePaths, + AnonymousSenderTag, MixnetClientBuilder, MixnetMessageSender, ReconstructedMessage, + StoragePaths, }; use std::path::PathBuf; @@ -28,7 +29,10 @@ async fn main() { 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; + client + .send_plain_message(*our_address, "hello there") + .await + .unwrap(); // 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. @@ -57,7 +61,10 @@ async fn main() { // 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; + client + .send_reply(return_recipient, "hi an0n!") + .await + .unwrap(); println!("Waiting for message (once you see it, ctrl-c to exit)\n"); client diff --git a/sdk/rust/nym-sdk/src/bandwidth.rs b/sdk/rust/nym-sdk/src/bandwidth.rs index 5b95d5aae6..e9cfb0f8fb 100644 --- a/sdk/rust/nym-sdk/src/bandwidth.rs +++ b/sdk/rust/nym-sdk/src/bandwidth.rs @@ -6,7 +6,7 @@ //! # Basic example //! //! ```no_run -//! use nym_sdk::mixnet; +//! use nym_sdk::mixnet::{self, MixnetMessageSender}; //! //! #[tokio::main] //! async fn main() { @@ -27,7 +27,7 @@ //! let our_address = client.nym_address(); //! //! // Send a message throughout the mixnet to ourselves -//! client.send_str(*our_address, "hello there").await; +//! client.send_plain_message(*our_address, "hello there").await.unwrap(); //! //! println!("Waiting for message"); //! if let Some(received) = client.wait_for_messages().await { diff --git a/sdk/rust/nym-sdk/src/bandwidth/client.rs b/sdk/rust/nym-sdk/src/bandwidth/client.rs index 3be519bf49..7171282a71 100644 --- a/sdk/rust/nym-sdk/src/bandwidth/client.rs +++ b/sdk/rust/nym-sdk/src/bandwidth/client.rs @@ -6,9 +6,8 @@ use nym_bandwidth_controller::acquire::state::State; use nym_credential_storage::storage::Storage; use nym_credentials::coconut::bandwidth::BandwidthVoucher; use nym_network_defaults::NymNetworkDetails; -use nym_validator_client::nyxd::{Coin, SigningNyxdClient}; -use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet; -use nym_validator_client::{Client, Config}; +use nym_validator_client::nyxd::Coin; +use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient}; /// The serialized version of the yet untransformed bandwidth voucher. It can be used to complete /// the acquirement process of a bandwidth credential. @@ -22,7 +21,7 @@ pub type VoucherBlob = Vec; /// client. pub struct BandwidthAcquireClient<'a, St: Storage> { network_details: NymNetworkDetails, - client: Client>, + client: DirectSigningHttpRpcNyxdClient, storage: &'a St, } @@ -36,8 +35,14 @@ where mnemonic: String, storage: &'a St, ) -> Result { - let config = Config::try_from_nym_network_details(&network_details)?; - let client = nym_validator_client::Client::new_signing(config, mnemonic.parse()?)?; + let nyxd_url = network_details.endpoints[0].nyxd_url.as_str(); + let config = nyxd::Config::try_from_nym_network_details(&network_details)?; + + let client = DirectSigningHttpRpcNyxdClient::connect_with_mnemonic( + config, + nyxd_url, + mnemonic.parse()?, + )?; Ok(Self { network_details, client, @@ -51,7 +56,7 @@ where /// associated bandwidth credential, using [`Self::recover`]. pub async fn acquire(&self, amount: u128) -> Result<()> { let amount = Coin::new(amount, &self.network_details.chain_details.mix_denom.base); - let state = nym_bandwidth_controller::acquire::deposit(&self.client.nyxd, amount).await?; + let state = nym_bandwidth_controller::acquire::deposit(&self.client, amount).await?; nym_bandwidth_controller::acquire::get_credential(&state, &self.client, self.storage) .await .map_err(|reason| Error::UnconvertedDeposit { diff --git a/sdk/rust/nym-sdk/src/error.rs b/sdk/rust/nym-sdk/src/error.rs index 48469271e4..e88368b19a 100644 --- a/sdk/rust/nym-sdk/src/error.rs +++ b/sdk/rust/nym-sdk/src/error.rs @@ -1,3 +1,4 @@ +use nym_validator_client::nyxd::error::NyxdError; use std::path::PathBuf; /// Top-level Error enum for the mixnet client and its relevant types. @@ -46,7 +47,7 @@ pub enum Error { DisabledCredentialsMode, #[error("bad validator details: {0}")] - BadValidatorDetails(#[from] nym_validator_client::ValidatorClientError), + BadValidatorDetails(#[from] NyxdError), #[error("socks5 configuration set: {}, but expected to be {}", set, !set)] Socks5Config { set: bool }, @@ -89,6 +90,9 @@ pub enum Error { #[error("loaded shared gateway key without providing information about what gateway it corresponds to")] GatewayWithUnknownEndpoint, + + #[error("failed to send the provided message")] + MessageSendingFailure, } pub type Result = std::result::Result; diff --git a/sdk/rust/nym-sdk/src/mixnet.rs b/sdk/rust/nym-sdk/src/mixnet.rs index c1b85aaff3..88a8604d6b 100644 --- a/sdk/rust/nym-sdk/src/mixnet.rs +++ b/sdk/rust/nym-sdk/src/mixnet.rs @@ -4,20 +4,20 @@ //! # Basic example //! //! ```no_run -//! use nym_sdk::mixnet; +//! use nym_sdk::mixnet::{self, MixnetMessageSender}; //! //! #[tokio::main] //! async fn main() { //! // Passing no config makes the client fire up an ephemeral session and figure stuff out on //! // its own -//! let mut client = mixnet::MixnetClient::connect_new().await.unwrap(); +//! let mut client = mixnet::MixnetClient::connect_new().await.unwrap(); //! //! // Be able to get our client address //! let our_address = client.nym_address(); //! println!("Our client nym address is: {our_address}"); //! //! // Send a message throught the mixnet to ourselves -//! client.send_str(*our_address, "hello there").await; +//! client.send_plain_message(*our_address, "hello there").await.unwrap(); //! //! println!("Waiting for message"); //! if let Some(received) = client.wait_for_messages().await { @@ -36,6 +36,7 @@ mod connection_state; mod native_client; mod paths; mod socks5_client; +mod traits; pub use client::{DisconnectedMixnetClient, IncludedSurbs, MixnetClientBuilder}; pub use config::{Config, KeyMode}; @@ -73,3 +74,4 @@ pub use nym_sphinx::{ pub use nym_topology::{provider_trait::TopologyProvider, NymTopology}; pub use paths::StoragePaths; pub use socks5_client::Socks5MixnetClient; +pub use traits::MixnetMessageSender; diff --git a/sdk/rust/nym-sdk/src/mixnet/client.rs b/sdk/rust/nym-sdk/src/mixnet/client.rs index 33185ccd88..215305f408 100644 --- a/sdk/rust/nym-sdk/src/mixnet/client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/client.rs @@ -24,8 +24,7 @@ use nym_network_defaults::NymNetworkDetails; use nym_socks5_client_core::config::Socks5; use nym_task::manager::TaskStatus; use nym_topology::provider_trait::TopologyProvider; -use nym_validator_client::nyxd::QueryNyxdClient; -use nym_validator_client::Client; +use nym_validator_client::{nyxd, QueryHttpRpcNyxdClient}; use std::path::Path; use std::path::PathBuf; use url::Url; @@ -214,7 +213,7 @@ where /// In the case of enabled credentials, a client instance responsible for querying the state of the /// dkg and coconut contracts - dkg_query_client: Option>, + dkg_query_client: Option, /// Alternative provider of network topology used for constructing sphinx packets. custom_topology_provider: Option>, @@ -244,10 +243,12 @@ where ) -> Result> { // don't create dkg client for the bandwidth controller if credentials are disabled let dkg_query_client = if config.enabled_credentials_mode { - let client_config = nym_validator_client::Config::try_from_nym_network_details( - &config.network_details, + let client_config = + nyxd::Config::try_from_nym_network_details(&config.network_details)?; + let client = QueryHttpRpcNyxdClient::connect( + client_config, + config.network_details.endpoints[0].nyxd_url.as_str(), )?; - let client = nym_validator_client::Client::new_query(client_config)?; Some(client) } else { None @@ -323,7 +324,7 @@ where // this will perform necessary key and details load and optional store let _init_result = nym_client_core::init::setup_gateway( - &gateway_setup, + gateway_setup, self.storage.key_store(), self.storage.gateway_details_store(), !self.config.key_mode.is_keep(), @@ -496,15 +497,15 @@ where let reconstructed_receiver = client_output.register_receiver()?; - Ok(MixnetClient { + Ok(MixnetClient::new( nym_address, client_input, client_output, client_state, reconstructed_receiver, - task_manager: started_client.task_manager, - packet_type: None, - }) + started_client.task_manager, + None, + )) } } diff --git a/sdk/rust/nym-sdk/src/mixnet/native_client.rs b/sdk/rust/nym-sdk/src/mixnet/native_client.rs index 569997dcc6..3266b37506 100644 --- a/sdk/rust/nym-sdk/src/mixnet/native_client.rs +++ b/sdk/rust/nym-sdk/src/mixnet/native_client.rs @@ -1,3 +1,9 @@ +use crate::mixnet::client::MixnetClientBuilder; +use crate::mixnet::traits::MixnetMessageSender; +use crate::{Error, Result}; +use async_trait::async_trait; +use futures::{ready, Stream, StreamExt}; +use log::error; use nym_client_core::client::{ base_client::{ClientInput, ClientOutput, ClientState}, inbound_messages::InputMessage, @@ -6,16 +12,12 @@ use nym_client_core::client::{ use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::{params::PacketType, receiver::ReconstructedMessage}; use nym_task::{ - connections::{ConnectionCommandSender, LaneQueueLengths, TransmissionLane}, + connections::{ConnectionCommandSender, LaneQueueLengths}, TaskManager, }; - -use futures::StreamExt; -use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_topology::NymTopology; - -use crate::mixnet::client::{IncludedSurbs, MixnetClientBuilder}; -use crate::Result; +use std::pin::Pin; +use std::task::{Context, Poll}; /// Client connected to the Nym mixnet. pub struct MixnetClient { @@ -41,9 +43,33 @@ pub struct MixnetClient { /// The task manager that controlls all the spawned tasks that the clients uses to do it's job. pub(crate) task_manager: TaskManager, pub(crate) packet_type: Option, + + // internal state used for the `Stream` implementation + _buffered: Vec, } impl MixnetClient { + pub(crate) fn new( + nym_address: Recipient, + client_input: ClientInput, + client_output: ClientOutput, + client_state: ClientState, + reconstructed_receiver: ReconstructedMessagesReceiver, + task_manager: TaskManager, + packet_type: Option, + ) -> Self { + Self { + nym_address, + client_input, + client_output, + client_state, + reconstructed_receiver, + task_manager, + packet_type, + _buffered: Vec::new(), + } + } + /// Create a new client and connect to the mixnet using ephemeral in-memory keys that are /// discarded at application close. /// @@ -74,9 +100,10 @@ impl MixnetClient { /// Get a shallow clone of [`MixnetClientSender`]. Useful if you want split the send and /// receive logic in different locations. - pub fn sender(&self) -> MixnetClientSender { + pub fn split_sender(&self) -> MixnetClientSender { MixnetClientSender { client_input: self.client_input.clone(), + packet_type: self.packet_type, } } @@ -112,132 +139,6 @@ impl MixnetClient { self.client_state.topology_accessor.release_manual_control() } - /// Sends stringy data to the supplied Nym address - /// - /// # Example - /// - /// ```no_run - /// use nym_sdk::mixnet; - /// - /// #[tokio::main] - /// async fn main() { - /// let address = "foobar"; - /// let recipient = mixnet::Recipient::try_from_base58_string(address).unwrap(); - /// let mut client = mixnet::MixnetClient::connect_new().await.unwrap(); - /// client.send_str(recipient, "hi").await; - /// } - /// ``` - pub async fn send_str(&self, address: Recipient, message: &str) { - self.send_bytes(address, message, IncludedSurbs::default()) - .await; - } - - /// Sends bytes to the supplied Nym address. There is the option to specify the number of - /// reply-SURBs to include. - /// - /// # Example - /// - /// ```no_run - /// use nym_sdk::mixnet; - /// - /// #[tokio::main] - /// async fn main() { - /// let address = "foobar"; - /// let recipient = mixnet::Recipient::try_from_base58_string(address).unwrap(); - /// let mut client = mixnet::MixnetClient::connect_new().await.unwrap(); - /// let surbs = mixnet::IncludedSurbs::default(); - /// client.send_bytes(recipient, "hi".to_owned().into_bytes(), surbs).await; - /// } - /// ``` - pub async fn send_bytes>( - &self, - address: Recipient, - message: M, - surbs: IncludedSurbs, - ) { - let lane = TransmissionLane::General; - let input_msg = match surbs { - IncludedSurbs::Amount(surbs) => InputMessage::new_anonymous( - address, - message.as_ref().to_vec(), - surbs, - lane, - self.packet_type, - ), - IncludedSurbs::ExposeSelfAddress => InputMessage::new_regular( - address, - message.as_ref().to_vec(), - lane, - self.packet_type, - ), - }; - self.send(input_msg).await - } - - /// Sends stringy reply data to the supplied anonymous recipient. - /// - /// # Example - /// - /// ```no_run - /// use nym_sdk::mixnet; - /// - /// #[tokio::main] - /// async fn main() { - /// let mut client = mixnet::MixnetClient::connect_new().await.unwrap(); - /// // note: the tag is something you would have received from a remote client sending you surbs! - /// let tag = mixnet::AnonymousSenderTag::try_from_base58_string("foobar").unwrap(); - /// client.send_str_reply(tag, "hi").await; - /// } - /// ``` - pub async fn send_str_reply(&self, recipient_tag: AnonymousSenderTag, message: &str) { - self.send_reply(recipient_tag, message).await; - } - - /// Sends binary reply data to the supplied anonymous recipient. - /// - /// # Example - /// - /// ```no_run - /// use nym_sdk::mixnet; - /// - /// #[tokio::main] - /// async fn main() { - /// let mut client = mixnet::MixnetClient::connect_new().await.unwrap(); - /// // note: the tag is something you would have received from a remote client sending you surbs! - /// let tag = mixnet::AnonymousSenderTag::try_from_base58_string("foobar").unwrap(); - /// client.send_reply(tag, b"hi").await; - /// } - /// ``` - pub async fn send_reply>(&self, recipient_tag: AnonymousSenderTag, message: M) { - let lane = TransmissionLane::General; - let input_msg = InputMessage::new_reply( - recipient_tag, - message.as_ref().to_vec(), - lane, - self.packet_type, - ); - self.send(input_msg).await - } - - /// Sends a [`InputMessage`] to the mixnet. This is the most low-level sending function, for - /// full customization. - async fn send(&self, message: InputMessage) { - if self.client_input.send(message).await.is_err() { - log::error!("Failed to send message"); - } - } - - /// Sends a [`InputMessage`] to the mixnet. This is the most low-level sending function, for - /// full customization. - /// - /// Waits until the message is actually sent, or close to being sent, until returning. - /// - /// NOTE: this not yet implemented. - #[allow(unused)] - async fn send_wait(&self, _message: InputMessage) { - todo!(); - } - /// Wait for messages from the mixnet pub async fn wait_for_messages(&mut self) -> Option> { self.reconstructed_receiver.next().await @@ -265,12 +166,63 @@ impl MixnetClient { pub struct MixnetClientSender { client_input: ClientInput, + packet_type: Option, } -impl MixnetClientSender { - pub async fn send_input_message(&mut self, message: InputMessage) { - if self.client_input.send(message).await.is_err() { - log::error!("Failed to send message"); +impl Stream for MixnetClient { + type Item = ReconstructedMessage; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if let Some(next) = self._buffered.pop() { + cx.waker().wake_by_ref(); + return Poll::Ready(Some(next)); + } + match ready!(Pin::new(&mut self.reconstructed_receiver).poll_next(cx)) { + None => Poll::Ready(None), + Some(mut msgs) => { + // the vector itself should never be empty + if let Some(next) = msgs.pop() { + // there's more than a single message - buffer them and wake the waker + // to get polled again immediately + if !msgs.is_empty() { + self._buffered = msgs; + cx.waker().wake_by_ref(); + } + Poll::Ready(Some(next)) + } else { + error!("the reconstructed messages vector is empty - please let the developers know if you see this message"); + cx.waker().wake_by_ref(); + Poll::Pending + } + } } } } + +#[async_trait] +impl MixnetMessageSender for MixnetClient { + fn packet_type(&self) -> Option { + self.packet_type + } + + async fn send(&self, message: InputMessage) -> Result<()> { + self.client_input + .send(message) + .await + .map_err(|_| Error::MessageSendingFailure) + } +} + +#[async_trait] +impl MixnetMessageSender for MixnetClientSender { + fn packet_type(&self) -> Option { + self.packet_type + } + + async fn send(&self, message: InputMessage) -> Result<()> { + self.client_input + .send(message) + .await + .map_err(|_| Error::MessageSendingFailure) + } +} diff --git a/sdk/rust/nym-sdk/src/mixnet/paths.rs b/sdk/rust/nym-sdk/src/mixnet/paths.rs index 5c09752280..288218f5c5 100644 --- a/sdk/rust/nym-sdk/src/mixnet/paths.rs +++ b/sdk/rust/nym-sdk/src/mixnet/paths.rs @@ -51,7 +51,8 @@ impl StoragePaths { /// /// This function will return an error if it is passed a path to an existing file instead of a /// directory. - pub fn new_from_dir(dir: &Path) -> Result { + pub fn new_from_dir>(dir: P) -> Result { + let dir = dir.as_ref(); if dir.is_file() { return Err(Error::ExpectedDirectory(dir.to_owned())); } diff --git a/sdk/rust/nym-sdk/src/mixnet/traits.rs b/sdk/rust/nym-sdk/src/mixnet/traits.rs new file mode 100644 index 0000000000..8eb78109f7 --- /dev/null +++ b/sdk/rust/nym-sdk/src/mixnet/traits.rs @@ -0,0 +1,119 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use crate::mixnet::{AnonymousSenderTag, IncludedSurbs, Recipient}; +use crate::Result; +use async_trait::async_trait; +use nym_client_core::client::inbound_messages::InputMessage; +use nym_sphinx::params::PacketType; +use nym_task::connections::TransmissionLane; + +// defined to guarantee common interface regardless of whether you're using the full client +// or just the sending handler +#[async_trait] +pub trait MixnetMessageSender { + fn packet_type(&self) -> Option { + None + } + + /// Sends a [`InputMessage`] to the mixnet. This is the most low-level sending function, for + /// full customization. + async fn send(&self, message: InputMessage) -> Result<()>; + + /// Sends data to the supplied Nym address with the default surb behaviour. + /// + /// # Example + /// + /// ```no_run + /// use nym_sdk::mixnet::{self, MixnetMessageSender}; + /// + /// #[tokio::main] + /// async fn main() { + /// let address = "foobar"; + /// let recipient = mixnet::Recipient::try_from_base58_string(address).unwrap(); + /// let mut client = mixnet::MixnetClient::connect_new().await.unwrap(); + /// client.send_plain_message(recipient, "hi").await.unwrap(); + /// } + /// ``` + async fn send_plain_message(&self, address: Recipient, message: M) -> Result<()> + where + M: AsRef<[u8]> + Send, + { + self.send_message(address, message, IncludedSurbs::default()) + .await + } + + /// Sends bytes to the supplied Nym address. There is the option to specify the number of + /// reply-SURBs to include. + /// + /// # Example + /// + /// ```no_run + /// use nym_sdk::mixnet::{self, MixnetMessageSender}; + /// + /// #[tokio::main] + /// async fn main() { + /// let address = "foobar"; + /// let recipient = mixnet::Recipient::try_from_base58_string(address).unwrap(); + /// let mut client = mixnet::MixnetClient::connect_new().await.unwrap(); + /// let surbs = mixnet::IncludedSurbs::default(); + /// client.send_message(recipient, "hi".to_owned().into_bytes(), surbs).await.unwrap(); + /// } + /// ``` + async fn send_message( + &self, + address: Recipient, + message: M, + surbs: IncludedSurbs, + ) -> Result<()> + where + M: AsRef<[u8]> + Send, + { + let lane = TransmissionLane::General; + let input_msg = match surbs { + IncludedSurbs::Amount(surbs) => InputMessage::new_anonymous( + address, + message.as_ref().to_vec(), + surbs, + lane, + self.packet_type(), + ), + IncludedSurbs::ExposeSelfAddress => InputMessage::new_regular( + address, + message.as_ref().to_vec(), + lane, + self.packet_type(), + ), + }; + self.send(input_msg).await + } + + /// Sends reply data to the supplied anonymous recipient. + /// + /// # Example + /// + /// ```no_run + /// use nym_sdk::mixnet::{self, MixnetMessageSender}; + /// + /// #[tokio::main] + /// async fn main() { + /// let mut client = mixnet::MixnetClient::connect_new().await.unwrap(); + /// // note: the tag is something you would have received from a remote client sending you surbs! + /// let tag = mixnet::AnonymousSenderTag::try_from_base58_string("foobar").unwrap(); + /// client.send_reply(tag, b"hi").await.unwrap(); + /// } + /// ``` + async fn send_reply(&self, recipient_tag: AnonymousSenderTag, message: M) -> Result<()> + where + M: AsRef<[u8]> + Send, + { + let lane = TransmissionLane::General; + let input_msg = InputMessage::new_reply( + recipient_tag, + message.as_ref().to_vec(), + lane, + self.packet_type(), + ); + self.send(input_msg).await + } +} diff --git a/sdk/typescript/packages/sdk/.gitignore b/sdk/typescript/packages/sdk/.gitignore index 6166d37c02..ed481bbc45 100644 --- a/sdk/typescript/packages/sdk/.gitignore +++ b/sdk/typescript/packages/sdk/.gitignore @@ -1,2 +1,3 @@ src/mixnet/wasm/worker.js -src/mixnet/node-tester/worker.js \ No newline at end of file +src/mixnet/node-tester/worker.js +docs/ \ No newline at end of file diff --git a/sdk/typescript/packages/sdk/README-GenerateDocs.md b/sdk/typescript/packages/sdk/README-GenerateDocs.md new file mode 100644 index 0000000000..7458ba1069 --- /dev/null +++ b/sdk/typescript/packages/sdk/README-GenerateDocs.md @@ -0,0 +1,17 @@ +## Install dependencies + +run `yarn install` to install dependencies + +## Generate docs + +We use TypeDoc for document generation https://typedoc.org/ + +To generate docs run `yarn docs:generate`. Generated docs can be found the `./docs` directory. + +To view the generated docs in a webpage run `yarn docs:serve`. The docs will be available to view at `http://localhost:3000`. + +## Local document development + +To support the development process we have a local server that will watch for changes to the src files and update the docs in real time. + +Run `yarn docs:dev` to start a local server to view the docs. Again, The docs will be available to view at `http://localhost:3000`. diff --git a/sdk/typescript/packages/sdk/package.json b/sdk/typescript/packages/sdk/package.json index c156bd5876..17d2c9d116 100644 --- a/sdk/typescript/packages/sdk/package.json +++ b/sdk/typescript/packages/sdk/package.json @@ -28,11 +28,15 @@ "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", + "docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"yarn docs:generate\"", + "docs:generate": "typedoc", + "docs:serve": "reload -b -d ./docs -p 3000", + "docs:dev": "run-p docs:watch docs:serve ", "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.0.0", + "@nymproject/nym-client-wasm": "1", "comlink": "^4.3.1", "lerna": "^6.6.2", "node-gyp": "^9.3.1" @@ -52,10 +56,10 @@ "@rollup/plugin-terser": "^0.2.1", "@rollup/plugin-typescript": "^10.0.1", "@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", + "@typescript-eslint/eslint-plugin": "^5.13.0", + "@typescript-eslint/parser": "^5.13.0", "eslint": "^8.10.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-typescript": "^16.1.0", @@ -67,13 +71,15 @@ "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-react": "^7.29.2", "eslint-plugin-react-hooks": "^4.3.0", + "jest": "^29.5.0", + "nodemon": "3.0.1", + "reload": "^3.2.1", "rimraf": "^3.0.2", "rollup": "^3.9.1", "rollup-plugin-base64": "^1.0.1", "rollup-plugin-web-worker-loader": "^1.6.1", - "typescript": "^4.8.4", - "jest": "^29.5.0", "ts-jest": "^29.1.0", - "ts-loader": "^9.4.2" + "typedoc": "^0.24.8", + "typescript": "^4.8.4" } } diff --git a/sdk/typescript/packages/sdk/src/coconut/index.ts b/sdk/typescript/packages/sdk/src/coconut/index.ts index 02138c9bab..3011daa2e0 100644 --- a/sdk/typescript/packages/sdk/src/coconut/index.ts +++ b/sdk/typescript/packages/sdk/src/coconut/index.ts @@ -1,2 +1,7 @@ // eslint-disable-next-line no-console + +/** + * @ignore + * @internal + */ export const notImplementedYet = () => console.log('Not implement, coming soon...'); diff --git a/sdk/typescript/packages/sdk/src/mixnet/node-tester/constants.ts b/sdk/typescript/packages/sdk/src/mixnet/node-tester/constants.ts deleted file mode 100644 index 55770cd39b..0000000000 --- a/sdk/typescript/packages/sdk/src/mixnet/node-tester/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -const QA_VALIDATOR_URL = 'https://qa-nym-api.qa.nymte.ch/api'; -const QWERTY_VALIDATOR_URL = 'https://qwerty-validator-api.qa.nymte.ch/api'; -const MAINNET_VALIDATOR_URL = 'https://validator.nymtech.net/api/'; - -export { QA_VALIDATOR_URL, QWERTY_VALIDATOR_URL, MAINNET_VALIDATOR_URL }; diff --git a/sdk/typescript/packages/sdk/src/mixnet/node-tester/types.ts b/sdk/typescript/packages/sdk/src/mixnet/node-tester/types.ts index a556fec243..5bf1afb4d1 100644 --- a/sdk/typescript/packages/sdk/src/mixnet/node-tester/types.ts +++ b/sdk/typescript/packages/sdk/src/mixnet/node-tester/types.ts @@ -25,8 +25,6 @@ export interface NodeTesterLoadedEvent { }; } -export type Network = 'QA' | 'SANDBOX' | 'MAINNET'; - export type NodeTestResultResponse = { score: number; sentPackets: number; @@ -35,31 +33,3 @@ export type NodeTestResultResponse = { duplicatePackets: number; duplicateAcks: number; }; - -export type Error = { - kind: 'Error'; - args: { message: string }; -}; - -export type WorkerLoaded = { - kind: 'WorkerLoaded'; -}; - -export type DisplayTesterResults = { - kind: 'DisplayTesterResults'; - args: { - result: NodeTestResultResponse; - }; -}; - -export type TestPacket = { - kind: 'TestPacket'; - args: { - mixnodeIdentity: string; - network: Network; - }; -}; - -export type TestStatus = 'Stopped' | 'Running' | 'Complete'; - -export type NodeTestEvent = Error | DisplayTesterResults | TestPacket | WorkerLoaded; diff --git a/sdk/typescript/packages/sdk/src/mixnet/wasm/index.ts b/sdk/typescript/packages/sdk/src/mixnet/wasm/index.ts index 1b80d844e8..e124b7136c 100644 --- a/sdk/typescript/packages/sdk/src/mixnet/wasm/index.ts +++ b/sdk/typescript/packages/sdk/src/mixnet/wasm/index.ts @@ -5,8 +5,8 @@ import { ConnectedEvent, EventKinds, IWebWorker, - IWebWorkerAsync, - IWebWorkerEvents, + Client, + Events, LoadedEvent, MimeTypes, RawMessageReceivedEvent, @@ -15,20 +15,40 @@ import { import { createSubscriptions } from './subscriptions'; /** - * Client for the Nym mixnet. + * Options for the Nym mixnet client. + * @property autoConvertStringMimeTypes - An array of mime types. + * @example + * ```typescript + * const client = await createNymMixnetClient({ + * autoConvertStringMimeTypes: [MimeTypes.ApplicationJson, MimeTypes.TextPlain], + * }); + * ``` + */ + +export interface NymMixnetClientOptions { + autoConvertStringMimeTypes?: string[] | MimeTypes[]; +} + +/** + * The client for the Nym mixnet which gives access to client methods and event subscriptions. + * Returned by the {@link createNymMixnetClient} function. + * */ export interface NymMixnetClient { - client: IWebWorkerAsync; - events: IWebWorkerEvents; + client: Client; + events: Events; } /** * Create a client to send and receive traffic from the Nym mixnet. - * + * @required + * @returns + * @example + * ```typescript + * const client = await createNymMixnetClient(); + * ``` */ -export const createNymMixnetClient = async (options?: { - autoConvertStringMimeTypes?: string[] | MimeTypes[]; -}): Promise => { +export const createNymMixnetClient = async (options?: NymMixnetClientOptions): Promise => { // create a web worker that runs the WASM client on another thread and wait until it signals that it is ready // eslint-disable-next-line @typescript-eslint/no-use-before-define const worker = await createWorker(); @@ -53,7 +73,7 @@ export const createNymMixnetClient = async (options?: { }); // manage the subscribers, returning self-unsubscribe methods - const events: IWebWorkerEvents = { + const events: Events = { subscribeToConnected: (handler) => addSubscription(EventKinds.Connected, handler), subscribeToLoaded: (handler) => addSubscription(EventKinds.Loaded, handler), subscribeToTextMessageReceivedEvent: (handler) => @@ -65,7 +85,7 @@ export const createNymMixnetClient = async (options?: { }; // let comlink handle interop with the web worker - const client: IWebWorkerAsync = Comlink.wrap(worker); + const client: Client = Comlink.wrap(worker); // set any options if (options?.autoConvertStringMimeTypes) { diff --git a/sdk/typescript/packages/sdk/src/mixnet/wasm/types.ts b/sdk/typescript/packages/sdk/src/mixnet/wasm/types.ts index 1f11384b1a..7df48ba73b 100644 --- a/sdk/typescript/packages/sdk/src/mixnet/wasm/types.ts +++ b/sdk/typescript/packages/sdk/src/mixnet/wasm/types.ts @@ -3,32 +3,138 @@ import type { DebugWasm } from './types-from-wasm-pack'; export * from './types-from-wasm-pack'; /** - * Some common mime types, however, you can always just specify the mime-type as a string + * + * @ignore + * @hidden + * @internal */ -export enum MimeTypes { - ApplicationOctetStream = 'application/octet-stream', - TextPlain = 'text/plain', - ApplicationJson = 'application/json', +export interface IWebWorker { + start: (config: NymClientConfig) => void; + stop: () => void; + selfAddress: () => string | undefined; + setTextMimeTypes: (mimeTypes: string[]) => void; + getTextMimeTypes: () => string[]; + send: (args: { payload: Payload; recipient: string; replySurbs?: number }) => void; + rawSend: (args: { payload: Uint8Array; recipient: string; replySurbs?: number }) => void; } -export interface Payload { - message: string | Uint8Array; - - mimeType?: MimeTypes | string; - - headers?: string; +export interface Client { + /** + * Start the client. + * + * @example + * + * ```typescript + * const client = await createNymMixnetClient(); + * await client.start({ + * clientId: 'my-client', + * nymApiUrl: 'https://validator.nymtech.net/api', + * }); + * + */ + start: (config: NymClientConfig) => Promise; + /** + * Stop the client. + * @example + * ```typescript + * const client = await createNymMixnetClient(); + * await client.start({ + * clientId: 'my-client', + * nymApiUrl: 'https://validator.nymtech.net/api', + * }); + * await client.stop(); + * ``` + */ + stop: () => Promise; + /** + * Get the client address + * @example + * ```typescript + * const client = await createNymMixnetClient(); + * await client.start({ + * clientId: 'my-client', + * nymApiUrl: 'https://validator.nymtech.net/api', + * }); + * const address = await client.selfAddress(); + * ``` + */ + selfAddress: () => Promise; + /** + * Set the mime-types that should be used when using the {@link Client.send} method. + * @example + * ```typescript + * const client = await createNymMixnetClient(); + * await client.start({ + * clientId: 'my-client', + * nymApiUrl: 'https://validator.nymtech.net/api', + * }); + * await client.setTextMimeTypes(['text/plain', 'application/json']); + * ``` + * @param mimeTypes + * @see {@link MimeTypes} + * @see {@link Client.send} + * @see {@link Client.getTextMimeTypes} + */ + setTextMimeTypes: (mimeTypes: string[]) => void; + /** + * Get the mime-types that are automatically converted to strings. + * @example + * ```typescript + * const client = await createNymMixnetClient(); + * await client.start({ + * clientId: 'my-client', + * nymApiUrl: 'https://validator.nymtech.net/api', + * }); + * const mimeTypes = await client.getTextMimeTypes(); + * ``` + * @see {@link MimeTypes} + * @see {@link Payload} + * @see {@link Client.send} + * @see {@link Client.setTextMimeTypes} + */ + getTextMimeTypes: () => Promise; + /** + * Send some data through the mixnet message. + * @example + * ```typescript + * const client = await createNymMixnetClient(); + * await client.start({ + * clientId: 'my-client', + * nymApiUrl: 'https://validator.nymtech.net/api', + * }); + * await client.send({ + * payload: 'Hello world', + * recipient: // recipient address, + * }); + * ``` + * @see {@link MimeTypes} + * @see {@link Payload} + */ + send: (args: { payload: Payload; recipient: string; replySurbs?: number }) => Promise; + /** + * Send a raw payload, without any mime-type conversion. + * @example + * ```typescript + * const client = await createNymMixnetClient(); + * await client.start({ + * clientId: 'my-client', + * nymApiUrl: 'https://validator.nymtech.net/api', + * }); + * const payload = new Uint8Array([1, 2, 3]); + * await client.rawSend({ + * payload, + * recipient: // recipient address, + * }); + * ``` + * @see {@link MimeTypes} + * @see {@link Payload} + */ + rawSend: (args: { payload: Uint8Array; recipient: string; replySurbs?: number }) => Promise; } -export type OnPayloadFn = (payload: Payload) => void; - -export type OnRawPayloadFn = (payload: Uint8Array) => void; - -export type EventHandlerFn = (e: E) => void | Promise; - -export type EventHandlerSubscribeFn = (fn: EventHandlerFn) => EventHandlerUnsubscribeFn; - -export type EventHandlerUnsubscribeFn = () => void; - +/** + * The configuration passed to the {@link Client.start} method of the {@link Client} + */ export interface NymClientConfig { /** * A human-readable id for the client. @@ -56,31 +162,99 @@ export interface NymClientConfig { debug?: DebugWasm; } -export interface IWebWorker { - start: (config: NymClientConfig) => void; - stop: () => void; - selfAddress: () => string | undefined; - setTextMimeTypes: (mimeTypes: string[]) => void; - getTextMimeTypes: () => string[]; - send: (args: { payload: Payload; recipient: string; replySurbs?: number }) => void; - rawSend: (args: { payload: Uint8Array; recipient: string; replySurbs?: number }) => void; -} - -export interface IWebWorkerAsync { - start: (config: NymClientConfig) => Promise; - stop: () => Promise; - selfAddress: () => Promise; - setTextMimeTypes: (mimeTypes: string[]) => void; - getTextMimeTypes: () => Promise; - send: (args: { payload: Payload; recipient: string; replySurbs?: number }) => Promise; - rawSend: (args: { payload: Uint8Array; recipient: string; replySurbs?: number }) => Promise; +export interface Events { + /** + * @see {@link LoadedEvent} + * @example + * ```typescript + * events.subscribeToLoaded((e) => { + * console.log(e.args); // { loaded: true } + * }); + * ``` + */ + subscribeToLoaded: EventHandlerSubscribeFn; + /** + * @see {@link ConnectedEvent} + * @example + * ```typescript + * events.subscribeConnected((e) => { + * console.log(e.args.address); // Client address + * }); + * + */ + subscribeToConnected: EventHandlerSubscribeFn; + /** + * @returns {@link EventHandlerUnsubscribeFn} + * @see {@link StringMessageReceivedEvent} + * @example + * ```typescript + * const unsubscribe = events.subscribeToTextMessageReceivedEvent((e) => { + * console.log(e.args.payload); // string + * }); + * + * // Stop listening to the event + * unsubscribe(); + * ``` + */ + subscribeToTextMessageReceivedEvent: EventHandlerSubscribeFn; + /** + * @returns {@link EventHandlerUnsubscribeFn} + * @see {@link BinaryMessageReceivedEvent} + * @example + * ```typescript + * const unsubscribe = events.subscribeToBinaryMessageReceivedEvent((e) => { + * console.log(e.args.payload); // Uint8Array + * }); + * + * // Stop listening to the event + * unsubscribe(); + * ``` + */ + subscribeToBinaryMessageReceivedEvent: EventHandlerSubscribeFn; + /** + * @returns {@link EventHandlerUnsubscribeFn} + * @see {@link RawMessageReceivedEvent} + * @example + * ```typescript + * const unsubscribe = events.subscribeToRawMessageReceivedEvent((e) => { + * console.log(e.args.payload); // Uint8Array + * }); + * + * // Stop listening to the event + * unsubscribe(); + * ``` + */ + subscribeToRawMessageReceivedEvent: EventHandlerSubscribeFn; } +/** + * Enum representing various event kinds. + * @enum + */ export enum EventKinds { + /** + * The event emitted when the nodetester is ready to be used. + */ Loaded = 'Loaded', + + /** + * The event emitted when connection to the gateway is established. + */ Connected = 'Connected', + + /** + * The event for when a message is received and interpreted as a string. + */ StringMessageReceived = 'StringMessageReceived', + + /** + * The event for when a binary message is received. BinaryMessage is a type of message that contains additional metadata, such as MIME type and some headers, along with the actual payload data. + */ BinaryMessageReceived = 'BinaryMessageReceived', + + /** + * The event for when a raw message is received. RawMessage represents the bytes that are received directly from the mixnet with no further parsing or interpretation done on them. + */ RawMessageReceived = 'RawMessageReceived', } @@ -107,7 +281,6 @@ export interface StringMessageReceivedEvent { headers?: string; }; } - export interface BinaryMessageReceivedEvent { kind: EventKinds.BinaryMessageReceived; args: { @@ -124,10 +297,54 @@ export interface RawMessageReceivedEvent { }; } -export interface IWebWorkerEvents { - subscribeToLoaded: EventHandlerSubscribeFn; - subscribeToConnected: EventHandlerSubscribeFn; - subscribeToTextMessageReceivedEvent: EventHandlerSubscribeFn; - subscribeToBinaryMessageReceivedEvent: EventHandlerSubscribeFn; - subscribeToRawMessageReceivedEvent: EventHandlerSubscribeFn; +/** + * Some common mime types, however, you can always just specify the mime-type as a string + */ +export enum MimeTypes { + ApplicationOctetStream = 'application/octet-stream', + TextPlain = 'text/plain', + ApplicationJson = 'application/json', } + +export interface Payload { + message: string | Uint8Array; + mimeType?: MimeTypes | string; + headers?: string; +} + +/** + * @ignore + * @internal + */ +export type OnPayloadFn = (payload: Payload) => void; +/** + * @ignore + * @internal + */ +export type OnRawPayloadFn = (payload: Uint8Array) => void; + +/** + * The **EventHandlerSubscribeFn** is a function that takes a callback of type {@link EventHandlerFn} + * + * @see {@link Events} + * @see {@link EventHandlerFn} + * @see {@link EventHandlerUnsubscribeFn} + */ +export type EventHandlerSubscribeFn = (fn: EventHandlerFn) => EventHandlerUnsubscribeFn; + +/** + * The **EventHandlerFn** is a callback function that is passed to the {@link EventHandlerSubscribeFn} + * @see {@link Events} + * @see {@link EventHandlerFn} + * @see {@link EventHandlerSubscribeFn} + */ +export type EventHandlerFn = (e: E) => void | Promise; + +/** + * The **EventHandlerUnsubscribeFn** function is returned by the {@link EventHandlerSubscribeFn} + * and can be used to stop listening for particular events + * @see {@link Events} + * @see {@link EventHandlerFn} + * @see {@link EventHandlerSubscribeFn} + */ +export type EventHandlerUnsubscribeFn = () => void; diff --git a/sdk/typescript/packages/sdk/typedoc.json b/sdk/typescript/packages/sdk/typedoc.json new file mode 100644 index 0000000000..507b6156cc --- /dev/null +++ b/sdk/typescript/packages/sdk/typedoc.json @@ -0,0 +1,32 @@ +{ + "sort": ["kind"], + "entryPoints": ["./src/index.ts"], + "out": "./docs", + "exclude": ["./src/**/node-tester/*", "./src/**/wasm/types-from-wasm-pack.ts"], + "kindSortOrder": [ + "Function", + "Interface", + "TypeAlias", + "Reference", + "Project", + "Module", + "Namespace", + "Enum", + "EnumMember", + "Class", + "Constructor", + "Property", + "Variable", + "Accessor", + "Method", + "ObjectLiteral", + "Parameter", + "TypeParameter", + "TypeLiteral", + "CallSignature", + "ConstructorSignature", + "IndexSignature", + "GetSignature", + "SetSignature" + ] +} diff --git a/service-providers/common/examples/control_requests.rs b/service-providers/common/examples/control_requests.rs index 17af10448c..14886390f8 100644 --- a/service-providers/common/examples/control_requests.rs +++ b/service-providers/common/examples/control_requests.rs @@ -3,7 +3,9 @@ // use nym_client::client::config::{BaseClientConfig, Config, GatewayEndpointConfig}; // use nym_client::client::{DirectClient, KeyManager, Recipient, ReconstructedMessage, SocketClient}; -use nym_sdk::mixnet::{IncludedSurbs, MixnetClient, Recipient, ReconstructedMessage}; +use nym_sdk::mixnet::{ + IncludedSurbs, MixnetClient, MixnetMessageSender, Recipient, ReconstructedMessage, +}; use nym_service_providers_common::interface::{ ControlRequest, ControlResponse, ProviderInterfaceVersion, Request, Response, ResponseContent, }; @@ -51,34 +53,34 @@ async fn main() -> anyhow::Result<()> { // TODO: currently we HAVE TO use surbs unfortunately println!("Sending 'Health' request..."); client - .send_bytes( + .send_message( provider, full_request_health.into_bytes(), IncludedSurbs::new(10), ) - .await; + .await?; let response = wait_for_control_response(&mut client).await; println!("response to 'Health' request: {response:#?}"); println!("Sending 'BinaryInfo' request..."); client - .send_bytes( + .send_message( provider, full_request_binary_info.into_bytes(), IncludedSurbs::none(), ) - .await; + .await?; let response = wait_for_control_response(&mut client).await; println!("response to 'BinaryInfo' request: {response:#?}"); println!("Sending 'SupportedRequestVersions' request..."); client - .send_bytes( + .send_message( provider, full_request_versions.into_bytes(), IncludedSurbs::none(), ) - .await; + .await?; let response = wait_for_control_response(&mut client).await; println!("response to 'SupportedRequestVersions' request: {response:#?}"); diff --git a/service-providers/common/src/interface/control.rs b/service-providers/common/src/interface/control.rs index 8e808d6fdd..d6a0a28a68 100644 --- a/service-providers/common/src/interface/control.rs +++ b/service-providers/common/src/interface/control.rs @@ -130,7 +130,7 @@ impl Serializable for ControlResponse { fn into_bytes(self) -> Vec { std::iter::once(self.tag() as u8) - .chain(self.serialize_inner().into_iter()) + .chain(self.serialize_inner()) .collect() } diff --git a/service-providers/common/src/interface/request.rs b/service-providers/common/src/interface/request.rs index fefa1ffae6..12a884d79e 100644 --- a/service-providers/common/src/interface/request.rs +++ b/service-providers/common/src/interface/request.rs @@ -81,7 +81,7 @@ where pub fn into_bytes(self) -> Vec { if let Some(version) = self.interface_version.as_u8() { std::iter::once(version) - .chain(self.content.into_bytes(self.interface_version).into_iter()) + .chain(self.content.into_bytes(self.interface_version)) .collect() } else { self.content.into_bytes(self.interface_version) @@ -137,7 +137,7 @@ where } } else { std::iter::once(self.tag() as u8) - .chain(self.serialize_inner().into_iter()) + .chain(self.serialize_inner()) .collect() } } diff --git a/service-providers/common/src/interface/response.rs b/service-providers/common/src/interface/response.rs index 5aff60c5ad..ad502e8b30 100644 --- a/service-providers/common/src/interface/response.rs +++ b/service-providers/common/src/interface/response.rs @@ -71,7 +71,7 @@ where pub fn into_bytes(self) -> Vec { if let Some(version) = self.interface_version.as_u8() { std::iter::once(version) - .chain(self.content.into_bytes(self.interface_version).into_iter()) + .chain(self.content.into_bytes(self.interface_version)) .collect() } else { self.content.into_bytes(self.interface_version) @@ -127,7 +127,7 @@ where } } else { std::iter::once(self.tag() as u8) - .chain(self.serialize_inner().into_iter()) + .chain(self.serialize_inner()) .collect() } } diff --git a/service-providers/network-requester/Cargo.toml b/service-providers/network-requester/Cargo.toml index 5b80088b30..cf66ae325e 100644 --- a/service-providers/network-requester/Cargo.toml +++ b/service-providers/network-requester/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-network-requester" -version = "1.1.23" +version = "1.1.27" authors.workspace = true edition.workspace = true rust-version = "1.65" @@ -15,7 +15,7 @@ async-trait = { workspace = true } bs58 = "0.4.0" clap = {version = "4.0", features = ["cargo", "derive"]} dirs = "4.0" -futures = "0.3.24" +futures = { workspace = true } humantime-serde = "1.1.1" ipnetwork = "0.20.0" lazy_static = { workspace = true } @@ -24,7 +24,7 @@ pretty_env_logger = "0.4.0" publicsuffix = "1.5" # Can't update this until bip updates to support newer idna version rand = "0.7.3" regex = "1.8.4" -reqwest = { version = "0.11.11", features = ["json"] } +reqwest = { workspace = true, features = ["json"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } sqlx = { version = "0.6.1", features = ["runtime-tokio-rustls", "chrono"]} diff --git a/service-providers/network-requester/examples/query.rs b/service-providers/network-requester/examples/query.rs index 5b89e6924b..87f410ee75 100644 --- a/service-providers/network-requester/examples/query.rs +++ b/service-providers/network-requester/examples/query.rs @@ -1,7 +1,9 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use nym_sdk::mixnet::{IncludedSurbs, MixnetClient, Recipient, ReconstructedMessage}; +use nym_sdk::mixnet::{ + IncludedSurbs, MixnetClient, MixnetMessageSender, Recipient, ReconstructedMessage, +}; use nym_service_providers_common::interface::{ ProviderInterfaceVersion, Request, Response, ResponseContent, }; @@ -47,23 +49,23 @@ async fn main() -> anyhow::Result<()> { ); println!("Sending 'OpenProxy' query..."); client - .send_bytes( + .send_message( provider, open_proxy_request.into_bytes(), IncludedSurbs::new(10), ) - .await; + .await?; let response = wait_for_response(&mut client).await; println!("response to 'OpenProxy' query: {response:#?}"); println!("Sending 'Description' query..."); client - .send_bytes( + .send_message( provider, description_request.into_bytes(), IncludedSurbs::none(), ) - .await; + .await?; let response = wait_for_response(&mut client).await; println!("response to 'Description' query: {response:#?}"); diff --git a/service-providers/network-requester/src/allowed_hosts/hosts.rs b/service-providers/network-requester/src/allowed_hosts/hosts.rs index 5c48369d0d..b9d534fe89 100644 --- a/service-providers/network-requester/src/allowed_hosts/hosts.rs +++ b/service-providers/network-requester/src/allowed_hosts/hosts.rs @@ -100,7 +100,7 @@ impl HostsStore { .unwrap(); if let Err(e) = writeln!(file, "{text}") { - eprintln!("Couldn't write to file: {e}"); + log::error!("Couldn't write to file: {e}"); } } @@ -136,7 +136,6 @@ impl HostsStore { }) .map(Host::from) .collect(); - dbg!(&hosts); Ok(hosts) } } diff --git a/service-providers/network-requester/src/cli/build_info.rs b/service-providers/network-requester/src/cli/build_info.rs new file mode 100644 index 0000000000..e3385bb933 --- /dev/null +++ b/service-providers/network-requester/src/cli/build_info.rs @@ -0,0 +1,16 @@ +// Copyright 2023 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use clap::Args; +use nym_bin_common::bin_info_owned; +use nym_bin_common::output_format::OutputFormat; + +#[derive(Args)] +pub(crate) struct BuildInfo { + #[clap(short, long, default_value_t = OutputFormat::default())] + output: OutputFormat, +} + +pub(crate) fn execute(args: BuildInfo) { + println!("{}", args.output.format(&bin_info_owned!())) +} diff --git a/service-providers/network-requester/src/cli/init.rs b/service-providers/network-requester/src/cli/init.rs index cea6253803..30797dc26e 100644 --- a/service-providers/network-requester/src/cli/init.rs +++ b/service-providers/network-requester/src/cli/init.rs @@ -106,7 +106,7 @@ fn init_paths(id: &str) -> io::Result<()> { } pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> { - eprintln!("Initialising client..."); + log::info!("Initialising client..."); let id = &args.id; @@ -114,7 +114,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> { // in case we're using old config, try to upgrade it // (if we're using the current version, it's a no-op) try_upgrade_config(id)?; - eprintln!("Client \"{id}\" was already initialised before"); + log::info!("Client \"{id}\" was already initialised before"); true } else { init_paths(&args.id)?; @@ -125,7 +125,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> { // re-registering if wanted. let user_wants_force_register = args.force_register_gateway; if user_wants_force_register { - eprintln!("Instructed to force registering gateway. This might overwrite keys!"); + log::warn!("Instructed to force registering gateway. This might overwrite keys!"); } // If the client was already initialized, don't generate new keys and don't re-register with @@ -142,6 +142,7 @@ pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> { // Load and potentially override config let config = override_config(Config::new(id), OverrideConfig::from(args.clone())); + log::debug!("Using config: {:#?}", config); // Setup gateway by either registering a new one, or creating a new config from the selected // one but with keys kept, or reusing the gateway configuration. @@ -149,27 +150,28 @@ pub(crate) async fn execute(args: &Init) -> Result<(), NetworkRequesterError> { let details_store = OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details); let init_details = nym_client_core::init::setup_gateway( - &gateway_setup, + gateway_setup, &key_store, &details_store, register_gateway, Some(&config.base.client.nym_api_urls), ) .await - .tap_err(|err| eprintln!("Failed to setup gateway\nError: {err}"))?; + .tap_err(|err| log::error!("Failed to setup gateway\nError: {err}"))? + .details; let config_save_location = config.default_location(); config.save_to_default_location().tap_err(|_| { log::error!("Failed to save the config file"); })?; - eprintln!( + log::info!( "Saved configuration file to {}", config_save_location.display() ); let address = init_details.client_address()?; - eprintln!("Client configuration completed.\n"); + log::info!("Client configuration completed.\n"); let init_results = InitResults::new(&config, &address, &init_details.gateway_details); println!("{}", args.output.format(&init_results)); diff --git a/service-providers/network-requester/src/cli/mod.rs b/service-providers/network-requester/src/cli/mod.rs index e6a34432df..1eeed74c28 100644 --- a/service-providers/network-requester/src/cli/mod.rs +++ b/service-providers/network-requester/src/cli/mod.rs @@ -10,7 +10,7 @@ use crate::{ }; use clap::{CommandFactory, Parser, Subcommand}; use log::{error, info}; -use nym_bin_common::build_information::BinaryBuildInformation; +use nym_bin_common::bin_info; use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::version_checker; use nym_client_core::client::base_client::storage::gateway_details::{ @@ -21,13 +21,13 @@ use nym_client_core::config::GatewayEndpointConfig; use nym_client_core::error::ClientCoreError; use nym_sphinx::params::PacketSize; +mod build_info; mod init; mod run; mod sign; lazy_static::lazy_static! { - pub static ref PRETTY_BUILD_INFORMATION: String = - BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print(); + pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print(); } // Helper for passing LONG_VERSION to clap @@ -42,6 +42,10 @@ pub(crate) struct Cli { #[clap(short, long)] pub(crate) config_env_file: Option, + /// Flag used for disabling the printed banner in tty. + #[clap(long)] + pub(crate) no_banner: bool, + #[clap(subcommand)] command: Commands, } @@ -59,6 +63,9 @@ pub(crate) enum Commands { /// Sign to prove ownership of this network requester Sign(sign::Sign), + /// Show build information of this binary + BuildInfo(build_info::BuildInfo), + /// Generate shell completions Completions(ArgShell), @@ -77,7 +84,12 @@ pub(crate) struct OverrideConfig { } pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config { - let disable_cover_traffic_with_keepalive = args.medium_toggle; + // These flags have overlapping effects, meaning the order matters here. Making it a bit messy. + // Since a big chunk of these are hidden experimental flags there is hope we can remove them + // soonish and clean this up. + + let disable_cover_traffic_with_keepalive = + config.network_requester.disable_poisson_rate || args.medium_toggle; let secondary_packet_size = args.medium_toggle.then_some(PacketSize::ExtendedPacket16); let no_per_hop_delays = args.medium_toggle; @@ -119,10 +131,11 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config { pub(crate) async fn execute(args: Cli) -> Result<(), NetworkRequesterError> { let bin_name = "nym-network-requester"; - match &args.command { - Commands::Init(m) => init::execute(m).await?, - Commands::Run(m) => run::execute(m).await?, - Commands::Sign(m) => sign::execute(m).await?, + match args.command { + Commands::Init(m) => init::execute(&m).await?, + Commands::Run(m) => run::execute(&m).await?, + Commands::Sign(m) => sign::execute(&m).await?, + Commands::BuildInfo(m) => build_info::execute(m), Commands::Completions(s) => s.generate(&mut Cli::command(), bin_name), Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), } diff --git a/service-providers/network-requester/src/cli/run.rs b/service-providers/network-requester/src/cli/run.rs index 3c3e31ecbb..6827c3ed82 100644 --- a/service-providers/network-requester/src/cli/run.rs +++ b/service-providers/network-requester/src/cli/run.rs @@ -84,6 +84,7 @@ pub(crate) async fn execute(args: &Run) -> Result<(), NetworkRequesterError> { let mut config = try_load_current_config(&args.id)?; config = override_config(config, OverrideConfig::from(args.clone())); + log::debug!("Using config: {:#?}", config); if !version_check(&config) { error!("failed the local version check"); diff --git a/service-providers/network-requester/src/config/mod.rs b/service-providers/network-requester/src/config/mod.rs index ee7b4ce60e..e4ba1fa9da 100644 --- a/service-providers/network-requester/src/config/mod.rs +++ b/service-providers/network-requester/src/config/mod.rs @@ -159,9 +159,19 @@ impl Config { } } -#[derive(Debug, Default, Clone, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] #[serde(default, deny_unknown_fields)] -pub struct NetworkRequester {} +pub struct NetworkRequester { + pub disable_poisson_rate: bool, +} + +impl Default for NetworkRequester { + fn default() -> Self { + NetworkRequester { + disable_poisson_rate: true, + } + } +} #[derive(Debug, Clone, Copy, Deserialize, PartialEq, Serialize)] #[serde(default, deny_unknown_fields)] diff --git a/service-providers/network-requester/src/config/old_config_v1_1_20_2.rs b/service-providers/network-requester/src/config/old_config_v1_1_20_2.rs index 92defcd702..20e3634132 100644 --- a/service-providers/network-requester/src/config/old_config_v1_1_20_2.rs +++ b/service-providers/network-requester/src/config/old_config_v1_1_20_2.rs @@ -79,7 +79,7 @@ pub struct NetworkRequesterV1_1_20_2 {} impl From for NetworkRequester { fn from(_value: NetworkRequesterV1_1_20_2) -> Self { - NetworkRequester {} + NetworkRequester::default() } } diff --git a/service-providers/network-requester/src/config/template.rs b/service-providers/network-requester/src/config/template.rs index 34df005821..811c9602f4 100644 --- a/service-providers/network-requester/src/config/template.rs +++ b/service-providers/network-requester/src/config/template.rs @@ -37,7 +37,7 @@ nym_api_urls = [ {{/each}} ] -[storage_paths] +[storage_paths] # Path to file containing private identity key. keys.private_identity_key_file = '{{ storage_paths.keys.private_identity_key_file }}' @@ -76,6 +76,14 @@ allowed_list_location = '{{ storage_paths.allowed_list_location }}' unknown_list_location = '{{ storage_paths.unknown_list_location }}' +[network_requester] + +# Disable Poisson sending rate, and only send cover traffic occasionally as keepalive messages. +# This is equivalent to setting debug.traffic.disable_main_poisson_packet_distribution = true, +# and debug.cover_traffic.loop_cover_traffic_average_delay = 5s. +disable_poisson_rate = {{ network_requester.disable_poisson_rate }} + + ##### logging configuration options ##### [logging] diff --git a/service-providers/network-requester/src/core.rs b/service-providers/network-requester/src/core.rs index b60bc30f41..cd9b57f2dd 100644 --- a/service-providers/network-requester/src/core.rs +++ b/service-providers/network-requester/src/core.rs @@ -13,9 +13,10 @@ use crate::{reply, socks5}; use async_trait::async_trait; use futures::channel::mpsc; use log::warn; -use nym_bin_common::build_information::BinaryBuildInformation; +use nym_bin_common::bin_info_owned; use nym_client_core::config::disk_persistence::CommonClientPaths; use nym_network_defaults::NymNetworkDetails; +use nym_sdk::mixnet::MixnetMessageSender; use nym_service_providers_common::interface::{ BinaryInformation, ProviderInterfaceVersion, Request, RequestVersion, }; @@ -101,7 +102,7 @@ impl ServiceProvider for NRServiceProvider { ) -> Result { Ok(BinaryInformation { binary_name: env!("CARGO_PKG_NAME").to_string(), - build_information: BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).to_owned(), + build_information: bin_info_owned!(), }) } @@ -223,7 +224,7 @@ impl NRServiceProviderBuilder { }; let stats_collector_clone = stats_collector.clone(); - let mixnet_client_sender = mixnet_client.sender(); + let mixnet_client_sender = mixnet_client.split_sender(); let self_address = *mixnet_client.nym_address(); // start the listener for mix messages @@ -297,7 +298,7 @@ impl NRServiceProvider { /// Listens for any messages from `mix_reader` that should be written back to the mix network /// via the `websocket_writer`. async fn mixnet_response_listener( - mut mixnet_client_sender: nym_sdk::mixnet::MixnetClientSender, + mixnet_client_sender: nym_sdk::mixnet::MixnetClientSender, mut mix_input_reader: MixProxyReader, stats_collector: Option, ) { @@ -321,7 +322,7 @@ impl NRServiceProvider { } let response_message = msg.into_input_message(); - mixnet_client_sender.send_input_message(response_message).await; + mixnet_client_sender.send(response_message).await.unwrap(); } else { log::error!("Exiting: channel closed!"); break; diff --git a/service-providers/network-requester/src/main.rs b/service-providers/network-requester/src/main.rs index 696cb41c21..89ac717d37 100644 --- a/service-providers/network-requester/src/main.rs +++ b/service-providers/network-requester/src/main.rs @@ -18,11 +18,13 @@ mod statistics; #[tokio::main] async fn main() -> Result<(), NetworkRequesterError> { - setup_logging(); - maybe_print_banner(crate_name!(), crate_version!()); - let args = cli::Cli::parse(); setup_env(args.config_env_file.as_ref()); + if !args.no_banner { + maybe_print_banner(crate_name!(), crate_version!()); + } + setup_logging(); + cli::execute(args).await } diff --git a/service-providers/network-statistics/Cargo.toml b/service-providers/network-statistics/Cargo.toml index cc7024c545..197ee6e49a 100644 --- a/service-providers/network-statistics/Cargo.toml +++ b/service-providers/network-statistics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-network-statistics" -version = "1.1.23" +version = "1.1.27" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/tools/nym-cli/Cargo.toml b/tools/nym-cli/Cargo.toml index ea5cad9e65..d571f87d7f 100644 --- a/tools/nym-cli/Cargo.toml +++ b/tools/nym-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nym-cli" -version = "1.1.23" +version = "1.1.27" authors.workspace = true edition = "2021" diff --git a/tools/nym-cli/src/coconut/mod.rs b/tools/nym-cli/src/coconut/mod.rs new file mode 100644 index 0000000000..ea4fd45381 --- /dev/null +++ b/tools/nym-cli/src/coconut/mod.rs @@ -0,0 +1,26 @@ +use nym_cli_commands::context::{create_query_client, create_signing_client, ClientArgs}; +use nym_network_defaults::NymNetworkDetails; + +pub(crate) async fn execute( + global_args: ClientArgs, + coconut: nym_cli_commands::coconut::Coconut, + network_details: &NymNetworkDetails, +) -> anyhow::Result<()> { + match coconut.command { + nym_cli_commands::coconut::CoconutCommands::IssueCredentials(args) => { + nym_cli_commands::coconut::issue_credentials::execute( + args, + create_signing_client(global_args, network_details)?, + ) + .await? + } + nym_cli_commands::coconut::CoconutCommands::RecoverCredentials(args) => { + nym_cli_commands::coconut::recover_credentials::execute( + args, + create_query_client(network_details)?, + ) + .await? + } + } + Ok(()) +} diff --git a/tools/nym-cli/src/main.rs b/tools/nym-cli/src/main.rs index a5acae77b9..16b771d33e 100644 --- a/tools/nym-cli/src/main.rs +++ b/tools/nym-cli/src/main.rs @@ -7,6 +7,7 @@ use nym_bin_common::logging::setup_logging; use nym_cli_commands::context::{get_network_details, ClientArgs}; use nym_validator_client::nyxd::AccountId; +mod coconut; mod completion; mod validator; @@ -61,6 +62,8 @@ pub(crate) enum Commands { Account(nym_cli_commands::validator::account::Account), /// Sign and verify messages Signature(nym_cli_commands::validator::signature::Signature), + /// Coconut related stuff + Coconut(nym_cli_commands::coconut::Coconut), /// Query chain blocks Block(nym_cli_commands::validator::block::Block), /// Manage and execute WASM smart contracts @@ -101,6 +104,7 @@ async fn execute(cli: Cli) -> anyhow::Result<()> { Commands::Signature(signature) => { validator::signature::execute(signature, &network_details, mnemonic).await? } + Commands::Coconut(coconut) => coconut::execute(args, coconut, &network_details).await?, Commands::Block(block) => validator::block::execute(block, &network_details).await?, Commands::Cosmwasm(cosmwasm) => { validator::cosmwasm::execute(args, cosmwasm, &network_details).await? diff --git a/tools/nym-cli/src/validator/mixnet/operators/name/mod.rs b/tools/nym-cli/src/validator/mixnet/operators/name/mod.rs index d7873a9839..5ab1f3dcba 100644 --- a/tools/nym-cli/src/validator/mixnet/operators/name/mod.rs +++ b/tools/nym-cli/src/validator/mixnet/operators/name/mod.rs @@ -23,6 +23,14 @@ pub(crate) async fn execute( }; res }, + nym_cli_commands::validator::mixnet::operators::name::MixnetOperatorsNameCommands::CreateNameRegisterPayload(args) => { + let res = nym_cli_commands::validator::mixnet::operators::name::register_sign_payload::create_payload(args, create_signing_client(global_args, network_details)?).await; + match res { + Ok(_) => (), + Err(_) => println!("Failed to create payload") + }; + res + } }; Ok(res?) } diff --git a/tools/nym-nr-query/src/main.rs b/tools/nym-nr-query/src/main.rs index 97d376f6d5..2de1c08468 100644 --- a/tools/nym-nr-query/src/main.rs +++ b/tools/nym-nr-query/src/main.rs @@ -2,7 +2,7 @@ use std::fmt; use clap::{Parser, ValueEnum}; use nym_bin_common::output_format::OutputFormat; -use nym_sdk::mixnet::{self, IncludedSurbs}; +use nym_sdk::mixnet::{self, IncludedSurbs, MixnetMessageSender}; use nym_service_providers_common::interface::{ ControlRequest, ControlResponse, ProviderInterfaceVersion, Request, Response, ResponseContent, }; @@ -154,34 +154,37 @@ impl QueryClient { async fn query_bin_info(&mut self) -> ControlResponse { self.client - .send_bytes( + .send_message( self.provider, new_bin_info_request().into_bytes(), IncludedSurbs::new(10), ) - .await; + .await + .unwrap(); wait_for_control_response(&mut self.client).await } async fn query_supported_versions(&mut self) -> ControlResponse { self.client - .send_bytes( + .send_message( self.provider, new_supported_request_versions_request().into_bytes(), IncludedSurbs::new(10), ) - .await; + .await + .unwrap(); wait_for_control_response(&mut self.client).await } async fn query_open_proxy(&mut self) -> QueryResponse { self.client - .send_bytes( + .send_message( self.provider, new_open_proxy_request().into_bytes(), IncludedSurbs::new(10), ) - .await; + .await + .unwrap(); let response = wait_for_socks5_response(&mut self.client).await; response .content @@ -193,12 +196,13 @@ impl QueryClient { async fn ping(&mut self) -> PingResponse { let now = std::time::Instant::now(); self.client - .send_bytes( + .send_message( self.provider, new_ping_request().into_bytes(), IncludedSurbs::new(5), ) - .await; + .await + .unwrap(); let response = wait_for_control_response(&mut self.client).await; assert!(matches!(response, ControlResponse::Health)); let elapsed = now.elapsed(); diff --git a/tools/ts-rs-cli/Cargo.toml b/tools/ts-rs-cli/Cargo.toml index 41f9ead98d..5e315a761c 100644 --- a/tools/ts-rs-cli/Cargo.toml +++ b/tools/ts-rs-cli/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] anyhow = "1" -ts-rs = "6.1.2" +ts-rs = { workspace = true } walkdir = "2" nym-validator-client = { path = "../../common/client-libs/validator-client", features = [ diff --git a/yarn.lock b/yarn.lock index 8c6092b23f..ca5ac7a967 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1724,23 +1724,6 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@emotion/babel-plugin@^11.10.6": - version "11.10.6" - resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz#a68ee4b019d661d6f37dec4b8903255766925ead" - integrity sha512-p2dAqtVrkhSa7xz1u/m9eHYdLi+en8NowrmXeF/dKtJpU8lCWli8RUAati7NcSl0afsBott48pdnANuD0wh9QQ== - dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/runtime" "^7.18.3" - "@emotion/hash" "^0.9.0" - "@emotion/memoize" "^0.8.0" - "@emotion/serialize" "^1.1.1" - babel-plugin-macros "^3.1.0" - convert-source-map "^1.5.0" - escape-string-regexp "^4.0.0" - find-root "^1.1.0" - source-map "^0.5.7" - stylis "4.1.3" - "@emotion/babel-plugin@^11.11.0": version "11.11.0" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c" @@ -1769,19 +1752,19 @@ "@emotion/weak-memoize" "^0.3.1" stylis "4.2.0" -"@emotion/hash@^0.9.0", "@emotion/hash@^0.9.1": +"@emotion/hash@^0.9.1": version "0.9.1" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== -"@emotion/is-prop-valid@^1.2.0", "@emotion/is-prop-valid@^1.2.1": +"@emotion/is-prop-valid@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz#23116cf1ed18bfeac910ec6436561ecb1a3885cc" integrity sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw== dependencies: "@emotion/memoize" "^0.8.1" -"@emotion/memoize@^0.8.0", "@emotion/memoize@^0.8.1": +"@emotion/memoize@^0.8.1": version "0.8.1" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== @@ -1800,7 +1783,7 @@ "@emotion/weak-memoize" "^0.3.1" hoist-non-react-statics "^3.3.1" -"@emotion/serialize@^1.1.1", "@emotion/serialize@^1.1.2": +"@emotion/serialize@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.2.tgz#017a6e4c9b8a803bd576ff3d52a0ea6fa5a62b51" integrity sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA== @@ -1828,29 +1811,17 @@ "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" "@emotion/utils" "^1.2.1" -"@emotion/styled@^11.7.0": - version "11.10.6" - resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.6.tgz#d886afdc51ef4d66c787ebde848f3cc8b117ebba" - integrity sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og== - dependencies: - "@babel/runtime" "^7.18.3" - "@emotion/babel-plugin" "^11.10.6" - "@emotion/is-prop-valid" "^1.2.0" - "@emotion/serialize" "^1.1.1" - "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" - "@emotion/utils" "^1.2.0" - "@emotion/unitless@^0.8.1": version "0.8.1" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== -"@emotion/use-insertion-effect-with-fallbacks@^1.0.0", "@emotion/use-insertion-effect-with-fallbacks@^1.0.1": +"@emotion/use-insertion-effect-with-fallbacks@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963" integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw== -"@emotion/utils@^1.2.0", "@emotion/utils@^1.2.1": +"@emotion/utils@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.1.tgz#bbab58465738d31ae4cb3dbb6fc00a5991f755e4" integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg== @@ -1942,11 +1913,6 @@ resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.9.11.tgz#9ce96e7746625a89239f68ca57c4f654264c17ef" integrity sha512-bA3aZ79UgcHj7tFV7RlgThzwSSHZgvfbt2wprldRkYBcMopdMvHyO17Wwp/twcJasNFischFfS7oz8Katz8DdQ== -"@hookform/resolvers@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.1.0.tgz#ff83ef4aa6078173201da131ceea4c3583b67034" - integrity sha512-z0A8K+Nxq+f83Whm/ajlwE6VtQlp/yPHZnXw7XWVPIGm1Vx0QV8KThU3BpbBRfAZ7/dYqCKKBNnQh85BkmBKkA== - "@humanwhocodes/config-array@^0.11.10": version "0.11.10" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" @@ -2691,20 +2657,6 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@mui/base@5.0.0-beta.0": - version "5.0.0-beta.0" - resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.0.tgz#4145f8a700e04d68d703da70d3c26c62d278f4e5" - integrity sha512-ap+juKvt8R8n3cBqd/pGtZydQ4v2I/hgJKnvJRGjpSh3RvsvnDHO4rXov8MHQlH6VqpOekwgilFLGxMZjNTucA== - dependencies: - "@babel/runtime" "^7.21.0" - "@emotion/is-prop-valid" "^1.2.0" - "@mui/types" "^7.2.4" - "@mui/utils" "^5.12.3" - "@popperjs/core" "^2.11.7" - clsx "^1.2.1" - prop-types "^15.8.1" - react-is "^18.2.0" - "@mui/base@5.0.0-beta.4": version "5.0.0-beta.4" resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.4.tgz#e3f4f4a056b88ab357194a245e223177ce35e0b0" @@ -2733,11 +2685,6 @@ prop-types "^15.8.1" react-is "^18.2.0" -"@mui/core-downloads-tracker@^5.13.0": - version "5.13.7" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.13.7.tgz#f4d9af5fe113b80b98b2cb158263d7b8f77e61c7" - integrity sha512-/suIo4WoeL/OyO3KUsFVpdOmKiSAr6NpWXmQ4WLSxwKrTiha1FJxM6vwAki5W/5kR9WnVLw5E8JC4oHHsutT8w== - "@mui/core-downloads-tracker@^5.13.4": version "5.13.4" resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.13.4.tgz#7e4b491d8081b6d45ae51556d82cb16b31315a19" @@ -2750,13 +2697,6 @@ dependencies: "@babel/runtime" "^7.21.0" -"@mui/icons-material@^5.11.11": - version "5.11.11" - resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.11.11.tgz#d4e01bd405b0dac779f5e060586277f91f3acb6e" - integrity sha512-Eell3ADmQVE8HOpt/LZ3zIma8JSvPh3XgnhwZLT0k5HRqZcd6F/QDHc7xsWtgz09t+UEFvOYJXjtrwKmLdwwpw== - dependencies: - "@babel/runtime" "^7.21.0" - "@mui/lab@^5.0.0-alpha.72": version "5.0.0-alpha.134" resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.134.tgz#e48c108fce91fbb89446dcf86ca35e7e761bc078" @@ -2789,25 +2729,7 @@ react-is "^18.2.0" react-transition-group "^4.4.5" -"@mui/material@^5.11.15": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.13.0.tgz#23c63a9d117dca22b81bb82db6e9c4d761c8fbf1" - integrity sha512-ckS+9tCpAzpdJdaTF+btF0b6mF9wbXg/EVKtnoAWYi0UKXoXBAVvEUMNpLGA5xdpCdf+A6fPbVUEHs9TsfU+Yw== - dependencies: - "@babel/runtime" "^7.21.0" - "@mui/base" "5.0.0-beta.0" - "@mui/core-downloads-tracker" "^5.13.0" - "@mui/system" "^5.12.3" - "@mui/types" "^7.2.4" - "@mui/utils" "^5.12.3" - "@types/react-transition-group" "^4.4.6" - clsx "^1.2.1" - csstype "^3.1.2" - prop-types "^15.8.1" - react-is "^18.2.0" - react-transition-group "^4.4.5" - -"@mui/private-theming@^5.13.1", "@mui/private-theming@^5.13.7": +"@mui/private-theming@^5.13.1": version "5.13.7" resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.13.7.tgz#2f8ef5da066f3c6c6423bd4260d003a28d10b099" integrity sha512-qbSr+udcij5F9dKhGX7fEdx2drXchq7htLNr2Qg2Ma+WJ6q0ERlEqGSBiPiVDJkptcjeVL4DGmcf1wl5+vD4EA== @@ -2863,20 +2785,6 @@ csstype "^3.1.2" prop-types "^15.8.1" -"@mui/system@^5.11.15", "@mui/system@^5.12.3": - version "5.13.7" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.13.7.tgz#b02e6284bbaab4201b142546ebbb2012ec0fa63d" - integrity sha512-7R2KdI6vr8KtnauEfg9e9xQmPk6Gg/1vGNiALYyhSI+cYztxN6WmlSqGX4bjWn/Sygp1TUE1jhFEgs7MWruhkQ== - dependencies: - "@babel/runtime" "^7.22.5" - "@mui/private-theming" "^5.13.7" - "@mui/styled-engine" "^5.13.2" - "@mui/types" "^7.2.4" - "@mui/utils" "^5.13.7" - clsx "^1.2.1" - csstype "^3.1.2" - prop-types "^15.8.1" - "@mui/types@^7.2.4": version "7.2.4" resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328" @@ -2893,7 +2801,7 @@ prop-types "^15.8.1" react-is "^18.2.0" -"@mui/utils@^5.12.3", "@mui/utils@^5.13.7": +"@mui/utils@^5.13.7": version "5.13.7" resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.13.7.tgz#7e6a8336e05eb2642667a5c02eb605351e27ec20" integrity sha512-/3BLptG/q0u36eYED7Nhf4fKXmcKb6LjjT7ZMwhZIZSdSxVqDqSTmATW3a56n3KEPQUXCU9TpxAfCBQhs6brVA== @@ -3211,9 +3119,6 @@ dependencies: nx "15.9.4" -"@nymproject/extension-storage@file:./nym-browser-extension/storage/pkg": - version "0.1.0" - "@nymproject/nym-validator-client@^0.18.0": version "0.18.0" resolved "https://registry.yarnpkg.com/@nymproject/nym-validator-client/-/nym-validator-client-0.18.0.tgz#4dd72bafdf6c72b603242f32c0bb9a1f9e475b98" @@ -3386,7 +3291,7 @@ schema-utils "^3.0.0" source-map "^0.7.3" -"@popperjs/core@^2.11.7", "@popperjs/core@^2.11.8": +"@popperjs/core@^2.11.8": version "2.11.8" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== @@ -3449,11 +3354,6 @@ resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.0.tgz#550a8d5760b78efc5d60f62b5829b0f74c1fde81" integrity sha512-Eu1V3kz3mV0wUpVTiFHuaT8UD1gj/0VnoFHQYX35xlslQUpe8CuYoKFn9d4WZFHm3yDywz6ALZuGdnUPKrNeAw== -"@remix-run/router@1.7.1": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.1.tgz#fea7ac35ae4014637c130011f59428f618730498" - integrity sha512-bgVQM4ZJ2u2CM8k1ey70o1ePFXsEzYVZoWghh6WjM8p59jQ7HxzbHW4SbnWFG7V9ig9chLawQxDTZ3xzOF8MkQ== - "@rollup/plugin-commonjs@^24.0.1": version "24.1.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz#79e54bd83bb64396761431eee6c44152ef322100" @@ -4548,7 +4448,7 @@ react-docgen-typescript "^2.1.1" tslib "^2.0.0" -"@storybook/react@^6.5.15", "@storybook/react@^6.5.16": +"@storybook/react@^6.5.15": version "6.5.16" resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.5.16.tgz#f7b82ba87f5bb73b4e4e83cce298a98710a88398" integrity sha512-cBtNlOzf/MySpNLBK22lJ8wFU22HnfTB2xJyBk7W7Zi71Lm7Uxkhv1Pz8HdiQndJ0SlsAAQOWjQYsSZsGkZIaA== @@ -4915,20 +4815,6 @@ lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/dom@^9.0.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.2.0.tgz#0e1f45e956f2a16f471559c06edd8827c4832f04" - integrity sha512-xTEnpUKiV/bMyEsE5bT4oYA0x0Z/colMtxzUY8bKyPXBNLn/e0V4ZjBZkEhms0xE4pv9QsPfSRu9AWS4y5wGvA== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/runtime" "^7.12.5" - "@types/aria-query" "^5.0.1" - aria-query "^5.0.0" - chalk "^4.1.0" - dom-accessibility-api "^0.5.9" - lz-string "^1.5.0" - pretty-format "^27.0.2" - "@testing-library/jest-dom@^5.14.1": version "5.16.5" resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e" @@ -4953,15 +4839,6 @@ "@testing-library/dom" "^8.0.0" "@types/react-dom" "<18.0.0" -"@testing-library/react@^14.0.0": - version "14.0.0" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.0.0.tgz#59030392a6792450b9ab8e67aea5f3cc18d6347c" - integrity sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg== - dependencies: - "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^9.0.0" - "@types/react-dom" "^18.0.0" - "@testing-library/user-event@^13.2.1": version "13.5.0" resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.5.0.tgz#69d77007f1e124d55314a2b73fd204b333b13295" @@ -5104,11 +4981,6 @@ dependencies: "@types/node" "*" -"@types/crypto-js@4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.1.1.tgz#602859584cecc91894eb23a4892f38cfa927890d" - integrity sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA== - "@types/d3-array@^3.0.3": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.0.5.tgz#857c1afffd3f51319bbc5b301956aca68acaa7b8" @@ -5475,11 +5347,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.37.tgz#a1f8728e4dc30163deb41e9b7aba65d0c2d4eda1" integrity sha512-ql+4dw4PlPFBP495k8JzUX/oMNRI2Ei4PrMHgj8oT4VhGlYUzF4EYr0qk2fW+XBVGIrq8Zzk13m4cvyXZuv4pA== -"@types/node@^18.16.1": - version "18.16.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.7.tgz#86d0ba2541f808cb78d4dc5d3e40242a349d6db8" - integrity sha512-MFg7ua/bRtnA1hYE3pVyWxGd/r7aMqjNOdHvlSsXV3n8iaeGKkOaPzpJh6/ovf4bEXWcojkeMJpTsq3mzXW4IQ== - "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -5539,13 +5406,6 @@ dependencies: "@types/react" "^17" -"@types/react-dom@^18.0.0": - version "18.2.4" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.4.tgz#13f25bfbf4e404d26f62ac6e406591451acba9e0" - integrity sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw== - dependencies: - "@types/react" "*" - "@types/react-dom@^18.0.10": version "18.2.6" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.6.tgz#ad621fa71a8db29af7c31b41b2ea3d8a6f4144d1" @@ -6133,11 +5993,6 @@ resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== -"@webpack-cli/configtest@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.0.1.tgz#a69720f6c9bad6aef54a8fa6ba9c3533e7ef4c7f" - integrity sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A== - "@webpack-cli/info@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" @@ -6145,21 +6000,11 @@ dependencies: envinfo "^7.7.3" -"@webpack-cli/info@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.1.tgz#eed745799c910d20081e06e5177c2b2569f166c0" - integrity sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA== - "@webpack-cli/serve@^1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== -"@webpack-cli/serve@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.2.tgz#10aa290e44a182c02e173a89452781b1acbc86d9" - integrity sha512-S9h3GmOmzUseyeFW3tYNnWS7gNUuwxZ3mmMq0JyW78Vx1SGKPSkt5bT4pB0rUnVfHjP0EL9gW2bOzmtiTfQt0A== - "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -6421,6 +6266,11 @@ ansi-regex@^6.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== +ansi-sequence-parser@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz#4d790f31236ac20366b23b3916b789e1bde39aed" + integrity sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -7821,6 +7671,17 @@ cli-boxes@^2.2.1: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== +cli-color@~2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-2.0.3.tgz#73769ba969080629670f3f2ef69a4bf4e7cc1879" + integrity sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ== + dependencies: + d "^1.0.1" + es5-ext "^0.10.61" + es6-iterator "^2.0.3" + memoizee "^0.4.15" + timers-ext "^0.1.7" + cli-cursor@3.1.0, cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -8063,11 +7924,6 @@ commander@2, commander@^2.19.0, commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" - integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== - commander@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" @@ -8093,6 +7949,11 @@ commander@^9.4.1: resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== +commander@~9.4.0: + version "9.4.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd" + integrity sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw== + common-ancestor-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" @@ -8330,18 +8191,6 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== -copy-webpack-plugin@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" - integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== - dependencies: - fast-glob "^3.2.11" - glob-parent "^6.0.1" - globby "^13.1.1" - normalize-path "^3.0.0" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" - core-js-compat@^3.30.1, core-js-compat@^3.30.2, core-js-compat@^3.8.1: version "3.31.0" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.0.tgz#4030847c0766cc0e803dcdfb30055d7ef2064bf1" @@ -8511,11 +8360,6 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" -crypto-js@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" - integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== - crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -8871,6 +8715,14 @@ d3-zoom@^2.0.0: d3-selection "2" d3-transition "2" +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -9393,13 +9245,6 @@ dotenv-webpack@^7.0.3: dependencies: dotenv-defaults "^2.0.2" -dotenv-webpack@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-8.0.1.tgz#6656550460a8076fab20e5ac2eac867e72478645" - integrity sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w== - dependencies: - dotenv-defaults "^2.0.2" - dotenv@^16.0.3: version "16.3.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" @@ -9678,16 +9523,52 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.61, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: + version "0.10.62" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" + integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + next-tick "^1.1.0" + es5-shim@^4.5.13: version "4.6.7" resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.6.7.tgz#bc67ae0fc3dd520636e0a1601cc73b450ad3e955" integrity sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ== +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + es6-shim@^0.35.5: version "0.35.8" resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.8.tgz#89216f6fbf8bacba3f897c8c0e814d2a41c05fb7" integrity sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg== +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -10150,6 +10031,14 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -10330,6 +10219,13 @@ express@^4.17.1, express@^4.17.3, express@^4.18.2: utils-merge "1.0.1" vary "~1.1.2" +ext@^1.1.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -10411,17 +10307,6 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" -fast-glob@^3.2.11, fast-glob@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" - integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" @@ -10590,7 +10475,7 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.2.0: +finalhandler@1.2.0, finalhandler@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== @@ -11120,7 +11005,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^6.0.1, glob-parent@^6.0.2: +glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== @@ -11251,17 +11136,6 @@ globby@11.1.0, globby@^11.0.1, globby@^11.0.2, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^13.1.1: - version "13.2.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" - integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== - dependencies: - dir-glob "^3.0.1" - fast-glob "^3.3.0" - ignore "^5.2.4" - merge2 "^1.4.1" - slash "^4.0.0" - globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -11865,7 +11739,7 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.0.4, ignore@^5.2.0, ignore@^5.2.4: +ignore@^5.0.4, ignore@^5.2.0: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== @@ -12025,11 +11899,6 @@ interpret@^2.2.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== -interpret@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" - integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== - ip@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" @@ -12090,7 +11959,7 @@ is-alphanumerical@^2.0.0: is-alphabetical "^2.0.0" is-decimal "^2.0.0" -is-arguments@^1.0.4, is-arguments@^1.1.1: +is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== @@ -12281,13 +12150,6 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - is-glob@^3.0.0, is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -12432,6 +12294,11 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-promise@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + is-reference@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" @@ -12509,7 +12376,7 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" -is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: +is-typed-array@^1.1.10, is-typed-array@^1.1.9: version "1.1.10" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== @@ -13749,7 +13616,7 @@ json5@^1.0.1, json5@^1.0.2: dependencies: minimist "^1.2.0" -jsonc-parser@3.2.0, jsonc-parser@^3.0.0: +jsonc-parser@3.2.0, jsonc-parser@^3.0.0, jsonc-parser@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== @@ -14314,6 +14181,13 @@ lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61" integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw== +lru-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ== + dependencies: + es5-ext "~0.10.2" + lunr@^2.3.9: version "2.3.9" resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" @@ -14464,7 +14338,7 @@ markdown-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-1.1.1.tgz#fea03b539faeaee9b4ef02a3769b455b189f7fc3" integrity sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q== -marked@^4.0.16: +marked@^4.0.16, marked@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== @@ -14662,6 +14536,20 @@ memfs@^3.1.2, memfs@^3.2.2, memfs@^3.4.1, memfs@^3.4.3: dependencies: fs-monkey "^1.0.4" +memoizee@^0.4.15: + version "0.4.15" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" + integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== + dependencies: + d "^1.0.1" + es5-ext "^0.10.53" + es6-weak-map "^2.0.3" + event-emitter "^0.3.5" + is-promise "^2.2.2" + lru-queue "^0.1.0" + next-tick "^1.1.0" + timers-ext "^0.1.7" + memoizerific@^1.11.3: version "1.11.3" resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" @@ -15195,7 +15083,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.0: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -15505,6 +15393,11 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5" integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw== +next-tick@1, next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -15632,6 +15525,22 @@ node-releases@^2.0.12: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== +nodemon@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.0.1.tgz#affe822a2c5f21354466b2fc8ae83277d27dadc7" + integrity sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw== + dependencies: + chokidar "^3.5.2" + debug "^3.2.7" + ignore-by-default "^1.0.1" + minimatch "^3.1.2" + pstree.remy "^1.1.8" + semver "^7.5.3" + simple-update-notifier "^2.0.0" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + nodemon@^2.0.21: version "2.0.22" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.22.tgz#182c45c3a78da486f673d6c1702e00728daf5258" @@ -16143,7 +16052,7 @@ open@^7.0.3: is-docker "^2.0.0" is-wsl "^2.1.1" -open@^8.0.9, open@^8.4.0: +open@^8.0.0, open@^8.0.9, open@^8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== @@ -17286,7 +17195,7 @@ prop-types@^15.0.0, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, object-assign "^4.1.1" react-is "^16.13.1" -property-expr@^2.0.4: +property-expr@^2.0.4, property-expr@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.5.tgz#278bdb15308ae16af3e3b9640024524f4dc02cb4" integrity sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA== @@ -17450,11 +17359,6 @@ qrcode.react@^1.0.1: prop-types "^15.6.0" qr.js "0.0.0" -qrcode.react@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-3.1.0.tgz#5c91ddc0340f768316fbdb8fff2765134c2aecd8" - integrity sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q== - qs@6.11.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" @@ -17602,13 +17506,6 @@ react-error-boundary@^3.1.3, react-error-boundary@^3.1.4: dependencies: "@babel/runtime" "^7.12.5" -react-error-boundary@^4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-4.0.4.tgz#d2e84505b0a67cec7a6bf33b0146faadfe31597d" - integrity sha512-AbqMFx8bCsob8rCHZvJYQ42MQijK0/034RUvan9qrqyJCpazr8d9vKHrysbxcr6odoHLZvQEcYomFPoIqH9fow== - dependencies: - "@babel/runtime" "^7.12.5" - react-google-charts@^3.0.15: version "3.0.15" resolved "https://registry.yarnpkg.com/react-google-charts/-/react-google-charts-3.0.15.tgz#30759a470f48336e744fd383d054122b039a1ff2" @@ -17616,7 +17513,7 @@ react-google-charts@^3.0.15: dependencies: react-load-script "^0.0.6" -react-hook-form@^7.14.2, react-hook-form@^7.43.9: +react-hook-form@^7.14.2: version "7.45.1" resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.45.1.tgz#e352c7f4dbc7540f0756abbb4dcfd1122fecc9bb" integrity sha512-6dWoFJwycbuFfw/iKMcl+RdAOAOHDiF11KWYhNDRN/OkUt+Di5qsZHwA0OwsVnu9y135gkHpTw9DJA+WzCeR9w== @@ -17691,11 +17588,6 @@ react-refresh-typescript@^2.0.2, react-refresh-typescript@^2.0.3: resolved "https://registry.yarnpkg.com/react-refresh-typescript/-/react-refresh-typescript-2.0.9.tgz#f8a86efcb34f8d717100230564b9b57477d74b10" integrity sha512-chAnOO4vpxm/3WkgOVmti+eN8yUtkJzeGkOigV6UA9eDFz12W34e/SsYe2H5+RwYJ3+sfSZkVbiXcG1chEBxlg== -react-refresh-typescript@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/react-refresh-typescript/-/react-refresh-typescript-2.0.8.tgz#84d086f9da742d7de87ad15824301763bf8135ba" - integrity sha512-7zsaM8jIpUZ2hxMdTo9GNZRauTI11AD5YNNaNXVGb8mtYVwdSbc6IQV8YaHevSWYbx+ZpZispezdCfaJuTrh3w== - react-refresh@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3" @@ -17706,11 +17598,6 @@ react-refresh@^0.11.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== -react-refresh@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" - integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ== - react-resize-detector@^8.0.4: version "8.1.0" resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-8.1.0.tgz#1c7817db8bc886e2dbd3fbe3b26ea8e56be0524a" @@ -17726,14 +17613,6 @@ react-router-dom@6, react-router-dom@^6.7.0: "@remix-run/router" "1.7.0" react-router "6.14.0" -react-router-dom@^6.9.0: - version "6.14.1" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.1.tgz#0ad7ba7abdf75baa61169d49f096f0494907a36f" - integrity sha512-ssF6M5UkQjHK70fgukCJyjlda0Dgono2QGwqGvuk7D+EDGHdacEN3Yke2LTMjkrpHuFwBfDFsEjGVXBDmL+bWw== - dependencies: - "@remix-run/router" "1.7.1" - react-router "6.14.1" - react-router@6.14.0: version "6.14.0" resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.0.tgz#1c3e8e922d934d43a253fd862c72c82167c0a7f1" @@ -17741,13 +17620,6 @@ react-router@6.14.0: dependencies: "@remix-run/router" "1.7.0" -react-router@6.14.1: - version "6.14.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.1.tgz#5e82bcdabf21add859dc04b1859f91066b3a5810" - integrity sha512-U4PfgvG55LdvbQjg5Y9QRWyVxIdO1LlpYT7x+tMAxd9/vmiPuJhIwdxZuIQLN/9e3O4KFDHYfR9gzGeYMasW8g== - dependencies: - "@remix-run/router" "1.7.1" - react-simple-maps@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/react-simple-maps/-/react-simple-maps-2.3.0.tgz#7fdb55120335a9b1ef5bf62e0dcb669511705f3e" @@ -18007,13 +17879,6 @@ rechoir@^0.7.0: dependencies: resolve "^1.9.0" -rechoir@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" - integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== - dependencies: - resolve "^1.20.0" - redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -18108,6 +17973,20 @@ relateurl@^0.2.7: resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== +reload@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/reload/-/reload-3.2.1.tgz#42d43e33e327efe1348c723272c6835fe333349a" + integrity sha512-ZdM8ZSEeI72zkhh6heMEvJ0vHZoovZXcJI6Zae8CzS7o5vO/WjZsAMMr0y1+3I/fCN7y7ZxABoUwwCswcLHkjQ== + dependencies: + cli-color "~2.0.0" + commander "~9.4.0" + finalhandler "~1.2.0" + minimist "~1.2.0" + open "^8.0.0" + serve-static "~1.15.0" + supervisor "~0.12.0" + ws "~8.11.0" + remark-external-links@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/remark-external-links/-/remark-external-links-8.0.0.tgz#308de69482958b5d1cd3692bc9b725ce0240f345" @@ -18730,7 +18609,7 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@1.15.0: +serve-static@1.15.0, serve-static@~1.15.0: version "1.15.0" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== @@ -18851,6 +18730,16 @@ shiki@^0.10.1: vscode-oniguruma "^1.6.1" vscode-textmate "5.2.0" +shiki@^0.14.1: + version "0.14.3" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.14.3.tgz#d1a93c463942bdafb9866d74d619a4347d0bbf64" + integrity sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g== + dependencies: + ansi-sequence-parser "^1.1.0" + jsonc-parser "^3.2.0" + vscode-oniguruma "^1.7.0" + vscode-textmate "^8.0.0" + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -18908,6 +18797,13 @@ simple-update-notifier@^1.0.7: dependencies: semver "~7.0.0" +simple-update-notifier@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" + integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== + dependencies: + semver "^7.5.3" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -18928,11 +18824,6 @@ slash@^2.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== -slash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" - integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== - slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -19517,16 +19408,16 @@ stylehacks@^5.1.1: browserslist "^4.21.4" postcss-selector-parser "^6.0.4" -stylis@4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7" - integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA== - stylis@4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== +supervisor@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/supervisor/-/supervisor-0.12.0.tgz#de7e6337015b291851c10f3538c4a7f04917ecc1" + integrity sha512-iBYeU5Or4WiiIa3+ns1DpHIiHjNNXSuYUiixKcznewwo4ImBJ8EobktaAo2csOcauhrz4SvKRTou8Z2C3W28+A== + supports-color@8.1.1, supports-color@^8.0.0: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" @@ -19847,6 +19738,19 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +timers-ext@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== + dependencies: + es5-ext "~0.10.46" + next-tick "1" + +tiny-case@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03" + integrity sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q== + tiny-warning@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" @@ -20237,6 +20141,16 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" + integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -20269,6 +20183,16 @@ typedoc@^0.22.13: minimatch "^5.1.0" shiki "^0.10.1" +typedoc@^0.24.8: + version "0.24.8" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.24.8.tgz#cce9f47ba6a8d52389f5e583716a2b3b4335b63e" + integrity sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w== + dependencies: + lunr "^2.3.9" + marked "^4.3.0" + minimatch "^9.0.0" + shiki "^0.14.1" + "typescript@^3 || ^4", typescript@^4.6.2, typescript@^4.8.4: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" @@ -20659,17 +20583,6 @@ util@^0.11.0: dependencies: inherits "2.0.3" -util@^0.12.5: - version "0.12.5" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" - integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - which-typed-array "^1.1.2" - utila@~0.4: version "0.4.0" resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" @@ -20850,7 +20763,7 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -vscode-oniguruma@^1.6.1: +vscode-oniguruma@^1.6.1, vscode-oniguruma@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== @@ -20860,6 +20773,11 @@ vscode-textmate@5.2.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== +vscode-textmate@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" + integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== + w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" @@ -20964,25 +20882,6 @@ webpack-cli@^4.8.0: rechoir "^0.7.0" webpack-merge "^5.7.3" -webpack-cli@^5.0.1: - version "5.0.2" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.0.2.tgz#2954c10ecb61c5d4dad6f68ee2d77f051741946c" - integrity sha512-4y3W5Dawri5+8dXm3+diW6Mn1Ya+Dei6eEVAdIduAmYNLzv1koKVAqsfgrrc9P2mhrYHQphx5htnGkcNwtubyQ== - dependencies: - "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^2.0.1" - "@webpack-cli/info" "^2.0.1" - "@webpack-cli/serve" "^2.0.2" - colorette "^2.0.14" - commander "^10.0.1" - cross-spawn "^7.0.3" - envinfo "^7.7.3" - fastest-levenshtein "^1.0.12" - import-local "^3.0.2" - interpret "^3.1.1" - rechoir "^0.8.0" - webpack-merge "^5.7.3" - webpack-dev-middleware@^3.7.3: version "3.7.3" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" @@ -21238,7 +21137,7 @@ which-collection@^1.0.1: is-weakmap "^2.0.1" is-weakset "^2.0.1" -which-typed-array@^1.1.2, which-typed-array@^1.1.9: +which-typed-array@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== @@ -21416,6 +21315,11 @@ ws@^8.13.0, ws@^8.2.3: resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== +ws@~8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" + integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== + x-default-browser@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/x-default-browser/-/x-default-browser-0.4.0.tgz#70cf0da85da7c0ab5cb0f15a897f2322a6bdd481" @@ -21585,10 +21489,15 @@ yup@^0.32.9: property-expr "^2.0.4" toposort "^2.0.2" -zod@^3.21.4: - version "3.21.4" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db" - integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw== +yup@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/yup/-/yup-1.2.0.tgz#9e51af0c63bdfc9be0fdc6c10aa0710899d8aff6" + integrity sha512-PPqYKSAXjpRCgLgLKVGPA33v5c/WgEx3wi6NFjIiegz90zSwyMpvTFp/uGcVnnbx6to28pgnzp/q8ih3QRjLMQ== + dependencies: + property-expr "^2.0.5" + tiny-case "^1.0.3" + toposort "^2.0.2" + type-fest "^2.19.0" zwitch@^1.0.0: version "1.0.5"