Compare commits

..

44 Commits

Author SHA1 Message Date
Tommy Verrall 8d83e14b8b Update nym-connect-publish-windows10.yml 2023-07-26 13:43:18 +02:00
Tommy Verrall 433282eb92 Update package.json 2023-07-25 17:34:39 +02:00
Tommy Verrall b208221a3b Update package.json
remove browser extension
2023-07-25 17:33:31 +02:00
Tommy Verrall a2018bb2d7 Update nym-connect-publish-windows10.yml 2023-07-25 17:19:31 +02:00
Tommy Verrall bed1d56f22 Update package.json 2023-07-25 17:18:27 +02:00
Tommy Verrall b0dd1400c3 Update package.json 2023-07-25 17:15:53 +02:00
Tommy Verrall f867e92cce Update package.json 2023-07-25 17:13:41 +02:00
Tommy Verrall d3eac3a3b7 Update package.json 2023-07-25 17:12:36 +02:00
Tommy Verrall 599307d1e9 Delete delete.txt 2023-07-25 17:06:18 +02:00
Tommy Verrall 382b5110bb Update package.json 2023-07-25 17:05:11 +02:00
Tommy Verrall b2025bb532 Create delete.txt 2023-07-25 17:02:04 +02:00
Tommy Verrall 02bec4b8e6 Update package.json 2023-07-25 16:57:24 +02:00
Tommy Verrall 170e2c5dfa Update package.json 2023-07-25 16:55:54 +02:00
Tommy Verrall 70a183a7c3 Update package.json 2023-07-25 16:52:06 +02:00
pierre 275417c186 fix workflows (try) 2023-07-25 16:05:01 +02:00
pierre 40a92e07d6 fix workflows 2023-07-25 15:47:24 +02:00
benedetta davico e83a12bd91 Update CHANGELOG.md 2023-07-25 15:36:59 +02:00
Tommy Verrall 381162554e Update nym-connect-publish-ubuntu.yml (#3706)
* Update nym-connect-publish-ubuntu.yml

* Update nym-connect-publish-ubuntu.yml

* Update nym-connect-publish-macos.yml

* Update nym-connect-publish-windows10.yml
2023-07-25 14:40:46 +02:00
Tommy Verrall 7a740c06fd Update nym-connect-publish-macos.yml (#3705)
* Update nym-connect-publish-macos.yml

install wasm and build

* Update nym-connect-publish-macos.yml

use the correct download
2023-07-25 13:45:32 +02:00
Tommy Verrall e65285ac7b bump versions for NC 2023-07-25 10:52:17 +02:00
mx 715a3bd687 Merge pull request #3696 from nymtech/feature/sdk-surb-example
Feature/sdk surb example
2023-07-24 07:16:03 +00:00
mfahampshire 858f1ac13c fixed clippy warning 2023-07-23 12:00:39 +02:00
pierre 3ee1328626 add alephium to supported wallets 2023-07-21 16:15:39 +02:00
pierre 36ac825b43 build(nc-desktop): sentry dsn as env var 2023-07-21 16:00:39 +02:00
mfahampshire 165f189115 ran fmt 2023-07-21 14:11:22 +02:00
mfahampshire b5fcfbe2fe added reply with surbs example to rust sdk examples dir 2023-07-21 14:04:33 +02:00
mfahampshire 7a38f1f469 added rust sdk surb example 2023-07-21 13:59:52 +02:00
pierre 5faca46235 ci: fix connect-desktop-ci workflow 2023-07-21 13:20:18 +02:00
Pierre Dommerc d780ac55b1 feat(nc-desktop): add sentry to backend (#3652) 2023-07-21 12:48:56 +02:00
Pierre Dommerc c5f7d066b0 refactor(nc-desktop): add privacy level user settings (#3664) 2023-07-21 12:48:30 +02:00
mx 8cc90be8c6 Merge pull request #3685 from nymtech/feature/network-requester-updates
Feature/network requester updates
2023-07-19 12:07:21 +00:00
mfahampshire aae96e7537 included url 2023-07-19 13:56:47 +02:00
mfahampshire 39b521bc1f updated NR guide with list explainer + info on comments in local allow list 2023-07-19 13:51:55 +02:00
mx 7339695ce8 Merge pull request #3677 from nymtech/feature/v1-1-24-docs
Feature/v1 1 25 docs (outdated branch name due to lack of release)
2023-07-19 11:43:11 +00:00
mfahampshire 0e1c9853aa version update 2023-07-19 13:16:11 +02:00
mfahampshire 76b9c669d7 * added serinko + alexia to book authors
* version bumps for next release
2023-07-19 12:45:30 +02:00
mfahampshire 553cfd098b updated sdk documentation with surb example 2023-07-19 12:41:29 +02:00
mfahampshire 40e1243f3c Merge branch 'feature/v1-1-24-docs' of github.com:nymtech/nym into feature/v1-1-24-docs 2023-07-11 15:52:59 +02:00
mfahampshire 50d2ca0a12 updated validator docs: upgrade to 0.32.0 instructions 2023-07-11 15:52:35 +02:00
mx 32d9baaf02 Merge pull request #3614 from twofaktor/twofaktor-requester-no-incoming-connection
[UPDATE] Fix + update network-requester-setup doc
2023-07-10 08:30:37 +00:00
mfahampshire 0179f7648c version bumps 2023-07-10 09:56:22 +02:00
mfahampshire 45c04d63e2 removed command information from mix node + gateway guide 2023-07-10 09:44:33 +02:00
⚡️2FakTor⚡️ 0f8ac1506b Update network-requester-setup.md 2023-06-28 16:07:32 +02:00
⚡️2FakTor⚡️ efdf27d1e9 Update network-requester-setup.md 2023-06-28 13:59:51 +02:00
1138 changed files with 36107 additions and 84434 deletions
@@ -109,7 +109,6 @@ jobs:
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR cp contracts/target/wasm32-unknown-unknown/release/nym_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_name_service.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_ephemera.wasm $OUTPUT_DIR
- name: Deploy branch to CI www - name: Deploy branch to CI www
continue-on-error: true continue-on-error: true
+76
View File
@@ -0,0 +1,76 @@
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
+13 -13
View File
@@ -1,7 +1,6 @@
name: CD docs name: CD docs
on: on:
workflow_dispatch:
push: push:
branches: master branches: master
paths: paths:
@@ -28,38 +27,39 @@ jobs:
command: build command: build
args: --workspace --release --all args: --workspace --release --all
- name: Install mdbook - name: Install mdbook
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook) run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook)
- name: Install mdbook plugins - name: Install mdbook plugins
run: | run: |
cargo install --vers "=0.2.2" mdbook-variables && cargo install \ cargo install --vers "^0.2.0" mdbook-variables && cargo install \
--vers "^1.8.0" mdbook-admonish && cargo install --vers \ --vers "^1.8.0" mdbook-admonish && cargo install --vers \
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \ "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" \
&& cargo install --vers "^0.7.7" mdbook-linkcheck mdbook-theme && cargo install --vers "^0.7.7" mdbook-linkcheck && \
- name: Build all projects in documentation/ & move to ~/dist/docs/ cargo install --vers "^0.5.0" mdbook-cmdrun
run: cd documentation && ./build_all_to_dist.sh - name: Clean website
continue-on-error: false run: cd documentation/docs && mdbook clean
- name: Build website
run: cd documentation/docs && mdbook build
- name: Deploy branch master to dev - name: Deploy branch master to dev
continue-on-error: true continue-on-error: true
uses: easingthemes/ssh-deploy@main uses: easingthemes/ssh-deploy@main
env: env:
SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }} SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete" ARGS: "-rltgoDzvO --delete"
SOURCE: "dist/docs/" SOURCE: "documentation/docs/book/"
REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_DEV }} REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_DEV }}
REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }} REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/ TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/
EXCLUDE: "/node_modules/" EXCLUDE: "/dist/, /node_modules/"
- name: Deploy branch master to prod - name: Deploy branch master to prod
if: github.ref == 'refs/heads/master'
uses: easingthemes/ssh-deploy@main uses: easingthemes/ssh-deploy@main
env: env:
SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }} SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete" ARGS: "-rltgoDzvO --delete"
SOURCE: "dist/docs/" SOURCE: "documentation/docs/book/"
REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_PROD }} REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_PROD }}
REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }} REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/ TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET }}/
EXCLUDE: "/node_modules/" EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install - name: Matrix - Node Install
run: npm install run: npm install
working-directory: .github/workflows/support-files working-directory: .github/workflows/support-files
@@ -1,31 +0,0 @@
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
+66
View File
@@ -0,0 +1,66 @@
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
+11 -10
View File
@@ -1,7 +1,6 @@
name: CI docs name: CI docs
on: on:
workflow_dispatch:
push: push:
branches-ignore: master branches-ignore: master
paths: paths:
@@ -28,27 +27,29 @@ jobs:
command: build command: build
args: --workspace --release --all args: --workspace --release --all
- name: Install mdbook - name: Install mdbook
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook) run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook)
- name: Install mdbook plugins - name: Install mdbook plugins
run: | run: |
cargo install --vers "=0.2.2" mdbook-variables && cargo install \ cargo install --vers "^0.2.0" mdbook-variables && cargo install \
--vers "^1.8.0" mdbook-admonish && cargo install --vers \ --vers "^1.8.0" mdbook-admonish && cargo install --vers \
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \ "^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" \
&& cargo install --vers "^0.7.7" mdbook-linkcheck mdbook-theme && cargo install --vers "^0.7.7" mdbook-linkcheck && \
- name: Build all projects in documentation/ & move to ~/dist/docs/ cargo install --vers "^0.5.0" mdbook-cmdrun
run: cd documentation && ./build_all_to_dist.sh - name: Clean website
continue-on-error: false run: cd documentation/docs && mdbook clean
- name: Build website
run: cd documentation/docs && mdbook build
- name: Deploy branch to CI www - name: Deploy branch to CI www
continue-on-error: true continue-on-error: true
uses: easingthemes/ssh-deploy@main uses: easingthemes/ssh-deploy@main
env: env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }} SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete" ARGS: "-rltgoDzvO --delete"
SOURCE: "dist/docs/" SOURCE: "documentation/docs/book/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }} REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/docs-${{ env.GITHUB_REF_SLUG }} TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/docs-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/node_modules/" EXCLUDE: "/dist/, /node_modules/"
- name: Matrix - Node Install - name: Matrix - Node Install
run: npm install run: npm install
working-directory: .github/workflows/support-files working-directory: .github/workflows/support-files
+72
View File
@@ -0,0 +1,72 @@
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
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
- name: Install Rust stable - name: Install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: 1.69.0 toolchain: stable
target: wasm32-unknown-unknown target: wasm32-unknown-unknown
override: true override: true
components: rustfmt, clippy components: rustfmt, clippy
+3 -3
View File
@@ -48,12 +48,12 @@ jobs:
RUSTFLAGS: '-C link-arg=-s' RUSTFLAGS: '-C link-arg=-s'
with: with:
command: build command: build
args: --manifest-path contracts/Cargo.toml --workspace --lib --target wasm32-unknown-unknown args: --manifest-path contracts/Cargo.toml --workspace --target wasm32-unknown-unknown
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --lib --manifest-path contracts/Cargo.toml args: --manifest-path contracts/Cargo.toml
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
with: with:
@@ -64,4 +64,4 @@ jobs:
if: ${{ matrix.rust != 'nightly' }} if: ${{ matrix.rust != 'nightly' }}
with: with:
command: clippy command: clippy
args: --lib --manifest-path contracts/Cargo.toml --workspace --all-targets -- -D warnings args: --manifest-path contracts/Cargo.toml --workspace --all-targets -- -D warnings
@@ -118,7 +118,7 @@ jobs:
ref=${{ github.ref_name }} ref=${{ github.ref_name }}
semver="${ref##nym-connect-}" && semver="${semver##v}" semver="${ref##nym-connect-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT" echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_${semver}_x64.dmg " >> "$GITHUB_OUTPUT" echo "filename=nym-connect_${version}_x64_en-US.msi" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/dmg/nym-connect_*_x64.dmg') }}" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/dmg/nym-connect_*_x64.dmg') }}" >> "$GITHUB_OUTPUT"
push-release-data: push-release-data:
@@ -85,9 +85,8 @@ jobs:
ref=${{ github.ref_name }} ref=${{ github.ref_name }}
semver="${ref##nym-connect-}" && semver="${semver##v}" semver="${ref##nym-connect-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT" echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_${semver}_amd64.AppImage" >> "$GITHUB_OUTPUT" echo "filename=nym-connect_${version}_amd64.AppImage" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/appimage/nym-connect_*_amd64.AppImage') }}" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/appimage/nym-connect_*_amd64.AppImage') }}" >> "$GITHUB_OUTPUT"
push-release-data: push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }} if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml uses: ./.github/workflows/push-release-data.yml
@@ -61,7 +61,7 @@ jobs:
with: with:
fileName: '.env' fileName: '.env'
encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }} encodedString: ${{ secrets.WALLET_ADMIN_ADDRESS }}
- name: Install project dependencies - name: Install project dependencies
shell: bash shell: bash
run: cd .. && yarn --network-timeout 100000 run: cd .. && yarn --network-timeout 100000
@@ -100,11 +100,12 @@ jobs:
nym-connect/desktop/target/release/bundle/msi/*.msi.zip* nym-connect/desktop/target/release/bundle/msi/*.msi.zip*
- id: release-info - id: release-info
name: Prepare release info name: Prepare release info
shell: bash
run: | run: |
ref=${{ github.ref_name }} ref=${{ github.ref_name }}
semver="${ref##nym-connect-}" && semver="${semver##v}" semver="${ref##nym-connect-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT" echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_${semver}_x64_en-US.msi" >> "$GITHUB_OUTPUT" echo "filename=nym-connect_${version}_x64_en-US.msi" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/msi/nym-connect_*_x64_en-US.msi') }}" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/msi/nym-connect_*_x64_en-US.msi') }}" >> "$GITHUB_OUTPUT"
push-release-data: push-release-data:
@@ -106,7 +106,7 @@ jobs:
ref=${{ github.ref_name }} ref=${{ github.ref_name }}
semver="${ref##nym-wallet-}" && semver="${semver##v}" semver="${ref##nym-wallet-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT" echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_${semver}_x64.dmg" >> "$GITHUB_OUTPUT" echo "filename=nym-wallet_${version}_x64.dmg" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/dmg/nym-wallet_*_x64.dmg') }}" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/dmg/nym-wallet_*_x64.dmg') }}" >> "$GITHUB_OUTPUT"
push-release-data: push-release-data:
@@ -83,7 +83,7 @@ jobs:
ref=${{ github.ref_name }} ref=${{ github.ref_name }}
semver="${ref##nym-wallet-}" && semver="${semver##v}" semver="${ref##nym-wallet-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT" echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_${semver}_amd64.AppImage" >> "$GITHUB_OUTPUT" echo "filename=nym-wallet_${version}_amd64.AppImage" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/appimage/nym-wallet_*_amd64.AppImage') }}" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/appimage/nym-wallet_*_amd64.AppImage') }}" >> "$GITHUB_OUTPUT"
push-release-data: push-release-data:
@@ -103,7 +103,7 @@ jobs:
ref=${{ github.ref_name }} ref=${{ github.ref_name }}
semver="${ref##nym-wallet-}" && semver="${semver##v}" semver="${ref##nym-wallet-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT" echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-wallet_${semver}_x64_en-US.msi" >> "$GITHUB_OUTPUT" echo "filename=nym-wallet_${version}_x64_en-US.msi" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/msi/nym-wallet_*_x64_en-US.msi') }}" >> "$GITHUB_OUTPUT" echo "file_hash=${{ hashFiles('nym-wallet/target/release/bundle/msi/nym-wallet_*_x64_en-US.msi') }}" >> "$GITHUB_OUTPUT"
push-release-data: push-release-data:
-50
View File
@@ -1,50 +0,0 @@
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
+4
View File
@@ -7,6 +7,8 @@ on:
- "sdk/typescript/**" - "sdk/typescript/**"
- "nym-connect/desktop/src/**" - "nym-connect/desktop/src/**"
- "nym-connect/desktop/package.json" - "nym-connect/desktop/package.json"
- "nym-connect/mobile/src/**"
- "nym-connect/mobile/package.json"
- "nym-wallet/src/**" - "nym-wallet/src/**"
- "nym-wallet/package.json" - "nym-wallet/package.json"
- "explorer/**" - "explorer/**"
@@ -16,6 +18,8 @@ on:
- "sdk/typescript/**" - "sdk/typescript/**"
- "nym-connect/desktop/src/**" - "nym-connect/desktop/src/**"
- "nym-connect/desktop/package.json" - "nym-connect/desktop/package.json"
- "nym-connect/mobile/src/**"
- "nym-connect/mobile/package.json"
- "nym-wallet/src/**" - "nym-wallet/src/**"
- "nym-wallet/package.json" - "nym-wallet/package.json"
- "explorer/**" - "explorer/**"
-49
View File
@@ -4,55 +4,6 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased] ## [Unreleased]
## [1.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
## [1.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) ## [v1.1.23] (2023-07-04)
- nym-cli: add client identity key signing support ([#3576]) - nym-cli: add client identity key signing support ([#3576])
Generated
+749 -3702
View File
File diff suppressed because it is too large Load Diff
+13 -24
View File
@@ -34,7 +34,6 @@ members = [
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract", "common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/coconut-dkg", "common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common", "common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/ephemera",
"common/cosmwasm-smart-contracts/group-contract", "common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract", "common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract", "common/cosmwasm-smart-contracts/multisig-contract",
@@ -75,7 +74,6 @@ members = [
"common/types", "common/types",
"common/wasm-utils", "common/wasm-utils",
"explorer-api", "explorer-api",
"explorer-api/explorer-api-requests",
"gateway", "gateway",
"gateway/gateway-requests", "gateway/gateway-requests",
"integrations/bity", "integrations/bity",
@@ -119,39 +117,30 @@ anyhow = "1.0.71"
async-trait = "0.1.64" async-trait = "0.1.64"
bip39 = { version = "2.0.0", features = ["zeroize"] } bip39 = { version = "2.0.0", features = ["zeroize"] }
cfg-if = "1.0.0" cfg-if = "1.0.0"
cosmwasm-derive = "=1.3.0" cosmwasm-derive = "=1.2.5"
cosmwasm-schema = "=1.3.0" cosmwasm-schema = "=1.2.5"
cosmwasm-std = "=1.3.0" cosmwasm-std = "=1.2.5"
# use 0.5.0 as that's the version used by cosmwasm-std 1.3.0 cosmwasm-storage = "=1.2.5"
# (and ideally we don't want to pull the same dependency twice) cosmrs = "=0.8.0"
serde-json-wasm = "=0.5.0"
cosmwasm-storage = "=1.3.0"
cosmrs = "=0.14.0"
# same version as used by cosmrs
cw-utils = "=1.0.1" cw-utils = "=1.0.1"
cw-storage-plus = "=1.1.0" cw-storage-plus = "=1.0.1"
cw2 = { version = "=1.1.0" } cw2 = { version = "=1.0.1" }
cw3 = { version = "=1.1.0" } cw3 = { version = "=1.0.1" }
cw4 = { version = "=1.1.0" } cw3-fixed-multisig = { version = "=1.0.1" }
cw-controllers = { version = "=1.1.0" } cw4 = { version = "=1.0.1" }
cw-controllers = { version = "=1.0.1" }
dotenvy = "0.15.6" dotenvy = "0.15.6"
futures = "0.3.28"
generic-array = "0.14.7" generic-array = "0.14.7"
k256 = "0.11"
getrandom = "0.2.10" getrandom = "0.2.10"
k256 = "0.13"
lazy_static = "1.4.0" lazy_static = "1.4.0"
log = "0.4" log = "0.4"
once_cell = "1.7.2" once_cell = "1.7.2"
rand = "0.8.5" rand = "0.8.5"
reqwest = "0.11.18"
serde = "1.0.152" serde = "1.0.152"
serde_json = "1.0.91" serde_json = "1.0.91"
tap = "1.0.1" tap = "1.0.1"
tendermint-rpc = "0.32" # same version as used by cosmrs
thiserror = "1.0.38" thiserror = "1.0.38"
tokio = "1.24.1" tokio = "1.24.1"
url = "2.4" url = "2.2"
zeroize = "1.6.0" zeroize = "1.6.0"
# wasm-related dependencies
wasmtimer = "0.2.0"
+2 -5
View File
@@ -73,7 +73,7 @@ endef
# Generate targets for the various cargo workspaces # Generate targets for the various cargo workspaces
$(eval $(call add_cargo_workspace,main,.)) $(eval $(call add_cargo_workspace,main,.))
$(eval $(call add_cargo_workspace,contracts,contracts,--lib --target wasm32-unknown-unknown)) $(eval $(call add_cargo_workspace,contracts,contracts,--target wasm32-unknown-unknown))
$(eval $(call add_cargo_workspace,wasm-client,clients/webassembly,--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,wallet,nym-wallet,))
$(eval $(call add_cargo_workspace,connect,nym-connect/desktop)) $(eval $(call add_cargo_workspace,connect,nym-connect/desktop))
@@ -104,7 +104,7 @@ NAME_SERVICE_CONTRACT=$(CONTRACTS_OUT_DIR)/nym_name_service.wasm
wasm: wasm-build wasm-opt wasm: wasm-build wasm-opt
wasm-build: wasm-build:
RUSTFLAGS='-C link-arg=-s' cargo build --lib --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown RUSTFLAGS='-C link-arg=-s' cargo build --manifest-path contracts/Cargo.toml --release --target wasm32-unknown-unknown
wasm-opt: wasm-opt:
wasm-opt --disable-sign-ext -Os $(VESTING_CONTRACT) -o $(VESTING_CONTRACT) wasm-opt --disable-sign-ext -Os $(VESTING_CONTRACT) -o $(VESTING_CONTRACT)
@@ -112,9 +112,6 @@ wasm-opt:
wasm-opt --disable-sign-ext -Os $(SERVICE_PROVIDER_DIRECTORY_CONTRACT) -o $(SERVICE_PROVIDER_DIRECTORY_CONTRACT) 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) wasm-opt --disable-sign-ext -Os $(NAME_SERVICE_CONTRACT) -o $(NAME_SERVICE_CONTRACT)
contract-schema:
$(MAKE) -C contracts schema
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Misc # Misc
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
+1 -1
View File
@@ -21,5 +21,5 @@ nym-credential-storage = { path = "../../common/credential-storage" }
nym-bin-common = { path = "../../common/bin-common"} nym-bin-common = { path = "../../common/bin-common"}
nym-network-defaults = { path = "../../common/network-defaults" } nym-network-defaults = { path = "../../common/network-defaults" }
nym-pemstore = { path = "../../common/pemstore" } nym-pemstore = { path = "../../common/pemstore" }
nym-validator-client = { path = "../../common/client-libs/validator-client" } nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
+4 -4
View File
@@ -4,14 +4,14 @@
use crate::error::Result; use crate::error::Result;
use bip39::Mnemonic; use bip39::Mnemonic;
use nym_network_defaults::{NymNetworkDetails, VOUCHER_INFO}; use nym_network_defaults::{NymNetworkDetails, VOUCHER_INFO};
use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient; use nym_validator_client::nyxd;
use nym_validator_client::nyxd::{self, DirectSigningHttpRpcNyxdClient}; use nym_validator_client::nyxd::traits::CoconutBandwidthSigningClient;
use nym_validator_client::nyxd::{Coin, Fee, NyxdClient}; use nym_validator_client::nyxd::{Coin, DirectSigningNyxdClient, Fee, NyxdClient};
use std::str::FromStr; use std::str::FromStr;
use url::Url; use url::Url;
pub(crate) struct Client { pub(crate) struct Client {
nyxd_client: DirectSigningHttpRpcNyxdClient, nyxd_client: NyxdClient<DirectSigningNyxdClient>,
mix_denom_base: String, mix_denom_base: String,
} }
+1 -1
View File
@@ -6,7 +6,7 @@ use log::*;
use nym_bandwidth_controller::acquire::state::State; use nym_bandwidth_controller::acquire::state::State;
use nym_bin_common::completions::ArgShell; use nym_bin_common::completions::ArgShell;
use nym_credential_storage::persistent_storage::PersistentStorage; use nym_credential_storage::persistent_storage::PersistentStorage;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::traits::DkgQueryClient;
use crate::error::Result; use crate::error::Result;
use crate::recovery_storage::RecoveryStorage; use crate::recovery_storage::RecoveryStorage;
+11 -14
View File
@@ -17,9 +17,9 @@ use std::time::{Duration, SystemTime};
use clap::{CommandFactory, Parser}; use clap::{CommandFactory, Parser};
use nym_bin_common::logging::setup_logging; use nym_bin_common::logging::setup_logging;
use nym_client_core::config::disk_persistence::CommonClientPaths; use nym_client_core::config::disk_persistence::CommonClientPaths;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::traits::DkgQueryClient;
use nym_validator_client::nyxd::{Coin, Config}; use nym_validator_client::nyxd::{Coin, CosmWasmClient};
use nym_validator_client::DirectSigningHttpRpcNyxdClient; use nym_validator_client::Config;
const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute
@@ -34,11 +34,11 @@ struct Cli {
pub(crate) command: Command, pub(crate) command: Command,
} }
async fn block_until_coconut_is_available<C: DkgQueryClient + Send + Sync>( async fn block_until_coconut_is_available<C: CosmWasmClient + Send + Sync>(
client: &C, client: &nym_validator_client::Client<C>,
) -> Result<()> { ) -> Result<()> {
loop { loop {
let epoch = client.get_current_epoch().await?; let epoch = client.nyxd.get_current_epoch().await?;
let current_timestamp_secs = SystemTime::now() let current_timestamp_secs = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)? .duration_since(SystemTime::UNIX_EPOCH)?
.as_secs(); .as_secs();
@@ -89,18 +89,15 @@ async fn main() -> Result<()> {
r.amount as u128, r.amount as u128,
network_details.chain_details.mix_denom.base, network_details.chain_details.mix_denom.base,
); );
let endpoint = network_details.endpoints[0].nyxd_url.as_str(); let client =
let client = DirectSigningHttpRpcNyxdClient::connect_with_mnemonic( nym_validator_client::Client::new_signing(config, r.mnemonic.parse().unwrap())?;
config,
endpoint,
r.mnemonic.parse().unwrap(),
)?;
block_until_coconut_is_available(&client).await?; block_until_coconut_is_available(&client).await?;
info!("Starting depositing funds, don't kill the process"); info!("Starting depositing funds, don't kill the process");
if !r.recovery_mode { if !r.recovery_mode {
let state = nym_bandwidth_controller::acquire::deposit(&client, amount).await?; let state =
nym_bandwidth_controller::acquire::deposit(&client.nyxd, amount).await?;
if nym_bandwidth_controller::acquire::get_credential( if nym_bandwidth_controller::acquire::get_credential(
&state, &state,
&client, &client,
@@ -120,7 +117,7 @@ async fn main() -> Result<()> {
} }
} }
} else { } else {
recover_credentials(&client, &recovery_storage, &shared_storage).await?; recover_credentials(&client.nyxd, &recovery_storage, &shared_storage).await?;
} }
} }
Command::Completions(c) => c.generate(&mut Cli::command(), bin_name), Command::Completions(c) => c.generate(&mut Cli::command(), bin_name),
+5 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-client" name = "nym-client"
version = "1.1.26" version = "1.1.23"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
description = "Implementation of the Nym Client" description = "Implementation of the Nym Client"
edition = "2021" edition = "2021"
@@ -14,11 +14,11 @@ path = "src/lib.rs"
[dependencies] [dependencies]
# dependencies to review: # dependencies to review:
futures = { workspace = true } # bunch of futures stuff, however, now that I think about it, it could perhaps be completely removed futures = "0.3" # 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 # 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 # 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 # and the single instance of abortable we have should really be refactored anyway
url = { workspace = true } url = "2.2"
clap = { version = "4.0", features = ["cargo", "derive"] } clap = { version = "4.0", features = ["cargo", "derive"] }
dirs = "4.0" 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 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 = { workspace = true, features = ["derive"] } # for config serialization/deserialization
serde_json = { workspace = true } serde_json = { workspace = true }
thiserror = { workspace = true } thiserror = "1.0.34"
tap = "1.0.1" tap = "1.0.1"
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } # async runtime tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } # async runtime
tokio-tungstenite = "0.14" # websocket tokio-tungstenite = "0.14" # websocket
@@ -48,7 +48,7 @@ nym-sphinx = { path = "../../common/nymsphinx" }
nym-pemstore = { path = "../../common/pemstore" } nym-pemstore = { path = "../../common/pemstore" }
nym-task = { path = "../../common/task" } nym-task = { path = "../../common/task" }
nym-topology = { path = "../../common/topology" } nym-topology = { path = "../../common/topology" }
nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] } nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["nyxd-client"] }
nym-client-websocket-requests = { path = "websocket-requests" } nym-client-websocket-requests = { path = "websocket-requests" }
[dev-dependencies] [dev-dependencies]
+3 -2
View File
@@ -19,7 +19,8 @@ use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_sphinx::params::PacketType; use nym_sphinx::params::PacketType;
use nym_task::connections::TransmissionLane; use nym_task::connections::TransmissionLane;
use nym_task::TaskManager; use nym_task::TaskManager;
use nym_validator_client::QueryHttpRpcNyxdClient; use nym_validator_client::nyxd::QueryNyxdClient;
use nym_validator_client::Client;
use std::error::Error; use std::error::Error;
use tokio::sync::watch::error::SendError; use tokio::sync::watch::error::SendError;
@@ -28,7 +29,7 @@ pub use nym_sphinx::receiver::ReconstructedMessage;
pub mod config; pub mod config;
type NativeClientBuilder<'a> = BaseClientBuilder<'a, QueryHttpRpcNyxdClient, OnDiskPersistent>; type NativeClientBuilder<'a> = BaseClientBuilder<'a, Client<QueryNyxdClient>, OnDiskPersistent>;
pub struct SocketClient { pub struct SocketClient {
/// Client configuration options, including, among other things, packet sending rates, /// Client configuration options, including, among other things, packet sending rates,
-16
View File
@@ -1,16 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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!()))
}
+2 -3
View File
@@ -174,15 +174,14 @@ pub(crate) async fn execute(args: &Init) -> Result<(), ClientError> {
let details_store = let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details); OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let init_details = nym_client_core::init::setup_gateway( let init_details = nym_client_core::init::setup_gateway(
gateway_setup, &gateway_setup,
&key_store, &key_store,
&details_store, &details_store,
register_gateway, register_gateway,
Some(&config.base.client.nym_api_urls), Some(&config.base.client.nym_api_urls),
) )
.await .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(); let config_save_location = config.default_location();
config.save_to_default_location().tap_err(|_| { config.save_to_default_location().tap_err(|_| {
+7 -15
View File
@@ -10,7 +10,7 @@ use clap::CommandFactory;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use log::{error, info}; use log::{error, info};
use nym_bin_common::bin_info; use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::client::base_client::storage::gateway_details::{ use nym_client_core::client::base_client::storage::gateway_details::{
OnDiskGatewayDetails, PersistedGatewayDetails, OnDiskGatewayDetails, PersistedGatewayDetails,
@@ -22,12 +22,12 @@ use nym_config::OptionalSet;
use std::error::Error; use std::error::Error;
use std::net::IpAddr; use std::net::IpAddr;
pub(crate) mod build_info;
pub(crate) mod init; pub(crate) mod init;
pub(crate) mod run; pub(crate) mod run;
lazy_static! { lazy_static! {
pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print(); pub static ref PRETTY_BUILD_INFORMATION: String =
BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print();
} }
// Helper for passing LONG_VERSION to clap // Helper for passing LONG_VERSION to clap
@@ -42,10 +42,6 @@ pub(crate) struct Cli {
#[clap(short, long)] #[clap(short, long)]
pub(crate) config_env_file: Option<std::path::PathBuf>, pub(crate) config_env_file: Option<std::path::PathBuf>,
/// Flag used for disabling the printed banner in tty.
#[clap(long)]
pub(crate) no_banner: bool,
#[clap(subcommand)] #[clap(subcommand)]
command: Commands, command: Commands,
} }
@@ -58,9 +54,6 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters /// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run), Run(run::Run),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
/// Generate shell completions /// Generate shell completions
Completions(ArgShell), Completions(ArgShell),
@@ -80,13 +73,12 @@ pub(crate) struct OverrideConfig {
enabled_credentials_mode: Option<bool>, enabled_credentials_mode: Option<bool>,
} }
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> { pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
let bin_name = "nym-native-client"; let bin_name = "nym-native-client";
match args.command { match &args.command {
Commands::Init(m) => init::execute(&m).await?, Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::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::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
} }
+4 -7
View File
@@ -14,13 +14,10 @@ pub mod websocket;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> { async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
setup_logging();
maybe_print_banner(crate_name!(), crate_version!());
let args = commands::Cli::parse(); let args = commands::Cli::parse();
setup_env(args.config_env_file.as_ref()); 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
} }
+3 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "nym-socks5-client" name = "nym-socks5-client"
version = "1.1.26" version = "1.1.23"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address" description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
edition = "2021" edition = "2021"
@@ -14,9 +14,9 @@ pretty_env_logger = "0.4"
serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization serde = { workspace = true, features = ["derive"] } # for config serialization/deserialization
serde_json = { workspace = true } serde_json = { workspace = true }
tap = "1.0.1" tap = "1.0.1"
thiserror = { workspace = true } thiserror = "1.0.34"
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] } tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
url = { workspace = true } url = "2.2"
# internal # internal
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] } nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
-16
View File
@@ -1,16 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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!()))
}
+2 -4
View File
@@ -94,7 +94,6 @@ impl From<Init> for OverrideConfig {
use_anonymous_replies: init_config.use_reply_surbs, use_anonymous_replies: init_config.use_reply_surbs,
fastmode: init_config.fastmode, fastmode: init_config.fastmode,
no_cover: init_config.no_cover, no_cover: init_config.no_cover,
geo_routing: None,
medium_toggle: false, medium_toggle: false,
nyxd_urls: init_config.nyxd_urls, nyxd_urls: init_config.nyxd_urls,
enabled_credentials_mode: init_config.enabled_credentials_mode, enabled_credentials_mode: init_config.enabled_credentials_mode,
@@ -186,15 +185,14 @@ pub(crate) async fn execute(args: &Init) -> Result<(), Socks5ClientError> {
let details_store = let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details); OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let init_details = nym_client_core::init::setup_gateway( let init_details = nym_client_core::init::setup_gateway(
gateway_setup, &gateway_setup,
&key_store, &key_store,
&details_store, &details_store,
register_gateway, register_gateway,
Some(&config.core.base.client.nym_api_urls), Some(&config.core.base.client.nym_api_urls),
) )
.await .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 // TODO: ask the service provider we specified for its interface version and set it in the config
+8 -29
View File
@@ -10,25 +10,24 @@ use clap::CommandFactory;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use log::{error, info}; use log::{error, info};
use nym_bin_common::bin_info; use nym_bin_common::build_information::BinaryBuildInformation;
use nym_bin_common::completions::{fig_generate, ArgShell}; use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::client::base_client::storage::gateway_details::{ use nym_client_core::client::base_client::storage::gateway_details::{
OnDiskGatewayDetails, PersistedGatewayDetails, OnDiskGatewayDetails, PersistedGatewayDetails,
}; };
use nym_client_core::client::key_manager::persistence::OnDiskKeys; use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup; use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::config::{GatewayEndpointConfig, TopologyStructure};
use nym_client_core::error::ClientCoreError; use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet; use nym_config::OptionalSet;
use nym_sphinx::params::{PacketSize, PacketType}; use nym_sphinx::params::{PacketSize, PacketType};
use std::error::Error; use std::error::Error;
pub(crate) mod build_info;
pub mod init; pub mod init;
pub(crate) mod run; pub(crate) mod run;
lazy_static! { lazy_static! {
pub static ref PRETTY_BUILD_INFORMATION: String = bin_info!().pretty_print(); pub static ref PRETTY_BUILD_INFORMATION: String =
BinaryBuildInformation::new(env!("CARGO_PKG_VERSION")).pretty_print();
} }
// Helper for passing LONG_VERSION to clap // Helper for passing LONG_VERSION to clap
@@ -43,10 +42,6 @@ pub(crate) struct Cli {
#[clap(short, long)] #[clap(short, long)]
pub(crate) config_env_file: Option<std::path::PathBuf>, pub(crate) config_env_file: Option<std::path::PathBuf>,
/// Flag used for disabling the printed banner in tty.
#[clap(long)]
pub(crate) no_banner: bool,
#[clap(subcommand)] #[clap(subcommand)]
command: Commands, command: Commands,
} }
@@ -59,9 +54,6 @@ pub(crate) enum Commands {
/// Run the Nym client with provided configuration client optionally overriding set parameters /// Run the Nym client with provided configuration client optionally overriding set parameters
Run(run::Run), Run(run::Run),
/// Show build information of this binary
BuildInfo(build_info::BuildInfo),
/// Generate shell completions /// Generate shell completions
Completions(ArgShell), Completions(ArgShell),
@@ -76,20 +68,18 @@ pub(crate) struct OverrideConfig {
use_anonymous_replies: Option<bool>, use_anonymous_replies: Option<bool>,
fastmode: bool, fastmode: bool,
no_cover: bool, no_cover: bool,
geo_routing: Option<CountryGroup>,
medium_toggle: bool, medium_toggle: bool,
nyxd_urls: Option<Vec<url::Url>>, nyxd_urls: Option<Vec<url::Url>>,
enabled_credentials_mode: Option<bool>, enabled_credentials_mode: Option<bool>,
outfox: bool, outfox: bool,
} }
pub(crate) async fn execute(args: Cli) -> Result<(), Box<dyn Error + Send + Sync>> { pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
let bin_name = "nym-socks5-client"; let bin_name = "nym-socks5-client";
match args.command { match &args.command {
Commands::Init(m) => init::execute(&m).await?, Commands::Init(m) => init::execute(m).await?,
Commands::Run(m) => run::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::Completions(s) => s.generate(&mut Cli::command(), bin_name),
Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name), Commands::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
} }
@@ -101,13 +91,6 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
let secondary_packet_size = args.medium_toggle.then_some(PacketSize::ExtendedPacket16); let secondary_packet_size = args.medium_toggle.then_some(PacketSize::ExtendedPacket16);
let no_per_hop_delays = args.medium_toggle; let no_per_hop_delays = args.medium_toggle;
let topology_structure = if args.medium_toggle || args.geo_routing.is_some() {
// TODO: rethink the default group. I just picked one for now.
TopologyStructure::GeoAware(args.geo_routing.unwrap_or(CountryGroup::Europe))
} else {
TopologyStructure::default()
};
let packet_type = if args.outfox { let packet_type = if args.outfox {
PacketType::Outfox PacketType::Outfox
} else { } else {
@@ -131,10 +114,6 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
// NOTE: see comment above about the order of the other disble cover traffic 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_disabled_cover_traffic, args.no_cover)
.with_base(BaseClientConfig::with_packet_type, packet_type) .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_anonymous_replies, args.use_anonymous_replies)
.with_optional(Config::with_port, args.port) .with_optional(Config::with_port, args.port)
.with_optional_base_custom_env( .with_optional_base_custom_env(
-13
View File
@@ -11,7 +11,6 @@ use clap::Args;
use log::*; use log::*;
use nym_bin_common::version_checker::is_minor_version_compatible; use nym_bin_common::version_checker::is_minor_version_compatible;
use nym_client_core::client::base_client::storage::OnDiskPersistent; 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_crypto::asymmetric::identity;
use nym_socks5_client_core::NymClient; use nym_socks5_client_core::NymClient;
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
@@ -61,10 +60,6 @@ pub(crate) struct Run {
#[clap(long, hide = true)] #[clap(long, hide = true)]
no_cover: bool, 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<CountryGroup>,
/// Enable medium mixnet traffic, for experiments only. /// Enable medium mixnet traffic, for experiments only.
/// This includes things like disabling cover traffic, no per hop delays, etc. /// This includes things like disabling cover traffic, no per hop delays, etc.
#[clap(long, hide = true)] #[clap(long, hide = true)]
@@ -87,7 +82,6 @@ impl From<Run> for OverrideConfig {
use_anonymous_replies: run_config.use_anonymous_replies, use_anonymous_replies: run_config.use_anonymous_replies,
fastmode: run_config.fastmode, fastmode: run_config.fastmode,
no_cover: run_config.no_cover, no_cover: run_config.no_cover,
geo_routing: run_config.geo_routing,
medium_toggle: run_config.medium_toggle, medium_toggle: run_config.medium_toggle,
nyxd_urls: run_config.nyxd_urls, nyxd_urls: run_config.nyxd_urls,
enabled_credentials_mode: run_config.enabled_credentials_mode, enabled_credentials_mode: run_config.enabled_credentials_mode,
@@ -96,13 +90,6 @@ impl From<Run> for OverrideConfig {
} }
} }
fn validate_country_group(s: &str) -> Result<CountryGroup, String> {
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 // this only checks compatibility between config the binary. It does not take into consideration
// network version. It might do so in the future. // network version. It might do so in the future.
fn version_check(cfg: &Config) -> bool { fn version_check(cfg: &Config) -> bool {
+4 -7
View File
@@ -13,13 +13,10 @@ pub mod error;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> { async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
setup_logging();
maybe_print_banner(crate_name!(), crate_version!());
let args = commands::Cli::parse(); let args = commands::Cli::parse();
setup_env(args.config_env_file.as_ref()); 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
} }
+610 -764
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -3,7 +3,7 @@ name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"] authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.1.1" version = "1.1.1"
edition = "2021" edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy"] keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0" license = "Apache-2.0"
repository = "https://github.com/nymtech/nym" 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." 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" thiserror = "1.0.40"
zeroize = "1.6.0" zeroize = "1.6.0"
wasmtimer = { version = "0.2.0", features = ["tokio"] } wasm-timer = { git = "https://github.com/mmsinclair/wasm-timer", rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6"}
# internal # internal
nym-node-tester-utils = { path = "../../common/node-tester-utils" } nym-node-tester-utils = { path = "../../common/node-tester-utils" }
-1
View File
@@ -246,7 +246,6 @@ impl From<TopologyWasm> for ConfigTopology {
topology.topology_resolution_timeout_ms, topology.topology_resolution_timeout_ms,
), ),
disable_refreshing: topology.disable_refreshing, disable_refreshing: topology.disable_refreshing,
topology_structure: Default::default(),
} }
} }
} }
+2 -2
View File
@@ -14,6 +14,7 @@ use crate::storage::traits::FullWasmClientStorage;
use crate::storage::ClientStorage; use crate::storage::ClientStorage;
use crate::topology::WasmNymTopology; use crate::topology::WasmNymTopology;
use js_sys::Promise; use js_sys::Promise;
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
use nym_client_core::client::base_client::{ use nym_client_core::client::base_client::{
BaseClientBuilder, ClientInput, ClientOutput, ClientState, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
}; };
@@ -25,7 +26,6 @@ use nym_task::TaskManager;
use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider}; use nym_topology::provider_trait::{HardcodedTopologyProvider, TopologyProvider};
use nym_topology::NymTopology; use nym_topology::NymTopology;
use nym_validator_client::client::IdentityKey; use nym_validator_client::client::IdentityKey;
use nym_validator_client::QueryReqwestRpcNyxdClient;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use rand::RngCore; use rand::RngCore;
use std::sync::Arc; use std::sync::Arc;
@@ -152,7 +152,7 @@ impl NymClientBuilder {
let maybe_topology_provider = self.topology_provider(); let maybe_topology_provider = self.topology_provider();
let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> = let mut base_builder: BaseClientBuilder<_, FullWasmClientStorage> =
BaseClientBuilder::<QueryReqwestRpcNyxdClient, _>::new( BaseClientBuilder::<FakeClient<DirectSigningNyxdClient>, _>::new(
&self.config.base, &self.config.base,
storage, storage,
None, None,
+5 -5
View File
@@ -8,7 +8,7 @@ use js_sys::Promise;
use nym_client_core::client::replies::reply_storage::browser_backend; use nym_client_core::client::replies::reply_storage::browser_backend;
use nym_client_core::config; use nym_client_core::config;
use nym_client_core::init::helpers::current_gateways; use nym_client_core::init::helpers::current_gateways;
use nym_client_core::init::{setup_gateway_from, GatewaySetup, InitialisationResult}; use nym_client_core::init::{setup_gateway_from, GatewaySetup, InitialisationDetails};
use nym_sphinx::addressing::clients::Recipient; use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag; use nym_sphinx::anonymous_replies::requests::AnonymousSenderTag;
use nym_topology::{gateway, NymTopology}; use nym_topology::{gateway, NymTopology};
@@ -82,14 +82,14 @@ async fn setup_gateway(
client_store: &ClientStorage, client_store: &ClientStorage,
chosen_gateway: Option<IdentityKey>, chosen_gateway: Option<IdentityKey>,
gateways: &[gateway::Node], gateways: &[gateway::Node],
) -> Result<InitialisationResult, WasmClientError> { ) -> Result<InitialisationDetails, WasmClientError> {
let setup = if client_store.has_full_gateway_info().await? { let setup = if client_store.has_full_gateway_info().await? {
GatewaySetup::MustLoad GatewaySetup::MustLoad
} else { } else {
GatewaySetup::new_fresh(chosen_gateway.clone(), None) 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 .await
.map_err(Into::into) .map_err(Into::into)
} }
@@ -98,7 +98,7 @@ pub(crate) async fn setup_gateway_from_api(
client_store: &ClientStorage, client_store: &ClientStorage,
chosen_gateway: Option<IdentityKey>, chosen_gateway: Option<IdentityKey>,
nym_apis: &[Url], nym_apis: &[Url],
) -> Result<InitialisationResult, WasmClientError> { ) -> Result<InitialisationDetails, WasmClientError> {
let mut rng = thread_rng(); let mut rng = thread_rng();
let gateways = current_gateways(&mut rng, nym_apis).await?; let gateways = current_gateways(&mut rng, nym_apis).await?;
setup_gateway(client_store, chosen_gateway, &gateways).await setup_gateway(client_store, chosen_gateway, &gateways).await
@@ -108,7 +108,7 @@ pub(crate) async fn setup_from_topology(
explicit_gateway: Option<IdentityKey>, explicit_gateway: Option<IdentityKey>,
topology: &NymTopology, topology: &NymTopology,
client_store: &ClientStorage, client_store: &ClientStorage,
) -> Result<InitialisationResult, WasmClientError> { ) -> Result<InitialisationDetails, WasmClientError> {
let gateways = topology.gateways(); let gateways = topology.gateways();
setup_gateway(client_store, explicit_gateway, gateways).await setup_gateway(client_store, explicit_gateway, gateways).await
} }
@@ -10,7 +10,6 @@ use std::collections::HashSet;
use std::time::Duration; use std::time::Duration;
use tokio::sync::MutexGuard as AsyncMutexGuard; use tokio::sync::MutexGuard as AsyncMutexGuard;
use wasm_utils::{console_error, console_log, console_warn}; use wasm_utils::{console_error, console_log, console_warn};
use wasmtimer::tokio::sleep;
pub(crate) struct EphemeralTestReceiver<'a> { pub(crate) struct EphemeralTestReceiver<'a> {
sent_packets: u32, sent_packets: u32,
@@ -91,7 +90,7 @@ impl<'a> EphemeralTestReceiver<'a> {
} }
pub(crate) async fn perform_test(mut self) -> NodeTestResult { pub(crate) async fn perform_test(mut self) -> NodeTestResult {
let mut timeout_fut = sleep(self.timeout_duration); let mut timeout_fut = wasm_timer::Delay::new(self.timeout_duration);
loop { loop {
tokio::select! { tokio::select! {
+21 -31
View File
@@ -12,9 +12,10 @@ use crate::tester::helpers::{
use crate::topology::WasmNymTopology; use crate::topology::WasmNymTopology;
use futures::channel::mpsc; use futures::channel::mpsc;
use js_sys::Promise; use js_sys::Promise;
use nym_bandwidth_controller::wasm_mockups::{Client as FakeClient, DirectSigningNyxdClient};
use nym_bandwidth_controller::BandwidthController; use nym_bandwidth_controller::BandwidthController;
use nym_client_core::client::key_manager::ManagedKeys; use nym_client_core::client::key_manager::ManagedKeys;
use nym_client_core::init::{InitialisationDetails, InitialisationResult}; use nym_client_core::init::InitialisationDetails;
use nym_credential_storage::ephemeral_storage::EphemeralStorage; use nym_credential_storage::ephemeral_storage::EphemeralStorage;
use nym_gateway_client::GatewayClient; use nym_gateway_client::GatewayClient;
use nym_node_tester_utils::receiver::SimpleMessageReceiver; use nym_node_tester_utils::receiver::SimpleMessageReceiver;
@@ -26,7 +27,6 @@ use nym_sphinx::preparer::PreparedFragment;
use nym_task::TaskManager; use nym_task::TaskManager;
use nym_topology::NymTopology; use nym_topology::NymTopology;
use nym_validator_client::client::IdentityKey; use nym_validator_client::client::IdentityKey;
use nym_validator_client::QueryReqwestRpcNyxdClient;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
@@ -42,7 +42,7 @@ pub(crate) mod helpers;
pub type NodeTestMessage = TestMessage<WasmTestMessageExt>; pub type NodeTestMessage = TestMessage<WasmTestMessageExt>;
type LockedGatewayClient = type LockedGatewayClient =
Arc<AsyncMutex<GatewayClient<QueryReqwestRpcNyxdClient, EphemeralStorage>>>; Arc<AsyncMutex<GatewayClient<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>>;
pub(crate) const DEFAULT_TEST_TIMEOUT: Duration = Duration::from_secs(10); pub(crate) const DEFAULT_TEST_TIMEOUT: Duration = Duration::from_secs(10);
pub(crate) const DEFAULT_TEST_PACKETS: u32 = 20; pub(crate) const DEFAULT_TEST_PACKETS: u32 = 20;
@@ -78,7 +78,8 @@ pub struct NymNodeTesterBuilder {
base_topology: NymTopology, base_topology: NymTopology,
// unimplemented // unimplemented
bandwidth_controller: Option<BandwidthController<QueryReqwestRpcNyxdClient, EphemeralStorage>>, bandwidth_controller:
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
} }
fn address(keys: &ManagedKeys, gateway_identity: NodeIdentity) -> Recipient { fn address(keys: &ManagedKeys, gateway_identity: NodeIdentity) -> Recipient {
@@ -129,9 +130,9 @@ impl NymNodeTesterBuilder {
async fn gateway_info( async fn gateway_info(
&self, &self,
client_store: &ClientStorage, client_store: &ClientStorage,
) -> Result<InitialisationResult, WasmClientError> { ) -> Result<InitialisationDetails, WasmClientError> {
if let Ok(loaded) = InitialisationDetails::try_load(client_store, client_store).await { if let Ok(loaded) = InitialisationDetails::try_load(client_store, client_store).await {
Ok(loaded.into()) Ok(loaded)
} else { } else {
setup_from_topology(self.gateway.clone(), &self.base_topology, client_store).await setup_from_topology(self.gateway.clone(), &self.base_topology, client_store).await
} }
@@ -147,37 +148,26 @@ impl NymNodeTesterBuilder {
}; };
let client_store = ClientStorage::new_async(&storage_id, None).await?; let client_store = ClientStorage::new_async(&storage_id, None).await?;
let initialisation_result = self.gateway_info(&client_store).await?;
let init_details = initialisation_result.details; let init_details = self.gateway_info(&client_store).await?;
let managed_keys = init_details.managed_keys;
let gateway_endpoint = init_details.gateway_details; let gateway_endpoint = init_details.gateway_details;
let gateway_identity = gateway_endpoint.try_get_gateway_identity_key()?; 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 (mixnet_message_sender, mixnet_message_receiver) = mpsc::unbounded();
let (ack_sender, ack_receiver) = mpsc::unbounded(); let (ack_sender, ack_receiver) = mpsc::unbounded();
let mut gateway_client = let mut gateway_client = GatewayClient::new(
if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client { gateway_endpoint.gateway_listener,
existing_client.upgrade( managed_keys.identity_keypair(),
mixnet_message_sender, gateway_identity,
ack_sender, Some(managed_keys.must_get_gateway_shared_key()),
Duration::from_secs(10), mixnet_message_sender,
self.bandwidth_controller.take(), ack_sender,
task_manager.subscribe(), Duration::from_secs(10),
) self.bandwidth_controller.take(),
} else { task_manager.subscribe(),
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.set_disabled_credentials_mode(true);
gateway_client.authenticate_and_start().await?; gateway_client.authenticate_and_start().await?;
+1 -1
View File
@@ -8,5 +8,5 @@ edition = "2021"
[dependencies] [dependencies]
log = "0.4" log = "0.4"
tokio = { workspace = true, features = ["time"] } tokio = { workspace = true, features = ["time"] }
futures = { workspace = true } futures = "0.3"
notify = "5.1.0" notify = "5.1.0"
+3 -2
View File
@@ -9,7 +9,7 @@ edition = "2021"
bip39 = { workspace = true } bip39 = { workspace = true }
rand = "0.7.3" rand = "0.7.3"
thiserror = "1.0" thiserror = "1.0"
url = { workspace = true } url = "2.2"
nym-coconut-interface = { path = "../coconut-interface" } nym-coconut-interface = { path = "../coconut-interface" }
nym-credential-storage = { path = "../credential-storage" } nym-credential-storage = { path = "../credential-storage" }
@@ -18,6 +18,7 @@ nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric"
nym-network-defaults = { path = "../network-defaults" } nym-network-defaults = { path = "../network-defaults" }
nym-validator-client = { path = "../client-libs/validator-client", default-features = false } nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
path = "../client-libs/validator-client" path = "../client-libs/validator-client"
features = ["http-client"] features = ["nyxd-client"]
@@ -8,11 +8,11 @@ use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use nym_credentials::coconut::utils::obtain_aggregate_signature; use nym_credentials::coconut::utils::obtain_aggregate_signature;
use nym_crypto::asymmetric::{encryption, identity}; use nym_crypto::asymmetric::{encryption, identity};
use nym_network_defaults::VOUCHER_INFO; use nym_network_defaults::VOUCHER_INFO;
use nym_validator_client::coconut::all_coconut_api_clients; use nym_validator_client::nyxd::traits::CoconutBandwidthSigningClient;
use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient; use nym_validator_client::nyxd::traits::DkgQueryClient;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient; use nym_validator_client::nyxd::tx::Hash;
use nym_validator_client::nyxd::Coin; use nym_validator_client::nyxd::Coin;
use nym_validator_client::nyxd::Hash; use nym_validator_client::CoconutApiClient;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use state::{KeyPair, State}; use state::{KeyPair, State};
use std::str::FromStr; use std::str::FromStr;
@@ -21,7 +21,7 @@ pub mod state;
pub async fn deposit<C>(client: &C, amount: Coin) -> Result<State, BandwidthControllerError> pub async fn deposit<C>(client: &C, amount: Coin) -> Result<State, BandwidthControllerError>
where where
C: CoconutBandwidthSigningClient + Sync, C: CoconutBandwidthSigningClient,
{ {
let mut rng = OsRng; let mut rng = OsRng;
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng)); let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
@@ -70,8 +70,7 @@ where
.get_current_epoch_threshold() .get_current_epoch_threshold()
.await? .await?
.ok_or(BandwidthControllerError::NoThreshold)?; .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( let signature = obtain_aggregate_signature(
&state.params, &state.params,
+1 -4
View File
@@ -6,18 +6,15 @@ use nym_credential_storage::error::StorageError;
use nym_credentials::error::Error as CredentialsError; use nym_credentials::error::Error as CredentialsError;
use nym_crypto::asymmetric::encryption::KeyRecoveryError; use nym_crypto::asymmetric::encryption::KeyRecoveryError;
use nym_crypto::asymmetric::identity::Ed25519RecoveryError; use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
use nym_validator_client::coconut::CoconutApiError;
use nym_validator_client::error::ValidatorClientError; use nym_validator_client::error::ValidatorClientError;
use thiserror::Error; use thiserror::Error;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum BandwidthControllerError { pub enum BandwidthControllerError {
#[cfg(not(target_arch = "wasm32"))]
#[error("Nyxd error: {0}")] #[error("Nyxd error: {0}")]
Nyxd(#[from] nym_validator_client::nyxd::error::NyxdError), 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}")] #[error("There was a credential storage error - {0}")]
CredentialStorageError(Box<dyn std::error::Error + Send + Sync>), CredentialStorageError(Box<dyn std::error::Error + Send + Sync>),
+17 -4
View File
@@ -2,10 +2,10 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::BandwidthControllerError; use crate::error::BandwidthControllerError;
use nym_credential_storage::error::StorageError; use nym_credential_storage::error::StorageError;
use nym_credential_storage::storage::Storage; 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 std::str::FromStr;
use { use {
nym_coconut_interface::Base58, nym_coconut_interface::Base58,
@@ -14,8 +14,17 @@ 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 acquire;
pub mod error; pub mod error;
#[cfg(target_arch = "wasm32")]
pub mod wasm_mockups;
pub struct BandwidthController<C, St> { pub struct BandwidthController<C, St> {
storage: St, storage: St,
@@ -55,8 +64,12 @@ impl<C, St: Storage> BandwidthController<C, St> {
let epoch_id = u64::from_str(&bandwidth_credential.epoch_id) let epoch_id = u64::from_str(&bandwidth_credential.epoch_id)
.map_err(|_| StorageError::InconsistentData)?; .map_err(|_| StorageError::InconsistentData)?;
let coconut_api_clients = all_coconut_api_clients(&self.client, epoch_id).await?; #[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 verification_key = obtain_aggregate_verification_key(&coconut_api_clients).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) // the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
@@ -0,0 +1,17 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<C> {
_phantom: PhantomData<C>,
}
impl<C> DkgQueryClient for Client<C> {}
+29 -70
View File
@@ -5,13 +5,9 @@
// and be used by our smart contracts // and be used by our smart contracts
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
#[derive(Debug)] #[derive(Debug)]
pub struct BinaryBuildInformation { 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 // VERGEN_BUILD_TIMESTAMP
/// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`. /// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.
pub build_timestamp: &'static str, pub build_timestamp: &'static str,
@@ -47,9 +43,8 @@ pub struct BinaryBuildInformation {
impl BinaryBuildInformation { impl BinaryBuildInformation {
// explicitly require the build_version to be passed as it's binary specific // explicitly require the build_version to be passed as it's binary specific
pub const fn new(binary_name: &'static str, build_version: &'static str) -> Self { pub const fn new(build_version: &'static str) -> Self {
BinaryBuildInformation { BinaryBuildInformation {
binary_name,
build_timestamp: env!("VERGEN_BUILD_TIMESTAMP"), build_timestamp: env!("VERGEN_BUILD_TIMESTAMP"),
build_version, build_version,
commit_sha: env!("VERGEN_GIT_SHA"), commit_sha: env!("VERGEN_GIT_SHA"),
@@ -63,7 +58,6 @@ impl BinaryBuildInformation {
pub fn to_owned(&self) -> BinaryBuildInformationOwned { pub fn to_owned(&self) -> BinaryBuildInformationOwned {
BinaryBuildInformationOwned { BinaryBuildInformationOwned {
binary_name: self.binary_name.to_owned(),
build_timestamp: self.build_timestamp.to_owned(), build_timestamp: self.build_timestamp.to_owned(),
build_version: self.build_version.to_owned(), build_version: self.build_version.to_owned(),
commit_sha: self.commit_sha.to_owned(), commit_sha: self.commit_sha.to_owned(),
@@ -76,15 +70,39 @@ impl BinaryBuildInformation {
} }
pub fn pretty_print(&self) -> String { pub fn pretty_print(&self) -> String {
self.to_owned().to_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,
)
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BinaryBuildInformationOwned { 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 // VERGEN_BUILD_TIMESTAMP
/// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`. /// Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.
pub build_timestamp: String, pub build_timestamp: String,
@@ -117,62 +135,3 @@ pub struct BinaryBuildInformationOwned {
/// Provides the cargo profile that was used for the build, for example `debug`. /// Provides the cargo profile that was used for the build, for example `debug`.
pub cargo_profile: String, 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()
};
}
+12 -10
View File
@@ -10,29 +10,27 @@ rust-version = "1.66"
[dependencies] [dependencies]
async-trait = { workspace = true } async-trait = { workspace = true }
base64 = "0.21.2" base64 = "0.21.2"
dashmap = "5.4.0"
dirs = "4.0" dirs = "4.0"
futures = { workspace = true } dashmap = "5.4.0"
futures = "0.3"
humantime-serde = "1.0" humantime-serde = "1.0"
log = { workspace = true } log = { workspace = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"] } rand = { version = "0.7.3", features = ["wasm-bindgen"] }
reqwest = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true } serde_json = { workspace = true }
sha2 = "0.10.6" sha2 = "0.10.6"
tap = "1.0.1" tap = "1.0.1"
thiserror = "1.0.34" thiserror = "1.0.34"
time = "0.3.17" url = { version ="2.2", features = ["serde"] }
tokio = { version = "1.24.1", features = ["macros"]}
tungstenite = { version = "0.13.0", default-features = false } tungstenite = { version = "0.13.0", default-features = false }
url = { workspace = true, features = ["serde"] } tokio = { version = "1.24.1", features = ["macros"]}
time = "0.3.17"
zeroize = { workspace = true } zeroize = { workspace = true }
# internal # internal
nym-bandwidth-controller = { path = "../bandwidth-controller" } nym-bandwidth-controller = { path = "../bandwidth-controller" }
nym-config = { path = "../config" } nym-config = { path = "../config" }
nym-crypto = { path = "../crypto" } nym-crypto = { path = "../crypto" }
nym-explorer-api-requests = { path = "../../explorer-api/explorer-api-requests" }
nym-gateway-client = { path = "../client-libs/gateway-client" } nym-gateway-client = { path = "../client-libs/gateway-client" }
#gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm", "coconut"] } #gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm", "coconut"] }
nym-gateway-requests = { path = "../../gateway/gateway-requests" } nym-gateway-requests = { path = "../../gateway/gateway-requests" }
@@ -45,6 +43,10 @@ nym-task = { path = "../task" }
nym-credential-storage = { path = "../credential-storage" } nym-credential-storage = { path = "../credential-storage" }
nym-network-defaults = { path = "../network-defaults" } nym-network-defaults = { path = "../network-defaults" }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
path = "../client-libs/validator-client"
features = ["nyxd-client"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream] [target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-stream]
version = "0.1.11" version = "0.1.11"
features = ["time"] features = ["time"]
@@ -67,9 +69,9 @@ version = "0.4"
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen] [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
version = "0.2.83" version = "0.2.83"
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer] [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-timer]
workspace = true git = "https://github.com/mmsinclair/wasm-timer"
features = ["tokio"] rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6"
[target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers] [target."cfg(target_arch = \"wasm32\")".dependencies.gloo-timers]
version = "0.2.4" version = "0.2.4"
@@ -2,12 +2,11 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use super::received_buffer::ReceivedBufferMessage; 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::base_client::storage::MixnetClientStorage;
use crate::client::cover_traffic_stream::LoopCoverTrafficStream; use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender}; use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
use crate::client::key_manager::persistence::KeyStore; use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ManagedKeys;
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController}; use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use crate::client::real_messages_control; use crate::client::real_messages_control;
use crate::client::real_messages_control::RealMessagesController; use crate::client::real_messages_control::RealMessagesController;
@@ -23,9 +22,8 @@ use crate::client::topology_control::nym_api_provider::NymApiTopologyProvider;
use crate::client::topology_control::{ use crate::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
}; };
use crate::config::{Config, DebugConfig}; use crate::config::{Config, DebugConfig, GatewayEndpointConfig};
use crate::error::ClientCoreError; use crate::error::ClientCoreError;
use crate::init::{setup_gateway, GatewaySetup, InitialisationDetails, InitialisationResult};
use crate::{config, spawn_future}; use crate::{config, spawn_future};
use futures::channel::mpsc; use futures::channel::mpsc;
use log::{debug, info}; use log::{debug, info};
@@ -44,11 +42,18 @@ use nym_sphinx::receiver::{ReconstructedMessage, SphinxMessageReceiver};
use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths}; use nym_task::connections::{ConnectionCommandReceiver, ConnectionCommandSender, LaneQueueLengths};
use nym_task::{TaskClient, TaskManager}; use nym_task::{TaskClient, TaskManager};
use nym_topology::provider_trait::TopologyProvider; use nym_topology::provider_trait::TopologyProvider;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use std::sync::Arc; use std::sync::Arc;
use tap::TapFallible; use tap::TapFallible;
use url::Url; 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"))] #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
pub mod non_wasm_helpers; pub mod non_wasm_helpers;
@@ -194,13 +199,16 @@ where
// note: do **NOT** make this method public as its only valid usage is from within `start_base` // 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 // because it relies on the crypto keys being already loaded
fn mix_address(details: &InitialisationDetails) -> Recipient { fn mix_address(
managed_keys: &ManagedKeys,
gateway_config: &GatewayEndpointConfig,
) -> Recipient {
Recipient::new( Recipient::new(
*details.managed_keys.identity_public_key(), *managed_keys.identity_public_key(),
*details.managed_keys.encryption_public_key(), *managed_keys.encryption_public_key(),
// TODO: below only works under assumption that gateway address == gateway id // TODO: below only works under assumption that gateway address == gateway id
// (which currently is true) // (which currently is true)
NodeIdentity::from_base58_string(&details.gateway_details.gateway_id).unwrap(), NodeIdentity::from_base58_string(&gateway_config.gateway_id).unwrap(),
) )
} }
@@ -285,7 +293,8 @@ where
async fn start_gateway_client( async fn start_gateway_client(
config: &Config, config: &Config,
initialisation_result: InitialisationResult, gateway_config: GatewayEndpointConfig,
managed_keys: &ManagedKeys,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>, bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
mixnet_message_sender: MixnetMessageSender, mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender, ack_sender: AcknowledgementSender,
@@ -295,39 +304,24 @@ where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static, <S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static, <S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
{ {
let managed_keys = initialisation_result.details.managed_keys; let gateway_address = gateway_config.gateway_listener.clone();
let gateway_id = gateway_config.gateway_id;
let mut gateway_client = // TODO: in theory, at this point, this should be infallible
if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client { let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
existing_client.upgrade( .map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
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 gateway_address = gateway_config.gateway_listener.clone(); let mut gateway_client = GatewayClient::new(
let gateway_id = gateway_config.gateway_id; gateway_address,
managed_keys.identity_keypair(),
// TODO: in theory, at this point, this should be infallible gateway_identity,
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id) Some(managed_keys.must_get_gateway_shared_key()),
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?; mixnet_message_sender,
ack_sender,
GatewayClient::new( config.debug.gateway_connection.gateway_response_timeout,
gateway_address, bandwidth_controller,
managed_keys.identity_keypair(), shutdown,
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); gateway_client.set_disabled_credentials_mode(config.client.disabled_credentials_mode);
@@ -345,20 +339,14 @@ where
fn setup_topology_provider( fn setup_topology_provider(
custom_provider: Option<Box<dyn TopologyProvider + Send + Sync>>, custom_provider: Option<Box<dyn TopologyProvider + Send + Sync>>,
provider_from_config: config::TopologyStructure,
nym_api_urls: Vec<Url>, nym_api_urls: Vec<Url>,
) -> Box<dyn TopologyProvider + Send + Sync> { ) -> Box<dyn TopologyProvider + Send + Sync> {
// if no custom provider was ... provided ..., create one using nym-api // if no custom provider was ... provided ..., create one using nym-api
custom_provider.unwrap_or_else(|| match provider_from_config { custom_provider.unwrap_or_else(|| {
config::TopologyStructure::NymApi => Box::new(NymApiTopologyProvider::new( Box::new(NymApiTopologyProvider::new(
nym_api_urls, nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(), env!("CARGO_PKG_VERSION").to_string(),
)), ))
config::TopologyStructure::GeoAware(group) => Box::new(GeoAwareTopologyProvider::new(
nym_api_urls,
env!("CARGO_PKG_VERSION").to_string(),
group,
)),
}) })
} }
@@ -450,23 +438,17 @@ where
Ok(mem_store) Ok(mem_store)
} }
async fn initialise_keys_and_gateway( async fn initialise_keys_and_gateway(&self) -> Result<InitialisationDetails, ClientCoreError>
setup_method: GatewaySetup,
key_store: &S::KeyStore,
details_store: &S::GatewayDetailsStore,
overwrite_data: bool,
validator_servers: Option<&[Url]>,
) -> Result<InitialisationResult, ClientCoreError>
where where
<S::KeyStore as KeyStore>::StorageError: Sync + Send, <S::KeyStore as KeyStore>::StorageError: Sync + Send,
<S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send, <S::GatewayDetailsStore as GatewayDetailsStore>::StorageError: Sync + Send,
{ {
setup_gateway( setup_gateway(
setup_method, &self.setup_method,
key_store, self.client_store.key_store(),
details_store, self.client_store.gateway_details_store(),
overwrite_data, false,
validator_servers, Some(&self.config.client.nym_api_urls),
) )
.await .await
} }
@@ -482,14 +464,9 @@ where
info!("Starting nym client"); info!("Starting nym client");
// derive (or load) client keys and gateway configuration // derive (or load) client keys and gateway configuration
let init_res = Self::initialise_keys_and_gateway( let details = self.initialise_keys_and_gateway().await?;
self.setup_method, let gateway_config = details.gateway_details;
self.client_store.key_store(), let managed_keys = details.managed_keys;
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(); let (reply_storage_backend, credential_store) = self.client_store.into_runtime_stores();
@@ -523,15 +500,14 @@ where
let (reply_controller_sender, reply_controller_receiver) = let (reply_controller_sender, reply_controller_receiver) =
reply_controller::requests::new_control_channels(); reply_controller::requests::new_control_channels();
let self_address = Self::mix_address(&init_res.details); let self_address = Self::mix_address(&managed_keys, &gateway_config);
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, // the components are started in very specific order. Unless you know what you are doing,
// do not change that. // do not change that.
let gateway_client = Self::start_gateway_client( let gateway_client = Self::start_gateway_client(
self.config, self.config,
init_res, gateway_config,
&managed_keys,
bandwidth_controller, bandwidth_controller,
mixnet_messages_sender, mixnet_messages_sender,
ack_sender, ack_sender,
@@ -545,10 +521,8 @@ where
let topology_provider = Self::setup_topology_provider( let topology_provider = Self::setup_topology_provider(
self.custom_topology_provider.take(), self.custom_topology_provider.take(),
self.config.debug.topology.topology_structure,
self.config.get_nym_api_endpoints(), self.config.get_nym_api_endpoints(),
); );
Self::start_topology_refresher( Self::start_topology_refresher(
topology_provider, topology_provider,
self.config.debug.topology, self.config.debug.topology,
@@ -558,7 +532,7 @@ where
.await?; .await?;
Self::start_received_messages_buffer_controller( Self::start_received_messages_buffer_controller(
encryption_keys, managed_keys.encryption_keypair(),
received_buffer_request_receiver, received_buffer_request_receiver,
mixnet_messages_receiver, mixnet_messages_receiver,
reply_storage.key_storage(), reply_storage.key_storage(),
@@ -583,7 +557,7 @@ where
let controller_config = real_messages_control::Config::new( let controller_config = real_messages_control::Config::new(
&self.config.debug, &self.config.debug,
Arc::clone(&ack_key), managed_keys.ack_key(),
self_address, self_address,
); );
@@ -610,7 +584,7 @@ where
{ {
Self::start_cover_traffic_stream( Self::start_cover_traffic_stream(
&self.config.debug, &self.config.debug,
ack_key, managed_keys.ack_key(),
self_address, self_address,
shared_topology_accessor.clone(), shared_topology_accessor.clone(),
message_sender, message_sender,
@@ -10,8 +10,8 @@ use crate::error::ClientCoreError;
use log::{error, info}; use log::{error, info};
use nym_bandwidth_controller::BandwidthController; use nym_bandwidth_controller::BandwidthController;
use nym_credential_storage::storage::Storage as CredentialStorage; use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_validator_client::nyxd; use nym_validator_client::nyxd::QueryNyxdClient;
use nym_validator_client::QueryHttpRpcNyxdClient; use nym_validator_client::Client;
use std::path::Path; use std::path::Path;
use std::{fs, io}; use std::{fs, io};
use time::OffsetDateTime; use time::OffsetDateTime;
@@ -104,38 +104,48 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
pub fn create_bandwidth_controller<St: CredentialStorage>( pub fn create_bandwidth_controller<St: CredentialStorage>(
config: &Config, config: &Config,
storage: St, storage: St,
) -> BandwidthController<QueryHttpRpcNyxdClient, St> { ) -> BandwidthController<Client<QueryNyxdClient>, St> {
let nyxd_url = config let nyxd_url = config
.get_validator_endpoints() .get_validator_endpoints()
.pop() .pop()
.expect("No nyxd validator endpoint provided"); .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, storage) create_bandwidth_controller_with_urls(nyxd_url, api_url, storage)
} }
pub fn create_bandwidth_controller_with_urls<St: CredentialStorage>( pub fn create_bandwidth_controller_with_urls<St: CredentialStorage>(
nyxd_url: Url, nyxd_url: Url,
nym_api_url: Url,
storage: St, storage: St,
) -> BandwidthController<QueryHttpRpcNyxdClient, St> { ) -> BandwidthController<Client<QueryNyxdClient>, St> {
let client = default_query_dkg_client(nyxd_url); let client = default_query_dkg_client(nyxd_url, nym_api_url);
BandwidthController::new(storage, client) BandwidthController::new(storage, client)
} }
pub fn default_query_dkg_client_from_config(config: &Config) -> QueryHttpRpcNyxdClient { pub fn default_query_dkg_client_from_config(config: &Config) -> Client<QueryNyxdClient> {
let nyxd_url = config let nyxd_url = config
.get_validator_endpoints() .get_validator_endpoints()
.pop() .pop()
.expect("No nyxd validator endpoint provided"); .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) default_query_dkg_client(nyxd_url, api_url)
} }
pub fn default_query_dkg_client(nyxd_url: Url) -> QueryHttpRpcNyxdClient { pub fn default_query_dkg_client(nyxd_url: Url, nym_api_url: Url) -> Client<QueryNyxdClient> {
let details = nym_network_defaults::NymNetworkDetails::new_from_env(); let details = nym_network_defaults::NymNetworkDetails::new_from_env();
let client_config = nyxd::Config::try_from_nym_network_details(&details) let mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details)
.expect("failed to construct validator client config"); .expect("failed to construct validator client config");
// overwrite env configuration with config URLs // overwrite env configuration with config URLs
QueryHttpRpcNyxdClient::connect(client_config, nyxd_url.as_str()) client_config = client_config.with_urls(nyxd_url, nym_api_url);
nym_validator_client::Client::new_query(client_config)
.expect("Could not construct query client") .expect("Could not construct query client")
} }
@@ -19,10 +19,10 @@ use std::time::Duration;
use tokio::sync::mpsc::error::TrySendError; use tokio::sync::mpsc::error::TrySendError;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::time::{sleep, Sleep}; use tokio::time;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::{sleep, Sleep}; use wasm_timer;
pub struct LoopCoverTrafficStream<R> pub struct LoopCoverTrafficStream<R>
where where
@@ -39,7 +39,11 @@ where
/// Internal state, determined by `average_message_sending_delay`, /// Internal state, determined by `average_message_sending_delay`,
/// used to keep track of when a next packet should be sent out. /// used to keep track of when a next packet should be sent out.
next_delay: Pin<Box<Sleep>>, #[cfg(not(target_arch = "wasm32"))]
next_delay: Pin<Box<time::Sleep>>,
#[cfg(target_arch = "wasm32")]
next_delay: Pin<Box<wasm_timer::Delay>>,
/// Channel used for sending prepared nym packets to `MixTrafficController` that sends them /// Channel used for sending prepared nym packets to `MixTrafficController` that sends them
/// out to the network without any further delays. /// out to the network without any further delays.
@@ -86,9 +90,17 @@ where
// The next interval value is `next_poisson_delay` after the one that just // The next interval value is `next_poisson_delay` after the one that just
// yielded. // yielded.
let now = self.next_delay.deadline(); #[cfg(not(target_arch = "wasm32"))]
let next = now + next_poisson_delay; {
self.next_delay.as_mut().reset(next); 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);
}
Poll::Ready(Some(())) Poll::Ready(Some(()))
} }
@@ -108,7 +120,11 @@ impl LoopCoverTrafficStream<OsRng> {
) -> Self { ) -> Self {
let rng = OsRng; let rng = OsRng;
let next_delay = Box::pin(sleep(Default::default())); #[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()));
LoopCoverTrafficStream { LoopCoverTrafficStream {
ack_key, ack_key,
@@ -126,7 +142,12 @@ impl LoopCoverTrafficStream<OsRng> {
} }
fn set_next_delay(&mut self, amount: Duration) { fn set_next_delay(&mut self, amount: Duration) {
let next_delay = Box::pin(sleep(amount)); #[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));
self.next_delay = next_delay; self.next_delay = next_delay;
} }
@@ -2,12 +2,13 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use std::time::Duration; use std::time::Duration;
use wasm_timer;
pub use wasmtimer::{std::Instant, tokio::*}; pub use wasm_timer::*;
pub type IntervalStream = gloo_timers::future::IntervalStream; pub type IntervalStream = gloo_timers::future::IntervalStream;
pub(crate) fn get_time_now() -> Instant { pub(crate) fn get_time_now() -> Instant {
Instant::now() wasm_timer::Instant::now()
} }
pub(crate) fn new_interval_stream(polling_rate: Duration) -> IntervalStream { pub(crate) fn new_interval_stream(polling_rate: Duration) -> IntervalStream {
+7 -2
View File
@@ -3,10 +3,15 @@
use crate::spawn_future; use crate::spawn_future;
use log::*; use log::*;
use nym_credential_storage::storage::Storage;
use nym_gateway_client::GatewayClient; use nym_gateway_client::GatewayClient;
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
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;
pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>; pub type BatchMixMessageSender = tokio::sync::mpsc::Sender<Vec<MixPacket>>;
pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>; pub type BatchMixMessageReceiver = tokio::sync::mpsc::Receiver<Vec<MixPacket>>;
@@ -27,10 +27,9 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::time::{sleep, Sleep}; use tokio::time;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::{sleep, Sleep}; use wasm_timer;
mod sending_delay_controller; mod sending_delay_controller;
@@ -83,7 +82,11 @@ where
/// Internal state, determined by `average_message_sending_delay`, /// Internal state, determined by `average_message_sending_delay`,
/// used to keep track of when a next packet should be sent out. /// used to keep track of when a next packet should be sent out.
next_delay: Option<Pin<Box<Sleep>>>, #[cfg(not(target_arch = "wasm32"))]
next_delay: Option<Pin<Box<time::Sleep>>>,
#[cfg(target_arch = "wasm32")]
next_delay: Option<Pin<Box<wasm_timer::Delay>>>,
// To make sure we don't overload the mix_tx channel, we limit the rate we are pushing // To make sure we don't overload the mix_tx channel, we limit the rate we are pushing
// messages. // messages.
@@ -370,9 +373,17 @@ where
// The next interval value is `next_poisson_delay` after the one that just // The next interval value is `next_poisson_delay` after the one that just
// yielded. // yielded.
let now = next_delay.deadline(); #[cfg(not(target_arch = "wasm32"))]
let next = now + next_poisson_delay; {
next_delay.as_mut().reset(next); 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);
}
// On every iteration we get new messages from upstream. Given that these come bunched // 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 // in `Vec`, this ensures that on average we will fetch messages faster than we can
@@ -410,7 +421,12 @@ where
self.config.traffic.message_sending_average_delay, self.config.traffic.message_sending_average_delay,
); );
let next_delay = Box::pin(sleep(sampled)); #[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));
self.next_delay = Some(next_delay); self.next_delay = Some(next_delay);
Poll::Pending Poll::Pending
@@ -1,319 +0,0 @@
use std::{collections::HashMap, fmt};
use log::{debug, error, info};
use nym_explorer_api_requests::PrettyDetailedMixNodeBond;
use nym_topology::{
nym_topology_from_detailed,
provider_trait::{async_trait, TopologyProvider},
NymTopology,
};
use nym_validator_client::client::MixId;
use rand::{prelude::SliceRandom, thread_rng};
use serde::{Deserialize, Serialize};
use url::Url;
const MIN_NODES_PER_LAYER: usize = 1;
const EXPLORER_API_MIXNODES_URL: &str = "https://explorer.nymtech.net/api/v1/mix-nodes";
// TODO: create a explorer-api-client
async fn fetch_mixnodes_from_explorer_api() -> Option<Vec<PrettyDetailedMixNodeBond>> {
reqwest::get(EXPLORER_API_MIXNODES_URL)
.await
.ok()?
.json::<Vec<PrettyDetailedMixNodeBond>>()
.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<Self, Self::Err> {
let group = CountryGroup::new(s);
if group == CountryGroup::Unknown {
Err(())
} else {
Ok(group)
}
}
}
impl CountryGroup {
#[allow(unused)]
fn known(self) -> Option<CountryGroup> {
use CountryGroup::*;
match self {
Europe | NorthAmerica | SouthAmerica | Oceania | Asia | Africa => Some(self),
Unknown => None,
}
}
}
fn group_mixnodes_by_country_code(
mixnodes: Vec<PrettyDetailedMixNodeBond>,
) -> HashMap<CountryGroup, Vec<MixId>> {
mixnodes
.into_iter()
.fold(HashMap::<CountryGroup, Vec<MixId>>::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 log_mixnode_distribution(mixnodes: &HashMap<CountryGroup, Vec<MixId>>) {
let mixnode_distribution = mixnodes
.iter()
.map(|(k, v)| format!("{}: {}", k, v.len()))
.collect::<Vec<_>>()
.join(", ");
debug!("Mixnode distribution - {}", mixnode_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: CountryGroup,
client_version: String,
}
impl GeoAwareTopologyProvider {
pub fn new(
mut nym_api_urls: Vec<Url>,
client_version: String,
filter_on: CountryGroup,
) -> 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<NymTopology> {
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;
};
// 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 Some(filtered_mixnode_ids) = mixnode_distribution.get(&self.filter_on) else {
error!("no mixnodes found for: {}", self.filter_on);
return None;
};
let mixnodes = mixnodes
.into_iter()
.filter(|m| filtered_mixnode_ids.contains(&m.mix_id()))
.collect::<Vec<_>>();
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<NymTopology> {
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<NymTopology> {
self.get_topology().await
}
}
@@ -10,7 +10,6 @@ use nym_topology::NymTopologyError;
use std::time::Duration; use std::time::Duration;
mod accessor; mod accessor;
pub mod geo_aware_provider;
pub(crate) mod nym_api_provider; pub(crate) mod nym_api_provider;
// TODO: move it to config later // TODO: move it to config later
+1 -21
View File
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
use std::time::Duration; use std::time::Duration;
use url::Url; use url::Url;
use crate::{client::topology_control::geo_aware_provider::CountryGroup, error::ClientCoreError}; use crate::error::ClientCoreError;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@@ -158,15 +158,6 @@ impl Config {
self 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 { pub fn with_no_per_hop_delays(mut self, no_per_hop_delays: bool) -> Self {
if no_per_hop_delays { if no_per_hop_delays {
self.set_no_per_hop_delays() self.set_no_per_hop_delays()
@@ -475,16 +466,6 @@ pub struct Topology {
/// the first valid instance. /// the first valid instance.
/// Supersedes `topology_refresh_rate_ms`. /// Supersedes `topology_refresh_rate_ms`.
pub disable_refreshing: bool, pub disable_refreshing: bool,
/// Specifies the mixnode topology to be used for sending packets.
pub topology_structure: TopologyStructure,
}
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TopologyStructure {
#[default]
NymApi,
GeoAware(CountryGroup),
} }
impl Default for Topology { impl Default for Topology {
@@ -493,7 +474,6 @@ impl Default for Topology {
topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE, topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE,
topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT, topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT,
disable_refreshing: false, disable_refreshing: false,
topology_structure: TopologyStructure::default(),
} }
} }
} }
@@ -267,7 +267,6 @@ impl From<TopologyV1_1_20_2> for Topology {
topology_refresh_rate: value.topology_refresh_rate, topology_refresh_rate: value.topology_refresh_rate,
topology_resolution_timeout: value.topology_resolution_timeout, topology_resolution_timeout: value.topology_resolution_timeout,
disable_refreshing: value.disable_refreshing, disable_refreshing: value.disable_refreshing,
topology_structure: Default::default(),
} }
} }
} }
+32 -36
View File
@@ -3,11 +3,11 @@
use crate::config::GatewayEndpointConfig; use crate::config::GatewayEndpointConfig;
use crate::error::ClientCoreError; use crate::error::ClientCoreError;
use crate::init::RegistrationResult;
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use log::{debug, info, trace, warn}; use log::{debug, info, trace, warn};
use nym_crypto::asymmetric::identity; use nym_crypto::asymmetric::identity;
use nym_gateway_client::GatewayClient; use nym_gateway_client::GatewayClient;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_topology::{filter::VersionFilterable, gateway}; use nym_topology::{filter::VersionFilterable, gateway};
use rand::{seq::SliceRandom, Rng}; use rand::{seq::SliceRandom, Rng};
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
@@ -15,6 +15,8 @@ use tap::TapFallible;
use tungstenite::Message; use tungstenite::Message;
use url::Url; use url::Url;
#[cfg(not(target_arch = "wasm32"))]
use nym_validator_client::nyxd::DirectSigningNyxdClient;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::net::TcpStream; use tokio::net::TcpStream;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@@ -25,20 +27,17 @@ use tokio_tungstenite::connect_async;
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>; type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
#[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")] #[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket; 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")] #[cfg(target_arch = "wasm32")]
type WsConn = JSWebsocket; type WsConn = JSWebsocket;
const CONCURRENT_GATEWAYS_MEASURED: usize = 20;
const MEASUREMENTS: usize = 3; const MEASUREMENTS: usize = 3;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@@ -125,9 +124,15 @@ async fn measure_latency(gateway: &gateway::Node) -> Result<GatewayWithLatency,
Ok::<(), ClientCoreError>(()) Ok::<(), ClientCoreError>(())
}; };
let timeout = sleep(PING_TIMEOUT); // 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"))]
tokio::pin!(timeout); tokio::pin!(timeout);
#[cfg(target_arch = "wasm32")]
let mut timeout = wasm_timer::Delay::new(PING_TIMEOUT);
tokio::select! { tokio::select! {
_ = &mut timeout => { _ = &mut timeout => {
warn!("timed out while trying to perform measurement...") warn!("timed out while trying to perform measurement...")
@@ -153,29 +158,23 @@ pub(super) async fn choose_gateway_by_latency<R: Rng>(
rng: &mut R, rng: &mut R,
gateways: &[gateway::Node], gateways: &[gateway::Node],
) -> Result<gateway::Node, ClientCoreError> { ) -> Result<gateway::Node, ClientCoreError> {
info!( info!("choosing gateway by latency...");
"choosing gateway by latency, pinging {} gateways ...",
gateways.len()
);
let gateways_with_latency = Arc::new(tokio::sync::Mutex::new(Vec::new())); let mut gateways_with_latency = Vec::new();
futures::stream::iter(gateways) for gateway in gateways {
.for_each_concurrent(CONCURRENT_GATEWAYS_MEASURED, |gateway| async { let id = *gateway.identity();
let id = *gateway.identity(); trace!("measuring latency to {id}...");
trace!("measuring latency to {id}..."); let with_latency = match measure_latency(gateway).await {
match measure_latency(gateway).await { Ok(res) => res,
Ok(with_latency) => { Err(err) => {
debug!("{id}: {:?}", with_latency.latency); warn!("failed to measure {id}: {err}");
gateways_with_latency.lock().await.push(with_latency); continue;
} }
Err(err) => { };
warn!("failed to measure {id}: {err}"); debug!("{id}: {:?}", with_latency.latency);
} gateways_with_latency.push(with_latency)
}; }
})
.await;
let gateways_with_latency = gateways_with_latency.lock().await;
let chosen = gateways_with_latency let chosen = gateways_with_latency
.choose_weighted(rng, |item| 1. / item.latency.as_secs_f32()) .choose_weighted(rng, |item| 1. / item.latency.as_secs_f32())
.expect("invalid selection weight!"); .expect("invalid selection weight!");
@@ -201,9 +200,9 @@ pub(super) fn uniformly_random_gateway<R: Rng>(
pub(super) async fn register_with_gateway( pub(super) async fn register_with_gateway(
gateway: &GatewayEndpointConfig, gateway: &GatewayEndpointConfig,
our_identity: Arc<identity::KeyPair>, our_identity: Arc<identity::KeyPair>,
) -> Result<RegistrationResult, ClientCoreError> { ) -> Result<Arc<SharedKeys>, ClientCoreError> {
let timeout = Duration::from_millis(1500); let timeout = Duration::from_millis(1500);
let mut gateway_client = GatewayClient::new_init( let mut gateway_client: GatewayClient<DirectSigningNyxdClient, _> = GatewayClient::new_init(
gateway.gateway_listener.clone(), gateway.gateway_listener.clone(),
gateway.try_get_gateway_identity_key()?, gateway.try_get_gateway_identity_key()?,
our_identity.clone(), our_identity.clone(),
@@ -217,8 +216,5 @@ pub(super) async fn register_with_gateway(
.perform_initial_authentication() .perform_initial_authentication()
.await .await
.tap_err(|_| log::warn!("Failed to register with the gateway!"))?; .tap_err(|_| log::warn!("Failed to register with the gateway!"))?;
Ok(RegistrationResult { Ok(shared_keys)
shared_keys,
authenticated_ephemeral_client: Some(gateway_client),
})
} }
+18 -67
View File
@@ -14,39 +14,16 @@ use crate::{
error::ClientCoreError, error::ClientCoreError,
}; };
use nym_crypto::asymmetric::identity; 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_sphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
use nym_topology::gateway; use nym_topology::gateway;
use nym_validator_client::client::IdentityKey; use nym_validator_client::client::IdentityKey;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use serde::Serialize; use serde::Serialize;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use std::sync::Arc;
use url::Url; use url::Url;
pub mod helpers; pub mod helpers;
pub struct RegistrationResult {
pub shared_keys: Arc<SharedKeys>,
pub authenticated_ephemeral_client: Option<GatewayClient<InitOnly>>,
}
pub struct InitialisationResult {
pub details: InitialisationDetails,
pub authenticated_ephemeral_client: Option<GatewayClient<InitOnly>>,
}
impl From<InitialisationDetails> for InitialisationResult {
fn from(details: InitialisationDetails) -> Self {
InitialisationResult {
details,
authenticated_ephemeral_client: None,
}
}
}
// TODO: rename to something better... // TODO: rename to something better...
#[derive(Debug)] #[derive(Debug)]
pub struct InitialisationDetails { pub struct InitialisationDetails {
@@ -97,6 +74,7 @@ impl InitialisationDetails {
} }
} }
#[derive(Debug, Clone)]
pub enum GatewaySetup { pub enum GatewaySetup {
/// The gateway specification MUST BE loaded from the underlying storage. /// The gateway specification MUST BE loaded from the underlying storage.
MustLoad, MustLoad,
@@ -114,13 +92,6 @@ pub enum GatewaySetup {
/// Full gateway configuration /// Full gateway configuration
details: PersistedGatewayDetails, details: PersistedGatewayDetails,
}, },
ReuseConnection {
/// The authenticated ephemeral client that was created during `init`
authenticated_ephemeral_client: GatewayClient<InitOnly>,
/// Details of this pre-initialised client
details: InitialisationDetails,
},
} }
impl From<PersistedGatewayDetails> for GatewaySetup { impl From<PersistedGatewayDetails> for GatewaySetup {
@@ -295,32 +266,18 @@ fn ensure_valid_details(
} }
pub async fn setup_gateway_from<K, D>( pub async fn setup_gateway_from<K, D>(
setup: GatewaySetup, setup: &GatewaySetup,
key_store: &K, key_store: &K,
details_store: &D, details_store: &D,
overwrite_data: bool, overwrite_data: bool,
gateways: Option<&[gateway::Node]>, gateways: Option<&[gateway::Node]>,
) -> Result<InitialisationResult, ClientCoreError> ) -> Result<InitialisationDetails, ClientCoreError>
where where
K: KeyStore, K: KeyStore,
D: GatewayDetailsStore, D: GatewayDetailsStore,
K::StorageError: Send + Sync + 'static, K::StorageError: Send + Sync + 'static,
D::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; let mut rng = OsRng;
// try load gateway details // try load gateway details
@@ -329,14 +286,14 @@ where
// try load keys and decide what to do based on the GatewaySetup // try load keys and decide what to do based on the GatewaySetup
let mut managed_keys = match ManagedKeys::try_load(key_store).await { let mut managed_keys = match ManagedKeys::try_load(key_store).await {
Ok(loaded_keys) => { Ok(loaded_keys) => {
match &setup { match setup {
GatewaySetup::MustLoad => { GatewaySetup::MustLoad => {
// get EVERYTHING from the storage // get EVERYTHING from the storage
let details = loaded_details?; let details = loaded_details?;
ensure_valid_details(&details, &loaded_keys)?; ensure_valid_details(&details, &loaded_keys)?;
// no need to persist anything as we got everything from the storage // no need to persist anything as we got everything from the storage
return Ok(InitialisationDetails::new(details.into(), loaded_keys).into()); return Ok(InitialisationDetails::new(details.into(), loaded_keys));
} }
GatewaySetup::Predefined { details } => { GatewaySetup::Predefined { details } => {
// we already have defined gateway details AND a shared key // we already have defined gateway details AND a shared key
@@ -347,9 +304,10 @@ where
_store_gateway_details(details_store, details).await?; _store_gateway_details(details_store, details).await?;
} }
return Ok( return Ok(InitialisationDetails::new(
InitialisationDetails::new(details.clone().into(), loaded_keys).into(), details.clone().into(),
); loaded_keys,
));
} }
GatewaySetup::Specified { gateway_identity } => { GatewaySetup::Specified { gateway_identity } => {
// if that data was already stored... // if that data was already stored...
@@ -365,8 +323,7 @@ where
return Ok(InitialisationDetails::new( return Ok(InitialisationDetails::new(
existing_gateway.into(), existing_gateway.into(),
loaded_keys, loaded_keys,
) ));
.into());
} }
} }
@@ -384,8 +341,7 @@ where
return Ok(InitialisationDetails::new( return Ok(InitialisationDetails::new(
existing_gateway.into(), existing_gateway.into(),
loaded_keys, loaded_keys,
) ));
.into());
} }
// we didn't get full details from the store and we have loaded some keys // we didn't get full details from the store and we have loaded some keys
@@ -396,9 +352,6 @@ where
return Err(ClientCoreError::ForbiddenKeyOverwrite); return Err(ClientCoreError::ForbiddenKeyOverwrite);
} }
} }
GatewaySetup::ReuseConnection { .. } => {
unreachable!("the reuse connection variant was already manually covered")
}
} }
} }
Err(_) => { Err(_) => {
@@ -418,9 +371,7 @@ where
let our_identity = managed_keys.identity_keypair(); let our_identity = managed_keys.identity_keypair();
// Establish connection, authenticate and generate keys for talking with the gateway // Establish connection, authenticate and generate keys for talking with the gateway
let registration_result = let shared_keys = helpers::register_with_gateway(&gateway_details, our_identity).await?;
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); let persisted_details = PersistedGatewayDetails::new(gateway_details, &shared_keys);
@@ -435,19 +386,19 @@ where
// persist gateway config // persist gateway config
_store_gateway_details(details_store, &persisted_details).await?; _store_gateway_details(details_store, &persisted_details).await?;
Ok(InitialisationResult { Ok(InitialisationDetails::new(
details: InitialisationDetails::new(persisted_details.into(), managed_keys), persisted_details.into(),
authenticated_ephemeral_client: registration_result.authenticated_ephemeral_client, managed_keys,
}) ))
} }
pub async fn setup_gateway<K, D>( pub async fn setup_gateway<K, D>(
setup: GatewaySetup, setup: &GatewaySetup,
key_store: &K, key_store: &K,
details_store: &D, details_store: &D,
overwrite_data: bool, overwrite_data: bool,
validator_servers: Option<&[Url]>, validator_servers: Option<&[Url]>,
) -> Result<InitialisationResult, ClientCoreError> ) -> Result<InitialisationDetails, ClientCoreError>
where where
K: KeyStore, K: KeyStore,
D: GatewayDetailsStore, D: GatewayDetailsStore,
+7 -7
View File
@@ -9,10 +9,10 @@ edition = "2021"
[dependencies] [dependencies]
# TODO: (for this and other crates), similarly to 'tokio', import only required "futures" modules rather than # TODO: (for this and other crates), similarly to 'tokio', import only required "futures" modules rather than
# the entire crate # the entire crate
futures = { workspace = true } futures = "0.3"
log = { workspace = true } log = { workspace = true }
thiserror = { workspace = true } thiserror = "1.0"
url = { workspace = true } url = "2.2"
rand = { version = "0.7.3", features = ["wasm-bindgen"] } rand = { version = "0.7.3", features = ["wasm-bindgen"] }
tokio = { version = "1.24.1", features = ["macros"] } 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-network-defaults = { path = "../../network-defaults" }
nym-sphinx = { path = "../../nymsphinx" } nym-sphinx = { path = "../../nymsphinx" }
nym-pemstore = { path = "../../pemstore" } nym-pemstore = { path = "../../pemstore" }
nym-validator-client = { path = "../validator-client", default-features = false } nym-validator-client = { path = "../validator-client" }
nym-task = { path = "../../task" } nym-task = { path = "../../task" }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
@@ -58,9 +58,9 @@ path = "../../wasm-utils"
features = ["websocket"] features = ["websocket"]
# only import it in wasm. Prefer proper tokio timer in non-wasm # only import it in wasm. Prefer proper tokio timer in non-wasm
[target."cfg(target_arch = \"wasm32\")".dependencies.wasmtimer] [target."cfg(target_arch = \"wasm32\")".dependencies.wasm-timer]
workspace = true git = "https://github.com/mmsinclair/wasm-timer"
features = ["tokio"] rev = "b9d1a54ad514c2f230a026afe0dde341e98cd7b6"
# this is due to tungstenite using `rand` 0.8 and associated changes in `getrandom` crate # 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. # which now does not support wasm32-unknown-unknown target by default.
+27 -53
View File
@@ -1,4 +1,4 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::error::GatewayClientError; use crate::error::GatewayClientError;
@@ -22,7 +22,6 @@ use nym_gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse,
use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN}; use nym_network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
use nym_sphinx::forwarding::packet::MixPacket; use nym_sphinx::forwarding::packet::MixPacket;
use nym_task::TaskClient; use nym_task::TaskClient;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::sync::Arc; use std::sync::Arc;
@@ -30,19 +29,21 @@ use std::time::Duration;
use tungstenite::protocol::Message; use tungstenite::protocol::Message;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio::time::sleep; use nym_validator_client::nyxd::traits::DkgQueryClient;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async; use tokio_tungstenite::connect_async;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket; use nym_bandwidth_controller::wasm_mockups::DkgQueryClient;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasmtimer::tokio::sleep; use wasm_timer;
#[cfg(target_arch = "wasm32")]
use wasm_utils::websocket::JSWebsocket;
const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10; const DEFAULT_RECONNECTION_ATTEMPTS: usize = 10;
const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5); const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
pub struct GatewayClient<C, St = EphemeralCredentialStorage> { pub struct GatewayClient<C, St> {
authenticated: bool, authenticated: bool,
disabled_credentials_mode: bool, disabled_credentials_mode: bool,
bandwidth_remaining: i64, bandwidth_remaining: i64,
@@ -196,7 +197,16 @@ impl<C, St> GatewayClient<C, St> {
return Ok(()); return Ok(());
} }
sleep(self.reconnection_backoff).await; #[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
);
}
} }
// final attempt (done separately to be able to return a proper error) // final attempt (done separately to be able to return a proper error)
@@ -225,9 +235,16 @@ impl<C, St> GatewayClient<C, St> {
_ => return Err(GatewayClientError::ConnectionInInvalidState), _ => return Err(GatewayClientError::ConnectionInInvalidState),
}; };
let timeout = sleep(self.response_timeout_duration); #[cfg(not(target_arch = "wasm32"))]
let timeout = tokio::time::sleep(self.response_timeout_duration);
#[cfg(not(target_arch = "wasm32"))]
tokio::pin!(timeout); 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 { loop {
tokio::select! { tokio::select! {
_ = self.shutdown.recv() => { _ = self.shutdown.recv() => {
@@ -467,14 +484,6 @@ impl<C, St> GatewayClient<C, St> {
pub async fn perform_initial_authentication( pub async fn perform_initial_authentication(
&mut self, &mut self,
) -> Result<Arc<SharedKeys>, GatewayClientError> { ) -> Result<Arc<SharedKeys>, 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() { if self.shared_key.is_some() {
self.authenticate(None).await?; self.authenticate(None).await?;
} else { } else {
@@ -759,9 +768,7 @@ impl<C, St> GatewayClient<C, St> {
} }
} }
pub struct InitOnly; impl<C> GatewayClient<C, EphemeralCredentialStorage> {
impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
// for initialisation we do not need credential storage. Though it's still a bit weird we have to set the generic... // 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( pub fn new_init(
gateway_address: String, gateway_address: String,
@@ -778,7 +785,7 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
let shutdown = TaskClient::dummy(); let shutdown = TaskClient::dummy();
let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown.clone()); let packet_router = PacketRouter::new(ack_tx, mix_tx, shutdown.clone());
GatewayClient { GatewayClient::<C, EphemeralCredentialStorage> {
authenticated: false, authenticated: false,
disabled_credentials_mode: true, disabled_credentials_mode: true,
bandwidth_remaining: 0, bandwidth_remaining: 0,
@@ -796,37 +803,4 @@ impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
shutdown, shutdown,
} }
} }
pub fn upgrade<C, St>(
self,
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
response_timeout_duration: Duration,
bandwidth_controller: Option<BandwidthController<C, St>>,
shutdown: TaskClient,
) -> GatewayClient<C, St> {
// 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,
}
}
} }
+1 -1
View File
@@ -7,7 +7,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
futures = { workspace = true } futures = "0.3"
log = { workspace = true } log = { workspace = true }
tokio = { version = "1.24.1", features = ["time", "net", "rt"] } tokio = { version = "1.24.1", features = ["time", "net", "rt"] }
tokio-util = { version = "0.7.4", features = ["codec"] } tokio-util = { version = "0.7.4", features = ["codec"] }
+48 -38
View File
@@ -13,7 +13,6 @@ colored = "2.0"
nym-coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" } nym-coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" }
nym-contracts-common = { path = "../../cosmwasm-smart-contracts/contracts-common" } 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-mixnet-contract-common = { path = "../../cosmwasm-smart-contracts/mixnet-contract" }
nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-contract" } nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-contract" }
nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" } nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
@@ -21,50 +20,43 @@ nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig
nym-name-service-common = { path = "../../cosmwasm-smart-contracts/name-service" } nym-name-service-common = { path = "../../cosmwasm-smart-contracts/name-service" }
nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" } nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts/service-provider-directory" } nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts/service-provider-directory" }
nym-vesting-contract = { path = "../../../contracts/vesting" }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true } serde_json = { workspace = true }
reqwest = { workspace = true, features = ["json"] } reqwest = { version = "0.11", features = ["json"] }
thiserror = { workspace = true } thiserror = "1"
log = { workspace = true } log = { workspace = true }
url = { workspace = true, features = ["serde"] } url = { version = "2.2", features = ["serde"] }
tokio = { workspace = true, features = ["sync", "time"] } tokio = { version = "1.24.1", features = ["sync", "time"] }
futures = { workspace = true } futures = "0.3"
openssl = { version = "^0.10.55", features = ["vendored"], optional = true } openssl = { version = "0.10", features = ["vendored"], optional = true }
nym-coconut-interface = { path = "../../coconut-interface" } nym-coconut-interface = { path = "../../coconut-interface" }
nym-network-defaults = { path = "../../network-defaults" } nym-network-defaults = { path = "../../network-defaults" }
nym-api-requests = { path = "../../../nym-api/nym-api-requests" } nym-api-requests = { path = "../../../nym-api/nym-api-requests" }
async-trait = { workspace = true } # required for nyxd-client
bip39 = { workspace = true, features = ["rand"] } # at some point it might be possible to make it wasm-compatible
nym-config = { path = "../../config" } # perhaps after https://github.com/cosmos/cosmos-rust/pull/97 is resolved (and tendermint-rs is updated)
cosmrs = { workspace = true, features = ["bip32", "cosmwasm"] } async-trait = { workspace = true, optional = true }
bip39 = { workspace = true, features = ["rand"], optional = true }
nym-config = { path = "../../config", optional = true }
cosmrs = { workspace = true, features = ["rpc", "bip32", "cosmwasm"], optional = true }
# note that this has the same version as used by cosmrs # note that this has the same version as used by cosmrs
eyre = { version = "0.6", optional = true }
# import it just for the `Client` trait cw3 = { workspace = true, optional = true }
tendermint-rpc = { workspace = true } cw4 = { workspace = true, optional = true }
prost = { version = "0.10", default-features = false, optional = true }
eyre = { version = "0.6" } flate2 = { version = "1.0.20", optional = true }
cw-utils = { workspace = true } sha2 = { version = "0.9.5", optional = true }
cw2 = { workspace = true } itertools = { version = "0.10", optional = true }
cw3 = { workspace = true } zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] }
cw4 = { workspace = true } cosmwasm-std = { workspace = true, optional = 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 = { 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] [dev-dependencies]
bip39 = { workspace = true } bip39 = { workspace = true }
cosmrs = { workspace = true, features = ["bip32"] } cosmrs = { workspace = true, features = ["rpc", "bip32"] }
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
ts-rs = "6.1.2" ts-rs = "6.1.2"
[[example]] [[example]]
@@ -72,18 +64,36 @@ name = "offline_signing"
# it should only really require the "signing" feature, # it should only really require the "signing" feature,
# but that would require another round of refactoring to make it possible # but that would require another round of refactoring to make it possible
# (traits would need to be moved around and refactored themselves) # (traits would need to be moved around and refactored themselves)
required-features = ["http-client"] required-features = ["nyxd-client"]
[[example]] [[example]]
name = "query_service_provider_directory" name = "query_service_provider_directory"
required-features = ["http-client"] required-features = ["nyxd-client"]
[[example]] [[example]]
name = "query_name_service" name = "query_name_service"
required-features = ["http-client"] required-features = ["nyxd-client"]
[features] [features]
default = ["http-client"] nyxd-client = [
http-client = ["cosmrs/rpc", "openssl"] "async-trait",
"cosmrs",
"cosmwasm-std",
"cw3",
"cw4",
"flate2",
"itertools",
"openssl",
"prost",
"sha2",
"signing"
]
signing = [
"bip39",
"cosmrs",
"eyre",
"nym-config",
"zeroize"
]
generate-ts = [] generate-ts = []
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use cosmrs::bank::MsgSend; use cosmrs::bank::MsgSend;
use cosmrs::rpc::HttpClient; use cosmrs::rpc::{self, HttpClient};
use cosmrs::tx::Msg; use cosmrs::tx::Msg;
use cosmrs::{tx, AccountId, Coin, Denom}; use cosmrs::{tx, AccountId, Coin, Denom};
use nym_validator_client::nyxd::CosmWasmClient; use nym_validator_client::nyxd::CosmWasmClient;
@@ -23,7 +23,7 @@ async fn main() {
let signer_address = signer.try_derive_accounts().unwrap()[0].address().clone(); let signer_address = signer.try_derive_accounts().unwrap()[0].address().clone();
// local 'client' ONLY signing messages // local 'client' ONLY signing messages
let tx_signer = signer; let tx_signer = TxSigner::new(signer);
// possibly remote client that doesn't do ANY signing // possibly remote client that doesn't do ANY signing
// (only broadcasts + queries for sequence numbers) // (only broadcasts + queries for sequence numbers)
@@ -54,7 +54,7 @@ async fn main() {
denom, denom,
amount: 2500u32.into(), amount: 2500u32.into(),
}, },
100000u32, 100000,
); );
let tx_raw = tx_signer let tx_raw = tx_signer
@@ -70,7 +70,7 @@ async fn main() {
.unwrap(); .unwrap();
// broadcast the tx // broadcast the tx
let res = tendermint_rpc::client::Client::broadcast_tx_commit(&broadcaster, tx_bytes) let res = rpc::Client::broadcast_tx_commit(&broadcaster, tx_bytes.into())
.await .await
.unwrap(); .unwrap();
@@ -3,9 +3,7 @@ use std::str::FromStr;
use cosmrs::AccountId; use cosmrs::AccountId;
use nym_name_service_common::Address; use nym_name_service_common::Address;
use nym_network_defaults::{setup_env, NymNetworkDetails}; use nym_network_defaults::{setup_env, NymNetworkDetails};
use nym_validator_client::nyxd::contract_traits::{ use nym_validator_client::nyxd::traits::NameServiceQueryClient;
NameServiceQueryClient, PagedNameServiceQueryClient,
};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@@ -28,7 +26,7 @@ async fn main() {
let names_by_owner = client.nyxd.get_names_by_owner(owner).await.unwrap(); let names_by_owner = client.nyxd.get_names_by_owner(owner).await.unwrap();
println!("names (by owner): {names_by_owner:#?}"); println!("names (by owner): {names_by_owner:#?}");
let nym_address = Address::new("client_id.client_key@gateway_id").unwrap(); let nym_address = Address::new("client_id.client_key@gateway_id");
let names_by_address = client.nyxd.get_names_by_address(nym_address).await.unwrap(); let names_by_address = client.nyxd.get_names_by_address(nym_address).await.unwrap();
println!("names (by address): {names_by_address:#?}"); println!("names (by address): {names_by_address:#?}");
@@ -3,9 +3,7 @@ use std::str::FromStr;
use cosmrs::AccountId; use cosmrs::AccountId;
use nym_network_defaults::{setup_env, NymNetworkDetails}; use nym_network_defaults::{setup_env, NymNetworkDetails};
use nym_service_provider_directory_common::NymAddress; use nym_service_provider_directory_common::NymAddress;
use nym_validator_client::nyxd::contract_traits::{ use nym_validator_client::nyxd::traits::SpDirectoryQueryClient;
PagedSpDirectoryQueryClient, SpDirectoryQueryClient,
};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
+538 -83
View File
@@ -1,44 +1,61 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::nyxd::{self, NyxdClient}; use crate::{nym_api, ValidatorClientError};
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::{ use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse, BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
}; };
use nym_api_requests::models::MixNodeBondAnnotated;
use nym_api_requests::models::{ use nym_api_requests::models::{
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse, GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
RewardEstimationResponse, StakeSaturationResponse, RewardEstimationResponse, StakeSaturationResponse,
}; };
use nym_network_defaults::NymNetworkDetails; use nym_coconut_dkg_common::types::NodeIndex;
use url::Url; use nym_coconut_interface::VerificationKey;
pub use nym_mixnet_contract_common::{ pub use nym_mixnet_contract_common::{
mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, MixId, mixnode::MixNodeDetails, GatewayBond, IdentityKey, IdentityKeyRef, MixId,
}; };
use url::Url;
// re-export the type to not break existing imports #[cfg(feature = "nyxd-client")]
pub use crate::coconut::CoconutApiClient; use crate::nyxd::traits::{DkgQueryClient, MixnetQueryClient};
#[cfg(feature = "nyxd-client")]
#[cfg(feature = "http-client")] use crate::nyxd::{self, CosmWasmClient, NyxdClient, QueryNyxdClient, SigningNyxdClient};
use crate::{DirectSigningHttpRpcValidatorClient, HttpRpcClient, QueryHttpRpcValidatorClient}; #[cfg(feature = "nyxd-client")]
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
#[cfg(feature = "nyxd-client")]
use nym_api_requests::models::MixNodeBondAnnotated;
#[cfg(feature = "nyxd-client")]
use nym_coconut_dkg_common::{types::EpochId, verification_key::ContractVKShare};
#[cfg(feature = "nyxd-client")]
use nym_coconut_interface::Base58;
#[cfg(feature = "nyxd-client")]
use nym_mixnet_contract_common::{
families::{Family, FamilyHead},
mixnode::MixNodeBond,
pending_events::{PendingEpochEvent, PendingIntervalEvent},
Delegation, RewardedSetNodeStatus, UnbondedMixnode,
};
#[cfg(feature = "nyxd-client")]
use nym_network_defaults::NymNetworkDetails;
#[cfg(feature = "nyxd-client")]
use std::str::FromStr;
#[cfg(feature = "nyxd-client")]
#[must_use] #[must_use]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Config { pub struct Config {
api_url: Url, api_url: Url,
nyxd_url: Url, nyxd_url: Url,
// TODO: until refactored, this is a dead field under some features
nyxd_config: nyxd::Config, nyxd_config: nyxd::Config,
mixnode_page_limit: Option<u32>,
gateway_page_limit: Option<u32>,
mixnode_delegations_page_limit: Option<u32>,
rewarded_set_page_limit: Option<u32>,
} }
#[cfg(feature = "nyxd-client")]
impl Config { impl Config {
pub fn try_from_nym_network_details( pub fn try_from_nym_network_details(
details: &NymNetworkDetails, details: &NymNetworkDetails,
@@ -61,6 +78,10 @@ impl Config {
.parse() .parse()
.map_err(ValidatorClientError::MalformedUrlProvided)?, .map_err(ValidatorClientError::MalformedUrlProvided)?,
nyxd_config: nyxd::Config::try_from_nym_network_details(details)?, 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,
}) })
} }
@@ -77,57 +98,88 @@ impl Config {
self self
} }
pub fn with_simulated_gas_multiplier(mut self, gas_multiplier: f32) -> Self { pub fn with_mixnode_page_limit(mut self, limit: Option<u32>) -> Config {
self.nyxd_config.simulated_gas_multiplier = gas_multiplier; self.mixnode_page_limit = limit;
self
}
pub fn with_gateway_page_limit(mut self, limit: Option<u32>) -> Config {
self.gateway_page_limit = limit;
self
}
pub fn with_mixnode_delegations_page_limit(mut self, limit: Option<u32>) -> Config {
self.mixnode_delegations_page_limit = limit;
self
}
pub fn with_rewarded_set_page_limit(mut self, limit: Option<u32>) -> Config {
self.rewarded_set_page_limit = limit;
self self
} }
} }
pub struct Client<C, S = NoSigner> { #[cfg(feature = "nyxd-client")]
pub struct Client<C> {
mixnode_page_limit: Option<u32>,
gateway_page_limit: Option<u32>,
mixnode_delegations_page_limit: Option<u32>,
rewarded_set_page_limit: Option<u32>,
// ideally they would have been read-only, but unfortunately rust doesn't have such features // ideally they would have been read-only, but unfortunately rust doesn't have such features
pub nym_api: nym_api::Client, pub nym_api: nym_api::Client,
pub nyxd: NyxdClient<C, S>, pub nyxd: NyxdClient<C>,
} }
#[cfg(feature = "http-client")] #[cfg(feature = "nyxd-client")]
impl Client<HttpRpcClient, DirectSecp256k1HdWallet> { impl Client<SigningNyxdClient<DirectSecp256k1HdWallet>> {
pub fn new_signing( pub fn new_signing(
config: Config, config: Config,
mnemonic: bip39::Mnemonic, mnemonic: bip39::Mnemonic,
) -> Result<DirectSigningHttpRpcValidatorClient, ValidatorClientError> { ) -> Result<Client<SigningNyxdClient<DirectSecp256k1HdWallet>>, ValidatorClientError> {
let rpc_client = HttpRpcClient::new(config.nyxd_url.as_str())?; let nym_api_client = nym_api::Client::new(config.api_url.clone());
let prefix = &config.nyxd_config.chain_details.bech32_account_prefix; let nyxd_client = NyxdClient::connect_with_mnemonic(
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic); config.nyxd_config.clone(),
config.nyxd_url.as_str(),
mnemonic,
None,
)?;
Ok(Self::new_signing_with_rpc_client( Ok(Client {
config, rpc_client, wallet, 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,
})
} }
pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
self.nyxd.change_endpoint(new_endpoint.as_ref())?; self.nyxd.change_endpoint(new_endpoint.as_ref())?;
Ok(()) Ok(())
} }
}
impl Client<ReqwestRpcClient, DirectSecp256k1HdWallet> { pub fn set_nyxd_simulated_gas_multiplier(&mut self, multiplier: f32) {
pub fn new_reqwest_signing( self.nyxd.set_simulated_gas_multiplier(multiplier)
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")] #[cfg(feature = "nyxd-client")]
impl Client<HttpRpcClient> { impl Client<QueryNyxdClient> {
pub fn new_query(config: Config) -> Result<QueryHttpRpcValidatorClient, ValidatorClientError> { pub fn new_query(config: Config) -> Result<Client<QueryNyxdClient>, ValidatorClientError> {
let rpc_client = HttpRpcClient::new(config.nyxd_url.as_str())?; let nym_api_client = nym_api::Client::new(config.api_url.clone());
Ok(Self::new_with_rpc_client(config, rpc_client)) 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,
})
} }
pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> { pub fn change_nyxd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
@@ -136,40 +188,392 @@ impl Client<HttpRpcClient> {
} }
} }
impl Client<ReqwestRpcClient> { // nyxd wrappers
pub fn new_reqwest_query(config: Config) -> QueryReqwestRpcValidatorClient { #[cfg(feature = "nyxd-client")]
let rpc_client = ReqwestRpcClient::new(config.nyxd_url.clone());
Self::new_with_rpc_client(config, rpc_client)
}
}
impl<C> Client<C> { impl<C> Client<C> {
pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self { // use case: somebody initialised client without a contract in order to upload and initialise one
let nym_api_client = nym_api::Client::new(config.api_url.clone()); // and now they want to actually use it without making new client
Client { pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) {
nym_api: nym_api_client, self.nyxd
nyxd: NyxdClient::new(config.nyxd_config, rpc_client), .set_mixnet_contract_address(mixnet_contract_address)
}
} }
}
impl<C, S> Client<C, S> { pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId {
pub fn new_signing_with_rpc_client(config: Config, rpc_client: C, signer: S) -> Self self.nyxd.mixnet_contract_address().clone()
}
pub async fn get_all_node_families(&self) -> Result<Vec<Family>, ValidatorClientError>
where where
S: OfflineSigner, C: CosmWasmClient + Sync + Send,
{ {
let nym_api_client = nym_api::Client::new(config.api_url.clone()); let mut families = Vec::new();
let mut start_after = None;
Client { loop {
nym_api: nym_api_client, let paged_response = self
nyxd: NyxdClient::new_signing(config.nyxd_config, rpc_client, signer), .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;
}
} }
Ok(families)
}
pub async fn get_all_family_members(
&self,
) -> Result<Vec<(IdentityKey, FamilyHead)>, ValidatorClientError>
where
C: CosmWasmClient + Sync + Send,
{
let mut members = Vec::new();
let mut start_after = None;
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;
}
}
Ok(members)
}
// basically handles paging for us
pub async fn get_all_nyxd_rewarded_set_mixnodes(
&self,
) -> Result<Vec<(MixId, RewardedSetNodeStatus)>, 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<Vec<MixNodeBond>, 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<Vec<MixNodeDetails>, 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<Vec<(MixId, UnbondedMixnode)>, 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<Vec<(MixId, UnbondedMixnode)>, 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<Vec<(MixId, UnbondedMixnode)>, 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<Vec<GatewayBond>, 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<Vec<Delegation>, 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<Vec<Delegation>, 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<Vec<Delegation>, 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<Vec<PendingEpochEvent>, 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<Vec<PendingIntervalEvent>, 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 // validator-api wrappers
impl<C, S> Client<C, S> { #[cfg(feature = "nyxd-client")]
impl<C> Client<C> {
pub fn change_nym_api(&mut self, new_endpoint: Url) { pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_url(new_endpoint) self.nym_api.change_url(new_endpoint)
} }
@@ -226,42 +630,87 @@ impl<C, S> Client<C, S> {
} }
} }
#[derive(Clone)]
pub struct CoconutApiClient {
pub api_client: NymApiClient,
pub verification_key: VerificationKey,
pub node_id: NodeIndex,
#[cfg(feature = "nyxd-client")]
pub cosmos_address: cosmrs::AccountId,
}
#[cfg(feature = "nyxd-client")]
impl CoconutApiClient {
pub async fn all_coconut_api_clients<C>(
client: &C,
epoch_id: EpochId,
) -> Result<Vec<Self>, 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<Self> {
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)] #[derive(Clone)]
pub struct NymApiClient { pub struct NymApiClient {
pub nym_api: nym_api::Client, pub nym_api_client: nym_api::Client,
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it) // 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 // we could re-implement the communication with the REST API on port 1317
} }
impl NymApiClient { impl NymApiClient {
pub fn new(api_url: Url) -> Self { pub fn new(api_url: Url) -> Self {
let nym_api = nym_api::Client::new(api_url); let nym_api_client = nym_api::Client::new(api_url);
NymApiClient { nym_api } NymApiClient { nym_api_client }
} }
pub fn change_nym_api(&mut self, new_endpoint: Url) { pub fn change_nym_api(&mut self, new_endpoint: Url) {
self.nym_api.change_url(new_endpoint); self.nym_api_client.change_url(new_endpoint);
} }
pub async fn get_cached_active_mixnodes( pub async fn get_cached_active_mixnodes(
&self, &self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> { ) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_active_mixnodes().await?) Ok(self.nym_api_client.get_active_mixnodes().await?)
} }
pub async fn get_cached_rewarded_mixnodes( pub async fn get_cached_rewarded_mixnodes(
&self, &self,
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> { ) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_rewarded_mixnodes().await?) Ok(self.nym_api_client.get_rewarded_mixnodes().await?)
} }
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> { pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
Ok(self.nym_api.get_mixnodes().await?) Ok(self.nym_api_client.get_mixnodes().await?)
} }
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> { pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
Ok(self.nym_api.get_gateways().await?) Ok(self.nym_api_client.get_gateways().await?)
} }
pub async fn get_gateway_core_status_count( pub async fn get_gateway_core_status_count(
@@ -270,7 +719,7 @@ impl NymApiClient {
since: Option<i64>, since: Option<i64>,
) -> Result<GatewayCoreStatusResponse, ValidatorClientError> { ) -> Result<GatewayCoreStatusResponse, ValidatorClientError> {
Ok(self Ok(self
.nym_api .nym_api_client
.get_gateway_core_status_count(identity, since) .get_gateway_core_status_count(identity, since)
.await?) .await?)
} }
@@ -281,7 +730,7 @@ impl NymApiClient {
since: Option<i64>, since: Option<i64>,
) -> Result<MixnodeCoreStatusResponse, ValidatorClientError> { ) -> Result<MixnodeCoreStatusResponse, ValidatorClientError> {
Ok(self Ok(self
.nym_api .nym_api_client
.get_mixnode_core_status_count(mix_id, since) .get_mixnode_core_status_count(mix_id, since)
.await?) .await?)
} }
@@ -290,28 +739,34 @@ impl NymApiClient {
&self, &self,
mix_id: MixId, mix_id: MixId,
) -> Result<MixnodeStatusResponse, ValidatorClientError> { ) -> Result<MixnodeStatusResponse, ValidatorClientError> {
Ok(self.nym_api.get_mixnode_status(mix_id).await?) Ok(self.nym_api_client.get_mixnode_status(mix_id).await?)
} }
pub async fn get_mixnode_reward_estimation( pub async fn get_mixnode_reward_estimation(
&self, &self,
mix_id: MixId, mix_id: MixId,
) -> Result<RewardEstimationResponse, ValidatorClientError> { ) -> Result<RewardEstimationResponse, ValidatorClientError> {
Ok(self.nym_api.get_mixnode_reward_estimation(mix_id).await?) Ok(self
.nym_api_client
.get_mixnode_reward_estimation(mix_id)
.await?)
} }
pub async fn get_mixnode_stake_saturation( pub async fn get_mixnode_stake_saturation(
&self, &self,
mix_id: MixId, mix_id: MixId,
) -> Result<StakeSaturationResponse, ValidatorClientError> { ) -> Result<StakeSaturationResponse, ValidatorClientError> {
Ok(self.nym_api.get_mixnode_stake_saturation(mix_id).await?) Ok(self
.nym_api_client
.get_mixnode_stake_saturation(mix_id)
.await?)
} }
pub async fn blind_sign( pub async fn blind_sign(
&self, &self,
request_body: &BlindSignRequestBody, request_body: &BlindSignRequestBody,
) -> Result<BlindedSignatureResponse, ValidatorClientError> { ) -> Result<BlindedSignatureResponse, ValidatorClientError> {
Ok(self.nym_api.blind_sign(request_body).await?) Ok(self.nym_api_client.blind_sign(request_body).await?)
} }
pub async fn verify_bandwidth_credential( pub async fn verify_bandwidth_credential(
@@ -319,7 +774,7 @@ impl NymApiClient {
request_body: &VerifyCredentialBody, request_body: &VerifyCredentialBody,
) -> Result<VerifyCredentialResponse, ValidatorClientError> { ) -> Result<VerifyCredentialResponse, ValidatorClientError> {
Ok(self Ok(self
.nym_api .nym_api_client
.verify_bandwidth_credential(request_body) .verify_bandwidth_credential(request_body)
.await?) .await?)
} }
@@ -1,97 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<ContractVKShare> for CoconutApiClient {
type Error = CoconutApiError;
fn try_from(share: ContractVKShare) -> Result<Self, Self::Error> {
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<C>(
client: &C,
epoch_id: EpochId,
) -> Result<Vec<CoconutApiClient>, 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::<Result<Vec<_>, _>>()
// ... 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::<Result<Vec<_>, _>>()
}
@@ -1,7 +1,8 @@
use crate::nyxd::contract_traits::MixnetQueryClient;
use crate::nyxd::error::NyxdError; use crate::nyxd::error::NyxdError;
use crate::nyxd::Config as ClientConfig; use crate::nyxd::{Config as ClientConfig, NyxdClient, QueryNyxdClient};
use crate::{NymApiClient, QueryHttpRpcNyxdClient, ValidatorClientError}; use crate::{NymApiClient, ValidatorClientError};
use crate::nyxd::traits::MixnetQueryClient;
use colored::Colorize; use colored::Colorize;
use core::fmt; use core::fmt;
use itertools::Itertools; use itertools::Itertools;
@@ -52,7 +53,7 @@ pub async fn test_nyxd_url_connection(
let config = ClientConfig::try_from_nym_network_details(&network) let config = ClientConfig::try_from_nym_network_details(&network)
.expect("failed to create valid nyxd client config"); .expect("failed to create valid nyxd client config");
let mut nyxd_client = QueryHttpRpcNyxdClient::connect(config, nyxd_url.as_str())?; let mut nyxd_client = NyxdClient::<QueryNyxdClient>::connect(config, nyxd_url.as_str())?;
// possibly redundant, but lets just leave it here // possibly redundant, but lets just leave it here
nyxd_client.set_mixnet_contract_address(address); nyxd_client.set_mixnet_contract_address(address);
match test_nyxd_connection(network, &nyxd_url, &nyxd_client).await { match test_nyxd_connection(network, &nyxd_url, &nyxd_client).await {
@@ -74,7 +75,7 @@ fn setup_connection_tests<H: BuildHasher + 'static>(
let config = ClientConfig::try_from_nym_network_details(&network) let config = ClientConfig::try_from_nym_network_details(&network)
.expect("failed to create valid nyxd client config"); .expect("failed to create valid nyxd client config");
if let Ok(mut client) = QueryHttpRpcNyxdClient::connect(config, url.as_str()) { if let Ok(mut client) = NyxdClient::<QueryNyxdClient>::connect(config, url.as_str()) {
// possibly redundant, but lets just leave it here // possibly redundant, but lets just leave it here
client.set_mixnet_contract_address(address); client.set_mixnet_contract_address(address);
Some(ClientForConnectionTest::Nyxd( Some(ClientForConnectionTest::Nyxd(
@@ -111,7 +112,7 @@ fn extract_and_collect_results_into_map(
async fn test_nyxd_connection( async fn test_nyxd_connection(
network: NymNetworkDetails, network: NymNetworkDetails,
url: &Url, url: &Url,
client: &QueryHttpRpcNyxdClient, client: &NyxdClient<QueryNyxdClient>,
) -> ConnectionResult { ) -> ConnectionResult {
let result = match timeout( let result = match timeout(
Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC), Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC),
@@ -119,7 +120,7 @@ async fn test_nyxd_connection(
) )
.await .await
{ {
Ok(Err(NyxdError::TendermintErrorRpc(e))) => { Ok(Err(NyxdError::TendermintError(e))) => {
// If we get a tendermint-rpc error, we classify the node as not contactable // If we get a tendermint-rpc error, we classify the node as not contactable
log::warn!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e); log::warn!("Checking: nyxd url: {url}: {}: {}", "failed".red(), e);
false false
@@ -185,7 +186,7 @@ async fn test_nym_api_connection(
} }
enum ClientForConnectionTest { enum ClientForConnectionTest {
Nyxd(NymNetworkDetails, Url, Box<QueryHttpRpcNyxdClient>), Nyxd(NymNetworkDetails, Url, Box<NyxdClient<QueryNyxdClient>>),
Api(NymNetworkDetails, Url, NymApiClient), Api(NymNetworkDetails, Url, NymApiClient),
} }
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::nym_api; use crate::nym_api;
pub use tendermint_rpc::error::Error as TendermintRpcError;
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
@@ -13,12 +12,10 @@ pub enum ValidatorClientError {
source: nym_api::error::NymAPIError, source: nym_api::error::NymAPIError,
}, },
#[error("Tendermint RPC request failure: {0}")]
TendermintErrorRpc(#[from] TendermintRpcError),
#[error("One of the provided URLs was malformed - {0}")] #[error("One of the provided URLs was malformed - {0}")]
MalformedUrlProvided(#[from] url::ParseError), MalformedUrlProvided(#[from] url::ParseError),
#[cfg(feature = "nyxd-client")]
#[error("nyxd request failed - {0}")] #[error("nyxd request failed - {0}")]
NyxdError(#[from] crate::nyxd::error::NyxdError), NyxdError(#[from] crate::nyxd::error::NyxdError),
+7 -31
View File
@@ -1,44 +1,20 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
pub mod client; pub mod client;
pub mod coconut; #[cfg(feature = "nyxd-client")]
#[cfg(feature = "http-client")]
pub mod connection_tester; pub mod connection_tester;
pub mod error; pub mod error;
pub mod nym_api; pub mod nym_api;
#[cfg(feature = "nyxd-client")]
pub mod nyxd; pub mod nyxd;
pub mod rpc;
#[cfg(feature = "signing")]
pub mod signing; pub mod signing;
pub use crate::error::ValidatorClientError; 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::NymApiClient;
pub use client::{Client, CoconutApiClient, Config};
pub use nym_api_requests::*; pub use nym_api_requests::*;
#[cfg(feature = "http-client")] #[cfg(feature = "nyxd-client")]
pub use cosmrs::rpc::HttpClient as HttpRpcClient; pub use client::{Client, CoconutApiClient, Config};
// some type aliasing
pub type ValidatorClient<C> = Client<C>;
pub type SigningValidatorClient<C, S> = Client<C, S>;
#[cfg(feature = "http-client")]
pub type QueryHttpRpcValidatorClient = Client<HttpRpcClient>;
#[cfg(feature = "http-client")]
pub type QueryHttpRpcNyxdClient = nyxd::NyxdClient<HttpRpcClient>;
#[cfg(feature = "http-client")]
pub type DirectSigningHttpRpcValidatorClient = Client<HttpRpcClient, DirectSecp256k1HdWallet>;
#[cfg(feature = "http-client")]
pub type DirectSigningHttpRpcNyxdClient = nyxd::NyxdClient<HttpRpcClient, DirectSecp256k1HdWallet>;
pub type QueryReqwestRpcValidatorClient = Client<ReqwestRpcClient>;
pub type QueryReqwestRpcNyxdClient = nyxd::NyxdClient<ReqwestRpcClient>;
pub type DirectSigningReqwestRpcValidatorClient = Client<ReqwestRpcClient, DirectSecp256k1HdWallet>;
pub type DirectSigningReqwestRpcNyxdClient =
nyxd::NyxdClient<ReqwestRpcClient, DirectSecp256k1HdWallet>;
@@ -7,11 +7,11 @@ use nym_api_requests::coconut::{
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse, BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
}; };
use nym_api_requests::models::{ use nym_api_requests::models::{
ComputeRewardEstParam, GatewayBondAnnotated, GatewayCoreStatusResponse, ComputeRewardEstParam, GatewayCoreStatusResponse, GatewayStatusReportResponse,
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse,
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse, MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse, StakeSaturationResponse,
StakeSaturationResponse, UptimeResponse, UptimeResponse,
}; };
use nym_mixnet_contract_common::mixnode::MixNodeDetails; use nym_mixnet_contract_common::mixnode::MixNodeDetails;
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId}; use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
@@ -148,19 +148,6 @@ impl Client {
.await .await
} }
pub async fn get_gateways_detailed(&self) -> Result<Vec<GatewayBondAnnotated>, NymAPIError> {
self.query_nym_api(
&[
routes::API_VERSION,
routes::STATUS,
routes::GATEWAYS,
routes::DETAILED,
],
NO_PARAMS,
)
.await
}
pub async fn get_mixnodes_detailed_unfiltered( pub async fn get_mixnodes_detailed_unfiltered(
&self, &self,
) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> { ) -> Result<Vec<MixNodeBondAnnotated>, NymAPIError> {
@@ -52,6 +52,7 @@ impl<'a> Div<GasPrice> for &'a Coin {
} else { } else {
implicit_gas_limit.u128() as u64 implicit_gas_limit.u128() as u64
} }
.into()
} }
} }
@@ -190,32 +191,32 @@ mod tests {
let amount = Coin::new(3938, "unym"); let amount = Coin::new(3938, "unym");
let gas_price = "0.025unym".parse().unwrap(); let gas_price = "0.025unym".parse().unwrap();
let res = amount / gas_price; let res = amount / gas_price;
assert_eq!(157520, res); assert_eq!(157520, res.value());
let amount = Coin::new(1234567890, "unym"); let amount = Coin::new(1234567890, "unym");
let gas_price = "0.025unym".parse().unwrap(); let gas_price = "0.025unym".parse().unwrap();
let res = amount / gas_price; let res = amount / gas_price;
assert_eq!(49382715600, res); assert_eq!(49382715600, res.value());
let amount = Coin::new(1, "unym"); let amount = Coin::new(1, "unym");
let gas_price = "0.025unym".parse().unwrap(); let gas_price = "0.025unym".parse().unwrap();
let res = amount / gas_price; let res = amount / gas_price;
assert_eq!(40, res); assert_eq!(40, res.value());
let amount = Coin::new(150_000_000, "unym"); let amount = Coin::new(150_000_000, "unym");
let gas_price = "0.001234unym".parse().unwrap(); let gas_price = "0.001234unym".parse().unwrap();
let res = amount / gas_price; let res = amount / gas_price;
assert_eq!(121555915721, res); assert_eq!(121555915721, res.value());
let amount = Coin::new(150_000_000, "unym"); let amount = Coin::new(150_000_000, "unym");
let gas_price = "1unym".parse().unwrap(); let gas_price = "1unym".parse().unwrap();
let res = amount / gas_price; let res = amount / gas_price;
assert_eq!(150_000_000, res); assert_eq!(150_000_000, res.value());
let amount = Coin::new(150_000_000, "unym"); let amount = Coin::new(150_000_000, "unym");
let gas_price = "1234.56unym".parse().unwrap(); let gas_price = "1234.56unym".parse().unwrap();
let res = amount / gas_price; let res = amount / gas_price;
assert_eq!(121500, res); assert_eq!(121500, res.value());
} }
#[test] #[test]
@@ -1,100 +0,0 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<T>(
&self,
query: CoconutBandwidthQueryMsg,
) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn get_spent_credential(
&self,
blinded_serial_number: String,
) -> Result<SpendCredentialResponse, NyxdError> {
self.query_coconut_bandwidth_contract(CoconutBandwidthQueryMsg::GetSpentCredential {
blinded_serial_number,
})
.await
}
async fn get_all_spent_credential_paged(
&self,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<PagedSpendCredentialResponse, NyxdError> {
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<Vec<SpendCredential>, NyxdError> {
collect_paged!(self, get_all_spent_credential_paged, spend_credentials)
}
}
#[async_trait]
impl<T> PagedCoconutBandwidthQueryClient for T where T: CoconutBandwidthQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> CoconutBandwidthQueryClient for C
where
C: CosmWasmClient + NymContractsProvider + Send + Sync,
{
async fn query_coconut_bandwidth_contract<T>(
&self,
query: CoconutBandwidthQueryMsg,
) -> Result<T, NyxdError>
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<C: CoconutBandwidthQueryClient + Send + Sync>(
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(),
};
}
}
@@ -1,153 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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<Fee>,
msg: CoconutBandwidthExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn deposit(
&self,
amount: Coin,
info: String,
verification_key: String,
encryption_key: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<C> CoconutBandwidthSigningClient for C
where
C: SigningCosmWasmClient + NymContractsProvider + Sync,
NyxdError: From<<Self as OfflineSigner>::Error>,
{
async fn execute_coconut_bandwidth_contract(
&self,
fee: Option<Fee>,
msg: CoconutBandwidthExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError> {
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<C: CoconutBandwidthSigningClient + Send + Sync>(
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()
}
};
}
}
@@ -1,182 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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<T>(&self, query: DkgQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn get_current_epoch(&self) -> Result<Epoch, NyxdError> {
let request = DkgQueryMsg::GetCurrentEpochState {};
self.query_dkg_contract(request).await
}
async fn get_current_epoch_threshold(&self) -> Result<Option<u64>, NyxdError> {
let request = DkgQueryMsg::GetCurrentEpochThreshold {};
self.query_dkg_contract(request).await
}
async fn get_initial_dealers(&self) -> Result<Option<InitialReplacementData>, NyxdError> {
let request = DkgQueryMsg::GetInitialDealers {};
self.query_dkg_contract(request).await
}
async fn get_dealer_details(
&self,
address: &AccountId,
) -> Result<DealerDetailsResponse, NyxdError> {
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<String>,
limit: Option<u32>,
) -> Result<PagedDealerResponse, NyxdError> {
let request = DkgQueryMsg::GetCurrentDealers { start_after, limit };
self.query_dkg_contract(request).await
}
async fn get_past_dealers_paged(
&self,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<PagedDealerResponse, NyxdError> {
let request = DkgQueryMsg::GetPastDealers { start_after, limit };
self.query_dkg_contract(request).await
}
async fn get_dealings_paged(
&self,
idx: u64,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<PagedDealingsResponse, NyxdError> {
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<String>,
limit: Option<u32>,
) -> Result<PagedVKSharesResponse, NyxdError> {
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<Vec<DealerDetails>, NyxdError> {
collect_paged!(self, get_current_dealers_paged, dealers)
}
async fn get_all_past_dealers(&self) -> Result<Vec<DealerDetails>, NyxdError> {
collect_paged!(self, get_past_dealers_paged, dealers)
}
async fn get_all_epoch_dealings(&self, idx: u64) -> Result<Vec<ContractDealing>, NyxdError> {
collect_paged!(self, get_dealings_paged, dealings, idx)
}
async fn get_all_verification_key_shares(
&self,
epoch_id: EpochId,
) -> Result<Vec<ContractVKShare>, NyxdError> {
collect_paged!(self, get_vk_shares_paged, shares, epoch_id)
}
}
#[async_trait]
impl<T> PagedDkgQueryClient for T where T: DkgQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> DkgQueryClient for C
where
C: CosmWasmClient + NymContractsProvider + Send + Sync,
{
async fn query_dkg_contract<T>(&self, query: DkgQueryMsg) -> Result<T, NyxdError>
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<C: DkgQueryClient + Send + Sync>(
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(),
};
}
}
@@ -1,176 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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<Fee>,
msg: DkgExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn advance_dkg_epoch_state(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
let req = DkgExecuteMsg::AdvanceEpochState {};
self.execute_dkg_contract(fee, req, "advancing DKG state".to_string(), vec![])
.await
}
async fn surpass_threshold(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
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<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<Fee>,
) -> Result<ExecuteResult, NyxdError> {
// 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<C> DkgSigningClient for C
where
C: SigningCosmWasmClient + NymContractsProvider + Sync,
NyxdError: From<<Self as OfflineSigner>::Error>,
{
async fn execute_dkg_contract(
&self,
fee: Option<Fee>,
msg: DkgExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError> {
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<C: DkgSigningClient + Send + Sync>(
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(),
};
}
}
@@ -1,79 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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<T>(&self, query: EphemeraQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn get_peers_paged(
&self,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<PagedPeerResponse, NyxdError> {
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<Vec<JsonPeerInfo>, NyxdError> {
collect_paged!(self, get_peers_paged, peers)
}
}
#[async_trait]
impl<T> PagedEphemeraQueryClient for T where T: EphemeraQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> EphemeraQueryClient for C
where
C: CosmWasmClient + NymContractsProvider + Send + Sync,
{
async fn query_ephemera_contract<T>(&self, query: EphemeraQueryMsg) -> Result<T, NyxdError>
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<C: EphemeraQueryClient + Send + Sync>(
client: C,
msg: EphemeraQueryMsg,
) {
match msg {
EphemeraQueryMsg::GetPeers { limit, start_after } => {
client.get_peers_paged(start_after, limit).ignore()
}
};
}
}
@@ -1,86 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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<Fee>,
msg: EphemeraExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn register_as_peer(
&self,
peer_info: JsonPeerInfo,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<C> EphemeraSigningClient for C
where
C: SigningCosmWasmClient + NymContractsProvider + Sync,
NyxdError: From<<Self as OfflineSigner>::Error>,
{
async fn execute_ephemera_contract(
&self,
fee: Option<Fee>,
msg: EphemeraExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError> {
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<C: EphemeraSigningClient + Send + Sync>(
client: C,
msg: EphemeraExecuteMsg,
) {
match msg {
EphemeraExecuteMsg::RegisterPeer { peer_info } => {
client.register_as_peer(peer_info, None).ignore()
}
};
}
}
@@ -1,118 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<T>(&self, query: GroupQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn admin(&self) -> Result<cw_controllers::AdminResponse, NyxdError> {
self.query_group_contract(GroupQueryMsg::Admin {}).await
}
async fn total_weight(&self, at_height: Option<u64>) -> Result<TotalWeightResponse, NyxdError> {
self.query_group_contract(GroupQueryMsg::TotalWeight { at_height })
.await
}
async fn list_members_paged(
&self,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<MemberListResponse, NyxdError> {
self.query_group_contract(GroupQueryMsg::ListMembers { start_after, limit })
.await
}
async fn member(
&self,
addr: String,
at_height: Option<u64>,
) -> Result<MemberResponse, NyxdError> {
self.query_group_contract(GroupQueryMsg::Member { addr, at_height })
.await
}
async fn hooks(&self) -> Result<cw_controllers::HooksResponse, NyxdError> {
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<Vec<Member>, 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<T> PagedGroupQueryClient for T where T: GroupQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> GroupQueryClient for C
where
C: CosmWasmClient + NymContractsProvider + Send + Sync,
{
async fn query_group_contract<T>(&self, query: GroupQueryMsg) -> Result<T, NyxdError>
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<C: GroupQueryClient + Send + Sync>(
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(),
};
}
}
@@ -1,131 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<Fee>,
msg: GroupExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn update_admin(
&self,
admin: Option<String>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_group_contract(
fee,
GroupExecuteMsg::UpdateAdmin { admin },
"GroupExecuteMsg::UpdateAdmin".to_string(),
vec![],
)
.await
}
async fn update_members(
&self,
add: Vec<Member>,
remove: Vec<String>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_group_contract(
fee,
GroupExecuteMsg::UpdateMembers { add, remove },
"GroupExecuteMsg::UpdateMembers".to_string(),
vec![],
)
.await
}
async fn add_hook(&self, addr: String, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
self.execute_group_contract(
fee,
GroupExecuteMsg::AddHook { addr },
"GroupExecuteMsg::AddHook".to_string(),
vec![],
)
.await
}
async fn remove_hook(
&self,
addr: String,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<C> GroupSigningClient for C
where
C: SigningCosmWasmClient + NymContractsProvider + Sync,
NyxdError: From<<Self as OfflineSigner>::Error>,
{
async fn execute_group_contract(
&self,
fee: Option<Fee>,
msg: GroupExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError> {
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<C: GroupSigningClient + Send + Sync>(
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(),
};
}
}
@@ -1,745 +0,0 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<T>(&self, query: MixnetQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
// state/sys-params-related
async fn get_mixnet_contract_version(&self) -> Result<ContractBuildInformation, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetContractVersion {})
.await
}
async fn get_mixnet_contract_cw2_version(&self) -> Result<cw2::ContractVersion, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetCW2ContractVersion {})
.await
}
async fn get_rewarding_validator_address(&self) -> Result<AccountId, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetRewardingValidatorAddress {})
.await
}
async fn get_mixnet_contract_settings(&self) -> Result<ContractStateParams, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetStateParams {})
.await
}
async fn get_mixnet_contract_state_params(&self) -> Result<ContractStateParams, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetStateParams {})
.await
}
async fn get_mixnet_contract_state(&self) -> Result<ContractState, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetState {})
.await
}
async fn get_rewarding_parameters(&self) -> Result<RewardingParams, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetRewardingParams {})
.await
}
async fn get_current_epoch_status(&self) -> Result<EpochStatus, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetEpochStatus {})
.await
}
async fn get_current_interval_details(&self) -> Result<CurrentIntervalResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetCurrentIntervalDetails {})
.await
}
async fn get_rewarded_set_paged(
&self,
start_after: Option<MixId>,
limit: Option<u32>,
) -> Result<PagedRewardedSetResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetRewardedSet { limit, start_after })
.await
}
async fn get_all_node_families_paged(
&self,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<PagedFamiliesResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetAllFamiliesPaged { limit, start_after })
.await
}
async fn get_all_family_members_paged(
&self,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<PagedMembersResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetAllMembersPaged { limit, start_after })
.await
}
async fn get_family_members_by_head<S: Into<String> + Send>(
&self,
head: S,
) -> Result<FamilyMembersByHeadResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyMembersByHead { head: head.into() })
.await
}
async fn get_family_members_by_label<S: Into<String> + Send>(
&self,
label: S,
) -> Result<FamilyMembersByLabelResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyMembersByLabel {
label: label.into(),
})
.await
}
// mixnode-related:
async fn get_mixnode_bonds_paged(
&self,
start_after: Option<MixId>,
limit: Option<u32>,
) -> Result<PagedMixnodeBondsResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetMixNodeBonds { limit, start_after })
.await
}
async fn get_mixnodes_detailed_paged(
&self,
start_after: Option<MixId>,
limit: Option<u32>,
) -> Result<PagedMixnodesDetailsResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetMixNodesDetailed { limit, start_after })
.await
}
async fn get_unbonded_paged(
&self,
start_after: Option<MixId>,
limit: Option<u32>,
) -> Result<PagedUnbondedMixnodesResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodes { limit, start_after })
.await
}
async fn get_unbonded_by_owner_paged(
&self,
owner: &AccountId,
start_after: Option<MixId>,
limit: Option<u32>,
) -> Result<PagedUnbondedMixnodesResponse, NyxdError> {
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<MixId>,
limit: Option<u32>,
) -> Result<PagedUnbondedMixnodesResponse, NyxdError> {
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<MixOwnershipResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetOwnedMixnode {
address: address.to_string(),
})
.await
}
async fn get_mixnode_details(
&self,
mix_id: MixId,
) -> Result<MixnodeDetailsResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeDetails { mix_id })
.await
}
async fn get_mixnode_details_by_identity(
&self,
mix_identity: IdentityKey,
) -> Result<MixnodeDetailsByIdentityResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetBondedMixnodeDetailsByIdentity {
mix_identity,
})
.await
}
async fn get_mixnode_rewarding_details(
&self,
mix_id: MixId,
) -> Result<MixnodeRewardingDetailsResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetMixnodeRewardingDetails { mix_id })
.await
}
async fn get_mixnode_stake_saturation(
&self,
mix_id: MixId,
) -> Result<StakeSaturationResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetStakeSaturation { mix_id })
.await
}
async fn get_unbonded_mixnode_information(
&self,
mix_id: MixId,
) -> Result<UnbondedMixnodeResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetUnbondedMixNodeInformation { mix_id })
.await
}
async fn get_layer_distribution(&self) -> Result<LayerDistribution, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetLayerDistribution {})
.await
}
// gateway-related:
async fn get_gateways_paged(
&self,
start_after: Option<IdentityKey>,
limit: Option<u32>,
) -> Result<PagedGatewayResponse, NyxdError> {
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<GatewayBondResponse, NyxdError> {
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<GatewayOwnershipResponse, NyxdError> {
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<String>,
limit: Option<u32>,
) -> Result<PagedMixNodeDelegationsResponse, NyxdError> {
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<u32>,
) -> Result<PagedDelegatorDelegationsResponse, NyxdError> {
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<String>,
) -> Result<MixNodeDelegationResponse, NyxdError> {
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<delegation::StorageKey>,
limit: Option<u32>,
) -> Result<PagedAllDelegationsResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetAllDelegations { start_after, limit })
.await
}
// rewards related
async fn get_pending_operator_reward(
&self,
operator: &AccountId,
) -> Result<PendingRewardResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetPendingOperatorReward {
address: operator.to_string(),
})
.await
}
async fn get_pending_mixnode_operator_reward(
&self,
mix_id: MixId,
) -> Result<PendingRewardResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetPendingMixNodeOperatorReward { mix_id })
.await
}
async fn get_pending_delegator_reward(
&self,
delegator: &AccountId,
mix_id: MixId,
proxy: Option<String>,
) -> Result<PendingRewardResponse, NyxdError> {
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<EstimatedCurrentEpochRewardResponse, NyxdError> {
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<String>,
estimated_performance: Performance,
) -> Result<EstimatedCurrentEpochRewardResponse, NyxdError> {
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<EpochEventId>,
limit: Option<u32>,
) -> Result<PendingEpochEventsResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvents { start_after, limit })
.await
}
async fn get_pending_interval_events_paged(
&self,
start_after: Option<IntervalEventId>,
limit: Option<u32>,
) -> Result<PendingIntervalEventsResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvents { start_after, limit })
.await
}
async fn get_pending_epoch_event(
&self,
event_id: EpochEventId,
) -> Result<PendingEpochEventResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetPendingEpochEvent { event_id })
.await
}
async fn get_pending_interval_event(
&self,
event_id: IntervalEventId,
) -> Result<PendingIntervalEventResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetPendingIntervalEvent { event_id })
.await
}
async fn get_number_of_pending_events(
&self,
) -> Result<NumberOfPendingEventsResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetNumberOfPendingEvents {})
.await
}
async fn get_signing_nonce(&self, address: &AccountId) -> Result<Nonce, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetSigningNonce {
address: address.to_string(),
})
.await
}
async fn get_node_family_by_label(
&self,
label: String,
) -> Result<FamilyByLabelResponse, NyxdError> {
self.query_mixnet_contract(MixnetQueryMsg::GetFamilyByLabel { label })
.await
}
async fn get_node_family_by_head(
&self,
head: String,
) -> Result<FamilyByHeadResponse, NyxdError> {
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<Vec<Family>, NyxdError> {
collect_paged!(self, get_all_node_families_paged, families)
}
async fn get_all_family_members(&self) -> Result<Vec<(IdentityKey, FamilyHead)>, NyxdError> {
collect_paged!(self, get_all_family_members_paged, members)
}
async fn get_all_rewarded_set_mixnodes(
&self,
) -> Result<Vec<(MixId, RewardedSetNodeStatus)>, NyxdError> {
collect_paged!(self, get_rewarded_set_paged, nodes)
}
async fn get_all_mixnode_bonds(&self) -> Result<Vec<MixNodeBond>, NyxdError> {
collect_paged!(self, get_mixnode_bonds_paged, nodes)
}
async fn get_all_mixnodes_detailed(&self) -> Result<Vec<MixNodeDetails>, NyxdError> {
collect_paged!(self, get_mixnodes_detailed_paged, nodes)
}
async fn get_all_unbonded_mixnodes(&self) -> Result<Vec<(MixId, UnbondedMixnode)>, NyxdError> {
collect_paged!(self, get_unbonded_paged, nodes)
}
async fn get_all_unbonded_mixnodes_by_owner(
&self,
owner: &AccountId,
) -> Result<Vec<(MixId, UnbondedMixnode)>, NyxdError> {
collect_paged!(self, get_unbonded_by_owner_paged, nodes, owner)
}
async fn get_all_unbonded_mixnodes_by_identity(
&self,
identity_key: IdentityKeyRef<'_>,
) -> Result<Vec<(MixId, UnbondedMixnode)>, NyxdError> {
collect_paged!(self, get_unbonded_by_identity_paged, nodes, identity_key)
}
async fn get_all_gateways(&self) -> Result<Vec<GatewayBond>, NyxdError> {
collect_paged!(self, get_gateways_paged, nodes)
}
async fn get_all_single_mixnode_delegations(
&self,
mix_id: MixId,
) -> Result<Vec<Delegation>, NyxdError> {
collect_paged!(self, get_mixnode_delegations_paged, delegations, mix_id)
}
async fn get_all_delegator_delegations(
&self,
delegation_owner: &AccountId,
) -> Result<Vec<Delegation>, NyxdError> {
collect_paged!(
self,
get_delegator_delegations_paged,
delegations,
delegation_owner
)
}
async fn get_all_network_delegations(&self) -> Result<Vec<Delegation>, NyxdError> {
collect_paged!(self, get_all_network_delegations_paged, delegations)
}
async fn get_all_pending_epoch_events(&self) -> Result<Vec<PendingEpochEvent>, NyxdError> {
collect_paged!(self, get_pending_epoch_events_paged, events)
}
async fn get_all_pending_interval_events(
&self,
) -> Result<Vec<PendingIntervalEvent>, NyxdError> {
collect_paged!(self, get_pending_interval_events_paged, events)
}
}
#[async_trait]
impl<T> PagedMixnetQueryClient for T where T: MixnetQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> MixnetQueryClient for C
where
C: CosmWasmClient + NymContractsProvider + Send + Sync,
{
async fn query_mixnet_contract<T>(&self, query: MixnetQueryMsg) -> Result<T, NyxdError>
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<C: MixnetQueryClient + Send + Sync>(
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()
}
}
}
}
@@ -1,192 +0,0 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<AccountId>,
pub vesting_contract_address: Option<AccountId>,
pub coconut_bandwidth_contract_address: Option<AccountId>,
pub group_contract_address: Option<AccountId>,
pub multisig_contract_address: Option<AccountId>,
pub coconut_dkg_contract_address: Option<AccountId>,
pub ephemera_contract_address: Option<AccountId>,
pub service_provider_directory_contract_address: Option<AccountId>,
pub name_service_contract_address: Option<AccountId>,
}
impl TryFrom<NymContracts> for TypedNymContracts {
type Error = <AccountId as FromStr>::Err;
fn try_from(value: NymContracts) -> Result<Self, Self::Error> {
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<T> IgnoreValue for T {}
pub(crate) fn mock_coin() -> Coin {
Coin::new(42, "ufoomp")
}
}
@@ -1,178 +0,0 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// 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<T>(&self, query: MultisigQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn query_threshold(&self) -> Result<ThresholdResponse, NyxdError> {
self.query_multisig_contract(MultisigQueryMsg::Threshold {})
.await
}
async fn query_proposal(&self, proposal_id: u64) -> Result<ProposalResponse, NyxdError> {
self.query_multisig_contract(MultisigQueryMsg::Proposal { proposal_id })
.await
}
async fn list_proposals(
&self,
start_after: Option<u64>,
limit: Option<u32>,
) -> Result<ProposalListResponse, NyxdError> {
self.query_multisig_contract(MultisigQueryMsg::ListProposals { start_after, limit })
.await
}
async fn reverse_proposals(
&self,
start_before: Option<u64>,
limit: Option<u32>,
) -> Result<ProposalListResponse, NyxdError> {
self.query_multisig_contract(MultisigQueryMsg::ReverseProposals {
start_before,
limit,
})
.await
}
async fn query_vote(&self, proposal_id: u64, voter: String) -> Result<VoteResponse, NyxdError> {
self.query_multisig_contract(MultisigQueryMsg::Vote { proposal_id, voter })
.await
}
async fn list_votes(
&self,
proposal_id: u64,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<VoteListResponse, NyxdError> {
self.query_multisig_contract(MultisigQueryMsg::ListVotes {
proposal_id,
start_after,
limit,
})
.await
}
async fn query_voter(&self, address: String) -> Result<VoterResponse, NyxdError> {
self.query_multisig_contract(MultisigQueryMsg::Voter { address })
.await
}
async fn list_voters(
&self,
start_after: Option<String>,
limit: Option<u32>,
) -> Result<VoterListResponse, NyxdError> {
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<Vec<ProposalResponse>, 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<T> PagedMultisigQueryClient for T where T: MultisigQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> MultisigQueryClient for C
where
C: CosmWasmClient + NymContractsProvider + Send + Sync,
{
async fn query_multisig_contract<T>(&self, query: MultisigQueryMsg) -> Result<T, NyxdError>
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<C: MultisigQueryClient + Send + Sync>(
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(),
};
}
}
@@ -1,192 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// 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<Fee>,
msg: MultisigExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn propose_release_funds(
&self,
title: String,
blinded_serial_number: String,
voucher_value: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_multisig_contract(
fee,
MultisigExecuteMsg::Close { proposal_id },
"Multisig::Close".to_string(),
vec![],
)
.await
}
async fn changed_member_hook(
&self,
member_diff: Vec<MemberDiff>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
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<C> MultisigSigningClient for C
where
C: SigningCosmWasmClient + NymContractsProvider + Sync,
NyxdError: From<<Self as OfflineSigner>::Error>,
{
async fn execute_multisig_contract(
&self,
fee: Option<Fee>,
msg: MultisigExecuteMsg,
memo: String,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError> {
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<C: MultisigSigningClient + Send + Sync>(
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()
}
};
}
}
@@ -1,144 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// 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<T>(&self, query: NameQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn get_name_service_config(&self) -> Result<ConfigResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::Config {})
.await
}
async fn get_name_entry(&self, name_id: NameId) -> Result<RegisteredName, NyxdError> {
self.query_name_service_contract(NameQueryMsg::NameId { name_id })
.await
}
async fn get_names_paged(
&self,
start_after: Option<NameId>,
limit: Option<u32>,
) -> Result<PagedNamesListResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::All { limit, start_after })
.await
}
async fn get_name_signing_nonce(&self, address: &AccountId) -> Result<Nonce, NyxdError> {
self.query_name_service_contract(NameQueryMsg::SigningNonce {
address: address.to_string(),
})
.await
}
async fn get_names_by_owner(&self, owner: AccountId) -> Result<NamesListResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::ByOwner {
owner: owner.to_string(),
})
.await
}
async fn get_names_by_nym_name(&self, name: NymName) -> Result<NamesListResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::ByName { name })
.await
}
async fn get_names_by_address(&self, address: Address) -> Result<NamesListResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::ByAddress { address })
.await
}
async fn get_name_service_contract_version(
&self,
) -> Result<ContractBuildInformation, NyxdError> {
self.query_name_service_contract(NameQueryMsg::GetContractVersion {})
.await
}
async fn get_name_service_contract_cw2_version(
&self,
) -> Result<cw2::ContractVersion, NyxdError> {
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<Vec<RegisteredName>, NyxdError> {
collect_paged!(self, get_names_paged, names)
}
}
#[async_trait]
impl<T> PagedNameServiceQueryClient for T where T: NameServiceQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> NameServiceQueryClient for C
where
C: CosmWasmClient + NymContractsProvider + Send + Sync,
{
async fn query_name_service_contract<T>(&self, query: NameQueryMsg) -> Result<T, NyxdError>
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<C: NameServiceQueryClient + Send + Sync>(
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()
}
};
}
}
@@ -1,142 +0,0 @@
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<T>(&self, query: SpQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn get_service_config(&self) -> Result<ConfigResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::Config {})
.await
}
async fn get_service_info(
&self,
service_id: ServiceId,
) -> Result<ServiceInfoResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::ServiceId { service_id })
.await
}
async fn get_services_paged(
&self,
start_after: Option<ServiceId>,
limit: Option<u32>,
) -> Result<PagedServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::All { limit, start_after })
.await
}
async fn get_services_by_announcer(
&self,
announcer: AccountId,
) -> Result<ServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::ByAnnouncer {
announcer: announcer.to_string(),
})
.await
}
async fn get_services_by_nym_address(
&self,
nym_address: NymAddress,
) -> Result<ServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::ByNymAddress { nym_address })
.await
}
async fn get_sp_contract_version(&self) -> Result<ContractBuildInformation, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::GetContractVersion {})
.await
}
async fn get_sp_contract_cw2_version(&self) -> Result<cw2::ContractVersion, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::GetCW2ContractVersion {})
.await
}
async fn get_service_signing_nonce(&self, address: &AccountId) -> Result<Nonce, NyxdError> {
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<Vec<Service>, NyxdError> {
collect_paged!(self, get_services_paged, services)
}
}
#[async_trait]
impl<T> PagedSpDirectoryQueryClient for T where T: SpDirectoryQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> SpDirectoryQueryClient for C
where
C: CosmWasmClient + NymContractsProvider + Send + Sync,
{
async fn query_service_provider_contract<T>(&self, query: SpQueryMsg) -> Result<T, NyxdError>
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<C: SpDirectoryQueryClient + Send + Sync>(
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(),
};
}
}
@@ -1,16 +1,15 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::nyxd; use crate::nyxd;
use crate::nyxd::coin::Coin; use crate::nyxd::coin::Coin;
use crate::nyxd::cosmwasm_client::helpers::{create_pagination, next_page_key}; use crate::nyxd::cosmwasm_client::helpers::{create_pagination, next_page_key};
use crate::nyxd::cosmwasm_client::types::{ use crate::nyxd::cosmwasm_client::types::{
Account, CodeDetails, Contract, ContractCodeId, SequenceResponse, SimulateResponse, Account, Code, CodeDetails, Contract, ContractCodeHistoryEntry, ContractCodeId,
SequenceResponse, SimulateResponse,
}; };
use crate::nyxd::error::NyxdError; use crate::nyxd::error::NyxdError;
use crate::rpc::TendermintRpcClient;
use async_trait::async_trait; use async_trait::async_trait;
use cosmrs::cosmwasm::{CodeInfoResponse, ContractCodeHistoryEntry};
use cosmrs::proto::cosmos::auth::v1beta1::{QueryAccountRequest, QueryAccountResponse}; use cosmrs::proto::cosmos::auth::v1beta1::{QueryAccountRequest, QueryAccountResponse};
use cosmrs::proto::cosmos::bank::v1beta1::{ use cosmrs::proto::cosmos::bank::v1beta1::{
QueryAllBalancesRequest, QueryAllBalancesResponse, QueryBalanceRequest, QueryBalanceResponse, QueryAllBalancesRequest, QueryAllBalancesResponse, QueryBalanceRequest, QueryBalanceResponse,
@@ -19,61 +18,50 @@ use cosmrs::proto::cosmos::bank::v1beta1::{
use cosmrs::proto::cosmos::tx::v1beta1::{ use cosmrs::proto::cosmos::tx::v1beta1::{
SimulateRequest, SimulateResponse as ProtoSimulateResponse, SimulateRequest, SimulateResponse as ProtoSimulateResponse,
}; };
use cosmrs::proto::cosmwasm::wasm::v1::{ use cosmrs::proto::cosmwasm::wasm::v1::*;
QueryCodeRequest, QueryCodeResponse, QueryCodesRequest, QueryCodesResponse, use cosmrs::rpc::endpoint::block::Response as BlockResponse;
QueryContractHistoryRequest, QueryContractHistoryResponse, QueryContractInfoRequest, use cosmrs::rpc::endpoint::broadcast;
QueryContractInfoResponse, QueryContractsByCodeRequest, QueryContractsByCodeResponse, use cosmrs::rpc::endpoint::tx::Response as TxResponse;
QueryRawContractStateRequest, QueryRawContractStateResponse, QuerySmartContractStateRequest, use cosmrs::rpc::query::Query;
QuerySmartContractStateResponse, use cosmrs::rpc::{self, HttpClient, Order};
}; use cosmrs::tendermint::abci::Transaction;
use cosmrs::tendermint::{block, chain, Hash}; use cosmrs::tendermint::{abci, block, chain};
use cosmrs::{AccountId, Coin as CosmosCoin, Tx}; use cosmrs::{tx, AccountId, Coin as CosmosCoin, Tx};
use log::trace;
use prost::Message; use prost::Message;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::TryFrom; use std::convert::{TryFrom, TryInto};
use std::time::Duration; use std::time::Duration;
use tendermint_rpc::{
endpoint::{block::Response as BlockResponse, broadcast, tx::Response as TxResponse},
query::Query,
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] #[async_trait]
impl CosmWasmClient for cosmrs::rpc::HttpClient {} impl CosmWasmClient for 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: rpc::Client {
// 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;
#[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 // 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 // TODO: perhaps it should have an additional argument to determine whether the response should
// require proof? // require proof?
async fn make_abci_query<Req, Res>( async fn make_abci_query<Req, Res>(
&self, &self,
path: Option<String>, path: Option<abci::Path>,
req: Req, req: Req,
) -> Result<Res, NyxdError> ) -> Result<Res, NyxdError>
where where
Req: Message, Req: Message,
Res: Message + Default, 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()); let mut buf = Vec::with_capacity(req.encoded_len());
req.encode(&mut buf)?; req.encode(&mut buf)?;
@@ -93,7 +81,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
// TODO: the return type should probably be changed to a non-proto, type-safe Account alternative // TODO: the return type should probably be changed to a non-proto, type-safe Account alternative
async fn get_account(&self, address: &AccountId) -> Result<Option<Account>, NyxdError> { async fn get_account(&self, address: &AccountId) -> Result<Option<Account>, NyxdError> {
let path = Some("/cosmos.auth.v1beta1.Query/Account".to_owned()); let path = Some("/cosmos.auth.v1beta1.Query/Account".parse().unwrap());
let req = QueryAccountRequest { let req = QueryAccountRequest {
address: address.to_string(), address: address.to_string(),
@@ -131,7 +119,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
address: &AccountId, address: &AccountId,
search_denom: String, search_denom: String,
) -> Result<Option<Coin>, NyxdError> { ) -> Result<Option<Coin>, NyxdError> {
let path = Some("/cosmos.bank.v1beta1.Query/Balance".to_owned()); let path = Some("/cosmos.bank.v1beta1.Query/Balance".parse().unwrap());
let req = QueryBalanceRequest { let req = QueryBalanceRequest {
address: address.to_string(), address: address.to_string(),
@@ -149,7 +137,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
} }
async fn get_all_balances(&self, address: &AccountId) -> Result<Vec<Coin>, NyxdError> { async fn get_all_balances(&self, address: &AccountId) -> Result<Vec<Coin>, NyxdError> {
let path = Some("/cosmos.bank.v1beta1.Query/AllBalances".to_owned()); let path = Some("/cosmos.bank.v1beta1.Query/AllBalances".parse().unwrap());
let mut raw_balances = Vec::new(); let mut raw_balances = Vec::new();
let mut pagination = None; let mut pagination = None;
@@ -180,7 +168,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
} }
async fn get_total_supply(&self) -> Result<Vec<Coin>, NyxdError> { async fn get_total_supply(&self) -> Result<Vec<Coin>, NyxdError> {
let path = Some("/cosmos.bank.v1beta1.Query/TotalSupply".to_owned()); let path = Some("/cosmos.bank.v1beta1.Query/TotalSupply".parse().unwrap());
let mut supply = Vec::new(); let mut supply = Vec::new();
let mut pagination = None; let mut pagination = None;
@@ -207,7 +195,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
.map_err(|_| NyxdError::SerializationError("Coins".to_owned())) .map_err(|_| NyxdError::SerializationError("Coins".to_owned()))
} }
async fn get_tx(&self, id: Hash) -> Result<TxResponse, NyxdError> { async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NyxdError> {
Ok(self.tx(id, false).await?) Ok(self.tx(id, false).await?)
} }
@@ -243,46 +231,30 @@ pub trait CosmWasmClient: TendermintRpcClient {
} }
/// Broadcast a transaction, returning immediately. /// Broadcast a transaction, returning immediately.
async fn broadcast_tx_async<T>(&self, tx: T) -> Result<broadcast::tx_async::Response, NyxdError> async fn broadcast_tx_async(
where &self,
T: Into<Vec<u8>> + Send, tx: Transaction,
{ ) -> Result<broadcast::tx_async::Response, NyxdError> {
Ok(TendermintRpcClient::broadcast_tx_async(self, tx).await?) Ok(rpc::Client::broadcast_tx_async(self, tx).await?)
} }
/// Broadcast a transaction, returning the response from `CheckTx`. /// Broadcast a transaction, returning the response from `CheckTx`.
async fn broadcast_tx_sync<T>(&self, tx: T) -> Result<broadcast::tx_sync::Response, NyxdError> async fn broadcast_tx_sync(
where &self,
T: Into<Vec<u8>> + Send, tx: Transaction,
{ ) -> Result<broadcast::tx_sync::Response, NyxdError> {
Ok(TendermintRpcClient::broadcast_tx_sync(self, tx).await?) Ok(rpc::Client::broadcast_tx_sync(self, tx).await?)
} }
/// Broadcast a transaction, returning the response from `DeliverTx`. /// Broadcast a transaction, returning the response from `DeliverTx`.
async fn broadcast_tx_commit<T>( async fn broadcast_tx_commit(
&self, &self,
tx: T, tx: Transaction,
) -> Result<broadcast::tx_commit::Response, NyxdError> ) -> Result<broadcast::tx_commit::Response, NyxdError> {
where Ok(rpc::Client::broadcast_tx_commit(self, tx).await?)
T: Into<Vec<u8>> + Send,
{
Ok(TendermintRpcClient::broadcast_tx_commit(self, tx).await?)
} }
async fn broadcast_tx<T>( async fn broadcast_tx(&self, tx: Transaction) -> Result<TxResponse, NyxdError> {
&self,
tx: T,
timeout: impl Into<Option<Duration>> + Send,
poll_interval: impl Into<Option<Duration>> + Send,
) -> Result<TxResponse, NyxdError>
where
T: Into<Vec<u8>> + 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?; let broadcasted = CosmWasmClient::broadcast_tx_sync(self, tx).await?;
if broadcasted.code.is_err() { if broadcasted.code.is_err() {
@@ -297,16 +269,16 @@ pub trait CosmWasmClient: TendermintRpcClient {
let tx_hash = broadcasted.hash; let tx_hash = broadcasted.hash;
let start = Instant::now(); let start = tokio::time::Instant::now();
loop { loop {
log::debug!( log::debug!(
"Polling for result of including {} in a block...", "Polling for result of including {} in a block...",
broadcasted.hash broadcasted.hash
); );
if Instant::now().duration_since(start) >= timeout { if tokio::time::Instant::now().duration_since(start) >= self.broadcast_timeout() {
return Err(NyxdError::BroadcastTimeout { return Err(NyxdError::BroadcastTimeout {
hash: tx_hash, hash: tx_hash,
timeout, timeout: self.broadcast_timeout(),
}); });
} }
@@ -314,12 +286,12 @@ pub trait CosmWasmClient: TendermintRpcClient {
return Ok(poll_res); return Ok(poll_res);
} }
sleep(poll_interval).await; tokio::time::sleep(self.broadcast_polling_rate()).await;
} }
} }
async fn get_codes(&self) -> Result<Vec<CodeInfoResponse>, NyxdError> { async fn get_codes(&self) -> Result<Vec<Code>, NyxdError> {
let path = Some("/cosmwasm.wasm.v1.Query/Codes".to_owned()); let path = Some("/cosmwasm.wasm.v1.Query/Codes".parse().unwrap());
let mut raw_codes = Vec::new(); let mut raw_codes = Vec::new();
let mut pagination = None; let mut pagination = None;
@@ -339,14 +311,14 @@ pub trait CosmWasmClient: TendermintRpcClient {
} }
} }
Ok(raw_codes raw_codes
.into_iter() .into_iter()
.map(TryFrom::try_from) .map(TryFrom::try_from)
.collect::<Result<_, _>>()?) .collect::<Result<_, _>>()
} }
async fn get_code_details(&self, code_id: ContractCodeId) -> Result<CodeDetails, NyxdError> { async fn get_code_details(&self, code_id: ContractCodeId) -> Result<CodeDetails, NyxdError> {
let path = Some("/cosmwasm.wasm.v1.Query/Code".to_owned()); let path = Some("/cosmwasm.wasm.v1.Query/Code".parse().unwrap());
let req = QueryCodeRequest { code_id }; let req = QueryCodeRequest { code_id };
@@ -361,7 +333,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
} }
} }
async fn get_contracts(&self, code_id: ContractCodeId) -> Result<Vec<AccountId>, NyxdError> { async fn get_contracts(&self, code_id: ContractCodeId) -> Result<Vec<AccountId>, NyxdError> {
let path = Some("/cosmwasm.wasm.v1.Query/ContractsByCode".to_owned()); let path = Some("/cosmwasm.wasm.v1.Query/ContractsByCode".parse().unwrap());
let mut raw_contracts = Vec::new(); let mut raw_contracts = Vec::new();
let mut pagination = None; let mut pagination = None;
@@ -392,7 +364,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
} }
async fn get_contract(&self, address: &AccountId) -> Result<Contract, NyxdError> { async fn get_contract(&self, address: &AccountId) -> Result<Contract, NyxdError> {
let path = Some("/cosmwasm.wasm.v1.Query/ContractInfo".to_owned()); let path = Some("/cosmwasm.wasm.v1.Query/ContractInfo".parse().unwrap());
let req = QueryContractInfoRequest { let req = QueryContractInfoRequest {
address: address.to_string(), address: address.to_string(),
@@ -417,7 +389,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
&self, &self,
address: &AccountId, address: &AccountId,
) -> Result<Vec<ContractCodeHistoryEntry>, NyxdError> { ) -> Result<Vec<ContractCodeHistoryEntry>, NyxdError> {
let path = Some("/cosmwasm.wasm.v1.Query/ContractHistory".to_owned()); let path = Some("/cosmwasm.wasm.v1.Query/ContractHistory".parse().unwrap());
let mut raw_entries = Vec::new(); let mut raw_entries = Vec::new();
let mut pagination = None; let mut pagination = None;
@@ -440,10 +412,10 @@ pub trait CosmWasmClient: TendermintRpcClient {
} }
} }
Ok(raw_entries raw_entries
.into_iter() .into_iter()
.map(TryFrom::try_from) .map(TryFrom::try_from)
.collect::<Result<_, _>>()?) .collect::<Result<_, _>>()
} }
async fn query_contract_raw( async fn query_contract_raw(
@@ -451,7 +423,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
address: &AccountId, address: &AccountId,
query_data: Vec<u8>, query_data: Vec<u8>,
) -> Result<Vec<u8>, NyxdError> { ) -> Result<Vec<u8>, NyxdError> {
let path = Some("/cosmwasm.wasm.v1.Query/RawContractState".to_owned()); let path = Some("/cosmwasm.wasm.v1.Query/RawContractState".parse().unwrap());
let req = QueryRawContractStateRequest { let req = QueryRawContractStateRequest {
address: address.to_string(), address: address.to_string(),
@@ -492,7 +464,6 @@ pub trait CosmWasmClient: TendermintRpcClient {
.make_abci_query::<_, QuerySmartContractStateResponse>(path, req) .make_abci_query::<_, QuerySmartContractStateResponse>(path, req)
.await?; .await?;
trace!("raw query response: {}", String::from_utf8_lossy(&res.data));
Ok(serde_json::from_slice(&res.data)?) Ok(serde_json::from_slice(&res.data)?)
} }
@@ -508,7 +479,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
tx: Option<Tx>, tx: Option<Tx>,
tx_bytes: Vec<u8>, tx_bytes: Vec<u8>,
) -> Result<SimulateResponse, NyxdError> { ) -> Result<SimulateResponse, NyxdError> {
let path = Some("/cosmos.tx.v1beta1.Service/Simulate".to_owned()); let path = Some("/cosmos.tx.v1beta1.Service/Simulate".parse().unwrap());
let req = SimulateRequest { let req = SimulateRequest {
tx: tx.map(Into::into), tx: tx.map(Into::into),
@@ -1,8 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod query_client;
pub mod signing_client;
pub use query_client::CosmWasmClient;
pub use signing_client::SigningCosmWasmClient;
@@ -3,7 +3,12 @@
use crate::nyxd::error::NyxdError; use crate::nyxd::error::NyxdError;
use cosmrs::proto::cosmos::base::query::v1beta1::{PageRequest, PageResponse}; use cosmrs::proto::cosmos::base::query::v1beta1::{PageRequest, PageResponse};
use tendermint_rpc::endpoint::broadcast; use cosmrs::proto::cosmos::base::v1beta1::Coin as ProtoCoin;
use cosmrs::rpc::endpoint::broadcast;
use cosmrs::Coin;
use flate2::write::GzEncoder;
use flate2::Compression;
use std::io::Write;
pub(crate) trait CheckResponse: Sized { pub(crate) trait CheckResponse: Sized {
fn check_response(self) -> Result<Self, NyxdError>; fn check_response(self) -> Result<Self, NyxdError>;
@@ -16,7 +21,7 @@ impl CheckResponse for broadcast::tx_commit::Response {
hash: self.hash, hash: self.hash,
height: Some(self.height), height: Some(self.height),
code: self.check_tx.code.value(), code: self.check_tx.code.value(),
raw_log: self.check_tx.log, raw_log: self.check_tx.log.value().to_owned(),
}); });
} }
@@ -25,7 +30,7 @@ impl CheckResponse for broadcast::tx_commit::Response {
hash: self.hash, hash: self.hash,
height: Some(self.height), height: Some(self.height),
code: self.deliver_tx.code.value(), code: self.deliver_tx.code.value(),
raw_log: self.deliver_tx.log, raw_log: self.deliver_tx.log.value().to_owned(),
}); });
} }
@@ -40,7 +45,7 @@ impl CheckResponse for crate::nyxd::TxResponse {
hash: self.hash, hash: self.hash,
height: Some(self.height), height: Some(self.height),
code: self.tx_result.code.value(), code: self.tx_result.code.value(),
raw_log: self.tx_result.log, raw_log: self.tx_result.log.value().to_owned(),
}); });
} }
@@ -49,10 +54,6 @@ impl CheckResponse for crate::nyxd::TxResponse {
} }
pub(crate) fn compress_wasm_code(code: &[u8]) -> Result<Vec<u8>, NyxdError> { pub(crate) fn compress_wasm_code(code: &[u8]) -> Result<Vec<u8>, NyxdError> {
use flate2::write::GzEncoder;
use flate2::Compression;
use std::io::Write;
// using compression level 9, same as cosmjs, that optimises for size // using compression level 9, same as cosmjs, that optimises for size
let mut encoder = GzEncoder::new(Vec::new(), Compression::best()); let mut encoder = GzEncoder::new(Vec::new(), Compression::best());
encoder encoder
@@ -82,3 +83,14 @@ pub(crate) fn next_page_key(pagination_info: Option<PageResponse>) -> Option<Vec
None None
} }
pub(crate) fn parse_proto_coin_vec(value: Vec<ProtoCoin>) -> Result<Vec<Coin>, NyxdError> {
value
.into_iter()
.map(|proto_coin| {
Coin::try_from(&proto_coin).map_err(|_| NyxdError::MalformedCoin {
coin_representation: format!("{:?}", proto_coin),
})
})
.collect()
}
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::nyxd::error::NyxdError; use crate::nyxd::error::NyxdError;
use cosmrs::tendermint::abci;
use itertools::Itertools; use itertools::Itertools;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -48,7 +49,7 @@ fn parse_raw_str_logs(raw: &str) -> Result<Vec<Log>, NyxdError> {
Ok(logs) Ok(logs)
} }
pub fn parse_raw_logs(raw: String) -> Result<Vec<Log>, NyxdError> { pub fn parse_raw_logs(raw: abci::Log) -> Result<Vec<Log>, NyxdError> {
parse_raw_str_logs(raw.as_ref()) parse_raw_str_logs(raw.as_ref())
} }
@@ -1,158 +1,31 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient};
use crate::nyxd::error::NyxdError; use crate::nyxd::error::NyxdError;
use crate::nyxd::{Config, GasPrice}; use crate::nyxd::GasPrice;
use crate::rpc::TendermintRpcClient; use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl};
use crate::signing::{ use std::convert::TryInto;
signer::{NoSigner, OfflineSigner},
AccountData,
};
use async_trait::async_trait;
use tendermint_rpc::{Error as TendermintRpcError, SimpleRequest};
#[cfg(feature = "http-client")] pub mod client;
use cosmrs::rpc::{HttpClient, HttpClientUrl};
use cosmrs::tx::{Raw, SignDoc};
pub mod client_traits;
mod helpers; mod helpers;
pub mod logs; pub mod logs;
pub mod signing_client;
pub mod types; pub mod types;
#[derive(Debug)] pub fn connect<U>(endpoint: U) -> Result<HttpClient, NyxdError>
pub(crate) struct SigningClientOptions { where
gas_price: GasPrice, U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
simulated_gas_multiplier: f32, {
Ok(HttpClient::new(endpoint)?)
} }
impl<'a> From<&'a Config> for SigningClientOptions { pub fn connect_with_signer<S, U: Clone>(
fn from(value: &'a Config) -> Self { endpoint: U,
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<C, S = NoSigner> {
client: C,
signer: S, signer: S,
opts: SigningClientOptions, gas_price: GasPrice,
} ) -> Result<signing_client::Client<S>, NyxdError>
impl<C> MaybeSigningClient<C> {
pub(crate) fn new(client: C, opts: SigningClientOptions) -> Self {
MaybeSigningClient {
client,
signer: Default::default(),
opts,
}
}
}
impl<C, S> MaybeSigningClient<C, S> {
pub(crate) fn new_signing(client: C, signer: S, opts: SigningClientOptions) -> Self
where
S: OfflineSigner,
{
MaybeSigningClient {
client,
signer,
opts,
}
}
}
#[cfg(feature = "http-client")]
impl<S> MaybeSigningClient<HttpClient, S> {
pub(crate) fn change_endpoint<U>(&mut self, new_endpoint: U) -> Result<(), NyxdError>
where
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
{
self.client = HttpClient::new(new_endpoint)?;
Ok(())
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C, S> TendermintRpcClient for MaybeSigningClient<C, S>
where where
C: TendermintRpcClient + Send + Sync, U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
S: Send + Sync,
{ {
async fn perform<R>(&self, request: R) -> Result<R::Output, TendermintRpcError> signing_client::Client::connect_with_signer(endpoint, signer, gas_price)
where
R: SimpleRequest,
{
self.client.perform(request).await
}
} }
impl<C, S> OfflineSigner for MaybeSigningClient<C, S>
where
S: OfflineSigner,
{
type Error = S::Error;
fn get_accounts(&self) -> Result<Vec<AccountData>, Self::Error> {
self.signer.get_accounts()
}
fn sign_direct_with_account(
&self,
signer: &AccountData,
sign_doc: SignDoc,
) -> Result<Raw, Self::Error> {
self.signer.sign_direct_with_account(signer, sign_doc)
}
}
#[async_trait]
impl<C, S> CosmWasmClient for MaybeSigningClient<C, S>
where
C: TendermintRpcClient + Send + Sync,
S: Send + Sync,
{
}
#[async_trait]
impl<C, S> SigningCosmWasmClient for MaybeSigningClient<C, S>
where
C: TendermintRpcClient + Send + Sync,
S: OfflineSigner + Send + Sync,
NyxdError: From<S::Error>,
{
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<U>(endpoint: U) -> Result<MaybeSigningClient<HttpClient>, NyxdError>
// where
// U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
// {
// Ok(HttpClient::new(endpoint)?)
// }
//
// #[cfg(all(feature = "signing", feature = "http-client"))]
// pub fn connect_with_signer<S, U: Clone>(
// endpoint: U,
// signer: S,
// gas_price: crate::nyxd::GasPrice,
// ) -> Result<MaybeSigningClient<HttpClient, S>, NyxdError>
// where
// U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
// {
// signing_client::Client::connect_with_signer(endpoint, signer, gas_price)
// }
@@ -1,7 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net> // Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::nyxd::cosmwasm_client::client_traits::CosmWasmClient; use crate::nyxd::cosmwasm_client::client::CosmWasmClient;
use crate::nyxd::cosmwasm_client::helpers::{compress_wasm_code, CheckResponse}; 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::logs::{self, parse_raw_logs};
use crate::nyxd::cosmwasm_client::types::*; use crate::nyxd::cosmwasm_client::types::*;
@@ -12,23 +12,26 @@ use crate::signing::signer::OfflineSigner;
use crate::signing::tx_signer::TxSigner; use crate::signing::tx_signer::TxSigner;
use crate::signing::SignerData; use crate::signing::SignerData;
use async_trait::async_trait; use async_trait::async_trait;
use cosmrs::abci::GasInfo;
use cosmrs::bank::MsgSend; use cosmrs::bank::MsgSend;
use cosmrs::distribution::MsgWithdrawDelegatorReward; use cosmrs::distribution::MsgWithdrawDelegatorReward;
use cosmrs::feegrant::{ use cosmrs::feegrant::{
AllowedMsgAllowance, BasicAllowance, MsgGrantAllowance, MsgRevokeAllowance, AllowedMsgAllowance, BasicAllowance, MsgGrantAllowance, MsgRevokeAllowance,
}; };
use cosmrs::proto::cosmos::tx::signing::v1beta1::SignMode; use cosmrs::proto::cosmos::tx::signing::v1beta1::SignMode;
use cosmrs::rpc::endpoint::broadcast;
use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl, SimpleRequest};
use cosmrs::staking::{MsgDelegate, MsgUndelegate}; use cosmrs::staking::{MsgDelegate, MsgUndelegate};
use cosmrs::tx::{self, Msg}; use cosmrs::tx::{self, Msg, Raw};
use cosmrs::{cosmwasm, AccountId, Any, Tx}; use cosmrs::{cosmwasm, rpc, AccountId, Any, Tx};
use log::debug; use log::debug;
use serde::Serialize; use serde::Serialize;
use sha2::Digest; use sha2::Digest;
use sha2::Sha256; use sha2::Sha256;
use std::convert::TryInto; use std::convert::TryInto;
use std::time::SystemTime; use std::time::{Duration, SystemTime};
use tendermint_rpc::endpoint::broadcast;
const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4);
const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60);
fn empty_fee() -> tx::Fee { fn empty_fee() -> tx::Fee {
tx::Fee { tx::Fee {
@@ -53,17 +56,18 @@ fn single_unspecified_signer_auth(
.auth_info(empty_fee()) .auth_info(empty_fee())
} }
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[async_trait]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait SigningCosmWasmClient: CosmWasmClient {
pub trait SigningCosmWasmClient: CosmWasmClient + TxSigner type Signer: OfflineSigner + Send + Sync;
where
NyxdError: From<<Self as OfflineSigner>::Error>, fn signer(&self) -> &Self::Signer;
{
// 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 gas_price(&self) -> &GasPrice;
fn simulated_gas_multiplier(&self) -> f32; fn signer_public_key(&self, signer_address: &AccountId) -> Option<tx::SignerPublicKey> {
let account = self.signer().find_account(signer_address).ok()?;
Some(account.public_key().into())
}
async fn simulate( async fn simulate(
&self, &self,
@@ -119,10 +123,7 @@ where
.check_response()?; .check_response()?;
let logs = parse_raw_logs(tx_res.tx_result.log)?; let logs = parse_raw_logs(tx_res.tx_result.log)?;
let gas_info = GasInfo { let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(),
gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(),
};
// TODO: should those strings be extracted into some constants? // TODO: should those strings be extracted into some constants?
// the reason I think unwrap here is fine is that if the transaction succeeded and those // the reason I think unwrap here is fine is that if the transaction succeeded and those
@@ -183,10 +184,8 @@ where
.check_response()?; .check_response()?;
let logs = parse_raw_logs(tx_res.tx_result.log)?; let logs = parse_raw_logs(tx_res.tx_result.log)?;
let gas_info = GasInfo { let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(),
gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(),
};
// TODO: should those strings be extracted into some constants? // TODO: should those strings be extracted into some constants?
// the reason I think unwrap here is fine is that if the transaction succeeded and those // the reason I think unwrap here is fine is that if the transaction succeeded and those
// fields do not exist or address is malformed, there's no way we can recover, we're probably connected // fields do not exist or address is malformed, there's no way we can recover, we're probably connected
@@ -213,7 +212,7 @@ where
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<ChangeAdminResult, NyxdError> { ) -> Result<ChangeAdminResult, NyxdError> {
let change_admin_msg = sealed::cosmwasm::MsgUpdateAdmin { let change_admin_msg = cosmwasm::MsgUpdateAdmin {
sender: sender_address.clone(), sender: sender_address.clone(),
new_admin: new_admin.clone(), new_admin: new_admin.clone(),
contract: contract_address.clone(), contract: contract_address.clone(),
@@ -226,10 +225,8 @@ where
.await? .await?
.check_response()?; .check_response()?;
let gas_info = GasInfo { let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(),
gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(),
};
Ok(ChangeAdminResult { Ok(ChangeAdminResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.tx_result.log)?,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
@@ -244,7 +241,7 @@ where
fee: Fee, fee: Fee,
memo: impl Into<String> + Send + 'static, memo: impl Into<String> + Send + 'static,
) -> Result<ChangeAdminResult, NyxdError> { ) -> Result<ChangeAdminResult, NyxdError> {
let change_admin_msg = sealed::cosmwasm::MsgClearAdmin { let change_admin_msg = cosmwasm::MsgClearAdmin {
sender: sender_address.clone(), sender: sender_address.clone(),
contract: contract_address.clone(), contract: contract_address.clone(),
} }
@@ -256,10 +253,8 @@ where
.await? .await?
.check_response()?; .check_response()?;
let gas_info = GasInfo { let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(),
gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(),
};
Ok(ChangeAdminResult { Ok(ChangeAdminResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.tx_result.log)?,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
@@ -293,10 +288,8 @@ where
.await? .await?
.check_response()?; .check_response()?;
let gas_info = GasInfo { let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(),
gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(),
};
Ok(MigrateResult { Ok(MigrateResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.tx_result.log)?,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
@@ -330,13 +323,11 @@ where
.await? .await?
.check_response()?; .check_response()?;
let gas_info = GasInfo { let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(),
gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(),
};
Ok(ExecuteResult { Ok(ExecuteResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.tx_result.log)?,
data: tx_res.tx_result.data.into(), data: tx_res.tx_result.data,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
gas_info, gas_info,
}) })
@@ -373,13 +364,11 @@ where
.await? .await?
.check_response()?; .check_response()?;
let gas_info = GasInfo { let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
gas_wanted: tx_res.tx_result.gas_wanted.try_into().unwrap_or_default(),
gas_used: tx_res.tx_result.gas_used.try_into().unwrap_or_default(),
};
Ok(ExecuteResult { Ok(ExecuteResult {
logs: parse_raw_logs(tx_res.tx_result.log)?, logs: parse_raw_logs(tx_res.tx_result.log)?,
data: tx_res.tx_result.data.into(), data: tx_res.tx_result.data,
transaction_hash: tx_res.hash, transaction_hash: tx_res.hash,
gas_info, gas_info,
}) })
@@ -575,9 +564,9 @@ where
let multiplier = multiplier.unwrap_or(DEFAULT_SIMULATED_GAS_MULTIPLIER); let multiplier = multiplier.unwrap_or(DEFAULT_SIMULATED_GAS_MULTIPLIER);
let gas = gas_estimation.adjust_gas(multiplier); let gas = gas_estimation.adjust_gas(multiplier);
debug!("Gas estimation: {gas_estimation}"); debug!("Gas estimation: {}", gas_estimation);
debug!("Multiplying the estimation by {multiplier}"); debug!("Multiplying the estimation by {}", multiplier);
debug!("Final gas limit used: {gas}"); debug!("Final gas limit used: {}", gas);
let fee = self.gas_price() * gas; let fee = self.gas_price() * gas;
Ok::<tx::Fee, NyxdError>(tx::Fee::from_amount_and_gas(fee, gas)) Ok::<tx::Fee, NyxdError>(tx::Fee::from_amount_and_gas(fee, gas))
@@ -613,7 +602,7 @@ where
.to_bytes() .to_bytes()
.map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?;
CosmWasmClient::broadcast_tx_async(self, tx_bytes).await CosmWasmClient::broadcast_tx_async(self, tx_bytes.into()).await
} }
/// Broadcast a transaction, returning the response from `CheckTx`. /// Broadcast a transaction, returning the response from `CheckTx`.
@@ -633,7 +622,7 @@ where
.to_bytes() .to_bytes()
.map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?;
CosmWasmClient::broadcast_tx_sync(self, tx_bytes).await CosmWasmClient::broadcast_tx_sync(self, tx_bytes.into()).await
} }
/// Broadcast a transaction, returning the response from `DeliverTx`. /// Broadcast a transaction, returning the response from `DeliverTx`.
@@ -654,7 +643,7 @@ where
.to_bytes() .to_bytes()
.map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?;
CosmWasmClient::broadcast_tx_commit(self, tx_bytes).await CosmWasmClient::broadcast_tx_commit(self, tx_bytes.into()).await
} }
/// Broadcast a transaction to the network and monitors its inclusion in a block. /// Broadcast a transaction to the network and monitors its inclusion in a block.
@@ -675,7 +664,7 @@ where
.to_bytes() .to_bytes()
.map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?; .map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?;
self.broadcast_tx(tx_bytes, None, None).await self.broadcast_tx(tx_bytes.into()).await
} }
async fn sign( async fn sign(
@@ -698,177 +687,155 @@ where
} }
}; };
Ok(<Self as TxSigner>::sign_direct( self.sign_direct(signer_address, messages, fee, memo, signer_data)
self, }
signer_address,
messages, fn sign_amino(
fee, &self,
memo, signer_address: &AccountId,
signer_data, messages: Vec<Any>,
)?) fee: tx::Fee,
memo: impl Into<String> + Send + 'static,
signer_data: SignerData,
) -> Result<tx::Raw, NyxdError>;
fn sign_direct(
&self,
signer_address: &AccountId,
messages: Vec<Any>,
fee: tx::Fee,
memo: impl Into<String> + Send + 'static,
signer_data: SignerData,
) -> Result<tx::Raw, NyxdError>;
}
#[derive(Debug)]
pub struct Client<S> {
// 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<S>,
gas_price: GasPrice,
broadcast_polling_rate: Duration,
broadcast_timeout: Duration,
}
impl<S> Client<S> {
pub fn connect_with_signer<U: Clone>(
endpoint: U,
signer: S,
gas_price: GasPrice,
) -> Result<Self, NyxdError>
where
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
{
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<S>
where
S: OfflineSigner,
{
TxSigner::new(signer)
}
pub fn change_endpoint<U>(&mut self, new_endpoint: U) -> Result<(), NyxdError>
where
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
{
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
} }
} }
// a temporary bypass until https://github.com/cosmos/cosmos-rust/pull/419 is merged #[async_trait]
mod sealed { impl<S> rpc::Client for Client<S>
pub mod cosmwasm { where
use cosmrs::{proto, tx::Msg, AccountId, ErrorReport, Result}; S: Send + Sync,
{
/// MsgUpdateAdmin sets a new admin for a smart contract async fn perform<R>(&self, request: R) -> Result<R::Response, rpc::Error>
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] where
pub struct MsgUpdateAdmin { R: SimpleRequest,
/// Sender is the that actor that signed the messages {
pub sender: AccountId, self.rpc_client.perform(request).await
}
/// NewAdmin address to be set }
pub new_admin: AccountId,
#[async_trait]
/// Contract is the address of the smart contract impl<S> CosmWasmClient for Client<S>
pub contract: AccountId, where
} S: Send + Sync,
{
impl Msg for MsgUpdateAdmin { fn broadcast_polling_rate(&self) -> Duration {
type Proto = proto::cosmwasm::wasm::v1::MsgUpdateAdmin; self.broadcast_polling_rate
} }
impl TryFrom<proto::cosmwasm::wasm::v1::MsgUpdateAdmin> for MsgUpdateAdmin { fn broadcast_timeout(&self) -> Duration {
type Error = ErrorReport; self.broadcast_timeout
}
fn try_from( }
proto: proto::cosmwasm::wasm::v1::MsgUpdateAdmin,
) -> Result<MsgUpdateAdmin> { #[async_trait]
MsgUpdateAdmin::try_from(&proto) impl<S> SigningCosmWasmClient for Client<S>
} where
} S: OfflineSigner + Send + Sync,
NyxdError: From<S::Error>,
impl TryFrom<&proto::cosmwasm::wasm::v1::MsgUpdateAdmin> for MsgUpdateAdmin { {
type Error = ErrorReport; type Signer = S;
fn try_from( fn signer(&self) -> &Self::Signer {
proto: &proto::cosmwasm::wasm::v1::MsgUpdateAdmin, self.tx_signer.signer()
) -> Result<MsgUpdateAdmin> { }
Ok(MsgUpdateAdmin {
sender: proto.sender.parse()?, fn gas_price(&self) -> &GasPrice {
new_admin: proto.new_admin.parse()?, &self.gas_price
contract: proto.contract.parse()?, }
})
} fn sign_amino(
} &self,
signer_address: &AccountId,
impl From<MsgUpdateAdmin> for proto::cosmwasm::wasm::v1::MsgUpdateAdmin { messages: Vec<Any>,
fn from(msg: MsgUpdateAdmin) -> proto::cosmwasm::wasm::v1::MsgUpdateAdmin { fee: tx::Fee,
proto::cosmwasm::wasm::v1::MsgUpdateAdmin::from(&msg) memo: impl Into<String> + Send + 'static,
} signer_data: SignerData,
} ) -> Result<Raw, NyxdError> {
Ok(self
impl From<&MsgUpdateAdmin> for proto::cosmwasm::wasm::v1::MsgUpdateAdmin { .tx_signer
fn from(msg: &MsgUpdateAdmin) -> proto::cosmwasm::wasm::v1::MsgUpdateAdmin { .sign_amino(signer_address, messages, fee, memo, signer_data)?)
proto::cosmwasm::wasm::v1::MsgUpdateAdmin { }
sender: msg.sender.to_string(),
new_admin: msg.new_admin.to_string(), fn sign_direct(
contract: msg.contract.to_string(), &self,
} signer_address: &AccountId,
} messages: Vec<Any>,
} fee: tx::Fee,
memo: impl Into<String> + Send + 'static,
/// MsgUpdateAdminResponse returns empty data signer_data: SignerData,
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] ) -> Result<Raw, NyxdError> {
pub struct MsgUpdateAdminResponse {} Ok(self
.tx_signer
impl Msg for MsgUpdateAdminResponse { .sign_direct(signer_address, messages, fee, memo, signer_data)?)
type Proto = proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse;
}
impl TryFrom<proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse> for MsgUpdateAdminResponse {
type Error = ErrorReport;
fn try_from(
_proto: proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse,
) -> Result<MsgUpdateAdminResponse> {
Ok(MsgUpdateAdminResponse {})
}
}
impl From<MsgUpdateAdminResponse> for proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse {
fn from(
_msg: MsgUpdateAdminResponse,
) -> proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse {
proto::cosmwasm::wasm::v1::MsgUpdateAdminResponse {}
}
}
/// MsgClearAdmin removes any admin stored for a smart contract
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct MsgClearAdmin {
/// Sender is the that actor that signed the messages
pub sender: AccountId,
/// Contract is the address of the smart contract
pub contract: AccountId,
}
impl Msg for MsgClearAdmin {
type Proto = proto::cosmwasm::wasm::v1::MsgClearAdmin;
}
impl TryFrom<proto::cosmwasm::wasm::v1::MsgClearAdmin> for MsgClearAdmin {
type Error = ErrorReport;
fn try_from(proto: proto::cosmwasm::wasm::v1::MsgClearAdmin) -> Result<MsgClearAdmin> {
MsgClearAdmin::try_from(&proto)
}
}
impl TryFrom<&proto::cosmwasm::wasm::v1::MsgClearAdmin> for MsgClearAdmin {
type Error = ErrorReport;
fn try_from(proto: &proto::cosmwasm::wasm::v1::MsgClearAdmin) -> Result<MsgClearAdmin> {
Ok(MsgClearAdmin {
sender: proto.sender.parse()?,
contract: proto.contract.parse()?,
})
}
}
impl From<MsgClearAdmin> for proto::cosmwasm::wasm::v1::MsgClearAdmin {
fn from(msg: MsgClearAdmin) -> proto::cosmwasm::wasm::v1::MsgClearAdmin {
proto::cosmwasm::wasm::v1::MsgClearAdmin::from(&msg)
}
}
impl From<&MsgClearAdmin> for proto::cosmwasm::wasm::v1::MsgClearAdmin {
fn from(msg: &MsgClearAdmin) -> proto::cosmwasm::wasm::v1::MsgClearAdmin {
proto::cosmwasm::wasm::v1::MsgClearAdmin {
sender: msg.sender.to_string(),
contract: msg.contract.to_string(),
}
}
}
/// MsgClearAdminResponse returns empty data
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct MsgClearAdminResponse {}
impl Msg for MsgClearAdminResponse {
type Proto = proto::cosmwasm::wasm::v1::MsgClearAdminResponse;
}
impl TryFrom<proto::cosmwasm::wasm::v1::MsgClearAdminResponse> for MsgClearAdminResponse {
type Error = ErrorReport;
fn try_from(
_proto: proto::cosmwasm::wasm::v1::MsgClearAdminResponse,
) -> Result<MsgClearAdminResponse> {
Ok(MsgClearAdminResponse {})
}
}
impl From<MsgClearAdminResponse> for proto::cosmwasm::wasm::v1::MsgClearAdminResponse {
fn from(
_msg: MsgClearAdminResponse,
) -> proto::cosmwasm::wasm::v1::MsgClearAdminResponse {
proto::cosmwasm::wasm::v1::MsgClearAdminResponse {}
}
}
} }
} }
@@ -3,35 +3,36 @@
// TODO: There's a significant argument to pull those out of the package and make a PR on https://github.com/cosmos/cosmos-rust/ // TODO: There's a significant argument to pull those out of the package and make a PR on https://github.com/cosmos/cosmos-rust/
use crate::nyxd::cosmwasm_client::helpers::parse_proto_coin_vec;
use crate::nyxd::cosmwasm_client::logs::Log; use crate::nyxd::cosmwasm_client::logs::Log;
use crate::nyxd::error::NyxdError; use crate::nyxd::error::NyxdError;
use cosmrs::auth::{BaseAccount, ModuleAccount}; use cosmrs::crypto::PublicKey;
use cosmrs::cosmwasm::{CodeInfoResponse, ContractInfo};
use cosmrs::proto::cosmos::auth::v1beta1::{ use cosmrs::proto::cosmos::auth::v1beta1::{
BaseAccount as ProtoBaseAccount, ModuleAccount as ProtoModuleAccount, BaseAccount as ProtoBaseAccount, ModuleAccount as ProtoModuleAccount,
}; };
use cosmrs::proto::cosmos::base::abci::v1beta1::Result as ProtoAbciResult; use cosmrs::proto::cosmos::base::abci::v1beta1::{
GasInfo as ProtoGasInfo, Result as ProtoAbciResult,
};
use cosmrs::proto::cosmos::tx::v1beta1::SimulateResponse as ProtoSimulateResponse; use cosmrs::proto::cosmos::tx::v1beta1::SimulateResponse as ProtoSimulateResponse;
use cosmrs::proto::cosmos::vesting::v1beta1::{ use cosmrs::proto::cosmos::vesting::v1beta1::{
BaseVestingAccount as ProtoBaseVestingAccount, BaseVestingAccount as ProtoBaseVestingAccount,
ContinuousVestingAccount as ProtoContinuousVestingAccount, ContinuousVestingAccount as ProtoContinuousVestingAccount,
DelayedVestingAccount as ProtoDelayedVestingAccount, DelayedVestingAccount as ProtoDelayedVestingAccount, Period as ProtoPeriod,
PeriodicVestingAccount as ProtoPeriodicVestingAccount, PeriodicVestingAccount as ProtoPeriodicVestingAccount,
PermanentLockedAccount as ProtoPermanentLockedAccount, PermanentLockedAccount as ProtoPermanentLockedAccount,
}; };
use cosmrs::tendermint::{abci, Hash}; use cosmrs::proto::cosmwasm::wasm::v1::{
use cosmrs::tx::{AccountNumber, SequenceNumber}; CodeInfoResponse, ContractCodeHistoryEntry as ProtoContractCodeHistoryEntry,
use cosmrs::vesting::{ ContractCodeHistoryOperationType, ContractInfo as ProtoContractInfo,
BaseVestingAccount, ContinuousVestingAccount, DelayedVestingAccount, PeriodicVestingAccount,
PermanentLockedAccount,
}; };
use cosmrs::{AccountId, Any, Coin as CosmosCoin}; use cosmrs::tendermint::abci;
use cosmrs::tendermint::abci::Data;
use cosmrs::tx::{AccountNumber, Gas, SequenceNumber};
use cosmrs::{tx, AccountId, Any, Coin as CosmosCoin};
use prost::Message; use prost::Message;
use serde::Serialize; use serde::Serialize;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
pub use cosmrs::abci::GasInfo;
pub type ContractCodeId = u64; pub type ContractCodeId = u64;
#[derive(Serialize)] #[derive(Serialize)]
@@ -43,6 +44,215 @@ pub struct SequenceResponse {
pub sequence: SequenceNumber, pub sequence: SequenceNumber,
} }
/// BaseAccount defines a base account type. It contains all the necessary fields
/// for basic account functionality. Any custom account type should extend this
/// type for additional functionality (e.g. vesting).
#[derive(Debug)]
pub struct BaseAccount {
/// Bech32 account address
pub address: AccountId,
pub pubkey: Option<PublicKey>,
pub account_number: AccountNumber,
pub sequence: SequenceNumber,
}
impl TryFrom<ProtoBaseAccount> for BaseAccount {
type Error = NyxdError;
fn try_from(value: ProtoBaseAccount) -> Result<Self, Self::Error> {
let address: AccountId = value
.address
.parse()
.map_err(|_| NyxdError::MalformedAccountAddress(value.address.clone()))?;
let pubkey = value
.pub_key
.map(PublicKey::try_from)
.transpose()
.map_err(|_| NyxdError::InvalidPublicKey(address.clone()))?;
Ok(BaseAccount {
address,
pubkey,
account_number: value.account_number,
sequence: value.sequence,
})
}
}
/// ModuleAccount defines an account for modules that holds coins on a pool.
#[derive(Debug)]
pub struct ModuleAccount {
pub base_account: Option<BaseAccount>,
pub name: String,
pub permissions: Vec<String>,
}
impl TryFrom<ProtoModuleAccount> for ModuleAccount {
type Error = NyxdError;
fn try_from(value: ProtoModuleAccount) -> Result<Self, Self::Error> {
let base_account = value.base_account.map(TryFrom::try_from).transpose()?;
Ok(ModuleAccount {
base_account,
name: value.name,
permissions: value.permissions,
})
}
}
/// BaseVestingAccount implements the VestingAccount interface. It contains all
/// the necessary fields needed for any vesting account implementation.
#[derive(Debug)]
pub struct BaseVestingAccount {
pub base_account: Option<BaseAccount>,
pub original_vesting: Vec<CosmosCoin>,
pub delegated_free: Vec<CosmosCoin>,
pub delegated_vesting: Vec<CosmosCoin>,
pub end_time: i64,
}
impl TryFrom<ProtoBaseVestingAccount> for BaseVestingAccount {
type Error = NyxdError;
fn try_from(value: ProtoBaseVestingAccount) -> Result<Self, Self::Error> {
let base_account = value.base_account.map(TryFrom::try_from).transpose()?;
let original_vesting = parse_proto_coin_vec(value.original_vesting)?;
let delegated_free = parse_proto_coin_vec(value.delegated_free)?;
let delegated_vesting = parse_proto_coin_vec(value.delegated_vesting)?;
Ok(BaseVestingAccount {
base_account,
original_vesting,
delegated_free,
delegated_vesting,
end_time: value.end_time,
})
}
}
/// ContinuousVestingAccount implements the VestingAccount interface. It
/// continuously vests by unlocking coins linearly with respect to time.
#[derive(Debug)]
pub struct ContinuousVestingAccount {
pub base_vesting_account: Option<BaseVestingAccount>,
pub start_time: i64,
}
impl TryFrom<ProtoContinuousVestingAccount> for ContinuousVestingAccount {
type Error = NyxdError;
fn try_from(value: ProtoContinuousVestingAccount) -> Result<Self, Self::Error> {
let base_vesting_account = value
.base_vesting_account
.map(TryFrom::try_from)
.transpose()?;
Ok(ContinuousVestingAccount {
base_vesting_account,
start_time: value.start_time,
})
}
}
/// DelayedVestingAccount implements the VestingAccount interface. It vests all
/// coins after a specific time, but non prior. In other words, it keeps them
/// locked until a specified time.
#[derive(Debug)]
pub struct DelayedVestingAccount {
pub base_vesting_account: Option<BaseVestingAccount>,
}
impl TryFrom<ProtoDelayedVestingAccount> for DelayedVestingAccount {
type Error = NyxdError;
fn try_from(value: ProtoDelayedVestingAccount) -> Result<Self, Self::Error> {
let base_vesting_account = value
.base_vesting_account
.map(TryFrom::try_from)
.transpose()?;
Ok(DelayedVestingAccount {
base_vesting_account,
})
}
}
/// Period defines a length of time and amount of coins that will vest.
#[derive(Debug)]
pub struct Period {
pub length: i64,
pub amount: Vec<CosmosCoin>,
}
impl TryFrom<ProtoPeriod> for Period {
type Error = NyxdError;
fn try_from(value: ProtoPeriod) -> Result<Self, Self::Error> {
Ok(Period {
length: value.length,
amount: parse_proto_coin_vec(value.amount)?,
})
}
}
/// PeriodicVestingAccount implements the VestingAccount interface. It
/// periodically vests by unlocking coins during each specified period.
#[derive(Debug)]
pub struct PeriodicVestingAccount {
pub base_vesting_account: Option<BaseVestingAccount>,
pub start_time: i64,
pub vesting_periods: Vec<Period>,
}
impl TryFrom<ProtoPeriodicVestingAccount> for PeriodicVestingAccount {
type Error = NyxdError;
fn try_from(value: ProtoPeriodicVestingAccount) -> Result<Self, Self::Error> {
let base_vesting_account = value
.base_vesting_account
.map(TryFrom::try_from)
.transpose()?;
let vesting_periods = value
.vesting_periods
.into_iter()
.map(TryFrom::try_from)
.collect::<Result<_, _>>()?;
Ok(PeriodicVestingAccount {
base_vesting_account,
start_time: value.start_time,
vesting_periods,
})
}
}
/// PermanentLockedAccount implements the VestingAccount interface. It does
/// not ever release coins, locking them indefinitely. Coins in this account can
/// still be used for delegating and for governance votes even while locked.
#[derive(Debug)]
pub struct PermanentLockedAccount {
pub base_vesting_account: Option<BaseVestingAccount>,
}
impl TryFrom<ProtoPermanentLockedAccount> for PermanentLockedAccount {
type Error = NyxdError;
fn try_from(value: ProtoPermanentLockedAccount) -> Result<Self, Self::Error> {
let base_vesting_account = value
.base_vesting_account
.map(TryFrom::try_from)
.transpose()?;
Ok(PermanentLockedAccount {
base_vesting_account,
})
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum Account { pub enum Account {
Base(BaseAccount), Base(BaseAccount),
@@ -125,32 +335,183 @@ impl TryFrom<Any> for Account {
} }
} }
#[derive(Debug)]
pub struct Code {
pub code_id: ContractCodeId,
/// Bech32 account address
pub creator: AccountId,
/// sha256 hash of the code stored
pub data_hash: Vec<u8>,
}
impl TryFrom<CodeInfoResponse> for Code {
type Error = NyxdError;
fn try_from(value: CodeInfoResponse) -> Result<Self, Self::Error> {
let CodeInfoResponse {
code_id,
creator,
data_hash,
} = value;
let creator = creator
.parse()
.map_err(|_| NyxdError::MalformedAccountAddress(creator))?;
Ok(Code {
code_id,
creator,
data_hash,
})
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct CodeDetails { pub struct CodeDetails {
pub code_info: CodeInfoResponse, pub code_info: Code,
/// The original wasm bytes /// The original wasm bytes
pub data: Vec<u8>, pub data: Vec<u8>,
} }
impl CodeDetails { impl CodeDetails {
pub fn new(code_info: CodeInfoResponse, data: Vec<u8>) -> Self { pub fn new(code_info: Code, data: Vec<u8>) -> Self {
CodeDetails { code_info, data } CodeDetails { code_info, data }
} }
} }
#[derive(Debug)]
pub(crate) struct ContractInfo {
code_id: ContractCodeId,
creator: AccountId,
admin: Option<AccountId>,
label: String,
}
impl TryFrom<ProtoContractInfo> for ContractInfo {
type Error = NyxdError;
fn try_from(value: ProtoContractInfo) -> Result<Self, Self::Error> {
let ProtoContractInfo {
code_id,
creator,
admin,
label,
..
} = value;
let admin = if admin.is_empty() {
None
} else {
Some(
admin
.parse()
.map_err(|_| NyxdError::MalformedAccountAddress(admin))?,
)
};
Ok(ContractInfo {
code_id,
creator: creator
.parse()
.map_err(|_| NyxdError::MalformedAccountAddress(creator))?,
admin,
label,
})
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Contract { pub struct Contract {
pub address: AccountId, pub address: AccountId,
pub contract_info: ContractInfo, pub code_id: ContractCodeId,
/// Bech32 account address
pub creator: AccountId,
/// Bech32-encoded admin address
pub admin: Option<AccountId>,
pub label: String,
} }
impl Contract { impl Contract {
pub(crate) fn new(address: AccountId, contract_info: ContractInfo) -> Self { pub(crate) fn new(address: AccountId, contract_info: ContractInfo) -> Self {
Contract { Contract {
address, address,
contract_info, code_id: contract_info.code_id,
creator: contract_info.creator,
admin: contract_info.admin,
label: contract_info.label,
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum ContractCodeHistoryEntryOperation {
Init,
Genesis,
Migrate,
}
#[derive(Debug)]
pub struct ContractCodeHistoryEntry {
/// The source of this history entry
pub operation: ContractCodeHistoryEntryOperation,
pub code_id: ContractCodeId,
pub msg_json: String,
}
impl TryFrom<ProtoContractCodeHistoryEntry> for ContractCodeHistoryEntry {
type Error = NyxdError;
fn try_from(value: ProtoContractCodeHistoryEntry) -> Result<Self, Self::Error> {
let operation = match ContractCodeHistoryOperationType::from_i32(value.operation)
.ok_or(NyxdError::InvalidContractHistoryOperation)?
{
ContractCodeHistoryOperationType::Unspecified => {
return Err(NyxdError::InvalidContractHistoryOperation)
}
ContractCodeHistoryOperationType::Init => ContractCodeHistoryEntryOperation::Init,
ContractCodeHistoryOperationType::Genesis => ContractCodeHistoryEntryOperation::Genesis,
ContractCodeHistoryOperationType::Migrate => ContractCodeHistoryEntryOperation::Migrate,
};
Ok(ContractCodeHistoryEntry {
operation,
code_id: value.code_id,
msg_json: String::from_utf8(value.msg)
.map_err(|_| NyxdError::DeserializationError("Contract history msg".to_owned()))?,
})
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize)]
pub struct GasInfo {
/// GasWanted is the maximum units of work we allow this tx to perform.
pub gas_wanted: Gas,
/// GasUsed is the amount of gas actually consumed.
pub gas_used: Gas,
}
impl From<ProtoGasInfo> for GasInfo {
fn from(value: ProtoGasInfo) -> Self {
GasInfo {
gas_wanted: value.gas_wanted.into(),
gas_used: value.gas_used.into(),
}
}
}
impl GasInfo {
pub fn new(gas_wanted: Gas, gas_used: Gas) -> Self {
GasInfo {
gas_wanted,
gas_used,
} }
} }
} }
@@ -174,15 +535,31 @@ impl TryFrom<ProtoAbciResult> for AbciResult {
type Error = NyxdError; type Error = NyxdError;
fn try_from(value: ProtoAbciResult) -> Result<Self, Self::Error> { fn try_from(value: ProtoAbciResult) -> Result<Self, Self::Error> {
let events = value let mut events = Vec::with_capacity(value.events.len());
.events
.into_iter() for proto_event in value.events.into_iter() {
.map(TryInto::try_into) let type_str = proto_event.r#type;
.collect::<Result<Vec<_>, _>>()?;
let mut attributes = Vec::with_capacity(proto_event.attributes.len());
for proto_attribute in proto_event.attributes.into_iter() {
let stringified_ked = String::from_utf8(proto_attribute.key)
.map_err(|_| NyxdError::DeserializationError("EventAttributeKey".to_owned()))?;
let stringified_value = String::from_utf8(proto_attribute.value)
.map_err(|_| NyxdError::DeserializationError("EventAttributeKey".to_owned()))?;
attributes.push(abci::tag::Tag {
key: stringified_ked.parse().unwrap(),
value: stringified_value.parse().unwrap(),
})
}
events.push(abci::Event {
type_str,
attributes,
})
}
#[allow(deprecated)]
Ok(AbciResult { Ok(AbciResult {
// TODO: make sure this actually works since technically we're converting from 0.37 protobuf definition as opposed to 0.34...
data: value.data, data: value.data,
log: value.log, log: value.log,
events, events,
@@ -201,10 +578,7 @@ impl TryFrom<ProtoSimulateResponse> for SimulateResponse {
fn try_from(value: ProtoSimulateResponse) -> Result<Self, Self::Error> { fn try_from(value: ProtoSimulateResponse) -> Result<Self, Self::Error> {
Ok(SimulateResponse { Ok(SimulateResponse {
gas_info: value gas_info: value.gas_info.map(|gas_info| gas_info.into()),
.gas_info
.map(|gas_info| gas_info.try_into())
.transpose()?,
result: value.result.map(|result| result.try_into()).transpose()?, result: value.result.map(|result| result.try_into()).transpose()?,
}) })
} }
@@ -234,7 +608,7 @@ pub struct UploadResult {
pub logs: Vec<Log>, pub logs: Vec<Log>,
/// Transaction hash (might be used as transaction ID) /// Transaction hash (might be used as transaction ID)
pub transaction_hash: Hash, pub transaction_hash: tx::Hash,
pub gas_info: GasInfo, pub gas_info: GasInfo,
} }
@@ -271,7 +645,7 @@ pub struct InstantiateResult {
pub logs: Vec<Log>, pub logs: Vec<Log>,
/// Transaction hash (might be used as transaction ID) /// Transaction hash (might be used as transaction ID)
pub transaction_hash: Hash, pub transaction_hash: tx::Hash,
pub gas_info: GasInfo, pub gas_info: GasInfo,
} }
@@ -281,7 +655,7 @@ pub struct ChangeAdminResult {
pub logs: Vec<Log>, pub logs: Vec<Log>,
/// Transaction hash (might be used as transaction ID) /// Transaction hash (might be used as transaction ID)
pub transaction_hash: Hash, pub transaction_hash: tx::Hash,
pub gas_info: GasInfo, pub gas_info: GasInfo,
} }
@@ -291,7 +665,7 @@ pub struct MigrateResult {
pub logs: Vec<Log>, pub logs: Vec<Log>,
/// Transaction hash (might be used as transaction ID) /// Transaction hash (might be used as transaction ID)
pub transaction_hash: Hash, pub transaction_hash: tx::Hash,
pub gas_info: GasInfo, pub gas_info: GasInfo,
} }
@@ -300,10 +674,10 @@ pub struct MigrateResult {
pub struct ExecuteResult { pub struct ExecuteResult {
pub logs: Vec<Log>, pub logs: Vec<Log>,
pub data: Vec<u8>, pub data: Data,
/// Transaction hash (might be used as transaction ID) /// Transaction hash (might be used as transaction ID)
pub transaction_hash: Hash, pub transaction_hash: tx::Hash,
pub gas_info: GasInfo, pub gas_info: GasInfo,
} }
@@ -2,21 +2,22 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
use crate::nyxd::cosmwasm_client::types::ContractCodeId; use crate::nyxd::cosmwasm_client::types::ContractCodeId;
use crate::signing::direct_wallet::DirectSecp256k1HdWalletError;
use cosmrs::tendermint::Hash;
use cosmrs::{ use cosmrs::{
tendermint::{abci::Code as AbciCode, block}, rpc::endpoint::abci_query::AbciQuery,
AccountId, tendermint::{
abci::{self, Code as AbciCode},
block,
},
tx, AccountId,
}; };
use std::{io, time::Duration};
use tendermint_rpc::endpoint::abci_query::AbciQuery;
use thiserror::Error; use thiserror::Error;
pub use cosmrs::tendermint::error::Error as TendermintError; use crate::signing::direct_wallet::DirectSecp256k1HdWalletError;
pub use tendermint_rpc::{ pub use cosmrs::rpc::{
error::{Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail}, error::{Error as TendermintRpcError, ErrorDetail as TendermintRpcErrorDetail},
response_error::{Code, ResponseError}, response_error::{Code, ResponseError},
}; };
use std::{io, time::Duration};
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum NyxdError { pub enum NyxdError {
@@ -26,12 +27,9 @@ pub enum NyxdError {
#[error(transparent)] #[error(transparent)]
WalletError(#[from] DirectSecp256k1HdWalletError), WalletError(#[from] DirectSecp256k1HdWalletError),
#[error("There was an issue on the cosmrs side: {0}")] #[error("There was an issue on the cosmrs side - {0}")]
CosmrsError(#[from] cosmrs::Error), CosmrsError(#[from] cosmrs::Error),
#[error("There was an issue on the cosmrs side: {0}")]
CosmrsErrorReport(#[from] cosmrs::ErrorReport),
#[error("Failed to derive account address")] #[error("Failed to derive account address")]
AccountDerivationError, AccountDerivationError,
@@ -45,10 +43,7 @@ pub enum NyxdError {
InvalidTxHash(String), InvalidTxHash(String),
#[error("Tendermint RPC request failed - {0}")] #[error("Tendermint RPC request failed - {0}")]
TendermintErrorRpc(#[from] TendermintRpcError), TendermintError(#[from] TendermintRpcError),
#[error("tendermint library failure: {0}")]
TendermintError(#[from] TendermintError),
#[error("Failed when attempting to serialize data ({0})")] #[error("Failed when attempting to serialize data ({0})")]
SerializationError(String), SerializationError(String),
@@ -96,7 +91,7 @@ pub enum NyxdError {
"Error when broadcasting tx {hash} at height {height:?}. Error occurred during CheckTx phase. Code: {code}; Raw log: {raw_log}" "Error when broadcasting tx {hash} at height {height:?}. Error occurred during CheckTx phase. Code: {code}; Raw log: {raw_log}"
)] )]
BroadcastTxErrorCheckTx { BroadcastTxErrorCheckTx {
hash: Hash, hash: tx::Hash,
height: Option<block::Height>, height: Option<block::Height>,
code: u32, code: u32,
raw_log: String, raw_log: String,
@@ -106,7 +101,7 @@ pub enum NyxdError {
"Error when broadcasting tx {hash} at height {height:?}. Error occurred during DeliverTx phase. Code: {code}; Raw log: {raw_log}" "Error when broadcasting tx {hash} at height {height:?}. Error occurred during DeliverTx phase. Code: {code}; Raw log: {raw_log}"
)] )]
BroadcastTxErrorDeliverTx { BroadcastTxErrorDeliverTx {
hash: Hash, hash: tx::Hash,
height: Option<block::Height>, height: Option<block::Height>,
code: u32, code: u32,
raw_log: String, raw_log: String,
@@ -121,7 +116,7 @@ pub enum NyxdError {
#[error("Abci query failed with code {code} - {log}")] #[error("Abci query failed with code {code} - {log}")]
AbciError { AbciError {
code: u32, code: u32,
log: String, log: abci::Log,
pretty_log: Option<String>, pretty_log: Option<String>,
}, },
@@ -135,7 +130,7 @@ pub enum NyxdError {
NoBaseAccountInformationAvailable, NoBaseAccountInformationAvailable,
#[error("Transaction with ID {hash} has been submitted but not yet found on the chain. You might want to check for it later. There was a total wait of {} seconds", .timeout.as_secs())] #[error("Transaction with ID {hash} has been submitted but not yet found on the chain. You might want to check for it later. There was a total wait of {} seconds", .timeout.as_secs())]
BroadcastTimeout { hash: Hash, timeout: Duration }, BroadcastTimeout { hash: tx::Hash, timeout: Duration },
#[error("Cosmwasm std error: {0}")] #[error("Cosmwasm std error: {0}")]
CosmwasmStdError(#[from] cosmwasm_std::StdError), CosmwasmStdError(#[from] cosmwasm_std::StdError),
@@ -153,7 +148,7 @@ pub fn parse_abci_query_result(query_result: AbciQuery) -> Result<AbciQuery, Nyx
match query_result.code { match query_result.code {
AbciCode::Ok => Ok(query_result), AbciCode::Ok => Ok(query_result),
AbciCode::Err(code) => Err(NyxdError::AbciError { AbciCode::Err(code) => Err(NyxdError::AbciError {
code: code.into(), code,
log: query_result.log.clone(), log: query_result.log.clone(),
pretty_log: try_parse_abci_log(&query_result.log), pretty_log: try_parse_abci_log(&query_result.log),
}), }),
@@ -162,8 +157,11 @@ pub fn parse_abci_query_result(query_result: AbciQuery) -> Result<AbciQuery, Nyx
// Some of the error strings returned by the query are a bit too technical to present to the // Some of the error strings returned by the query are a bit too technical to present to the
// enduser. So we special case some commonly encountered errors. // enduser. So we special case some commonly encountered errors.
fn try_parse_abci_log(log: &str) -> Option<String> { fn try_parse_abci_log(log: &abci::Log) -> Option<String> {
if log.contains("Maximum amount of locked coins has already been pledged") { if log
.value()
.contains("Maximum amount of locked coins has already been pledged")
{
Some("Maximum amount of locked tokens has already been used. You can only use up to 10% of your locked tokens for bonding and delegating.".to_string()) Some("Maximum amount of locked tokens has already been used. You can only use up to 10% of your locked tokens for bonding and delegating.".to_string())
} else { } else {
None None
@@ -173,7 +171,7 @@ fn try_parse_abci_log(log: &str) -> Option<String> {
impl NyxdError { impl NyxdError {
pub fn is_tendermint_response_timeout(&self) -> bool { pub fn is_tendermint_response_timeout(&self) -> bool {
match &self { match &self {
NyxdError::TendermintErrorRpc(TendermintRpcError( NyxdError::TendermintError(TendermintRpcError(
TendermintRpcErrorDetail::Response(err), TendermintRpcErrorDetail::Response(err),
_, _,
)) => { )) => {
@@ -200,7 +198,7 @@ impl NyxdError {
pub fn is_tendermint_response_duplicate(&self) -> bool { pub fn is_tendermint_response_duplicate(&self) -> bool {
match &self { match &self {
NyxdError::TendermintErrorRpc(TendermintRpcError( NyxdError::TendermintError(TendermintRpcError(
TendermintRpcErrorDetail::Response(err), TendermintRpcErrorDetail::Response(err),
_, _,
)) => { )) => {
@@ -221,8 +219,4 @@ impl NyxdError {
_ => false, _ => false,
} }
} }
pub fn unavailable_contract_address<S: Into<String>>(contract_type: S) -> Self {
NyxdError::NoContractAddressAvailable(contract_type.into())
}
} }

Some files were not shown because too many files have changed in this diff Show More