Compare commits
311 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e20ed854df | |||
| c6509c3a95 | |||
| 8a9d242d03 | |||
| 769a26fdeb | |||
| 2e7ddcb195 | |||
| 84d893198b | |||
| 2f6617daac | |||
| 9e483da802 | |||
| d530e492ec | |||
| 6c67cff235 | |||
| 66f04d17b7 | |||
| 492155d04a | |||
| 069226125b | |||
| 1ce536c2fa | |||
| 2fbb901deb | |||
| 3c3ec9c831 | |||
| 6a34a99fee | |||
| ec75a06c9d | |||
| 2fbe7bb350 | |||
| ee67f3d0f0 | |||
| 188650ea05 | |||
| decae2b54d | |||
| 8104761914 | |||
| 8503def37f | |||
| 48023eab41 | |||
| b050ae72de | |||
| 377c9be790 | |||
| 5d7fd66cfc | |||
| 5128aef193 | |||
| 6a327b0bd6 | |||
| 8a38c61065 | |||
| f0a888d59c | |||
| f1b9cf4d68 | |||
| 392e7b5268 | |||
| 776a9d508a | |||
| 2f624d4f10 | |||
| 3c9faff4ec | |||
| 026d52a218 | |||
| f8b70097d3 | |||
| 0d3df4b58d | |||
| 5241047f45 | |||
| 2651784e8b | |||
| 2cd2b1ccd4 | |||
| af4e8241e7 | |||
| 92350daca8 | |||
| b429c64168 | |||
| f88622ac08 | |||
| 0a5a2c6747 | |||
| 824bd636f9 | |||
| cf6411ac08 | |||
| 6428133122 | |||
| 3c69f9c2f9 | |||
| c1e4b87744 | |||
| 22246d0d5d | |||
| 29593ef3c9 | |||
| 12f21185e3 | |||
| 85ab3d5c80 | |||
| dffe171b7f | |||
| 6d3b198f00 | |||
| d4920b82f0 | |||
| 24099d159b | |||
| 463033aa64 | |||
| c1ba56ab1c | |||
| 8dfe8f4678 | |||
| c2a38c9b53 | |||
| 33e7ce53fb | |||
| bdf18e30fb | |||
| e0b32e1cc9 | |||
| 4ff69b77a9 | |||
| 5f095d703d | |||
| ff4dbd09d0 | |||
| 404d2d9857 | |||
| b5f2ecd79e | |||
| 38b9e14851 | |||
| 6ead19bd97 | |||
| c169971287 | |||
| 7b3cbdcd44 | |||
| 43dfb31eb2 | |||
| ff4350ec90 | |||
| c8516071fc | |||
| ee5b55fab6 | |||
| 11f3ce9224 | |||
| 0f43521497 | |||
| a1a1970dc1 | |||
| 89ea3039d5 | |||
| efa1e0353e | |||
| 96e2712039 | |||
| 84ad777c42 | |||
| f569ba1c17 | |||
| 54ff3ee987 | |||
| 1f2a2c37ab | |||
| b0d5bacafd | |||
| 8b65e44767 | |||
| a2445183bf | |||
| 230d8aa170 | |||
| 035535058e | |||
| df9aec1554 | |||
| 740846befc | |||
| 074e27291b | |||
| b1c3264f69 | |||
| 905318ad27 | |||
| 7065f870eb | |||
| ee14dfc75e | |||
| a93de8b4c5 | |||
| 534c8f49d1 | |||
| 3da3f66ea3 | |||
| 32bfceacb5 | |||
| 7a606e11f4 | |||
| d3d5346cfe | |||
| 8fe549daa5 | |||
| f15be9456a | |||
| 567d3c8173 | |||
| ce81f0c841 | |||
| 9652060c57 | |||
| 9c0317ab28 | |||
| d13116bda0 | |||
| 77f8eadd0e | |||
| 1e2de5067a | |||
| c43344d489 | |||
| 2711e6f679 | |||
| c85f7d2565 | |||
| e8506f6d53 | |||
| 6d5ff1146b | |||
| 1f226682f4 | |||
| f17d2cec0d | |||
| 089d5e6dbb | |||
| fca384fba2 | |||
| 3ae803a343 | |||
| 99224310e9 | |||
| d2e59f12f6 | |||
| 99a321c54d | |||
| 8fd832e217 | |||
| 09c4df8448 | |||
| 09b15ae020 | |||
| 585cf55026 | |||
| a0418daaea | |||
| f0c0ddb1c2 | |||
| b006e1820f | |||
| 50b755cfde | |||
| a002ddda8d | |||
| 49d909815d | |||
| 990f7d64dd | |||
| d422741be4 | |||
| 389ccffb63 | |||
| 9a76db479b | |||
| 55cd1a8b91 | |||
| cbee6153bd | |||
| 232032baf4 | |||
| 17e1c5f970 | |||
| 85ab634d9c | |||
| 3d245f745f | |||
| c8cee9b56e | |||
| aa64256ecd | |||
| 853d537e51 | |||
| 2993e85c7a | |||
| a8041eec7b | |||
| e849dc13fd | |||
| d13c8bde57 | |||
| aae4725005 | |||
| 42d08195f7 | |||
| 3409e86a63 | |||
| a255c6733b | |||
| c3a09a0860 | |||
| 4652ac882f | |||
| 1740cead37 | |||
| cc6fdfa110 | |||
| 9e40763f7f | |||
| 6bcc781a67 | |||
| 15bc4ae272 | |||
| 3c8dd7a72b | |||
| 8ef9560843 | |||
| c41e377c69 | |||
| dbe2790ba0 | |||
| 93214cabbb | |||
| ce47e91186 | |||
| a4f415cb45 | |||
| e911e9e7ee | |||
| dd887bd89b | |||
| e2bf9daf52 | |||
| aa7a6114be | |||
| a2a887627d | |||
| 2b283090fa | |||
| c097567f78 | |||
| 93ec0f1984 | |||
| edf5050ba6 | |||
| 3317804a80 | |||
| 0951c83ace | |||
| 9754cd8a06 | |||
| 6d3570913b | |||
| 8ef84df168 | |||
| 28f4ddb698 | |||
| c52cb4f963 | |||
| 59a7034121 | |||
| 9ec1d2dc36 | |||
| 022951ed0c | |||
| 1538eb0f6f | |||
| 82ea3add6a | |||
| 53b653d9cf | |||
| a97004b6e0 | |||
| 61aacd16cd | |||
| 55bf5ce5b0 | |||
| e9789681cc | |||
| d33d9d4409 | |||
| 852e1ace7e | |||
| cc3cfbfdc1 | |||
| 55f3fc278f | |||
| 9478472070 | |||
| 950919b36c | |||
| 5ee9c24b92 | |||
| de67055e4f | |||
| 52e27152f6 | |||
| 0c24f39300 | |||
| 204dba1a66 | |||
| 461a7a2df9 | |||
| f12f6b29cd | |||
| 5385685b3f | |||
| 691ac6d5ea | |||
| ed3535d510 | |||
| c6519ffdff | |||
| 69e4b1ae1c | |||
| d0ba4056d6 | |||
| c81a5ac002 | |||
| 93a8e2a9bf | |||
| 6c83b88247 | |||
| 83d5cc9bf4 | |||
| 6a54a6653c | |||
| d11cf4c9a3 | |||
| f9108ea400 | |||
| 9a5ce73dfd | |||
| af51ab3f71 | |||
| 3714ee76ff | |||
| 0e37c572f0 | |||
| b1b276e8ba | |||
| 9966d0fa5f | |||
| c5c6d3cc27 | |||
| 0f777204b8 | |||
| cbda950259 | |||
| d57e8efff2 | |||
| c4c4355a54 | |||
| aaef36a5d7 | |||
| 062f4911e1 | |||
| 59e4567342 | |||
| de2b106568 | |||
| ae79dc4ec6 | |||
| d49ba6e025 | |||
| 60ab58998d | |||
| 75bb9140b0 | |||
| b63f5409db | |||
| de6c1ba1f0 | |||
| e705b76b36 | |||
| 05259410b2 | |||
| 6be7c094be | |||
| 2db3297d1e | |||
| a1295a316b | |||
| fc7e4be175 | |||
| 3dd3692e8d | |||
| 4bb6f21faa | |||
| 62c03b3136 | |||
| e9d10caaf8 | |||
| 432f6a006f | |||
| 4f05169361 | |||
| be88a9b277 | |||
| fce2c704af | |||
| 23a669c4de | |||
| 88d2592a4e | |||
| 85c22525cf | |||
| 7e27413a12 | |||
| 2eeb8bb590 | |||
| 2565716816 | |||
| f954ce5c97 | |||
| baebbe4e4d | |||
| 2dd3e031bd | |||
| 860ec74b59 | |||
| bff806e5f5 | |||
| f8b9bb7e78 | |||
| 913095ee60 | |||
| d44b88842e | |||
| a67092ad17 | |||
| 0b29e28e3f | |||
| f26f159bb0 | |||
| a8ddaddaf4 | |||
| 84741f2020 | |||
| cc65236085 | |||
| e18c541400 | |||
| c1e8ebd46a | |||
| 0b6eb17793 | |||
| 10696ae4dc | |||
| 6395303466 | |||
| 60264486fd | |||
| 502a2d6a23 | |||
| 974cee1f22 | |||
| 7ac3bb10cb | |||
| 20d51437b5 | |||
| 1965df05e7 | |||
| 615c65cb22 | |||
| 432af5a204 | |||
| ce87df3026 | |||
| db407847e3 | |||
| 02f5fa6816 | |||
| 3aa9ad0197 | |||
| 7ccadffdba | |||
| c120196be0 | |||
| f2a395fe42 | |||
| cf79bdc098 | |||
| 34e2affef8 | |||
| cf71d655e2 | |||
| 141b9d396c | |||
| ff65d052b8 | |||
| 153e899e0e | |||
| 7e5c79848f | |||
| 92b4dbb250 |
@@ -57,7 +57,7 @@ jobs:
|
|||||||
echo $OUTPUT_DIR
|
echo $OUTPUT_DIR
|
||||||
|
|
||||||
- name: Install Dependencies (Linux)
|
- name: Install Dependencies (Linux)
|
||||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
|
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Install Rust stable
|
- name: Install Rust stable
|
||||||
@@ -98,7 +98,6 @@ jobs:
|
|||||||
cp target/release/nym-network-requester $OUTPUT_DIR
|
cp target/release/nym-network-requester $OUTPUT_DIR
|
||||||
cp target/release/nym-network-statistics $OUTPUT_DIR
|
cp target/release/nym-network-statistics $OUTPUT_DIR
|
||||||
cp target/release/nym-cli $OUTPUT_DIR
|
cp target/release/nym-cli $OUTPUT_DIR
|
||||||
cp target/release/nym-credential-client $OUTPUT_DIR
|
|
||||||
cp target/release/explorer-api $OUTPUT_DIR
|
cp target/release/explorer-api $OUTPUT_DIR
|
||||||
|
|
||||||
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
|
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
|
||||||
@@ -109,6 +108,7 @@ jobs:
|
|||||||
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
|
cp contracts/target/wasm32-unknown-unknown/release/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
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ on:
|
|||||||
- 'clients/**'
|
- 'clients/**'
|
||||||
- 'common/**'
|
- 'common/**'
|
||||||
- 'explorer-api/**'
|
- 'explorer-api/**'
|
||||||
|
- 'ephemera/**'
|
||||||
- 'gateway/**'
|
- 'gateway/**'
|
||||||
- 'integrations/**'
|
- 'integrations/**'
|
||||||
- 'mixnode/**'
|
- 'mixnode/**'
|
||||||
@@ -15,6 +16,7 @@ on:
|
|||||||
- 'nym-api/**'
|
- 'nym-api/**'
|
||||||
- 'nym-outfox/**'
|
- 'nym-outfox/**'
|
||||||
- 'tools/nym-cli/**'
|
- 'tools/nym-cli/**'
|
||||||
|
- 'tools/nym-nr-query/**'
|
||||||
- 'tools/ts-rs-cli/**'
|
- 'tools/ts-rs-cli/**'
|
||||||
- 'Cargo.toml'
|
- 'Cargo.toml'
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -22,6 +24,7 @@ on:
|
|||||||
- 'clients/**'
|
- 'clients/**'
|
||||||
- 'common/**'
|
- 'common/**'
|
||||||
- 'explorer-api/**'
|
- 'explorer-api/**'
|
||||||
|
- 'ephemera/**'
|
||||||
- 'gateway/**'
|
- 'gateway/**'
|
||||||
- 'integrations/**'
|
- 'integrations/**'
|
||||||
- 'mixnode/**'
|
- 'mixnode/**'
|
||||||
@@ -31,6 +34,7 @@ on:
|
|||||||
- 'nym-api/**'
|
- 'nym-api/**'
|
||||||
- 'nym-outfox/**'
|
- 'nym-outfox/**'
|
||||||
- 'tools/nym-cli/**'
|
- 'tools/nym-cli/**'
|
||||||
|
- 'tools/nym-nr-query/**'
|
||||||
- 'tools/ts-rs-cli/**'
|
- 'tools/ts-rs-cli/**'
|
||||||
- 'Cargo.toml'
|
- 'Cargo.toml'
|
||||||
|
|
||||||
@@ -42,7 +46,7 @@ jobs:
|
|||||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||||
steps:
|
steps:
|
||||||
- name: Install Dependencies (Linux)
|
- name: Install Dependencies (Linux)
|
||||||
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
|
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
name: CD dev-portal
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: master
|
|
||||||
paths:
|
|
||||||
- 'documentation/dev-portal/**'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: custom-runner-linux
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install rsync
|
|
||||||
run: sudo apt-get install rsync
|
|
||||||
- uses: rlespinasse/github-slug-action@v3.x
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 18
|
|
||||||
- name: Install Rust stable
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
- name: Install mdbook
|
|
||||||
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook)
|
|
||||||
- name: Install mdbook plugins
|
|
||||||
run: |
|
|
||||||
cargo install --vers "^0.2.0" mdbook-variables && cargo install \
|
|
||||||
--vers "^1.8.0" mdbook-admonish && cargo install --vers \
|
|
||||||
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" \
|
|
||||||
mdbook-theme && cargo install --vers "^0.7.7" mdbook-linkcheck
|
|
||||||
- name: Clean website
|
|
||||||
run: cd documentation/dev-portal && mdbook clean
|
|
||||||
- name: Build website
|
|
||||||
run: cd documentation/dev-portal && mdbook build
|
|
||||||
- name: Deploy branch master to dev
|
|
||||||
continue-on-error: true
|
|
||||||
uses: easingthemes/ssh-deploy@main
|
|
||||||
env:
|
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }}
|
|
||||||
ARGS: "-rltgoDzvO --delete"
|
|
||||||
SOURCE: "documentation/dev-portal/book/html/"
|
|
||||||
REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_DEV }}
|
|
||||||
REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }}
|
|
||||||
TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET_DEVP }}/
|
|
||||||
EXCLUDE: "/dist/, /node_modules/"
|
|
||||||
- name: Deploy branch master to prod
|
|
||||||
uses: easingthemes/ssh-deploy@main
|
|
||||||
env:
|
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.CD_WWW_SSH_PRIVATE_KEY }}
|
|
||||||
ARGS: "-rltgoDzvO --delete"
|
|
||||||
SOURCE: "documentation/dev-portal/book/html/"
|
|
||||||
REMOTE_HOST: ${{ secrets.CD_WWW_REMOTE_HOST_PROD }}
|
|
||||||
REMOTE_USER: ${{ secrets.CD_WWW_REMOTE_USER }}
|
|
||||||
TARGET: ${{ secrets.CD_WWW_REMOTE_TARGET_DEVP }}/
|
|
||||||
EXCLUDE: "/dist/, /node_modules/"
|
|
||||||
- name: Matrix - Node Install
|
|
||||||
run: npm install
|
|
||||||
working-directory: .github/workflows/support-files
|
|
||||||
- name: Matrix - Send Notification
|
|
||||||
env:
|
|
||||||
NYM_NOTIFICATION_KIND: cd-dev
|
|
||||||
NYM_PROJECT_NAME: "Dev portal CD"
|
|
||||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CD_WWW_BASE }}"
|
|
||||||
NYM_CI_WWW_LOCATION: "${{ env.GITHUB_REF_SLUG }}"
|
|
||||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
|
||||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
|
||||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
|
||||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_DEVP }}"
|
|
||||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
|
||||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
|
||||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
|
||||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
|
||||||
uses: docker://keybaseio/client:stable-node
|
|
||||||
with:
|
|
||||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
name: CD docs
|
name: CD docs
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches: master
|
branches: master
|
||||||
paths:
|
paths:
|
||||||
@@ -27,39 +28,38 @@ 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" mdbook)
|
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook)
|
||||||
- name: Install mdbook plugins
|
- name: Install mdbook plugins
|
||||||
run: |
|
run: |
|
||||||
cargo install --vers "^0.2.0" mdbook-variables && cargo install \
|
cargo install --vers "=0.2.2" mdbook-variables && cargo install \
|
||||||
--vers "^1.8.0" mdbook-admonish && cargo install --vers \
|
--vers "^1.8.0" mdbook-admonish && cargo install --vers \
|
||||||
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" \
|
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \
|
||||||
mdbook-theme && cargo install --vers "^0.7.7" mdbook-linkcheck && \
|
&& cargo install --vers "^0.7.7" mdbook-linkcheck
|
||||||
cargo install --vers "^0.5.0" mdbook-cmdrun
|
- name: Build all projects in documentation/ & move to ~/dist/docs/
|
||||||
- name: Clean website
|
run: cd documentation && ./build_all_to_dist.sh
|
||||||
run: cd documentation/docs && mdbook clean
|
continue-on-error: false
|
||||||
- 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: "documentation/docs/book/"
|
SOURCE: "dist/docs/"
|
||||||
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: "/dist/, /node_modules/"
|
EXCLUDE: "/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: "documentation/docs/book/"
|
SOURCE: "dist/docs/"
|
||||||
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: "/dist/, /node_modules/"
|
EXCLUDE: "/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
|
||||||
|
|||||||
@@ -13,13 +13,19 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
check-schema:
|
check-schema:
|
||||||
name: Generate and check schema
|
name: Generate and check schema
|
||||||
runs-on: ubuntu-latest
|
runs-on: custom-runner-linux
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
|
||||||
|
|
||||||
- name: Generate the schema
|
- name: Generate the schema
|
||||||
run: make contract-schema
|
run: make contract-schema
|
||||||
|
|
||||||
- name: Check for diff
|
- name: Check for diff
|
||||||
run: git diff --exit-code
|
run: git diff --exit-code -- contracts/*/schema
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
name: CI dev-portal
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches-ignore: master
|
|
||||||
paths:
|
|
||||||
- 'documentation/dev-portal/**'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: custom-runner-linux
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install rsync
|
|
||||||
run: sudo apt-get install rsync
|
|
||||||
- uses: rlespinasse/github-slug-action@v3.x
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
|
||||||
node-version: 18
|
|
||||||
- name: Install Rust stable
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
- name: Install mdbook
|
|
||||||
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4" mdbook)
|
|
||||||
- name: Install mdbook plugins
|
|
||||||
run: |
|
|
||||||
cargo install --vers "^0.2.0" mdbook-variables && cargo install \
|
|
||||||
--vers "^1.8.0" mdbook-admonish && cargo install --vers \
|
|
||||||
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \
|
|
||||||
&& cargo install --vers "^0.7.7" mdbook-linkcheck
|
|
||||||
- name: Clean website
|
|
||||||
run: cd documentation/dev-portal && mdbook clean
|
|
||||||
- name: Build website
|
|
||||||
run: cd documentation/dev-portal && mdbook build
|
|
||||||
- name: Deploy branch to CI www
|
|
||||||
continue-on-error: true
|
|
||||||
uses: easingthemes/ssh-deploy@main
|
|
||||||
env:
|
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
|
|
||||||
ARGS: "-rltgoDzvO --delete"
|
|
||||||
SOURCE: "documentation/dev-portal/book/html/"
|
|
||||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
|
||||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
|
||||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/dev-portal-${{ env.GITHUB_REF_SLUG }}
|
|
||||||
EXCLUDE: "/dist/, /node_modules/"
|
|
||||||
- name: Matrix - Node Install
|
|
||||||
run: npm install
|
|
||||||
working-directory: .github/workflows/support-files
|
|
||||||
- name: Matrix - Send Notification
|
|
||||||
env:
|
|
||||||
NYM_NOTIFICATION_KIND: ci-dev
|
|
||||||
NYM_PROJECT_NAME: "Dev portal CI"
|
|
||||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
|
||||||
NYM_CI_WWW_LOCATION: "dev-portal-${{ env.GITHUB_REF_SLUG }}"
|
|
||||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
|
||||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
|
||||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
|
||||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM_DEVP }}"
|
|
||||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
|
||||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
|
||||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
|
||||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
|
||||||
uses: docker://keybaseio/client:stable-node
|
|
||||||
with:
|
|
||||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
name: CI docs
|
name: CI docs
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches-ignore: master
|
branches-ignore: master
|
||||||
paths:
|
paths:
|
||||||
@@ -27,29 +28,27 @@ 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" mdbook)
|
run: (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.4.33" mdbook)
|
||||||
- name: Install mdbook plugins
|
- name: Install mdbook plugins
|
||||||
run: |
|
run: |
|
||||||
cargo install --vers "^0.2.0" mdbook-variables && cargo install \
|
cargo install --vers "=0.2.2" mdbook-variables && cargo install \
|
||||||
--vers "^1.8.0" mdbook-admonish && cargo install --vers \
|
--vers "^1.8.0" mdbook-admonish && cargo install --vers \
|
||||||
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" \
|
"^0.1.2" mdbook-last-changed && cargo install --vers "^0.1.2" mdbook-theme \
|
||||||
mdbook-theme && cargo install --vers "^0.7.7" mdbook-linkcheck && \
|
&& cargo install --vers "^0.7.7" mdbook-linkcheck
|
||||||
cargo install --vers "^0.5.0" mdbook-cmdrun
|
- name: Build all projects in documentation/ & move to ~/dist/docs/
|
||||||
- name: Clean website
|
run: cd documentation && ./build_all_to_dist.sh
|
||||||
run: cd documentation/docs && mdbook clean
|
continue-on-error: false
|
||||||
- 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: "documentation/docs/book/"
|
SOURCE: "dist/docs/"
|
||||||
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: "/dist/, /node_modules/"
|
EXCLUDE: "/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,72 +0,0 @@
|
|||||||
name: Nym Connect - mobile (Rust)
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- "nym-connect/mobile/src-tauri/**"
|
|
||||||
- "nym-connect/mobile/src-tauri/Cargo.toml"
|
|
||||||
- "!nym-connect/mobile/src-tauri/gen/**"
|
|
||||||
- "clients/client-core/**"
|
|
||||||
- "clients/socks5/**"
|
|
||||||
- "common/**"
|
|
||||||
- "gateway/gateway-requests/**"
|
|
||||||
- "contracts/vesting/**"
|
|
||||||
- "nym-api/nym-api-requests/**"
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "nym-connect/mobile/src-tauri/**"
|
|
||||||
- "nym-connect/mobile/src-tauri/Cargo.toml"
|
|
||||||
- "!nym-connect/mobile/src-tauri/gen/**"
|
|
||||||
- "clients/client-core/**"
|
|
||||||
- "clients/socks5/**"
|
|
||||||
- "common/**"
|
|
||||||
- "gateway/gateway-requests/**"
|
|
||||||
- "contracts/vesting/**"
|
|
||||||
- "nym-api/nym-api-requests/**"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
#runs-on: [self-hosted, custom-linux]
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
#env:
|
|
||||||
#RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
|
||||||
#defaults:
|
|
||||||
#run:
|
|
||||||
#working-directory: nym-connect/mobile/src-tauri/
|
|
||||||
steps:
|
|
||||||
- name: Install Dependencies (Linux)
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get -y install \
|
|
||||||
libwebkit2gtk-4.1-dev \
|
|
||||||
build-essential \
|
|
||||||
curl \
|
|
||||||
wget \
|
|
||||||
libssl-dev \
|
|
||||||
libgtk-3-dev \
|
|
||||||
squashfs-tools \
|
|
||||||
libayatana-appindicator3-dev \
|
|
||||||
librsvg2-dev \
|
|
||||||
libsoup-3.0-dev \
|
|
||||||
libjavascriptcoregtk-4.1-dev
|
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
uses: dtolnay/rust-toolchain@master
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
components: clippy, rustfmt
|
|
||||||
|
|
||||||
- name: Check formatting
|
|
||||||
run: cargo fmt --manifest-path nym-connect/mobile/src-tauri/Cargo.toml -- --check
|
|
||||||
|
|
||||||
- name: Build all binaries
|
|
||||||
run: cargo build --manifest-path nym-connect/mobile/src-tauri/Cargo.toml
|
|
||||||
|
|
||||||
- name: Run all tests
|
|
||||||
run: cargo test --manifest-path nym-connect/mobile/src-tauri/Cargo.toml
|
|
||||||
|
|
||||||
- name: Clippy
|
|
||||||
run: cargo clippy --manifest-path nym-connect/mobile/src-tauri/Cargo.toml --all-targets -- -D warnings
|
|
||||||
@@ -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: stable
|
toolchain: 1.69.0
|
||||||
target: wasm32-unknown-unknown
|
target: wasm32-unknown-unknown
|
||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
|
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Install Dependencies (Linux)
|
- name: Install Dependencies (Linux)
|
||||||
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
|
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
if: matrix.os == 'ubuntu-20.04'
|
if: matrix.os == 'ubuntu-20.04'
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
|
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Install Dependencies (Linux)
|
- name: Install Dependencies (Linux)
|
||||||
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools
|
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools protobuf-compiler
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
if: matrix.os == 'ubuntu-20.04'
|
if: matrix.os == 'ubuntu-20.04'
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
release_id: ${{ steps.create-release.outputs.id }}
|
release_id: ${{ steps.create-release.outputs.id }}
|
||||||
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
|
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
|
||||||
version: ${{ steps.release-info.outputs.version }}
|
version: ${{ steps.release-info.outputs.version }}
|
||||||
filename: ${{ steps.release-info.outputs.filename }}
|
filename: ${{ steps.release-info.outputs.filename }}
|
||||||
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
release_id: ${{ steps.create-release.outputs.id }}
|
release_id: ${{ steps.create-release.outputs.id }}
|
||||||
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
|
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
|
||||||
version: ${{ steps.release-info.outputs.version }}
|
version: ${{ steps.release-info.outputs.version }}
|
||||||
filename: ${{ steps.release-info.outputs.filename }}
|
filename: ${{ steps.release-info.outputs.filename }}
|
||||||
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
release_id: ${{ steps.create-release.outputs.id }}
|
release_id: ${{ steps.create-release.outputs.id }}
|
||||||
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
|
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
|
||||||
version: ${{ steps.release-info.outputs.version }}
|
version: ${{ steps.release-info.outputs.version }}
|
||||||
filename: ${{ steps.release-info.outputs.filename }}
|
filename: ${{ steps.release-info.outputs.filename }}
|
||||||
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
||||||
@@ -100,6 +100,7 @@ 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}"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ jobs:
|
|||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
release_id: ${{ steps.create-release.outputs.id }}
|
release_id: ${{ steps.create-release.outputs.id }}
|
||||||
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
|
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
|
||||||
client_hash: ${{ steps.binary-hashes.outputs.client_hash }}
|
client_hash: ${{ steps.binary-hashes.outputs.client_hash }}
|
||||||
mixnode_hash: ${{ steps.binary-hashes.outputs.mixnode_hash }}
|
mixnode_hash: ${{ steps.binary-hashes.outputs.mixnode_hash }}
|
||||||
gateway_hash: ${{ steps.binary-hashes.outputs.gateway_hash }}
|
gateway_hash: ${{ steps.binary-hashes.outputs.gateway_hash }}
|
||||||
@@ -40,6 +40,7 @@ jobs:
|
|||||||
netreq_version: ${{ steps.binary-versions.outputs.netreq_version }}
|
netreq_version: ${{ steps.binary-versions.outputs.netreq_version }}
|
||||||
cli_version: ${{ steps.binary-versions.outputs.cli_version }}
|
cli_version: ${{ steps.binary-versions.outputs.cli_version }}
|
||||||
netstat_version: ${{ steps.binary-versions.outputs.netstat_version }}
|
netstat_version: ${{ steps.binary-versions.outputs.netstat_version }}
|
||||||
|
platform: ${{ steps.get-platform-version.outputs.platform }}"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -96,6 +97,11 @@ jobs:
|
|||||||
target/release/nym-network-statistics
|
target/release/nym-network-statistics
|
||||||
target/release/nym-cli
|
target/release/nym-cli
|
||||||
|
|
||||||
|
- id: echo-outputs
|
||||||
|
name: echo output for assets
|
||||||
|
run: |
|
||||||
|
echo "data from release: $ ${{ steps.create-release.outputs }}"
|
||||||
|
|
||||||
- id: release-info
|
- id: release-info
|
||||||
name: Prepare release info
|
name: Prepare release info
|
||||||
run: |
|
run: |
|
||||||
@@ -124,6 +130,12 @@ jobs:
|
|||||||
v=$(rg '^version = "(.*)"' -or '$1' tools/nym-cli/Cargo.toml) && echo "cli_version=$v" >> "$GITHUB_OUTPUT"
|
v=$(rg '^version = "(.*)"' -or '$1' tools/nym-cli/Cargo.toml) && echo "cli_version=$v" >> "$GITHUB_OUTPUT"
|
||||||
v=$(rg '^version = "(.*)"' -or '$1' service-providers/network-statistics/Cargo.toml) && echo "netstat_version=$v" >> "$GITHUB_OUTPUT"
|
v=$(rg '^version = "(.*)"' -or '$1' service-providers/network-statistics/Cargo.toml) && echo "netstat_version=$v" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- id: get-platform-version
|
||||||
|
name: get platform version
|
||||||
|
run: |
|
||||||
|
echo "::set-output name=platform::$(uname -r)"
|
||||||
|
|
||||||
|
|
||||||
push-release-data-client:
|
push-release-data-client:
|
||||||
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
if: ${{ (startsWith(github.ref, 'refs/tags/nym-binaries-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
|
||||||
uses: ./.github/workflows/push-release-data.yml
|
uses: ./.github/workflows/push-release-data.yml
|
||||||
@@ -139,6 +151,7 @@ jobs:
|
|||||||
file_hash: ${{ needs.publish-nym.outputs.client_hash }}
|
file_hash: ${{ needs.publish-nym.outputs.client_hash }}
|
||||||
name: Client
|
name: Client
|
||||||
category: binaries
|
category: binaries
|
||||||
|
platform: "${{ needs.publish-nym.outputs.platform }}"
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
push-release-data-mixnode:
|
push-release-data-mixnode:
|
||||||
@@ -156,6 +169,7 @@ jobs:
|
|||||||
file_hash: ${{ needs.publish-nym.outputs.mixnode_hash }}
|
file_hash: ${{ needs.publish-nym.outputs.mixnode_hash }}
|
||||||
name: Mixnode
|
name: Mixnode
|
||||||
category: binaries
|
category: binaries
|
||||||
|
platform: "${{ needs.publish-nym.outputs.platform }}"
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
push-release-data-gateway:
|
push-release-data-gateway:
|
||||||
@@ -173,6 +187,7 @@ jobs:
|
|||||||
file_hash: ${{ needs.publish-nym.outputs.gateway_hash }}
|
file_hash: ${{ needs.publish-nym.outputs.gateway_hash }}
|
||||||
name: Gateway
|
name: Gateway
|
||||||
category: binaries
|
category: binaries
|
||||||
|
platform: "${{ needs.publish-nym.outputs.platform }}"
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
push-release-data-socks5:
|
push-release-data-socks5:
|
||||||
@@ -190,6 +205,7 @@ jobs:
|
|||||||
file_hash: ${{ needs.publish-nym.outputs.socks5_hash }}
|
file_hash: ${{ needs.publish-nym.outputs.socks5_hash }}
|
||||||
name: Socks5 Client
|
name: Socks5 Client
|
||||||
category: binaries
|
category: binaries
|
||||||
|
platform: "${{ needs.publish-nym.outputs.platform }}"
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
push-release-data-network-requester:
|
push-release-data-network-requester:
|
||||||
@@ -207,6 +223,7 @@ jobs:
|
|||||||
file_hash: ${{ needs.publish-nym.outputs.netreq_hash }}
|
file_hash: ${{ needs.publish-nym.outputs.netreq_hash }}
|
||||||
name: Network Requester
|
name: Network Requester
|
||||||
category: binaries
|
category: binaries
|
||||||
|
platform: "${{ needs.publish-nym.outputs.platform }}"
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
push-release-data-cli:
|
push-release-data-cli:
|
||||||
@@ -224,6 +241,7 @@ jobs:
|
|||||||
file_hash: ${{ needs.publish-nym.outputs.cli_hash }}
|
file_hash: ${{ needs.publish-nym.outputs.cli_hash }}
|
||||||
name: Cli
|
name: Cli
|
||||||
category: binaries
|
category: binaries
|
||||||
|
platform: "${{ needs.publish-nym.outputs.platform }}"
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
||||||
push-release-data-network-stat:
|
push-release-data-network-stat:
|
||||||
@@ -241,4 +259,5 @@ jobs:
|
|||||||
file_hash: ${{ needs.publish-nym.outputs.netstat_hash }}
|
file_hash: ${{ needs.publish-nym.outputs.netstat_hash }}
|
||||||
name: Network Statistics
|
name: Network Statistics
|
||||||
category: binaries
|
category: binaries
|
||||||
|
platform: "${{ needs.publish-nym.outputs.platform }}"
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
release_id: ${{ steps.create-release.outputs.id }}
|
release_id: ${{ steps.create-release.outputs.id }}
|
||||||
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
|
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
|
||||||
version: ${{ steps.release-info.outputs.version }}
|
version: ${{ steps.release-info.outputs.version }}
|
||||||
filename: ${{ steps.release-info.outputs.filename }}
|
filename: ${{ steps.release-info.outputs.filename }}
|
||||||
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
release_id: ${{ steps.create-release.outputs.id }}
|
release_id: ${{ steps.create-release.outputs.id }}
|
||||||
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
|
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
|
||||||
version: ${{ steps.release-info.outputs.version }}
|
version: ${{ steps.release-info.outputs.version }}
|
||||||
filename: ${{ steps.release-info.outputs.filename }}
|
filename: ${{ steps.release-info.outputs.filename }}
|
||||||
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
release_id: ${{ steps.create-release.outputs.id }}
|
release_id: ${{ steps.create-release.outputs.id }}
|
||||||
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
|
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
|
||||||
version: ${{ steps.release-info.outputs.version }}
|
version: ${{ steps.release-info.outputs.version }}
|
||||||
filename: ${{ steps.release-info.outputs.filename }}
|
filename: ${{ steps.release-info.outputs.filename }}
|
||||||
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
file_hash: ${{ steps.release-info.outputs.file_hash }}
|
||||||
|
|||||||
@@ -32,5 +32,5 @@ jobs:
|
|||||||
run: yarn
|
run: yarn
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: yarn test:qa
|
run: yarn test:sandbox
|
||||||
working-directory: nym-api/tests
|
working-directory: nym-api/tests
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ 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/**"
|
||||||
@@ -18,8 +16,6 @@ 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/**"
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
name: Upload nyxd to CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish-nyxd:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
platform: [ubuntu-20.04]
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Prepare build output directory
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
OUTPUT_DIR: ci-builds/nyxd
|
|
||||||
run: |
|
|
||||||
rm -rf ci-builds || true
|
|
||||||
mkdir -p $OUTPUT_DIR
|
|
||||||
echo $OUTPUT_DIR
|
|
||||||
|
|
||||||
- name: Install Dependencies (Linux)
|
|
||||||
run: sudo apt-get update && sudo apt-get -y install build-essential curl wget libssl-dev libgtk-3-dev libudev-dev squashfs-tools git
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Update env variables to include go
|
|
||||||
run: |
|
|
||||||
sudo rm -rf /usr/local/go
|
|
||||||
curl https://dl.google.com/go/go1.19.2.linux-amd64.tar.gz | sudo tar -C/usr/local -zxvf -
|
|
||||||
cat <<'EOF' >>$HOME/.profile
|
|
||||||
export GOROOT=/usr/local/go
|
|
||||||
export GOPATH=$HOME/go
|
|
||||||
export GO111MODULE=on
|
|
||||||
export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin
|
|
||||||
EOF
|
|
||||||
source $HOME/.profile
|
|
||||||
|
|
||||||
- name: Verify Go is installed
|
|
||||||
run: go version
|
|
||||||
|
|
||||||
- name: Clone nyxd repo
|
|
||||||
run: |
|
|
||||||
git clone https://github.com/tommyv1987/nyxd
|
|
||||||
cd nyxd
|
|
||||||
git checkout release/v0.30.2
|
|
||||||
|
|
||||||
- name: Run nyxd
|
|
||||||
run: |
|
|
||||||
pwd
|
|
||||||
cd nyxd && make build
|
|
||||||
sleep 10
|
|
||||||
ls /home/runner/work/nym/nym/nyxd/build
|
|
||||||
|
|
||||||
- name: Prepare build output
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
OUTPUT_DIR: ci-builds/nyxd
|
|
||||||
run: |
|
|
||||||
cp /home/runner/work/nym/nym/nyxd/build/nyxd $OUTPUT_DIR
|
|
||||||
WASMVM_SO=$(ldd /home/runner/work/nym/nym/nyxd/build/nyxd | grep "libwasm*" | awk '{ print $3 }')
|
|
||||||
ls $WASMVM_SO
|
|
||||||
sleep 3
|
|
||||||
cp $(echo $WASMVM_SO) $OUTPUT_DIR
|
|
||||||
|
|
||||||
- name: Deploy nyxd to CI www
|
|
||||||
continue-on-error: true
|
|
||||||
uses: easingthemes/ssh-deploy@main
|
|
||||||
env:
|
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
|
|
||||||
ARGS: "-avzr"
|
|
||||||
SOURCE: "ci-builds/"
|
|
||||||
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
|
||||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
|
||||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/builds/
|
|
||||||
EXCLUDE: "/dist/, /node_modules/"
|
|
||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: stable
|
toolchain: 1.71.0
|
||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,33 @@ 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)
|
## [v1.1.24] (2023-08-08)
|
||||||
|
|
||||||
- Latency based gateway selection is serial and slow ([#3710])
|
- Latency based gateway selection is serial and slow ([#3710])
|
||||||
|
|||||||
Generated
+3966
-596
File diff suppressed because it is too large
Load Diff
+5
-2
@@ -17,7 +17,6 @@ opt-level = 3
|
|||||||
|
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"clients/credential",
|
|
||||||
"clients/native",
|
"clients/native",
|
||||||
"clients/native/websocket-requests",
|
"clients/native/websocket-requests",
|
||||||
"clients/socks5",
|
"clients/socks5",
|
||||||
@@ -34,6 +33,7 @@ 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",
|
||||||
@@ -42,6 +42,7 @@ members = [
|
|||||||
"common/cosmwasm-smart-contracts/vesting-contract",
|
"common/cosmwasm-smart-contracts/vesting-contract",
|
||||||
"common/credential-storage",
|
"common/credential-storage",
|
||||||
"common/credentials",
|
"common/credentials",
|
||||||
|
"common/credential-utils",
|
||||||
"common/crypto",
|
"common/crypto",
|
||||||
"common/dkg",
|
"common/dkg",
|
||||||
"common/execute",
|
"common/execute",
|
||||||
@@ -134,6 +135,7 @@ cw3 = { version = "=1.1.0" }
|
|||||||
cw4 = { version = "=1.1.0" }
|
cw4 = { version = "=1.1.0" }
|
||||||
cw-controllers = { version = "=1.1.0" }
|
cw-controllers = { version = "=1.1.0" }
|
||||||
dotenvy = "0.15.6"
|
dotenvy = "0.15.6"
|
||||||
|
futures = "0.3.28"
|
||||||
generic-array = "0.14.7"
|
generic-array = "0.14.7"
|
||||||
getrandom = "0.2.10"
|
getrandom = "0.2.10"
|
||||||
k256 = "0.13"
|
k256 = "0.13"
|
||||||
@@ -148,7 +150,8 @@ tap = "1.0.1"
|
|||||||
tendermint-rpc = "0.32" # same version as used by cosmrs
|
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.2"
|
ts-rs = "7.0.0"
|
||||||
|
url = "2.4"
|
||||||
zeroize = "1.6.0"
|
zeroize = "1.6.0"
|
||||||
|
|
||||||
# wasm-related dependencies
|
# wasm-related dependencies
|
||||||
|
|||||||
@@ -77,9 +77,6 @@ $(eval $(call add_cargo_workspace,contracts,contracts,--lib --target wasm32-unkn
|
|||||||
$(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))
|
||||||
ifdef NYM_MOBILE
|
|
||||||
$(eval $(call add_cargo_workspace,connect-mobile,nym-connect/mobile/src-tauri))
|
|
||||||
endif
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Convenience targets for crates that are already part of the main workspace
|
# Convenience targets for crates that are already part of the main workspace
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
<!--
|
|
||||||
Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
SPDX-License-Identifier: Apache-2.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Credential binary
|
|
||||||
|
|
||||||
The credential binary is used to acquire coconut bandwidth credentials in exchange for nym tokens. Those credentials are stored in the client's `data` directory, so that they can be used as the client sees fit.
|
|
||||||
|
|
||||||
### Warning
|
|
||||||
|
|
||||||
The credential binary is still experimental software. The infrastructure for using it is not yet deployed to mainnet and it's still in the process of being deployed to sandbox.
|
|
||||||
|
|
||||||
### Building
|
|
||||||
|
|
||||||
From the project's root directory, run:
|
|
||||||
```
|
|
||||||
cargo build -p nym-credential-client
|
|
||||||
```
|
|
||||||
which generates the `nym-credential-client` binary in `target/debug/nym-credential-client`.
|
|
||||||
|
|
||||||
|
|
||||||
### Running
|
|
||||||
|
|
||||||
For example, you can get a credential worth 3 nym (3000000 unym) in a socks5 client that was already initialized like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
./target/debug/nym-credential-client --config-env-file envs/sandbox.env --client-home-directory ~/.nym/socks5-clients/cred_client --nyxd-url https://sandbox-validator1.nymtech.net --mnemonic $MNEMONIC --recovery-dir /tmp/recovery --amount 3000000
|
|
||||||
```
|
|
||||||
|
|
||||||
More information regarding how to run the binary can be found by running it with the `--help` argument.
|
|
||||||
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use crate::error::Result;
|
|
||||||
use bip39::Mnemonic;
|
|
||||||
use nym_network_defaults::{NymNetworkDetails, VOUCHER_INFO};
|
|
||||||
use nym_validator_client::nyxd;
|
|
||||||
use nym_validator_client::nyxd::traits::CoconutBandwidthSigningClient;
|
|
||||||
use nym_validator_client::nyxd::{Coin, DirectSigningNyxdClient, Fee, NyxdClient};
|
|
||||||
use std::str::FromStr;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
pub(crate) struct Client {
|
|
||||||
nyxd_client: NyxdClient<DirectSigningNyxdClient>,
|
|
||||||
mix_denom_base: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Client {
|
|
||||||
pub fn new(nyxd_url: &str, mnemonic: &str) -> Self {
|
|
||||||
let nyxd_url = Url::from_str(nyxd_url).unwrap();
|
|
||||||
let mnemonic = Mnemonic::from_str(mnemonic).unwrap();
|
|
||||||
let network_details = NymNetworkDetails::new_from_env();
|
|
||||||
let config = nyxd::Config::try_from_nym_network_details(&network_details)
|
|
||||||
.expect("failed to construct valid validator client config with the provided network");
|
|
||||||
let nyxd_client =
|
|
||||||
NyxdClient::connect_with_mnemonic(config, nyxd_url.as_ref(), mnemonic, None).unwrap();
|
|
||||||
|
|
||||||
Client {
|
|
||||||
nyxd_client,
|
|
||||||
mix_denom_base: network_details.chain_details.mix_denom.base,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn deposit(
|
|
||||||
&self,
|
|
||||||
amount: u64,
|
|
||||||
verification_key: String,
|
|
||||||
encryption_key: String,
|
|
||||||
fee: Option<Fee>,
|
|
||||||
) -> Result<String> {
|
|
||||||
let amount = Coin::new(amount as u128, self.mix_denom_base.clone());
|
|
||||||
Ok(self
|
|
||||||
.nyxd_client
|
|
||||||
.deposit(
|
|
||||||
amount,
|
|
||||||
String::from(VOUCHER_INFO),
|
|
||||||
verification_key,
|
|
||||||
encryption_key,
|
|
||||||
fee,
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
.transaction_hash
|
|
||||||
.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use clap::{ArgGroup, Args, Subcommand};
|
|
||||||
use log::*;
|
|
||||||
use nym_bandwidth_controller::acquire::state::State;
|
|
||||||
use nym_bin_common::completions::ArgShell;
|
|
||||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
|
||||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
|
||||||
|
|
||||||
use crate::error::Result;
|
|
||||||
use crate::recovery_storage::RecoveryStorage;
|
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub(crate) enum Command {
|
|
||||||
/// Run the binary to obtain a credential
|
|
||||||
Run(Run),
|
|
||||||
|
|
||||||
/// Generate shell completions
|
|
||||||
Completions(ArgShell),
|
|
||||||
|
|
||||||
/// Generate Fig specification
|
|
||||||
GenerateFigSpec,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Args)]
|
|
||||||
#[clap(group(
|
|
||||||
ArgGroup::new("recov")
|
|
||||||
.required(true)
|
|
||||||
.args(&["amount", "recovery_mode"]),
|
|
||||||
))]
|
|
||||||
pub(crate) struct Run {
|
|
||||||
/// Home directory of the client that is supposed to use the credential.
|
|
||||||
#[clap(long)]
|
|
||||||
pub(crate) client_home_directory: std::path::PathBuf,
|
|
||||||
|
|
||||||
/// A mnemonic for the account that buys the credential
|
|
||||||
#[clap(long)]
|
|
||||||
pub(crate) mnemonic: String,
|
|
||||||
|
|
||||||
/// The amount of utokens the credential will hold. If recovery mode is enabled, this value
|
|
||||||
/// is not needed
|
|
||||||
#[clap(long, default_value = "0")]
|
|
||||||
pub(crate) amount: u64,
|
|
||||||
|
|
||||||
/// Path to a directory used to store recovery files for unconsumed deposits
|
|
||||||
#[clap(long)]
|
|
||||||
pub(crate) recovery_dir: std::path::PathBuf,
|
|
||||||
|
|
||||||
/// Recovery mode, when enabled, tries to recover any deposit data dumped in recovery_dir
|
|
||||||
#[clap(long)]
|
|
||||||
pub(crate) recovery_mode: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn recover_credentials<C: DkgQueryClient + Send + Sync>(
|
|
||||||
client: &C,
|
|
||||||
recovery_storage: &RecoveryStorage,
|
|
||||||
shared_storage: &PersistentStorage,
|
|
||||||
) -> Result<()> {
|
|
||||||
for voucher in recovery_storage.unconsumed_vouchers()? {
|
|
||||||
let state = State::new(voucher);
|
|
||||||
if let Err(e) =
|
|
||||||
nym_bandwidth_controller::acquire::get_credential(&state, client, shared_storage).await
|
|
||||||
{
|
|
||||||
error!(
|
|
||||||
"Could not recover deposit {} due to {:?}, try again later",
|
|
||||||
state.voucher.tx_hash(),
|
|
||||||
e
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
info!(
|
|
||||||
"Converted deposit {} to a credential, removing recovery data for it",
|
|
||||||
state.voucher.tx_hash()
|
|
||||||
);
|
|
||||||
if let Err(e) = recovery_storage.remove_voucher(state.voucher.tx_hash().to_string()) {
|
|
||||||
warn!("Could not remove recovery data - {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use std::time::SystemTimeError;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use nym_credential_storage::error::StorageError;
|
|
||||||
use nym_credentials::error::Error as CredentialError;
|
|
||||||
use nym_validator_client::nyxd::error::NyxdError;
|
|
||||||
use nym_validator_client::ValidatorClientError;
|
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, CredentialClientError>;
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum CredentialClientError {
|
|
||||||
#[error("IO error: {0}")]
|
|
||||||
IOError(#[from] std::io::Error),
|
|
||||||
|
|
||||||
#[error("Bandwidth controller error: {0}")]
|
|
||||||
BandwidthControllerError(#[from] nym_bandwidth_controller::error::BandwidthControllerError),
|
|
||||||
|
|
||||||
#[error("Nyxd error: {0}")]
|
|
||||||
Nyxd(#[from] NyxdError),
|
|
||||||
|
|
||||||
#[error("Validator client error: {0}")]
|
|
||||||
ValidatorClientError(#[from] ValidatorClientError),
|
|
||||||
|
|
||||||
#[error("Credential error: {0}")]
|
|
||||||
Credential(#[from] CredentialError),
|
|
||||||
|
|
||||||
#[error("Could not use shared storage")]
|
|
||||||
SharedStorageError(#[from] StorageError),
|
|
||||||
|
|
||||||
#[error("Could not get system time")]
|
|
||||||
SysTimeError(#[from] SystemTimeError),
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
mod commands;
|
|
||||||
mod error;
|
|
||||||
mod recovery_storage;
|
|
||||||
|
|
||||||
use commands::*;
|
|
||||||
use error::Result;
|
|
||||||
use log::*;
|
|
||||||
use nym_bin_common::completions::fig_generate;
|
|
||||||
use nym_config::DEFAULT_DATA_DIR;
|
|
||||||
use nym_network_defaults::{setup_env, NymNetworkDetails};
|
|
||||||
use std::process::exit;
|
|
||||||
use std::time::{Duration, SystemTime};
|
|
||||||
|
|
||||||
use clap::{CommandFactory, Parser};
|
|
||||||
use nym_bin_common::logging::setup_logging;
|
|
||||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
|
||||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
|
||||||
use nym_validator_client::nyxd::{Coin, CosmWasmClient};
|
|
||||||
use nym_validator_client::Config;
|
|
||||||
|
|
||||||
const SAFETY_BUFFER_SECS: u64 = 60; // 1 minute
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
|
||||||
#[clap(author = "Nymtech", version, about)]
|
|
||||||
struct Cli {
|
|
||||||
/// Path pointing to an env file that configures the client.
|
|
||||||
#[clap(short, long)]
|
|
||||||
pub(crate) config_env_file: Option<std::path::PathBuf>,
|
|
||||||
|
|
||||||
#[clap(subcommand)]
|
|
||||||
pub(crate) command: Command,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn block_until_coconut_is_available<C: CosmWasmClient + Send + Sync>(
|
|
||||||
client: &nym_validator_client::Client<C>,
|
|
||||||
) -> Result<()> {
|
|
||||||
loop {
|
|
||||||
let epoch = client.nyxd.get_current_epoch().await?;
|
|
||||||
let current_timestamp_secs = SystemTime::now()
|
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)?
|
|
||||||
.as_secs();
|
|
||||||
if epoch.state.is_final() {
|
|
||||||
if current_timestamp_secs + SAFETY_BUFFER_SECS >= epoch.finish_timestamp.seconds() {
|
|
||||||
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
// Use 1 additional second to not start the next iteration immediately and spam get_current_epoch queries
|
|
||||||
let secs_until_final = epoch
|
|
||||||
.final_timestamp_secs()
|
|
||||||
.saturating_sub(current_timestamp_secs)
|
|
||||||
+ 1;
|
|
||||||
info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final);
|
|
||||||
std::thread::sleep(Duration::from_secs(secs_until_final));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> Result<()> {
|
|
||||||
let args = Cli::parse();
|
|
||||||
setup_logging();
|
|
||||||
setup_env(args.config_env_file.as_ref());
|
|
||||||
let bin_name = "nym-credential-client";
|
|
||||||
|
|
||||||
match args.command {
|
|
||||||
Command::Run(r) => {
|
|
||||||
// we assume the structure of <home-dir>/data
|
|
||||||
let data_dir = r.client_home_directory.join(DEFAULT_DATA_DIR);
|
|
||||||
let paths = CommonClientPaths::new_default(data_dir);
|
|
||||||
let db_path = paths.credentials_database;
|
|
||||||
|
|
||||||
let shared_storage =
|
|
||||||
nym_credential_storage::initialise_persistent_storage(db_path).await;
|
|
||||||
let recovery_storage = recovery_storage::RecoveryStorage::new(r.recovery_dir)?;
|
|
||||||
|
|
||||||
let network_details = NymNetworkDetails::new_from_env();
|
|
||||||
let config = Config::try_from_nym_network_details(&network_details).expect(
|
|
||||||
"failed to construct valid validator client config with the provided network",
|
|
||||||
);
|
|
||||||
let amount = Coin::new(
|
|
||||||
r.amount as u128,
|
|
||||||
network_details.chain_details.mix_denom.base,
|
|
||||||
);
|
|
||||||
let client =
|
|
||||||
nym_validator_client::Client::new_signing(config, r.mnemonic.parse().unwrap())?;
|
|
||||||
|
|
||||||
block_until_coconut_is_available(&client).await?;
|
|
||||||
info!("Starting depositing funds, don't kill the process");
|
|
||||||
|
|
||||||
if !r.recovery_mode {
|
|
||||||
let state =
|
|
||||||
nym_bandwidth_controller::acquire::deposit(&client.nyxd, amount).await?;
|
|
||||||
if nym_bandwidth_controller::acquire::get_credential(
|
|
||||||
&state,
|
|
||||||
&client,
|
|
||||||
&shared_storage,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
warn!("Failed to obtain credential. Dumping recovery data.",);
|
|
||||||
match recovery_storage.insert_voucher(&state.voucher) {
|
|
||||||
Ok(file_path) => {
|
|
||||||
warn!("Dumped recovery data to {:?}. Try using recovery mode to convert it to a credential", file_path);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("Could not dump recovery data to file system due to {:?}, the deposit will be lost!", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
recover_credentials(&client.nyxd, &recovery_storage, &shared_storage).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::Completions(c) => c.generate(&mut Cli::command(), bin_name),
|
|
||||||
Command::GenerateFigSpec => fig_generate(&mut Cli::command(), bin_name),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
|
|
||||||
use std::fs::{create_dir_all, read_dir, File};
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
pub struct RecoveryStorage {
|
|
||||||
recovery_dir: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RecoveryStorage {
|
|
||||||
pub fn new(recovery_dir: PathBuf) -> std::io::Result<Self> {
|
|
||||||
create_dir_all(&recovery_dir)?;
|
|
||||||
Ok(Self { recovery_dir })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unconsumed_vouchers(&self) -> std::io::Result<impl Iterator<Item = BandwidthVoucher>> {
|
|
||||||
Ok(read_dir(&self.recovery_dir)?
|
|
||||||
.filter_map(|entry| entry.ok())
|
|
||||||
.filter_map(|entry| {
|
|
||||||
let path = entry.path();
|
|
||||||
if path.is_file() {
|
|
||||||
Some(path)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter_map(|path| File::open(path).ok())
|
|
||||||
.filter_map(|mut f| {
|
|
||||||
let mut buff = Vec::new();
|
|
||||||
if f.read_to_end(&mut buff).is_ok() {
|
|
||||||
Some(buff)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter_map(|buff| BandwidthVoucher::try_from_bytes(&buff).ok()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_voucher(&self, voucher: &BandwidthVoucher) -> std::io::Result<PathBuf> {
|
|
||||||
let file_name = voucher.tx_hash().to_string();
|
|
||||||
let file_path = self.recovery_dir.join(file_name);
|
|
||||||
let mut file = File::create(&file_path)?;
|
|
||||||
let buff = voucher.to_bytes();
|
|
||||||
file.write_all(&buff)?;
|
|
||||||
|
|
||||||
Ok(file_path)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_voucher(&self, file_name: String) -> std::io::Result<()> {
|
|
||||||
let file_path = self.recovery_dir.join(file_name);
|
|
||||||
std::fs::remove_file(file_path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client"
|
name = "nym-client"
|
||||||
version = "1.1.24"
|
version = "1.1.26"
|
||||||
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 = "0.3" # bunch of futures stuff, however, now that I think about it, it could perhaps be completely removed
|
futures = { workspace = true } # bunch of futures stuff, however, now that I think about it, it could perhaps be completely removed
|
||||||
# the AsyncRead, AsyncWrite, Stream, Sink, etc. traits could be used from tokio
|
# 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 = "2.2"
|
url = { workspace = true }
|
||||||
|
|
||||||
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 = "1.0.34"
|
thiserror = { workspace = true }
|
||||||
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
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ 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::nyxd::QueryNyxdClient;
|
use nym_validator_client::QueryHttpRpcNyxdClient;
|
||||||
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;
|
||||||
|
|
||||||
@@ -29,7 +28,7 @@ pub use nym_sphinx::receiver::ReconstructedMessage;
|
|||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
||||||
type NativeClientBuilder<'a> = BaseClientBuilder<'a, Client<QueryNyxdClient>, OnDiskPersistent>;
|
type NativeClientBuilder<'a> = BaseClientBuilder<'a, QueryHttpRpcNyxdClient, 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,
|
||||||
|
|||||||
@@ -174,14 +174,15 @@ 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(|_| {
|
||||||
|
|||||||
@@ -105,10 +105,10 @@ impl ClientRequest {
|
|||||||
let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes();
|
let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes();
|
||||||
|
|
||||||
std::iter::once(ClientRequestTag::Send as u8)
|
std::iter::once(ClientRequestTag::Send as u8)
|
||||||
.chain(recipient.to_bytes().into_iter()) // will not be length prefixed because the length is constant
|
.chain(recipient.to_bytes()) // will not be length prefixed because the length is constant
|
||||||
.chain(conn_id_bytes.into_iter())
|
.chain(conn_id_bytes)
|
||||||
.chain(data_len_bytes.into_iter())
|
.chain(data_len_bytes)
|
||||||
.chain(data.into_iter())
|
.chain(data)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,11 +180,11 @@ impl ClientRequest {
|
|||||||
let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes();
|
let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes();
|
||||||
|
|
||||||
std::iter::once(ClientRequestTag::SendAnonymous as u8)
|
std::iter::once(ClientRequestTag::SendAnonymous as u8)
|
||||||
.chain(reply_surbs.to_be_bytes().into_iter())
|
.chain(reply_surbs.to_be_bytes())
|
||||||
.chain(recipient.to_bytes().into_iter()) // will not be length prefixed because the length is constant
|
.chain(recipient.to_bytes()) // will not be length prefixed because the length is constant
|
||||||
.chain(conn_id_bytes.into_iter())
|
.chain(conn_id_bytes)
|
||||||
.chain(data_len_bytes.into_iter())
|
.chain(data_len_bytes)
|
||||||
.chain(data.into_iter())
|
.chain(data)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,10 +258,10 @@ impl ClientRequest {
|
|||||||
let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes();
|
let conn_id_bytes = connection_id.unwrap_or(0).to_be_bytes();
|
||||||
|
|
||||||
std::iter::once(ClientRequestTag::Reply as u8)
|
std::iter::once(ClientRequestTag::Reply as u8)
|
||||||
.chain(sender_tag.to_bytes().into_iter())
|
.chain(sender_tag.to_bytes())
|
||||||
.chain(conn_id_bytes.into_iter())
|
.chain(conn_id_bytes)
|
||||||
.chain(message_len_bytes.into_iter())
|
.chain(message_len_bytes)
|
||||||
.chain(message.into_iter())
|
.chain(message)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,7 +332,7 @@ impl ClientRequest {
|
|||||||
fn serialize_closed_connection(connection_id: u64) -> Vec<u8> {
|
fn serialize_closed_connection(connection_id: u64) -> Vec<u8> {
|
||||||
let conn_id_bytes = connection_id.to_be_bytes();
|
let conn_id_bytes = connection_id.to_be_bytes();
|
||||||
std::iter::once(ClientRequestTag::ClosedConnection as u8)
|
std::iter::once(ClientRequestTag::ClosedConnection as u8)
|
||||||
.chain(conn_id_bytes.into_iter())
|
.chain(conn_id_bytes)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +359,7 @@ impl ClientRequest {
|
|||||||
fn serialize_get_lane_queue_lengths(connection_id: u64) -> Vec<u8> {
|
fn serialize_get_lane_queue_lengths(connection_id: u64) -> Vec<u8> {
|
||||||
let conn_id_bytes = connection_id.to_be_bytes();
|
let conn_id_bytes = connection_id.to_be_bytes();
|
||||||
std::iter::once(ClientRequestTag::GetLaneQueueLength as u8)
|
std::iter::once(ClientRequestTag::GetLaneQueueLength as u8)
|
||||||
.chain(conn_id_bytes.into_iter())
|
.chain(conn_id_bytes)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,15 +67,15 @@ impl ServerResponse {
|
|||||||
if let Some(sender_tag) = reconstructed_message.sender_tag {
|
if let Some(sender_tag) = reconstructed_message.sender_tag {
|
||||||
std::iter::once(ServerResponseTag::Received as u8)
|
std::iter::once(ServerResponseTag::Received as u8)
|
||||||
.chain(std::iter::once(true as u8))
|
.chain(std::iter::once(true as u8))
|
||||||
.chain(sender_tag.to_bytes().into_iter())
|
.chain(sender_tag.to_bytes())
|
||||||
.chain(message_len_bytes.iter().cloned())
|
.chain(message_len_bytes.iter().cloned())
|
||||||
.chain(reconstructed_message.message.into_iter())
|
.chain(reconstructed_message.message)
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
std::iter::once(ServerResponseTag::Received as u8)
|
std::iter::once(ServerResponseTag::Received as u8)
|
||||||
.chain(std::iter::once(false as u8))
|
.chain(std::iter::once(false as u8))
|
||||||
.chain(message_len_bytes.iter().cloned())
|
.chain(message_len_bytes.iter().cloned())
|
||||||
.chain(reconstructed_message.message.into_iter())
|
.chain(reconstructed_message.message)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ impl ServerResponse {
|
|||||||
// SELF_ADDRESS_RESPONSE_TAG || self_address
|
// SELF_ADDRESS_RESPONSE_TAG || self_address
|
||||||
fn serialize_self_address(address: Recipient) -> Vec<u8> {
|
fn serialize_self_address(address: Recipient) -> Vec<u8> {
|
||||||
std::iter::once(ServerResponseTag::SelfAddress as u8)
|
std::iter::once(ServerResponseTag::SelfAddress as u8)
|
||||||
.chain(address.to_bytes().into_iter())
|
.chain(address.to_bytes())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,8 +211,8 @@ impl ServerResponse {
|
|||||||
let message_len_bytes = (error.message.len() as u64).to_be_bytes();
|
let message_len_bytes = (error.message.len() as u64).to_be_bytes();
|
||||||
std::iter::once(ServerResponseTag::Error as u8)
|
std::iter::once(ServerResponseTag::Error as u8)
|
||||||
.chain(std::iter::once(error.kind as u8))
|
.chain(std::iter::once(error.kind as u8))
|
||||||
.chain(message_len_bytes.into_iter())
|
.chain(message_len_bytes)
|
||||||
.chain(error.message.into_bytes().into_iter())
|
.chain(error.message.into_bytes())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.1.24"
|
version = "1.1.26"
|
||||||
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 = "1.0.34"
|
thiserror = { workspace = true }
|
||||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
|
tokio = { version = "1.24.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||||
url = "2.2"
|
url = { workspace = true }
|
||||||
|
|
||||||
# internal
|
# internal
|
||||||
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
|
nym-bin-common = { path = "../../common/bin-common", features = ["output_format"] }
|
||||||
|
|||||||
@@ -186,14 +186,15 @@ 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
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use nym_client_core::client::base_client::storage::gateway_details::{
|
|||||||
};
|
};
|
||||||
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::client::topology_control::geo_aware_provider::CountryGroup;
|
||||||
use nym_client_core::config::{GatewayEndpointConfig, TopologyStructure};
|
use nym_client_core::config::{GatewayEndpointConfig, GroupBy, 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};
|
||||||
@@ -101,9 +101,17 @@ 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() {
|
let topology_structure = if args.medium_toggle {
|
||||||
// TODO: rethink the default group. I just picked one for now.
|
// Use the location of the network-requester
|
||||||
TopologyStructure::GeoAware(args.geo_routing.unwrap_or(CountryGroup::Europe))
|
let address = config
|
||||||
|
.core
|
||||||
|
.socks5
|
||||||
|
.provider_mix_address
|
||||||
|
.parse()
|
||||||
|
.expect("failed to parse provider mix address");
|
||||||
|
TopologyStructure::GeoAware(GroupBy::NymAddress(address))
|
||||||
|
} else if let Some(code) = args.geo_routing {
|
||||||
|
TopologyStructure::GeoAware(GroupBy::CountryGroup(code))
|
||||||
} else {
|
} else {
|
||||||
TopologyStructure::default()
|
TopologyStructure::default()
|
||||||
};
|
};
|
||||||
|
|||||||
Generated
+426
-415
File diff suppressed because it is too large
Load Diff
@@ -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", "client"]
|
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy"]
|
||||||
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."
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ impl WasmTopologyExt for Arc<ClientState> {
|
|||||||
let this = Arc::clone(self);
|
let this = Arc::clone(self);
|
||||||
future_to_promise(async move {
|
future_to_promise(async move {
|
||||||
let Some(current_topology) = this.topology_accessor.current_topology().await else {
|
let Some(current_topology) = this.topology_accessor.current_topology().await else {
|
||||||
return Err(WasmClientError::UnavailableNetworkTopology.into())
|
return Err(WasmClientError::UnavailableNetworkTopology.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
match current_topology.find_mix_by_identity(&mixnode_identity) {
|
match current_topology.find_mix_by_identity(&mixnode_identity) {
|
||||||
@@ -135,7 +135,7 @@ impl WasmTopologyExt for Arc<ClientState> {
|
|||||||
let this = Arc::clone(self);
|
let this = Arc::clone(self);
|
||||||
future_to_promise(async move {
|
future_to_promise(async move {
|
||||||
let Some(current_topology) = this.topology_accessor.current_topology().await else {
|
let Some(current_topology) = this.topology_accessor.current_topology().await else {
|
||||||
return Err(WasmClientError::UnavailableNetworkTopology.into())
|
return Err(WasmClientError::UnavailableNetworkTopology.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(mix) = current_topology.find_mix_by_identity(&mixnode_identity) else {
|
let Some(mix) = current_topology.find_mix_by_identity(&mixnode_identity) else {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ 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,
|
||||||
};
|
};
|
||||||
@@ -26,6 +25,7 @@ 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::<FakeClient<DirectSigningNyxdClient>, _>::new(
|
BaseClientBuilder::<QueryReqwestRpcNyxdClient, _>::new(
|
||||||
&self.config.base,
|
&self.config.base,
|
||||||
storage,
|
storage,
|
||||||
None,
|
None,
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ pub fn encode_payload_with_headers(
|
|||||||
Ok(metadata) => {
|
Ok(metadata) => {
|
||||||
let metadata = metadata.as_bytes().to_vec();
|
let metadata = metadata.as_bytes().to_vec();
|
||||||
let size = (metadata.len() as u64).to_be_bytes().to_vec();
|
let size = (metadata.len() as u64).to_be_bytes().to_vec();
|
||||||
Ok(vec![size, metadata, payload].concat())
|
Ok([size, metadata, payload].concat())
|
||||||
}
|
}
|
||||||
Err(e) => Err(JsValue::from(JsError::new(
|
Err(e) => Err(JsValue::from(JsError::new(
|
||||||
format!("Could not encode message: {}", e).as_str(),
|
format!("Could not encode message: {}", e).as_str(),
|
||||||
|
|||||||
@@ -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, InitialisationDetails};
|
use nym_client_core::init::{setup_gateway_from, GatewaySetup, InitialisationResult};
|
||||||
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<InitialisationDetails, WasmClientError> {
|
) -> Result<InitialisationResult, 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<InitialisationDetails, WasmClientError> {
|
) -> Result<InitialisationResult, 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<InitialisationDetails, WasmClientError> {
|
) -> Result<InitialisationResult, 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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
// 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
|
||||||
|
|
||||||
|
// After reading https://github.com/rust-lang/rust-clippy/issues/11382
|
||||||
|
// I suspect we *maybe* have hit a false positive, but I'm not sure.
|
||||||
|
#![allow(clippy::arc_with_non_send_sync)]
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ impl<'a> EphemeralTestReceiver<'a> {
|
|||||||
let Some(received_packet) = packet else {
|
let Some(received_packet) = packet else {
|
||||||
// can't do anything more...
|
// can't do anything more...
|
||||||
console_error!("packet receiver has stopped processing results!");
|
console_error!("packet receiver has stopped processing results!");
|
||||||
return true
|
return true;
|
||||||
};
|
};
|
||||||
match received_packet {
|
match received_packet {
|
||||||
Received::Message(msg) => {
|
Received::Message(msg) => {
|
||||||
|
|||||||
@@ -12,10 +12,9 @@ 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;
|
use nym_client_core::init::{InitialisationDetails, InitialisationResult};
|
||||||
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;
|
||||||
@@ -27,6 +26,7 @@ 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<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>>;
|
Arc<AsyncMutex<GatewayClient<QueryReqwestRpcNyxdClient, 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,8 +78,7 @@ pub struct NymNodeTesterBuilder {
|
|||||||
base_topology: NymTopology,
|
base_topology: NymTopology,
|
||||||
|
|
||||||
// unimplemented
|
// unimplemented
|
||||||
bandwidth_controller:
|
bandwidth_controller: Option<BandwidthController<QueryReqwestRpcNyxdClient, EphemeralStorage>>,
|
||||||
Option<BandwidthController<FakeClient<DirectSigningNyxdClient>, EphemeralStorage>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn address(keys: &ManagedKeys, gateway_identity: NodeIdentity) -> Recipient {
|
fn address(keys: &ManagedKeys, gateway_identity: NodeIdentity) -> Recipient {
|
||||||
@@ -130,9 +129,9 @@ impl NymNodeTesterBuilder {
|
|||||||
async fn gateway_info(
|
async fn gateway_info(
|
||||||
&self,
|
&self,
|
||||||
client_store: &ClientStorage,
|
client_store: &ClientStorage,
|
||||||
) -> Result<InitialisationDetails, WasmClientError> {
|
) -> Result<InitialisationResult, 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)
|
Ok(loaded.into())
|
||||||
} 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
|
||||||
}
|
}
|
||||||
@@ -148,26 +147,37 @@ 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 = self.gateway_info(&client_store).await?;
|
let init_details = initialisation_result.details;
|
||||||
|
let managed_keys = init_details.managed_keys;
|
||||||
let gateway_endpoint = init_details.gateway_details;
|
let gateway_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 = GatewayClient::new(
|
let mut gateway_client =
|
||||||
gateway_endpoint.gateway_listener,
|
if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client {
|
||||||
managed_keys.identity_keypair(),
|
existing_client.upgrade(
|
||||||
gateway_identity,
|
mixnet_message_sender,
|
||||||
Some(managed_keys.must_get_gateway_shared_key()),
|
ack_sender,
|
||||||
mixnet_message_sender,
|
Duration::from_secs(10),
|
||||||
ack_sender,
|
self.bandwidth_controller.take(),
|
||||||
Duration::from_secs(10),
|
task_manager.subscribe(),
|
||||||
self.bandwidth_controller.take(),
|
)
|
||||||
task_manager.subscribe(),
|
} else {
|
||||||
);
|
GatewayClient::new(
|
||||||
|
gateway_endpoint.gateway_listener,
|
||||||
|
managed_keys.identity_keypair(),
|
||||||
|
gateway_identity,
|
||||||
|
Some(managed_keys.must_get_gateway_shared_key()),
|
||||||
|
mixnet_message_sender,
|
||||||
|
ack_sender,
|
||||||
|
Duration::from_secs(10),
|
||||||
|
self.bandwidth_controller.take(),
|
||||||
|
task_manager.subscribe(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
gateway_client.set_disabled_credentials_mode(true);
|
gateway_client.set_disabled_credentials_mode(true);
|
||||||
gateway_client.authenticate_and_start().await?;
|
gateway_client.authenticate_and_start().await?;
|
||||||
|
|||||||
@@ -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 = "0.3"
|
futures = { workspace = true }
|
||||||
notify = "5.1.0"
|
notify = "5.1.0"
|
||||||
|
|||||||
@@ -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 = "2.2"
|
url = { workspace = true }
|
||||||
|
|
||||||
nym-coconut-interface = { path = "../coconut-interface" }
|
nym-coconut-interface = { path = "../coconut-interface" }
|
||||||
nym-credential-storage = { path = "../credential-storage" }
|
nym-credential-storage = { path = "../credential-storage" }
|
||||||
@@ -20,4 +20,4 @@ nym-validator-client = { path = "../client-libs/validator-client", default-featu
|
|||||||
|
|
||||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
|
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.nym-validator-client]
|
||||||
path = "../client-libs/validator-client"
|
path = "../client-libs/validator-client"
|
||||||
features = ["signing"]
|
features = ["http-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::nyxd::traits::CoconutBandwidthSigningClient;
|
use nym_validator_client::coconut::all_coconut_api_clients;
|
||||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient;
|
||||||
|
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||||
use nym_validator_client::nyxd::Coin;
|
use nym_validator_client::nyxd::Coin;
|
||||||
use nym_validator_client::nyxd::Hash;
|
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,
|
C: CoconutBandwidthSigningClient + Sync,
|
||||||
{
|
{
|
||||||
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,7 +70,8 @@ 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,
|
||||||
|
|||||||
@@ -6,15 +6,18 @@ 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>),
|
||||||
|
|
||||||
|
|||||||
@@ -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,17 +14,8 @@ use {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use crate::wasm_mockups::DkgQueryClient;
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
pub mod acquire;
|
pub mod 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,
|
||||||
@@ -64,12 +55,8 @@ 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)?;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
let coconut_api_clients = all_coconut_api_clients(&self.client, epoch_id).await?;
|
||||||
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)
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
// 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> {}
|
|
||||||
@@ -10,9 +10,10 @@ rust-version = "1.66"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
base64 = "0.21.2"
|
base64 = "0.21.2"
|
||||||
|
cfg-if = "1.0.0"
|
||||||
dashmap = "5.4.0"
|
dashmap = "5.4.0"
|
||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
futures = "0.3"
|
futures = { workspace = true }
|
||||||
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"] }
|
||||||
@@ -25,7 +26,7 @@ thiserror = "1.0.34"
|
|||||||
time = "0.3.17"
|
time = "0.3.17"
|
||||||
tokio = { version = "1.24.1", features = ["macros"]}
|
tokio = { version = "1.24.1", features = ["macros"]}
|
||||||
tungstenite = { version = "0.13.0", default-features = false }
|
tungstenite = { version = "0.13.0", default-features = false }
|
||||||
url = { version ="2.2", features = ["serde"] }
|
url = { workspace = true, features = ["serde"] }
|
||||||
zeroize = { workspace = true }
|
zeroize = { workspace = true }
|
||||||
|
|
||||||
# internal
|
# internal
|
||||||
@@ -45,10 +46,6 @@ 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 = ["signing", "http-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"]
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
|
|
||||||
use super::received_buffer::ReceivedBufferMessage;
|
use super::received_buffer::ReceivedBufferMessage;
|
||||||
use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider;
|
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,8 +23,9 @@ 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, GatewayEndpointConfig};
|
use crate::config::{Config, DebugConfig};
|
||||||
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};
|
||||||
@@ -43,18 +44,11 @@ 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;
|
||||||
|
|
||||||
@@ -200,16 +194,13 @@ 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(
|
fn mix_address(details: &InitialisationDetails) -> Recipient {
|
||||||
managed_keys: &ManagedKeys,
|
|
||||||
gateway_config: &GatewayEndpointConfig,
|
|
||||||
) -> Recipient {
|
|
||||||
Recipient::new(
|
Recipient::new(
|
||||||
*managed_keys.identity_public_key(),
|
*details.managed_keys.identity_public_key(),
|
||||||
*managed_keys.encryption_public_key(),
|
*details.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(&gateway_config.gateway_id).unwrap(),
|
NodeIdentity::from_base58_string(&details.gateway_details.gateway_id).unwrap(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,8 +285,7 @@ where
|
|||||||
|
|
||||||
async fn start_gateway_client(
|
async fn start_gateway_client(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
gateway_config: GatewayEndpointConfig,
|
initialisation_result: InitialisationResult,
|
||||||
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,
|
||||||
@@ -305,24 +295,39 @@ 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 gateway_address = gateway_config.gateway_listener.clone();
|
let managed_keys = initialisation_result.details.managed_keys;
|
||||||
let gateway_id = gateway_config.gateway_id;
|
|
||||||
|
|
||||||
// TODO: in theory, at this point, this should be infallible
|
let mut gateway_client =
|
||||||
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
if let Some(existing_client) = initialisation_result.authenticated_ephemeral_client {
|
||||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
existing_client.upgrade(
|
||||||
|
mixnet_message_sender,
|
||||||
|
ack_sender,
|
||||||
|
config.debug.gateway_connection.gateway_response_timeout,
|
||||||
|
bandwidth_controller,
|
||||||
|
shutdown,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let gateway_config = initialisation_result.details.gateway_details;
|
||||||
|
|
||||||
let mut gateway_client = GatewayClient::new(
|
let gateway_address = gateway_config.gateway_listener.clone();
|
||||||
gateway_address,
|
let gateway_id = gateway_config.gateway_id;
|
||||||
managed_keys.identity_keypair(),
|
|
||||||
gateway_identity,
|
// TODO: in theory, at this point, this should be infallible
|
||||||
Some(managed_keys.must_get_gateway_shared_key()),
|
let gateway_identity = identity::PublicKey::from_base58_string(gateway_id)
|
||||||
mixnet_message_sender,
|
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||||
ack_sender,
|
|
||||||
config.debug.gateway_connection.gateway_response_timeout,
|
GatewayClient::new(
|
||||||
bandwidth_controller,
|
gateway_address,
|
||||||
shutdown,
|
managed_keys.identity_keypair(),
|
||||||
);
|
gateway_identity,
|
||||||
|
Some(managed_keys.must_get_gateway_shared_key()),
|
||||||
|
mixnet_message_sender,
|
||||||
|
ack_sender,
|
||||||
|
config.debug.gateway_connection.gateway_response_timeout,
|
||||||
|
bandwidth_controller,
|
||||||
|
shutdown,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
gateway_client.set_disabled_credentials_mode(config.client.disabled_credentials_mode);
|
gateway_client.set_disabled_credentials_mode(config.client.disabled_credentials_mode);
|
||||||
|
|
||||||
@@ -349,11 +354,13 @@ where
|
|||||||
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(
|
config::TopologyStructure::GeoAware(group_by) => {
|
||||||
nym_api_urls,
|
Box::new(GeoAwareTopologyProvider::new(
|
||||||
env!("CARGO_PKG_VERSION").to_string(),
|
nym_api_urls,
|
||||||
group,
|
env!("CARGO_PKG_VERSION").to_string(),
|
||||||
)),
|
group_by,
|
||||||
|
))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,17 +452,23 @@ where
|
|||||||
Ok(mem_store)
|
Ok(mem_store)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn initialise_keys_and_gateway(&self) -> Result<InitialisationDetails, ClientCoreError>
|
async fn initialise_keys_and_gateway(
|
||||||
|
setup_method: GatewaySetup,
|
||||||
|
key_store: &S::KeyStore,
|
||||||
|
details_store: &S::GatewayDetailsStore,
|
||||||
|
overwrite_data: bool,
|
||||||
|
validator_servers: Option<&[Url]>,
|
||||||
|
) -> Result<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(
|
||||||
&self.setup_method,
|
setup_method,
|
||||||
self.client_store.key_store(),
|
key_store,
|
||||||
self.client_store.gateway_details_store(),
|
details_store,
|
||||||
false,
|
overwrite_data,
|
||||||
Some(&self.config.client.nym_api_urls),
|
validator_servers,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -471,9 +484,14 @@ 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 details = self.initialise_keys_and_gateway().await?;
|
let init_res = Self::initialise_keys_and_gateway(
|
||||||
let gateway_config = details.gateway_details;
|
self.setup_method,
|
||||||
let managed_keys = details.managed_keys;
|
self.client_store.key_store(),
|
||||||
|
self.client_store.gateway_details_store(),
|
||||||
|
false,
|
||||||
|
Some(&self.config.client.nym_api_urls),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let (reply_storage_backend, credential_store) = self.client_store.into_runtime_stores();
|
let (reply_storage_backend, credential_store) = self.client_store.into_runtime_stores();
|
||||||
|
|
||||||
@@ -507,14 +525,15 @@ 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(&managed_keys, &gateway_config);
|
let self_address = Self::mix_address(&init_res.details);
|
||||||
|
let ack_key = init_res.details.managed_keys.ack_key();
|
||||||
|
let encryption_keys = init_res.details.managed_keys.encryption_keypair();
|
||||||
|
|
||||||
// the components are started in very specific order. Unless you know what you are doing,
|
// 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,
|
||||||
gateway_config,
|
init_res,
|
||||||
&managed_keys,
|
|
||||||
bandwidth_controller,
|
bandwidth_controller,
|
||||||
mixnet_messages_sender,
|
mixnet_messages_sender,
|
||||||
ack_sender,
|
ack_sender,
|
||||||
@@ -541,7 +560,7 @@ where
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Self::start_received_messages_buffer_controller(
|
Self::start_received_messages_buffer_controller(
|
||||||
managed_keys.encryption_keypair(),
|
encryption_keys,
|
||||||
received_buffer_request_receiver,
|
received_buffer_request_receiver,
|
||||||
mixnet_messages_receiver,
|
mixnet_messages_receiver,
|
||||||
reply_storage.key_storage(),
|
reply_storage.key_storage(),
|
||||||
@@ -566,7 +585,7 @@ where
|
|||||||
|
|
||||||
let controller_config = real_messages_control::Config::new(
|
let controller_config = real_messages_control::Config::new(
|
||||||
&self.config.debug,
|
&self.config.debug,
|
||||||
managed_keys.ack_key(),
|
Arc::clone(&ack_key),
|
||||||
self_address,
|
self_address,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -593,7 +612,7 @@ where
|
|||||||
{
|
{
|
||||||
Self::start_cover_traffic_stream(
|
Self::start_cover_traffic_stream(
|
||||||
&self.config.debug,
|
&self.config.debug,
|
||||||
managed_keys.ack_key(),
|
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::QueryNyxdClient;
|
use nym_validator_client::nyxd;
|
||||||
use nym_validator_client::Client;
|
use nym_validator_client::QueryHttpRpcNyxdClient;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
@@ -104,48 +104,38 @@ 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<Client<QueryNyxdClient>, St> {
|
) -> BandwidthController<QueryHttpRpcNyxdClient, 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, api_url, storage)
|
create_bandwidth_controller_with_urls(nyxd_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<Client<QueryNyxdClient>, St> {
|
) -> BandwidthController<QueryHttpRpcNyxdClient, St> {
|
||||||
let client = default_query_dkg_client(nyxd_url, nym_api_url);
|
let client = default_query_dkg_client(nyxd_url);
|
||||||
|
|
||||||
BandwidthController::new(storage, client)
|
BandwidthController::new(storage, client)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_query_dkg_client_from_config(config: &Config) -> Client<QueryNyxdClient> {
|
pub fn default_query_dkg_client_from_config(config: &Config) -> QueryHttpRpcNyxdClient {
|
||||||
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, api_url)
|
default_query_dkg_client(nyxd_url)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_query_dkg_client(nyxd_url: Url, nym_api_url: Url) -> Client<QueryNyxdClient> {
|
pub fn default_query_dkg_client(nyxd_url: Url) -> QueryHttpRpcNyxdClient {
|
||||||
let details = nym_network_defaults::NymNetworkDetails::new_from_env();
|
let details = nym_network_defaults::NymNetworkDetails::new_from_env();
|
||||||
let mut client_config = nym_validator_client::Config::try_from_nym_network_details(&details)
|
let client_config = nyxd::Config::try_from_nym_network_details(&details)
|
||||||
.expect("failed to construct validator client config");
|
.expect("failed to construct validator client config");
|
||||||
// overwrite env configuration with config URLs
|
// overwrite env configuration with config URLs
|
||||||
client_config = client_config.with_urls(nyxd_url, nym_api_url);
|
QueryHttpRpcNyxdClient::connect(client_config, nyxd_url.as_str())
|
||||||
nym_validator_client::Client::new_query(client_config)
|
|
||||||
.expect("Could not construct query client")
|
.expect("Could not construct query client")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,10 @@
|
|||||||
|
|
||||||
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>>;
|
||||||
|
|||||||
@@ -1,23 +1,48 @@
|
|||||||
use std::{collections::HashMap, fmt};
|
use std::{collections::HashMap, fmt};
|
||||||
|
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
use nym_explorer_api_requests::PrettyDetailedMixNodeBond;
|
use nym_explorer_api_requests::{PrettyDetailedGatewayBond, PrettyDetailedMixNodeBond};
|
||||||
|
use nym_network_defaults::var_names::EXPLORER_API;
|
||||||
use nym_topology::{
|
use nym_topology::{
|
||||||
nym_topology_from_detailed,
|
nym_topology_from_detailed,
|
||||||
provider_trait::{async_trait, TopologyProvider},
|
provider_trait::{async_trait, TopologyProvider},
|
||||||
NymTopology,
|
NymTopology,
|
||||||
};
|
};
|
||||||
use nym_validator_client::client::MixId;
|
use nym_validator_client::client::{IdentityKey, MixId};
|
||||||
use rand::{prelude::SliceRandom, thread_rng};
|
use rand::{prelude::SliceRandom, thread_rng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tap::TapOptional;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::config::GroupBy;
|
||||||
|
|
||||||
const MIN_NODES_PER_LAYER: usize = 1;
|
const MIN_NODES_PER_LAYER: usize = 1;
|
||||||
const EXPLORER_API_MIXNODES_URL: &str = "https://explorer.nymtech.net/api/v1/mix-nodes";
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
fn reqwest_client() -> Option<reqwest::Client> {
|
||||||
|
reqwest::Client::builder().build().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn reqwest_client() -> Option<reqwest::Client> {
|
||||||
|
reqwest::Client::builder()
|
||||||
|
.timeout(std::time::Duration::from_secs(5))
|
||||||
|
.build()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: create a explorer-api-client
|
// TODO: create a explorer-api-client
|
||||||
async fn fetch_mixnodes_from_explorer_api() -> Option<Vec<PrettyDetailedMixNodeBond>> {
|
async fn fetch_mixnodes_from_explorer_api() -> Option<Vec<PrettyDetailedMixNodeBond>> {
|
||||||
reqwest::get(EXPLORER_API_MIXNODES_URL)
|
let explorer_api_url = std::env::var(EXPLORER_API).ok()?;
|
||||||
|
let explorer_api_url = Url::parse(&explorer_api_url)
|
||||||
|
.ok()?
|
||||||
|
.join("v1/mix-nodes")
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
debug!("Fetching: {}", explorer_api_url);
|
||||||
|
reqwest_client()?
|
||||||
|
.get(explorer_api_url)
|
||||||
|
.send()
|
||||||
.await
|
.await
|
||||||
.ok()?
|
.ok()?
|
||||||
.json::<Vec<PrettyDetailedMixNodeBond>>()
|
.json::<Vec<PrettyDetailedMixNodeBond>>()
|
||||||
@@ -25,6 +50,25 @@ async fn fetch_mixnodes_from_explorer_api() -> Option<Vec<PrettyDetailedMixNodeB
|
|||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: create a explorer-api-client
|
||||||
|
async fn fetch_gateways_from_explorer_api() -> Option<Vec<PrettyDetailedGatewayBond>> {
|
||||||
|
let explorer_api_url = std::env::var(EXPLORER_API).ok()?;
|
||||||
|
let explorer_api_url = Url::parse(&explorer_api_url)
|
||||||
|
.ok()?
|
||||||
|
.join("v1/gateways")
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
debug!("Fetching: {}", explorer_api_url);
|
||||||
|
reqwest_client()?
|
||||||
|
.get(explorer_api_url)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.ok()?
|
||||||
|
.json::<Vec<PrettyDetailedGatewayBond>>()
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
#[derive(Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||||
pub enum CountryGroup {
|
pub enum CountryGroup {
|
||||||
Europe,
|
Europe,
|
||||||
@@ -191,6 +235,23 @@ fn group_mixnodes_by_country_code(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn group_gateways_by_country_code(
|
||||||
|
gateways: Vec<PrettyDetailedGatewayBond>,
|
||||||
|
) -> HashMap<CountryGroup, Vec<IdentityKey>> {
|
||||||
|
gateways.into_iter().fold(
|
||||||
|
HashMap::<CountryGroup, Vec<IdentityKey>>::new(),
|
||||||
|
|mut acc, g| {
|
||||||
|
if let Some(ref location) = g.location {
|
||||||
|
let country_code = location.two_letter_iso_country_code.clone();
|
||||||
|
let group_code = CountryGroup::new(country_code.as_str());
|
||||||
|
let gateways = acc.entry(group_code).or_insert_with(Vec::new);
|
||||||
|
gateways.push(g.gateway.identity_key)
|
||||||
|
}
|
||||||
|
acc
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn log_mixnode_distribution(mixnodes: &HashMap<CountryGroup, Vec<MixId>>) {
|
fn log_mixnode_distribution(mixnodes: &HashMap<CountryGroup, Vec<MixId>>) {
|
||||||
let mixnode_distribution = mixnodes
|
let mixnode_distribution = mixnodes
|
||||||
.iter()
|
.iter()
|
||||||
@@ -200,6 +261,15 @@ fn log_mixnode_distribution(mixnodes: &HashMap<CountryGroup, Vec<MixId>>) {
|
|||||||
debug!("Mixnode distribution - {}", mixnode_distribution);
|
debug!("Mixnode distribution - {}", mixnode_distribution);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn log_gateway_distribution(gateways: &HashMap<CountryGroup, Vec<IdentityKey>>) {
|
||||||
|
let gateway_distribution = gateways
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| format!("{}: {}", k, v.len()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
debug!("Gateway distribution - {}", gateway_distribution);
|
||||||
|
}
|
||||||
|
|
||||||
fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> {
|
fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> {
|
||||||
let mixes = topology.mixes();
|
let mixes = topology.mixes();
|
||||||
if mixes.keys().len() < 3 {
|
if mixes.keys().len() < 3 {
|
||||||
@@ -222,7 +292,7 @@ fn check_layer_integrity(topology: NymTopology) -> Result<(), ()> {
|
|||||||
|
|
||||||
pub struct GeoAwareTopologyProvider {
|
pub struct GeoAwareTopologyProvider {
|
||||||
validator_client: nym_validator_client::client::NymApiClient,
|
validator_client: nym_validator_client::client::NymApiClient,
|
||||||
filter_on: CountryGroup,
|
filter_on: GroupBy,
|
||||||
client_version: String,
|
client_version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +300,7 @@ impl GeoAwareTopologyProvider {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
mut nym_api_urls: Vec<Url>,
|
mut nym_api_urls: Vec<Url>,
|
||||||
client_version: String,
|
client_version: String,
|
||||||
filter_on: CountryGroup,
|
filter_on: GroupBy,
|
||||||
) -> GeoAwareTopologyProvider {
|
) -> GeoAwareTopologyProvider {
|
||||||
log::info!(
|
log::info!(
|
||||||
"Creating geo-aware topology provider with filter on {:?}",
|
"Creating geo-aware topology provider with filter on {:?}",
|
||||||
@@ -272,6 +342,38 @@ impl GeoAwareTopologyProvider {
|
|||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug!("Fetching gateways from explorer-api...");
|
||||||
|
let Some(gateways_from_explorer_api) = fetch_gateways_from_explorer_api().await else {
|
||||||
|
error!("failed to get mixnodes from explorer-api");
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine what we should filter around
|
||||||
|
let filter_on = match self.filter_on {
|
||||||
|
GroupBy::CountryGroup(group) => group,
|
||||||
|
GroupBy::NymAddress(recipient) => {
|
||||||
|
// Convert recipient into a country group by extracting out the gateway part and
|
||||||
|
// using that as the country code.
|
||||||
|
let gateway = recipient.gateway().to_base58_string();
|
||||||
|
|
||||||
|
// Lookup the location of this gateway by using the location data from the
|
||||||
|
// explorer-api
|
||||||
|
let gateway_location = gateways_from_explorer_api
|
||||||
|
.iter()
|
||||||
|
.find(|g| g.gateway.identity_key == gateway)
|
||||||
|
.and_then(|g| g.location.clone())
|
||||||
|
.map(|location| location.two_letter_iso_country_code)
|
||||||
|
.tap_none(|| error!("No location found for the gateway: {}", gateway))?;
|
||||||
|
debug!(
|
||||||
|
"Filtering on nym-address: {}, with location: {}",
|
||||||
|
recipient, gateway_location
|
||||||
|
);
|
||||||
|
|
||||||
|
CountryGroup::new(&gateway_location)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug!("Filter group: {}", filter_on);
|
||||||
|
|
||||||
// Partition mixnodes_from_explorer_api according to the value of
|
// Partition mixnodes_from_explorer_api according to the value of
|
||||||
// two_letter_iso_country_code.
|
// two_letter_iso_country_code.
|
||||||
// NOTE: we construct the full distribution here, but only use the one we're interested in.
|
// NOTE: we construct the full distribution here, but only use the one we're interested in.
|
||||||
@@ -280,8 +382,16 @@ impl GeoAwareTopologyProvider {
|
|||||||
let mixnode_distribution = group_mixnodes_by_country_code(mixnodes_from_explorer_api);
|
let mixnode_distribution = group_mixnodes_by_country_code(mixnodes_from_explorer_api);
|
||||||
log_mixnode_distribution(&mixnode_distribution);
|
log_mixnode_distribution(&mixnode_distribution);
|
||||||
|
|
||||||
let Some(filtered_mixnode_ids) = mixnode_distribution.get(&self.filter_on) else {
|
let gateway_distribution = group_gateways_by_country_code(gateways_from_explorer_api);
|
||||||
error!("no mixnodes found for: {}", self.filter_on);
|
log_gateway_distribution(&gateway_distribution);
|
||||||
|
|
||||||
|
let Some(filtered_mixnode_ids) = mixnode_distribution.get(&filter_on) else {
|
||||||
|
error!("no mixnodes found for: {}", filter_on);
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(filtered_gateway_ids) = gateway_distribution.get(&filter_on) else {
|
||||||
|
error!("no gateways found for: {}", filter_on);
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -290,6 +400,11 @@ impl GeoAwareTopologyProvider {
|
|||||||
.filter(|m| filtered_mixnode_ids.contains(&m.mix_id()))
|
.filter(|m| filtered_mixnode_ids.contains(&m.mix_id()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let gateways = gateways
|
||||||
|
.into_iter()
|
||||||
|
.filter(|g| filtered_gateway_ids.contains(g.identity()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let topology = nym_topology_from_detailed(mixnodes, gateways)
|
let topology = nym_topology_from_detailed(mixnodes, gateways)
|
||||||
.filter_system_version(&self.client_version);
|
.filter_system_version(&self.client_version);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
use nym_config::defaults::NymNetworkDetails;
|
use nym_config::defaults::NymNetworkDetails;
|
||||||
use nym_crypto::asymmetric::identity;
|
use nym_crypto::asymmetric::identity;
|
||||||
use nym_sphinx::params::{PacketSize, PacketType};
|
use nym_sphinx::{
|
||||||
|
addressing::clients::Recipient,
|
||||||
|
params::{PacketSize, PacketType},
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@@ -480,11 +483,19 @@ pub struct Topology {
|
|||||||
pub topology_structure: TopologyStructure,
|
pub topology_structure: TopologyStructure,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum TopologyStructure {
|
pub enum TopologyStructure {
|
||||||
#[default]
|
#[default]
|
||||||
NymApi,
|
NymApi,
|
||||||
GeoAware(CountryGroup),
|
GeoAware(GroupBy),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum GroupBy {
|
||||||
|
CountryGroup(CountryGroup),
|
||||||
|
NymAddress(Recipient),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Topology {
|
impl Default for Topology {
|
||||||
|
|||||||
@@ -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,8 +15,6 @@ 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"))]
|
||||||
@@ -30,8 +28,6 @@ type WsConn = WebSocketStream<MaybeTlsStream<TcpStream>>;
|
|||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use nym_bandwidth_controller::wasm_mockups::DirectSigningNyxdClient;
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use wasm_utils::websocket::JSWebsocket;
|
use wasm_utils::websocket::JSWebsocket;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@@ -205,9 +201,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<Arc<SharedKeys>, ClientCoreError> {
|
) -> Result<RegistrationResult, ClientCoreError> {
|
||||||
let timeout = Duration::from_millis(1500);
|
let timeout = Duration::from_millis(1500);
|
||||||
let mut gateway_client: GatewayClient<DirectSigningNyxdClient, _> = GatewayClient::new_init(
|
let mut gateway_client = 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(),
|
||||||
@@ -221,5 +217,8 @@ 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(shared_keys)
|
Ok(RegistrationResult {
|
||||||
|
shared_keys,
|
||||||
|
authenticated_ephemeral_client: Some(gateway_client),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,16 +14,39 @@ 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 {
|
||||||
@@ -74,7 +97,6 @@ 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,
|
||||||
@@ -92,6 +114,13 @@ 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 {
|
||||||
@@ -266,18 +295,32 @@ 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<InitialisationDetails, ClientCoreError>
|
) -> Result<InitialisationResult, 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
|
||||||
@@ -286,14 +329,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));
|
return Ok(InitialisationDetails::new(details.into(), loaded_keys).into());
|
||||||
}
|
}
|
||||||
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
|
||||||
@@ -304,10 +347,9 @@ where
|
|||||||
_store_gateway_details(details_store, details).await?;
|
_store_gateway_details(details_store, details).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(InitialisationDetails::new(
|
return Ok(
|
||||||
details.clone().into(),
|
InitialisationDetails::new(details.clone().into(), loaded_keys).into(),
|
||||||
loaded_keys,
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
GatewaySetup::Specified { gateway_identity } => {
|
GatewaySetup::Specified { gateway_identity } => {
|
||||||
// if that data was already stored...
|
// if that data was already stored...
|
||||||
@@ -323,7 +365,8 @@ where
|
|||||||
return Ok(InitialisationDetails::new(
|
return Ok(InitialisationDetails::new(
|
||||||
existing_gateway.into(),
|
existing_gateway.into(),
|
||||||
loaded_keys,
|
loaded_keys,
|
||||||
));
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,7 +384,8 @@ 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
|
||||||
@@ -352,6 +396,9 @@ where
|
|||||||
return Err(ClientCoreError::ForbiddenKeyOverwrite);
|
return Err(ClientCoreError::ForbiddenKeyOverwrite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GatewaySetup::ReuseConnection { .. } => {
|
||||||
|
unreachable!("the reuse connection variant was already manually covered")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@@ -371,7 +418,9 @@ 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 shared_keys = helpers::register_with_gateway(&gateway_details, our_identity).await?;
|
let registration_result =
|
||||||
|
helpers::register_with_gateway(&gateway_details, our_identity).await?;
|
||||||
|
let shared_keys = registration_result.shared_keys;
|
||||||
|
|
||||||
let persisted_details = PersistedGatewayDetails::new(gateway_details, &shared_keys);
|
let persisted_details = PersistedGatewayDetails::new(gateway_details, &shared_keys);
|
||||||
|
|
||||||
@@ -386,19 +435,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(InitialisationDetails::new(
|
Ok(InitialisationResult {
|
||||||
persisted_details.into(),
|
details: InitialisationDetails::new(persisted_details.into(), managed_keys),
|
||||||
managed_keys,
|
authenticated_ephemeral_client: registration_result.authenticated_ephemeral_client,
|
||||||
))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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<InitialisationDetails, ClientCoreError>
|
) -> Result<InitialisationResult, ClientCoreError>
|
||||||
where
|
where
|
||||||
K: KeyStore,
|
K: KeyStore,
|
||||||
D: GatewayDetailsStore,
|
D: GatewayDetailsStore,
|
||||||
|
|||||||
@@ -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 = "0.3"
|
futures = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
thiserror = "1.0"
|
thiserror = { workspace = true }
|
||||||
url = "2.2"
|
url = { workspace = true }
|
||||||
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" }
|
nym-validator-client = { path = "../validator-client", default-features = false }
|
||||||
nym-task = { path = "../../task" }
|
nym-task = { path = "../../task" }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 - 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::error::GatewayClientError;
|
use crate::error::GatewayClientError;
|
||||||
@@ -22,21 +22,18 @@ 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;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tungstenite::protocol::Message;
|
use tungstenite::protocol::Message;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use nym_validator_client::nyxd::traits::DkgQueryClient;
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use tokio_tungstenite::connect_async;
|
use tokio_tungstenite::connect_async;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use nym_bandwidth_controller::wasm_mockups::DkgQueryClient;
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use wasm_utils::websocket::JSWebsocket;
|
use wasm_utils::websocket::JSWebsocket;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@@ -45,7 +42,7 @@ use wasmtimer::tokio::sleep;
|
|||||||
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> {
|
pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
||||||
authenticated: bool,
|
authenticated: bool,
|
||||||
disabled_credentials_mode: bool,
|
disabled_credentials_mode: bool,
|
||||||
bandwidth_remaining: i64,
|
bandwidth_remaining: i64,
|
||||||
@@ -199,7 +196,6 @@ impl<C, St> GatewayClient<C, St> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
sleep(self.reconnection_backoff).await;
|
sleep(self.reconnection_backoff).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,6 +467,14 @@ 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 {
|
||||||
@@ -755,7 +759,9 @@ impl<C, St> GatewayClient<C, St> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> GatewayClient<C, EphemeralCredentialStorage> {
|
pub struct InitOnly;
|
||||||
|
|
||||||
|
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,
|
||||||
@@ -772,7 +778,7 @@ impl<C> GatewayClient<C, 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::<C, EphemeralCredentialStorage> {
|
GatewayClient {
|
||||||
authenticated: false,
|
authenticated: false,
|
||||||
disabled_credentials_mode: true,
|
disabled_credentials_mode: true,
|
||||||
bandwidth_remaining: 0,
|
bandwidth_remaining: 0,
|
||||||
@@ -790,4 +796,37 @@ impl<C> GatewayClient<C, 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = "0.3"
|
futures = { workspace = true }
|
||||||
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"] }
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ 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" }
|
||||||
@@ -22,68 +23,67 @@ nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contr
|
|||||||
nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts/service-provider-directory" }
|
nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts/service-provider-directory" }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
reqwest = { workspace = true, features = ["json"] }
|
||||||
thiserror = "1"
|
thiserror = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
url = { version = "2.2", features = ["serde"] }
|
url = { workspace = true, features = ["serde"] }
|
||||||
tokio = { version = "1.24.1", features = ["sync", "time"] }
|
tokio = { workspace = true, features = ["sync", "time"] }
|
||||||
futures = "0.3"
|
futures = { workspace = true }
|
||||||
openssl = { version = "0.10", features = ["vendored"], optional = true }
|
openssl = { version = "^0.10.55", 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 }
|
async-trait = { workspace = true }
|
||||||
bip39 = { workspace = true, features = ["rand"], optional = true }
|
bip39 = { workspace = true, features = ["rand"] }
|
||||||
nym-config = { path = "../../config" }
|
nym-config = { path = "../../config" }
|
||||||
cosmrs = { workspace = true, features = ["bip32", "cosmwasm"] }
|
cosmrs = { workspace = true, features = ["bip32", "cosmwasm"] }
|
||||||
#cosmrs = { workspace = true, features = ["bip32", "rpc", "cosmwasm"], optional = true }
|
|
||||||
# note that this has the same version as used by cosmrs
|
# note that this has the same version as used by cosmrs
|
||||||
|
|
||||||
# import it just for the `Client` trait
|
# import it just for the `Client` trait
|
||||||
tendermint-rpc = { workspace = true }
|
tendermint-rpc = { workspace = true }
|
||||||
|
|
||||||
eyre = { version = "0.6", optional = true }
|
eyre = { version = "0.6" }
|
||||||
|
cw-utils = { workspace = true }
|
||||||
|
cw2 = { workspace = true }
|
||||||
cw3 = { workspace = true }
|
cw3 = { workspace = true }
|
||||||
cw4 = { workspace = true }
|
cw4 = { workspace = true }
|
||||||
|
cw-controllers = { workspace = true }
|
||||||
prost = { version = "0.11", default-features = false }
|
prost = { version = "0.11", default-features = false }
|
||||||
flate2 = { version = "1.0.20" }
|
flate2 = { version = "1.0.20" }
|
||||||
sha2 = { version = "0.9.5" }
|
sha2 = { version = "0.9.5" }
|
||||||
itertools = { version = "0.10" }
|
itertools = { version = "0.10" }
|
||||||
zeroize = { version = "1.5.7", optional = true, features = ["zeroize_derive"] }
|
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||||
cosmwasm-std = { workspace = true }
|
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 = ["rpc", "bip32"] }
|
|
||||||
cosmrs = { workspace = true, features = ["bip32"] }
|
cosmrs = { workspace = true, features = ["bip32"] }
|
||||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
ts-rs = { workspace = true }
|
||||||
ts-rs = "6.1.2"
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "offline_signing"
|
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", "signing"]
|
required-features = ["http-client"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "query_service_provider_directory"
|
name = "query_service_provider_directory"
|
||||||
# TODO: validate the requirements
|
required-features = ["http-client"]
|
||||||
required-features = ["http-client", "signing"]
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "query_name_service"
|
name = "query_name_service"
|
||||||
# TODO: validate the requirements
|
required-features = ["http-client"]
|
||||||
required-features = ["http-client", "signing"]
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["http-client"]
|
||||||
http-client = ["cosmrs/rpc", "openssl"]
|
http-client = ["cosmrs/rpc", "openssl"]
|
||||||
signing = [
|
|
||||||
"bip39",
|
|
||||||
"eyre",
|
|
||||||
"zeroize"
|
|
||||||
]
|
|
||||||
generate-ts = []
|
generate-ts = []
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
// 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::tx::Msg;
|
use cosmrs::tx::Msg;
|
||||||
use cosmrs::{tx, AccountId, Coin, Denom};
|
use cosmrs::{tx, AccountId, Coin, Denom};
|
||||||
|
use nym_validator_client::http_client;
|
||||||
use nym_validator_client::nyxd::CosmWasmClient;
|
use nym_validator_client::nyxd::CosmWasmClient;
|
||||||
use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet;
|
use nym_validator_client::signing::direct_wallet::DirectSecp256k1HdWallet;
|
||||||
use nym_validator_client::signing::tx_signer::TxSigner;
|
use nym_validator_client::signing::tx_signer::TxSigner;
|
||||||
@@ -23,11 +23,11 @@ async fn main() {
|
|||||||
let signer_address = signer.try_derive_accounts().unwrap()[0].address().clone();
|
let signer_address = signer.try_derive_accounts().unwrap()[0].address().clone();
|
||||||
|
|
||||||
// local 'client' ONLY signing messages
|
// local 'client' ONLY signing messages
|
||||||
let tx_signer = TxSigner::new(signer);
|
let tx_signer = 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)
|
||||||
let broadcaster = HttpClient::new(validator).unwrap();
|
let broadcaster = http_client(validator).unwrap();
|
||||||
|
|
||||||
// get signer information
|
// get signer information
|
||||||
let sequence_response = broadcaster.get_sequence(&signer_address).await.unwrap();
|
let sequence_response = broadcaster.get_sequence(&signer_address).await.unwrap();
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ 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::traits::NameServiceQueryClient;
|
use nym_validator_client::nyxd::contract_traits::{
|
||||||
|
NameServiceQueryClient, PagedNameServiceQueryClient,
|
||||||
|
};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
setup_env(Some(&"../../../envs/qa-qwerty.env".parse().unwrap()));
|
setup_env(Some("../../../envs/qa-qwerty.env"));
|
||||||
let network_details = NymNetworkDetails::new_from_env();
|
let network_details = NymNetworkDetails::new_from_env();
|
||||||
let config =
|
let config =
|
||||||
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
|
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
|
||||||
@@ -26,7 +28,7 @@ async fn main() {
|
|||||||
let names_by_owner = client.nyxd.get_names_by_owner(owner).await.unwrap();
|
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");
|
let nym_address = Address::new("client_id.client_key@gateway_id").unwrap();
|
||||||
let names_by_address = client.nyxd.get_names_by_address(nym_address).await.unwrap();
|
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,11 +3,13 @@ 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::traits::SpDirectoryQueryClient;
|
use nym_validator_client::nyxd::contract_traits::{
|
||||||
|
PagedSpDirectoryQueryClient, SpDirectoryQueryClient,
|
||||||
|
};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
setup_env(Some(&"../../../envs/qa-qwerty.env".parse().unwrap()));
|
setup_env(Some("../../../envs/qa-qwerty.env"));
|
||||||
let network_details = NymNetworkDetails::new_from_env();
|
let network_details = NymNetworkDetails::new_from_env();
|
||||||
let config =
|
let config =
|
||||||
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
|
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
|
||||||
|
|||||||
@@ -1,41 +1,35 @@
|
|||||||
// 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::{nym_api, ValidatorClientError};
|
use crate::nyxd::{self, NyxdClient};
|
||||||
|
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
|
||||||
|
use crate::signing::signer::{NoSigner, OfflineSigner};
|
||||||
|
use crate::{
|
||||||
|
nym_api, DirectSigningReqwestRpcValidatorClient, QueryReqwestRpcValidatorClient,
|
||||||
|
ReqwestRpcClient, ValidatorClientError,
|
||||||
|
};
|
||||||
use nym_api_requests::coconut::{
|
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_coconut_dkg_common::types::NodeIndex;
|
use nym_network_defaults::NymNetworkDetails;
|
||||||
use nym_coconut_interface::VerificationKey;
|
use url::Url;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
use crate::nyxd::traits::{DkgQueryClient, MixnetQueryClient};
|
// re-export the type to not break existing imports
|
||||||
|
pub use crate::coconut::CoconutApiClient;
|
||||||
|
|
||||||
#[cfg(feature = "http-client")]
|
#[cfg(feature = "http-client")]
|
||||||
use crate::nyxd::QueryNyxdClient;
|
use crate::rpc::http_client;
|
||||||
use crate::nyxd::{self, CosmWasmClient, NyxdClient};
|
#[cfg(feature = "http-client")]
|
||||||
use nym_api_requests::models::MixNodeBondAnnotated;
|
use crate::{DirectSigningHttpRpcValidatorClient, HttpRpcClient, QueryHttpRpcValidatorClient};
|
||||||
use nym_coconut_dkg_common::{types::EpochId, verification_key::ContractVKShare};
|
|
||||||
use nym_coconut_interface::Base58;
|
|
||||||
use nym_mixnet_contract_common::{
|
|
||||||
families::{Family, FamilyHead},
|
|
||||||
mixnode::MixNodeBond,
|
|
||||||
pending_events::{PendingEpochEvent, PendingIntervalEvent},
|
|
||||||
Delegation, RewardedSetNodeStatus, UnbondedMixnode,
|
|
||||||
};
|
|
||||||
use nym_network_defaults::NymNetworkDetails;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "signing", feature = "http-client"))]
|
|
||||||
use crate::nyxd::SigningNyxdClient;
|
|
||||||
#[cfg(all(feature = "signing", feature = "http-client"))]
|
|
||||||
use crate::signing::direct_wallet::DirectSecp256k1HdWallet;
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -44,13 +38,7 @@ pub struct Config {
|
|||||||
nyxd_url: Url,
|
nyxd_url: Url,
|
||||||
|
|
||||||
// TODO: until refactored, this is a dead field under some features
|
// TODO: until refactored, this is a dead field under some features
|
||||||
#[allow(dead_code)]
|
|
||||||
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>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -75,10 +63,6 @@ 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,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,87 +79,57 @@ impl Config {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_mixnode_page_limit(mut self, limit: Option<u32>) -> Config {
|
pub fn with_simulated_gas_multiplier(mut self, gas_multiplier: f32) -> Self {
|
||||||
self.mixnode_page_limit = limit;
|
self.nyxd_config.simulated_gas_multiplier = gas_multiplier;
|
||||||
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> {
|
pub struct Client<C, S = NoSigner> {
|
||||||
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>,
|
pub nyxd: NyxdClient<C, S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "signing", feature = "http-client"))]
|
#[cfg(feature = "http-client")]
|
||||||
impl Client<SigningNyxdClient<DirectSecp256k1HdWallet>> {
|
impl Client<HttpRpcClient, DirectSecp256k1HdWallet> {
|
||||||
pub fn new_signing(
|
pub fn new_signing(
|
||||||
config: Config,
|
config: Config,
|
||||||
mnemonic: bip39::Mnemonic,
|
mnemonic: bip39::Mnemonic,
|
||||||
) -> Result<Client<SigningNyxdClient<DirectSecp256k1HdWallet>>, ValidatorClientError> {
|
) -> Result<DirectSigningHttpRpcValidatorClient, ValidatorClientError> {
|
||||||
let nym_api_client = nym_api::Client::new(config.api_url.clone());
|
let rpc_client = http_client(config.nyxd_url.as_str())?;
|
||||||
let nyxd_client = NyxdClient::connect_with_mnemonic(
|
let prefix = &config.nyxd_config.chain_details.bech32_account_prefix;
|
||||||
config.nyxd_config.clone(),
|
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
|
||||||
config.nyxd_url.as_str(),
|
|
||||||
mnemonic,
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Client {
|
Ok(Self::new_signing_with_rpc_client(
|
||||||
mixnode_page_limit: config.mixnode_page_limit,
|
config, rpc_client, wallet,
|
||||||
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(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_nyxd_simulated_gas_multiplier(&mut self, multiplier: f32) {
|
impl Client<ReqwestRpcClient, DirectSecp256k1HdWallet> {
|
||||||
self.nyxd.set_simulated_gas_multiplier(multiplier)
|
pub fn new_reqwest_signing(
|
||||||
|
config: Config,
|
||||||
|
mnemonic: bip39::Mnemonic,
|
||||||
|
) -> DirectSigningReqwestRpcValidatorClient {
|
||||||
|
let rpc_client = ReqwestRpcClient::new(config.nyxd_url.clone());
|
||||||
|
let prefix = &config.nyxd_config.chain_details.bech32_account_prefix;
|
||||||
|
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic);
|
||||||
|
|
||||||
|
Self::new_signing_with_rpc_client(config, rpc_client, wallet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "http-client")]
|
#[cfg(feature = "http-client")]
|
||||||
impl Client<QueryNyxdClient> {
|
impl Client<HttpRpcClient> {
|
||||||
pub fn new_query(config: Config) -> Result<Client<QueryNyxdClient>, ValidatorClientError> {
|
pub fn new_query(config: Config) -> Result<QueryHttpRpcValidatorClient, ValidatorClientError> {
|
||||||
let nym_api_client = nym_api::Client::new(config.api_url.clone());
|
let rpc_client = http_client(config.nyxd_url.as_str())?;
|
||||||
let nyxd_client =
|
Ok(Self::new_with_rpc_client(config, rpc_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> {
|
||||||
@@ -184,390 +138,44 @@ impl Client<QueryNyxdClient> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// nyxd wrappers
|
impl Client<ReqwestRpcClient> {
|
||||||
|
pub fn new_reqwest_query(config: Config) -> QueryReqwestRpcValidatorClient {
|
||||||
|
let rpc_client = ReqwestRpcClient::new(config.nyxd_url.clone());
|
||||||
|
Self::new_with_rpc_client(config, rpc_client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<C> Client<C> {
|
impl<C> Client<C> {
|
||||||
// use case: somebody initialised client without a contract in order to upload and initialise one
|
pub fn new_with_rpc_client(config: Config, rpc_client: C) -> Self {
|
||||||
// and now they want to actually use it without making new client
|
let nym_api_client = nym_api::Client::new(config.api_url.clone());
|
||||||
|
|
||||||
pub fn set_mixnet_contract_address(&mut self, mixnet_contract_address: cosmrs::AccountId) {
|
Client {
|
||||||
self.nyxd
|
nym_api: nym_api_client,
|
||||||
.set_mixnet_contract_address(mixnet_contract_address)
|
nyxd: NyxdClient::new(config.nyxd_config, rpc_client),
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mixnet_contract_address(&self) -> cosmrs::AccountId {
|
|
||||||
self.nyxd.mixnet_contract_address().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_all_node_families(&self) -> Result<Vec<Family>, ValidatorClientError>
|
|
||||||
where
|
|
||||||
C: CosmWasmClient + Sync + Send,
|
|
||||||
{
|
|
||||||
let mut families = Vec::new();
|
|
||||||
let mut start_after = None;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let paged_response = self
|
|
||||||
.nyxd
|
|
||||||
.get_all_node_families_paged(start_after.take(), None)
|
|
||||||
.await?;
|
|
||||||
families.extend(paged_response.families);
|
|
||||||
|
|
||||||
if let Some(start_after_res) = paged_response.start_next_after {
|
|
||||||
start_after = Some(start_after_res)
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(families)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_all_family_members(
|
impl<C, S> Client<C, S> {
|
||||||
&self,
|
pub fn new_signing_with_rpc_client(config: Config, rpc_client: C, signer: S) -> Self
|
||||||
) -> Result<Vec<(IdentityKey, FamilyHead)>, ValidatorClientError>
|
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync + Send,
|
S: OfflineSigner,
|
||||||
{
|
{
|
||||||
let mut members = Vec::new();
|
let nym_api_client = nym_api::Client::new(config.api_url.clone());
|
||||||
let mut start_after = None;
|
|
||||||
|
|
||||||
loop {
|
Client {
|
||||||
let paged_response = self
|
nym_api: nym_api_client,
|
||||||
.nyxd
|
nyxd: NyxdClient::new_signing(config.nyxd_config, rpc_client, signer),
|
||||||
.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> Client<C> {
|
impl<C, S> Client<C, S> {
|
||||||
|
pub fn api_url(&self) -> &Url {
|
||||||
|
self.nym_api.current_url()
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -624,85 +232,46 @@ impl<C> Client<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct CoconutApiClient {
|
|
||||||
pub api_client: NymApiClient,
|
|
||||||
pub verification_key: VerificationKey,
|
|
||||||
pub node_id: NodeIndex,
|
|
||||||
pub cosmos_address: cosmrs::AccountId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CoconutApiClient {
|
|
||||||
pub async fn all_coconut_api_clients<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_client: nym_api::Client,
|
pub nym_api: nym_api::Client,
|
||||||
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
|
// 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_client = nym_api::Client::new(api_url);
|
let nym_api = nym_api::Client::new(api_url);
|
||||||
|
|
||||||
NymApiClient { nym_api_client }
|
NymApiClient { nym_api }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn api_url(&self) -> &Url {
|
||||||
|
self.nym_api.current_url()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_nym_api(&mut self, new_endpoint: Url) {
|
pub fn change_nym_api(&mut self, new_endpoint: Url) {
|
||||||
self.nym_api_client.change_url(new_endpoint);
|
self.nym_api.change_url(new_endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_cached_active_mixnodes(
|
pub async fn get_cached_active_mixnodes(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||||
Ok(self.nym_api_client.get_active_mixnodes().await?)
|
Ok(self.nym_api.get_active_mixnodes().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_cached_rewarded_mixnodes(
|
pub async fn get_cached_rewarded_mixnodes(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||||
Ok(self.nym_api_client.get_rewarded_mixnodes().await?)
|
Ok(self.nym_api.get_rewarded_mixnodes().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeDetails>, ValidatorClientError> {
|
||||||
Ok(self.nym_api_client.get_mixnodes().await?)
|
Ok(self.nym_api.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_client.get_gateways().await?)
|
Ok(self.nym_api.get_gateways().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_gateway_core_status_count(
|
pub async fn get_gateway_core_status_count(
|
||||||
@@ -711,7 +280,7 @@ impl NymApiClient {
|
|||||||
since: Option<i64>,
|
since: Option<i64>,
|
||||||
) -> Result<GatewayCoreStatusResponse, ValidatorClientError> {
|
) -> Result<GatewayCoreStatusResponse, ValidatorClientError> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.nym_api_client
|
.nym_api
|
||||||
.get_gateway_core_status_count(identity, since)
|
.get_gateway_core_status_count(identity, since)
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
@@ -722,7 +291,7 @@ impl NymApiClient {
|
|||||||
since: Option<i64>,
|
since: Option<i64>,
|
||||||
) -> Result<MixnodeCoreStatusResponse, ValidatorClientError> {
|
) -> Result<MixnodeCoreStatusResponse, ValidatorClientError> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.nym_api_client
|
.nym_api
|
||||||
.get_mixnode_core_status_count(mix_id, since)
|
.get_mixnode_core_status_count(mix_id, since)
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
@@ -731,34 +300,28 @@ impl NymApiClient {
|
|||||||
&self,
|
&self,
|
||||||
mix_id: MixId,
|
mix_id: MixId,
|
||||||
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
|
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
|
||||||
Ok(self.nym_api_client.get_mixnode_status(mix_id).await?)
|
Ok(self.nym_api.get_mixnode_status(mix_id).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_mixnode_reward_estimation(
|
pub async fn get_mixnode_reward_estimation(
|
||||||
&self,
|
&self,
|
||||||
mix_id: MixId,
|
mix_id: MixId,
|
||||||
) -> Result<RewardEstimationResponse, ValidatorClientError> {
|
) -> Result<RewardEstimationResponse, ValidatorClientError> {
|
||||||
Ok(self
|
Ok(self.nym_api.get_mixnode_reward_estimation(mix_id).await?)
|
||||||
.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
|
Ok(self.nym_api.get_mixnode_stake_saturation(mix_id).await?)
|
||||||
.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_client.blind_sign(request_body).await?)
|
Ok(self.nym_api.blind_sign(request_body).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn verify_bandwidth_credential(
|
pub async fn verify_bandwidth_credential(
|
||||||
@@ -766,7 +329,7 @@ impl NymApiClient {
|
|||||||
request_body: &VerifyCredentialBody,
|
request_body: &VerifyCredentialBody,
|
||||||
) -> Result<VerifyCredentialResponse, ValidatorClientError> {
|
) -> Result<VerifyCredentialResponse, ValidatorClientError> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.nym_api_client
|
.nym_api
|
||||||
.verify_bandwidth_credential(request_body)
|
.verify_bandwidth_credential(request_body)
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
// 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,8 +1,7 @@
|
|||||||
|
use crate::nyxd::contract_traits::MixnetQueryClient;
|
||||||
use crate::nyxd::error::NyxdError;
|
use crate::nyxd::error::NyxdError;
|
||||||
use crate::nyxd::{Config as ClientConfig, NyxdClient, QueryNyxdClient};
|
use crate::nyxd::Config as ClientConfig;
|
||||||
use crate::{NymApiClient, ValidatorClientError};
|
use crate::{NymApiClient, QueryHttpRpcNyxdClient, ValidatorClientError};
|
||||||
|
|
||||||
use crate::nyxd::traits::MixnetQueryClient;
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
@@ -53,7 +52,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 = NyxdClient::<QueryNyxdClient>::connect(config, nyxd_url.as_str())?;
|
let mut nyxd_client = QueryHttpRpcNyxdClient::connect(config, nyxd_url.as_str())?;
|
||||||
// possibly redundant, but lets just leave it here
|
// 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 {
|
||||||
@@ -75,7 +74,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) = NyxdClient::<QueryNyxdClient>::connect(config, url.as_str()) {
|
if let Ok(mut client) = QueryHttpRpcNyxdClient::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(
|
||||||
@@ -112,7 +111,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: &NyxdClient<QueryNyxdClient>,
|
client: &QueryHttpRpcNyxdClient,
|
||||||
) -> ConnectionResult {
|
) -> ConnectionResult {
|
||||||
let result = match timeout(
|
let result = match timeout(
|
||||||
Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC),
|
Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC),
|
||||||
@@ -186,7 +185,7 @@ async fn test_nym_api_connection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum ClientForConnectionTest {
|
enum ClientForConnectionTest {
|
||||||
Nyxd(NymNetworkDetails, Url, Box<NyxdClient<QueryNyxdClient>>),
|
Nyxd(NymNetworkDetails, Url, Box<QueryHttpRpcNyxdClient>),
|
||||||
Api(NymNetworkDetails, Url, NymApiClient),
|
Api(NymNetworkDetails, Url, NymApiClient),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// 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)]
|
||||||
@@ -12,6 +13,9 @@ 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),
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,46 @@
|
|||||||
// Copyright 2021 - 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
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
pub mod coconut;
|
||||||
#[cfg(feature = "http-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;
|
||||||
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::*;
|
||||||
|
|
||||||
pub use client::{Client, CoconutApiClient, Config};
|
#[cfg(feature = "http-client")]
|
||||||
|
pub use cosmrs::rpc::HttpClient as HttpRpcClient;
|
||||||
|
#[cfg(feature = "http-client")]
|
||||||
|
pub use rpc::http_client;
|
||||||
|
|
||||||
|
// some type aliasing
|
||||||
|
|
||||||
|
pub type ValidatorClient<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, GatewayCoreStatusResponse, GatewayStatusReportResponse,
|
ComputeRewardEstParam, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||||
GatewayUptimeHistoryResponse, InclusionProbabilityResponse, MixNodeBondAnnotated,
|
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
|
||||||
MixnodeCoreStatusResponse, MixnodeStatusReportResponse, MixnodeStatusResponse,
|
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
|
||||||
MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse, StakeSaturationResponse,
|
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RequestError, RewardEstimationResponse,
|
||||||
UptimeResponse,
|
StakeSaturationResponse, 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,6 +148,19 @@ 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> {
|
||||||
|
|||||||
+100
@@ -0,0 +1,100 @@
|
|||||||
|
// 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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+153
@@ -0,0 +1,153 @@
|
|||||||
|
// 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()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
// 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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
// 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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
// 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()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
// 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()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
// 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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
// 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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,745 @@
|
|||||||
|
// 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+257
-29
@@ -2,10 +2,11 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::nyxd::coin::Coin;
|
use crate::nyxd::coin::Coin;
|
||||||
pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient;
|
use crate::nyxd::contract_traits::NymContractsProvider;
|
||||||
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
|
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
|
||||||
use crate::nyxd::error::NyxdError;
|
use crate::nyxd::error::NyxdError;
|
||||||
use crate::nyxd::{Fee, NyxdClient, SigningCosmWasmClient};
|
use crate::nyxd::{Fee, SigningCosmWasmClient};
|
||||||
|
use crate::signing::signer::OfflineSigner;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use cosmrs::AccountId;
|
use cosmrs::AccountId;
|
||||||
use nym_contracts_common::signing::MessageSignature;
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
@@ -14,10 +15,12 @@ use nym_mixnet_contract_common::gateway::GatewayConfigUpdate;
|
|||||||
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
use nym_mixnet_contract_common::mixnode::{MixNodeConfigUpdate, MixNodeCostParams};
|
||||||
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
|
use nym_mixnet_contract_common::reward_params::{IntervalRewardingParamsUpdate, Performance};
|
||||||
use nym_mixnet_contract_common::{
|
use nym_mixnet_contract_common::{
|
||||||
ContractStateParams, ExecuteMsg as MixnetExecuteMsg, Gateway, LayerAssignment, MixId, MixNode,
|
ContractStateParams, ExecuteMsg as MixnetExecuteMsg, Gateway, Layer, LayerAssignment, MixId,
|
||||||
|
MixNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
pub trait MixnetSigningClient {
|
pub trait MixnetSigningClient {
|
||||||
async fn execute_mixnet_contract(
|
async fn execute_mixnet_contract(
|
||||||
&self,
|
&self,
|
||||||
@@ -131,6 +134,20 @@ pub trait MixnetSigningClient {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn assign_node_layer(
|
||||||
|
&self,
|
||||||
|
mix_id: MixId,
|
||||||
|
layer: Layer,
|
||||||
|
fee: Option<Fee>,
|
||||||
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_mixnet_contract(
|
||||||
|
fee,
|
||||||
|
MixnetExecuteMsg::AssignNodeLayer { mix_id, layer },
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn reconcile_epoch_events(
|
async fn reconcile_epoch_events(
|
||||||
&self,
|
&self,
|
||||||
limit: Option<u32>,
|
limit: Option<u32>,
|
||||||
@@ -665,12 +682,26 @@ pub trait MixnetSigningClient {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
|
||||||
|
async fn testing_resolve_all_pending_events(
|
||||||
|
fee: Option<Fee>,
|
||||||
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_mixnet_contract(
|
||||||
|
fee,
|
||||||
|
MixnetExecuteMsg::TestingResolveAllPendingEvents {},
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
impl<C> MixnetSigningClient for NyxdClient<C>
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
|
impl<C> MixnetSigningClient for C
|
||||||
where
|
where
|
||||||
C: SigningCosmWasmClient + Sync + Send,
|
C: SigningCosmWasmClient + NymContractsProvider + Sync,
|
||||||
|
NyxdError: From<<Self as OfflineSigner>::Error>,
|
||||||
{
|
{
|
||||||
async fn execute_mixnet_contract(
|
async fn execute_mixnet_contract(
|
||||||
&self,
|
&self,
|
||||||
@@ -678,32 +709,229 @@ where
|
|||||||
msg: MixnetExecuteMsg,
|
msg: MixnetExecuteMsg,
|
||||||
funds: Vec<Coin>,
|
funds: Vec<Coin>,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
let mixnet_contract_address = &self
|
||||||
|
.mixnet_contract_address()
|
||||||
|
.ok_or_else(|| NyxdError::unavailable_contract_address("mixnet contract"))?;
|
||||||
|
|
||||||
|
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||||
let memo = msg.default_memo();
|
let memo = msg.default_memo();
|
||||||
self.client
|
|
||||||
.execute(
|
let signer_address = &self.signer_addresses()?[0];
|
||||||
self.address(),
|
self.execute(
|
||||||
self.mixnet_contract_address(),
|
signer_address,
|
||||||
&msg,
|
mixnet_contract_address,
|
||||||
fee,
|
&msg,
|
||||||
memo,
|
fee,
|
||||||
funds,
|
memo,
|
||||||
)
|
funds,
|
||||||
.await
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[cfg(test)]
|
||||||
impl<C> MixnetSigningClient for crate::Client<C>
|
mod tests {
|
||||||
where
|
use super::*;
|
||||||
C: SigningCosmWasmClient + Sync + Send + Clone,
|
use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue};
|
||||||
{
|
|
||||||
async fn execute_mixnet_contract(
|
// it's enough that this compiles and clippy is happy about it
|
||||||
&self,
|
#[allow(dead_code)]
|
||||||
fee: Option<Fee>,
|
fn all_execute_variants_are_covered<C: MixnetSigningClient + Send + Sync>(
|
||||||
|
client: C,
|
||||||
msg: MixnetExecuteMsg,
|
msg: MixnetExecuteMsg,
|
||||||
funds: Vec<Coin>,
|
) {
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
match msg {
|
||||||
self.nyxd.execute_mixnet_contract(fee, msg, funds).await
|
MixnetExecuteMsg::AssignNodeLayer { mix_id, layer } => {
|
||||||
|
client.assign_node_layer(mix_id, layer, None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::CreateFamily { label } => client.create_family(label, None).ignore(),
|
||||||
|
MixnetExecuteMsg::JoinFamily {
|
||||||
|
join_permit,
|
||||||
|
family_head,
|
||||||
|
} => client.join_family(join_permit, family_head, None).ignore(),
|
||||||
|
MixnetExecuteMsg::LeaveFamily { family_head } => {
|
||||||
|
client.leave_family(family_head, None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::KickFamilyMember { member } => {
|
||||||
|
client.kick_family_member(member, None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::CreateFamilyOnBehalf {
|
||||||
|
owner_address,
|
||||||
|
label,
|
||||||
|
} => client
|
||||||
|
.create_family_on_behalf(owner_address, label, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::JoinFamilyOnBehalf {
|
||||||
|
member_address,
|
||||||
|
join_permit,
|
||||||
|
family_head,
|
||||||
|
} => client
|
||||||
|
.join_family_on_behalf(member_address, join_permit, family_head, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::LeaveFamilyOnBehalf {
|
||||||
|
member_address,
|
||||||
|
family_head,
|
||||||
|
} => client
|
||||||
|
.leave_family_on_behalf(member_address, family_head, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::KickFamilyMemberOnBehalf {
|
||||||
|
head_address,
|
||||||
|
member,
|
||||||
|
} => client
|
||||||
|
.kick_family_member_on_behalf(head_address, member, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::UpdateRewardingValidatorAddress { address } => client
|
||||||
|
.update_rewarding_validator_address(address.parse().unwrap(), None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::UpdateContractStateParams { updated_parameters } => client
|
||||||
|
.update_contract_state_params(updated_parameters, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::UpdateActiveSetSize {
|
||||||
|
active_set_size,
|
||||||
|
force_immediately,
|
||||||
|
} => client
|
||||||
|
.update_active_set_size(active_set_size, force_immediately, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::UpdateRewardingParams {
|
||||||
|
updated_params,
|
||||||
|
force_immediately,
|
||||||
|
} => client
|
||||||
|
.update_rewarding_parameters(updated_params, force_immediately, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::UpdateIntervalConfig {
|
||||||
|
epochs_in_interval,
|
||||||
|
epoch_duration_secs,
|
||||||
|
force_immediately,
|
||||||
|
} => client
|
||||||
|
.update_interval_config(
|
||||||
|
epochs_in_interval,
|
||||||
|
epoch_duration_secs,
|
||||||
|
force_immediately,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::BeginEpochTransition {} => {
|
||||||
|
client.begin_epoch_transition(None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::AdvanceCurrentEpoch {
|
||||||
|
new_rewarded_set,
|
||||||
|
expected_active_set_size,
|
||||||
|
} => client
|
||||||
|
.advance_current_epoch(new_rewarded_set, expected_active_set_size, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::ReconcileEpochEvents { limit } => {
|
||||||
|
client.reconcile_epoch_events(limit, None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::BondMixnode {
|
||||||
|
mix_node,
|
||||||
|
cost_params,
|
||||||
|
owner_signature,
|
||||||
|
} => client
|
||||||
|
.bond_mixnode(mix_node, cost_params, owner_signature, mock_coin(), None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::BondMixnodeOnBehalf {
|
||||||
|
mix_node,
|
||||||
|
cost_params,
|
||||||
|
owner_signature,
|
||||||
|
owner,
|
||||||
|
} => client
|
||||||
|
.bond_mixnode_on_behalf(
|
||||||
|
owner.parse().unwrap(),
|
||||||
|
mix_node,
|
||||||
|
cost_params,
|
||||||
|
owner_signature,
|
||||||
|
mock_coin(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::PledgeMore {} => client.pledge_more(mock_coin(), None).ignore(),
|
||||||
|
MixnetExecuteMsg::PledgeMoreOnBehalf { owner } => client
|
||||||
|
.pledge_more_on_behalf(owner.parse().unwrap(), mock_coin(), None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::DecreasePledge { decrease_by } => {
|
||||||
|
client.decrease_pledge(decrease_by.into(), None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::DecreasePledgeOnBehalf { owner, decrease_by } => client
|
||||||
|
.decrease_pledge_on_behalf(owner.parse().unwrap(), decrease_by.into(), None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::UnbondMixnode {} => client.unbond_mixnode(None).ignore(),
|
||||||
|
MixnetExecuteMsg::UnbondMixnodeOnBehalf { owner } => client
|
||||||
|
.unbond_mixnode_on_behalf(owner.parse().unwrap(), None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::UpdateMixnodeCostParams { new_costs } => {
|
||||||
|
client.update_mixnode_cost_params(new_costs, None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::UpdateMixnodeCostParamsOnBehalf { new_costs, owner } => client
|
||||||
|
.update_mixnode_cost_params_on_behalf(owner.parse().unwrap(), new_costs, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::UpdateMixnodeConfig { new_config } => {
|
||||||
|
client.update_mixnode_config(new_config, None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::UpdateMixnodeConfigOnBehalf { new_config, owner } => client
|
||||||
|
.update_mixnode_config_on_behalf(owner.parse().unwrap(), new_config, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::BondGateway {
|
||||||
|
gateway,
|
||||||
|
owner_signature,
|
||||||
|
} => client
|
||||||
|
.bond_gateway(gateway, owner_signature, mock_coin(), None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::BondGatewayOnBehalf {
|
||||||
|
gateway,
|
||||||
|
owner,
|
||||||
|
owner_signature,
|
||||||
|
} => client
|
||||||
|
.bond_gateway_on_behalf(
|
||||||
|
owner.parse().unwrap(),
|
||||||
|
gateway,
|
||||||
|
owner_signature,
|
||||||
|
mock_coin(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::UnbondGateway {} => client.unbond_gateway(None).ignore(),
|
||||||
|
MixnetExecuteMsg::UnbondGatewayOnBehalf { owner } => client
|
||||||
|
.unbond_gateway_on_behalf(owner.parse().unwrap(), None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::UpdateGatewayConfig { new_config } => {
|
||||||
|
client.update_gateway_config(new_config, None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::UpdateGatewayConfigOnBehalf { new_config, owner } => client
|
||||||
|
.update_gateway_config_on_behalf(owner.parse().unwrap(), new_config, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::DelegateToMixnode { mix_id } => client
|
||||||
|
.delegate_to_mixnode(mix_id, mock_coin(), None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::DelegateToMixnodeOnBehalf { mix_id, delegate } => client
|
||||||
|
.delegate_to_mixnode_on_behalf(delegate.parse().unwrap(), mix_id, mock_coin(), None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::UndelegateFromMixnode { mix_id } => {
|
||||||
|
client.undelegate_from_mixnode(mix_id, None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::UndelegateFromMixnodeOnBehalf { mix_id, delegate } => client
|
||||||
|
.undelegate_to_mixnode_on_behalf(delegate.parse().unwrap(), mix_id, None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::RewardMixnode {
|
||||||
|
mix_id,
|
||||||
|
performance,
|
||||||
|
} => client.reward_mixnode(mix_id, performance, None).ignore(),
|
||||||
|
MixnetExecuteMsg::WithdrawOperatorReward {} => {
|
||||||
|
client.withdraw_operator_reward(None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::WithdrawOperatorRewardOnBehalf { owner } => client
|
||||||
|
.withdraw_operator_reward_on_behalf(owner.parse().unwrap(), None)
|
||||||
|
.ignore(),
|
||||||
|
MixnetExecuteMsg::WithdrawDelegatorReward { mix_id } => {
|
||||||
|
client.withdraw_delegator_reward(mix_id, None).ignore()
|
||||||
|
}
|
||||||
|
MixnetExecuteMsg::WithdrawDelegatorRewardOnBehalf { mix_id, owner } => client
|
||||||
|
.withdraw_delegator_reward_on_behalf(owner.parse().unwrap(), mix_id, None)
|
||||||
|
.ignore(),
|
||||||
|
|
||||||
|
#[cfg(feature = "nym_mixnet_contract_common/contract-testing")]
|
||||||
|
MixnetExecuteMsg::TestingResolveAllPendingEvents {} => {
|
||||||
|
client.testing_resolve_all_pending_events(None).ignore()
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
// 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
+178
@@ -0,0 +1,178 @@
|
|||||||
|
// 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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+192
@@ -0,0 +1,192 @@
|
|||||||
|
// 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()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
// 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()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
-19
@@ -5,12 +5,14 @@ use async_trait::async_trait;
|
|||||||
use nym_contracts_common::signing::MessageSignature;
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_name_service_common::{msg::ExecuteMsg as NameExecuteMsg, NameDetails, NameId, NymName};
|
use nym_name_service_common::{msg::ExecuteMsg as NameExecuteMsg, NameDetails, NameId, NymName};
|
||||||
|
|
||||||
|
use crate::nyxd::contract_traits::NymContractsProvider;
|
||||||
use crate::nyxd::{
|
use crate::nyxd::{
|
||||||
coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, NyxdClient,
|
coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, SigningCosmWasmClient,
|
||||||
SigningCosmWasmClient,
|
|
||||||
};
|
};
|
||||||
|
use crate::signing::signer::OfflineSigner;
|
||||||
|
|
||||||
#[async_trait]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
pub trait NameServiceSigningClient {
|
pub trait NameServiceSigningClient {
|
||||||
async fn execute_name_service_contract(
|
async fn execute_name_service_contract(
|
||||||
&self,
|
&self,
|
||||||
@@ -71,10 +73,12 @@ pub trait NameServiceSigningClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
impl<C> NameServiceSigningClient for NyxdClient<C>
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
|
impl<C> NameServiceSigningClient for C
|
||||||
where
|
where
|
||||||
C: SigningCosmWasmClient + Sync + Send,
|
C: SigningCosmWasmClient + NymContractsProvider + Sync,
|
||||||
|
NyxdError: From<<Self as OfflineSigner>::Error>,
|
||||||
{
|
{
|
||||||
async fn execute_name_service_contract(
|
async fn execute_name_service_contract(
|
||||||
&self,
|
&self,
|
||||||
@@ -82,19 +86,53 @@ where
|
|||||||
msg: NameExecuteMsg,
|
msg: NameExecuteMsg,
|
||||||
funds: Vec<Coin>,
|
funds: Vec<Coin>,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
let name_service_contract_address = &self
|
||||||
|
.name_service_contract_address()
|
||||||
|
.ok_or_else(|| NyxdError::unavailable_contract_address("name service contract"))?;
|
||||||
|
|
||||||
|
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||||
let memo = msg.default_memo();
|
let memo = msg.default_memo();
|
||||||
self.client
|
|
||||||
.execute(
|
let signer_address = &self.signer_addresses()?[0];
|
||||||
self.address(),
|
self.execute(
|
||||||
self.name_service_contract_address().ok_or(
|
signer_address,
|
||||||
NyxdError::NoContractAddressAvailable("name service contract".to_string()),
|
name_service_contract_address,
|
||||||
)?,
|
&msg,
|
||||||
&msg,
|
fee,
|
||||||
fee,
|
memo,
|
||||||
memo,
|
funds,
|
||||||
funds,
|
)
|
||||||
)
|
.await
|
||||||
.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: NameServiceSigningClient + Send + Sync>(
|
||||||
|
client: C,
|
||||||
|
msg: NameExecuteMsg,
|
||||||
|
) {
|
||||||
|
match msg {
|
||||||
|
NameExecuteMsg::Register {
|
||||||
|
name,
|
||||||
|
owner_signature,
|
||||||
|
} => client
|
||||||
|
.register_name(name, owner_signature, mock_coin(), None)
|
||||||
|
.ignore(),
|
||||||
|
NameExecuteMsg::DeleteId { name_id } => {
|
||||||
|
client.delete_name_by_id(name_id, None).ignore()
|
||||||
|
}
|
||||||
|
NameExecuteMsg::DeleteName { name } => {
|
||||||
|
client.delete_service_provider_by_name(name, None).ignore()
|
||||||
|
}
|
||||||
|
NameExecuteMsg::UpdateDepositRequired { deposit_required } => client
|
||||||
|
.update_deposit_required(deposit_required.into(), None)
|
||||||
|
.ignore(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
use crate::collect_paged;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use cosmrs::AccountId;
|
||||||
|
use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
|
||||||
|
use nym_service_provider_directory_common::{
|
||||||
|
msg::QueryMsg as SpQueryMsg,
|
||||||
|
response::{
|
||||||
|
ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse,
|
||||||
|
},
|
||||||
|
NymAddress, Service, ServiceId,
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::nyxd::contract_traits::NymContractsProvider;
|
||||||
|
use crate::nyxd::{error::NyxdError, CosmWasmClient};
|
||||||
|
|
||||||
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
|
pub trait SpDirectoryQueryClient {
|
||||||
|
async fn query_service_provider_contract<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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
-24
@@ -1,18 +1,19 @@
|
|||||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::nyxd::contract_traits::NymContractsProvider;
|
||||||
|
use crate::nyxd::{
|
||||||
|
coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, SigningCosmWasmClient,
|
||||||
|
};
|
||||||
|
use crate::signing::signer::OfflineSigner;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use nym_contracts_common::signing::MessageSignature;
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
use nym_service_provider_directory_common::{
|
use nym_service_provider_directory_common::{
|
||||||
msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceDetails, ServiceId,
|
msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceDetails, ServiceId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::nyxd::{
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, NyxdClient,
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
SigningCosmWasmClient,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait SpDirectorySigningClient {
|
pub trait SpDirectorySigningClient {
|
||||||
async fn execute_service_provider_directory_contract(
|
async fn execute_service_provider_directory_contract(
|
||||||
&self,
|
&self,
|
||||||
@@ -81,10 +82,12 @@ pub trait SpDirectorySigningClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
impl<C> SpDirectorySigningClient for NyxdClient<C>
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
|
impl<C> SpDirectorySigningClient for C
|
||||||
where
|
where
|
||||||
C: SigningCosmWasmClient + Sync + Send,
|
C: SigningCosmWasmClient + NymContractsProvider + Sync,
|
||||||
|
NyxdError: From<<Self as OfflineSigner>::Error>,
|
||||||
{
|
{
|
||||||
async fn execute_service_provider_directory_contract(
|
async fn execute_service_provider_directory_contract(
|
||||||
&self,
|
&self,
|
||||||
@@ -92,21 +95,54 @@ where
|
|||||||
msg: SpExecuteMsg,
|
msg: SpExecuteMsg,
|
||||||
funds: Vec<Coin>,
|
funds: Vec<Coin>,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
let sp_directory_contract_address =
|
||||||
|
&self.service_provider_contract_address().ok_or_else(|| {
|
||||||
|
NyxdError::unavailable_contract_address("service provider directory contract")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||||
let memo = msg.default_memo();
|
let memo = msg.default_memo();
|
||||||
self.client
|
|
||||||
.execute(
|
let signer_address = &self.signer_addresses()?[0];
|
||||||
self.address(),
|
self.execute(
|
||||||
self.service_provider_contract_address().ok_or(
|
signer_address,
|
||||||
NyxdError::NoContractAddressAvailable(
|
sp_directory_contract_address,
|
||||||
"service provider directory contract".to_string(),
|
&msg,
|
||||||
),
|
fee,
|
||||||
)?,
|
memo,
|
||||||
&msg,
|
funds,
|
||||||
fee,
|
)
|
||||||
memo,
|
.await
|
||||||
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: SpDirectorySigningClient + Send + Sync>(
|
||||||
|
client: C,
|
||||||
|
msg: SpExecuteMsg,
|
||||||
|
) {
|
||||||
|
match msg {
|
||||||
|
SpExecuteMsg::Announce {
|
||||||
|
service,
|
||||||
|
owner_signature,
|
||||||
|
} => client
|
||||||
|
.announce_service_provider(service, owner_signature, mock_coin(), None)
|
||||||
|
.ignore(),
|
||||||
|
SpExecuteMsg::DeleteId { service_id } => client
|
||||||
|
.delete_service_provider_by_id(service_id, None)
|
||||||
|
.ignore(),
|
||||||
|
SpExecuteMsg::DeleteNymAddress { nym_address } => client
|
||||||
|
.delete_service_provider_by_nym_address(nym_address, None)
|
||||||
|
.ignore(),
|
||||||
|
SpExecuteMsg::UpdateDepositRequired { deposit_required } => client
|
||||||
|
.update_deposit_required(deposit_required.into(), None)
|
||||||
|
.ignore(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+150
-55
@@ -1,10 +1,11 @@
|
|||||||
// Copyright 2021 - 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::collect_paged;
|
||||||
use crate::nyxd::coin::Coin;
|
use crate::nyxd::coin::Coin;
|
||||||
pub use crate::nyxd::cosmwasm_client::client::CosmWasmClient;
|
use crate::nyxd::contract_traits::NymContractsProvider;
|
||||||
use crate::nyxd::error::NyxdError;
|
use crate::nyxd::error::NyxdError;
|
||||||
use crate::nyxd::NyxdClient;
|
use crate::nyxd::CosmWasmClient;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
|
use cosmwasm_std::{Coin as CosmWasmCoin, Timestamp};
|
||||||
use nym_contracts_common::ContractBuildInformation;
|
use nym_contracts_common::ContractBuildInformation;
|
||||||
@@ -16,7 +17,8 @@ use nym_vesting_contract_common::{
|
|||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[async_trait]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
pub trait VestingQueryClient {
|
pub trait VestingQueryClient {
|
||||||
async fn query_vesting_contract<T>(&self, query: VestingQueryMsg) -> Result<T, NyxdError>
|
async fn query_vesting_contract<T>(&self, query: VestingQueryMsg) -> Result<T, NyxdError>
|
||||||
where
|
where
|
||||||
@@ -27,6 +29,11 @@ pub trait VestingQueryClient {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_vesting_contract_cw2_version(&self) -> Result<cw2::ContractVersion, NyxdError> {
|
||||||
|
self.query_vesting_contract(VestingQueryMsg::GetCW2ContractVersion {})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_all_accounts_paged(
|
async fn get_all_accounts_paged(
|
||||||
&self,
|
&self,
|
||||||
start_next_after: Option<String>,
|
start_next_after: Option<String>,
|
||||||
@@ -274,73 +281,161 @@ pub trait VestingQueryClient {
|
|||||||
self.query_vesting_contract(VestingQueryMsg::GetAllDelegations { start_after, limit })
|
self.query_vesting_contract(VestingQueryMsg::GetAllDelegations { start_after, limit })
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
|
pub trait PagedVestingQueryClient: VestingQueryClient {
|
||||||
async fn get_all_vesting_delegations(&self) -> Result<Vec<VestingDelegation>, NyxdError> {
|
async fn get_all_vesting_delegations(&self) -> Result<Vec<VestingDelegation>, NyxdError> {
|
||||||
let mut delegations = Vec::new();
|
collect_paged!(self, get_all_vesting_delegations_paged, delegations)
|
||||||
let mut start_after = None;
|
|
||||||
loop {
|
|
||||||
let mut paged_response = self
|
|
||||||
.get_all_vesting_delegations_paged(start_after.take(), None)
|
|
||||||
.await?;
|
|
||||||
delegations.append(&mut paged_response.delegations);
|
|
||||||
|
|
||||||
if let Some(start_after_res) = paged_response.start_next_after {
|
|
||||||
start_after = Some(start_after_res)
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(delegations)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_all_accounts_info(&self) -> Result<Vec<BaseVestingAccountInfo>, NyxdError> {
|
async fn get_all_accounts_info(&self) -> Result<Vec<BaseVestingAccountInfo>, NyxdError> {
|
||||||
let mut accounts = Vec::new();
|
collect_paged!(self, get_all_accounts_paged, accounts)
|
||||||
let mut start_after = None;
|
|
||||||
loop {
|
|
||||||
let mut paged_response = self
|
|
||||||
.get_all_accounts_paged(start_after.take(), None)
|
|
||||||
.await?;
|
|
||||||
accounts.append(&mut paged_response.accounts);
|
|
||||||
|
|
||||||
if let Some(start_after_res) = paged_response.start_next_after {
|
|
||||||
start_after = Some(start_after_res.into_string())
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(accounts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_all_accounts_vesting_coins(&self) -> Result<Vec<AccountVestingCoins>, NyxdError> {
|
async fn get_all_accounts_vesting_coins(&self) -> Result<Vec<AccountVestingCoins>, NyxdError> {
|
||||||
let mut accounts = Vec::new();
|
collect_paged!(self, get_all_accounts_vesting_coins_paged, accounts)
|
||||||
let mut start_after = None;
|
|
||||||
loop {
|
|
||||||
let mut paged_response = self
|
|
||||||
.get_all_accounts_vesting_coins_paged(start_after.take(), None)
|
|
||||||
.await?;
|
|
||||||
accounts.append(&mut paged_response.accounts);
|
|
||||||
|
|
||||||
if let Some(start_after_res) = paged_response.start_next_after {
|
|
||||||
start_after = Some(start_after_res.into_string())
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(accounts)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NyxdClient<C> {
|
impl<T> PagedVestingQueryClient for T where T: VestingQueryClient {}
|
||||||
|
|
||||||
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
|
impl<C> VestingQueryClient for C
|
||||||
|
where
|
||||||
|
C: CosmWasmClient + NymContractsProvider + Send + Sync,
|
||||||
|
{
|
||||||
async fn query_vesting_contract<T>(&self, query: VestingQueryMsg) -> Result<T, NyxdError>
|
async fn query_vesting_contract<T>(&self, query: VestingQueryMsg) -> Result<T, NyxdError>
|
||||||
where
|
where
|
||||||
for<'a> T: Deserialize<'a>,
|
for<'a> T: Deserialize<'a>,
|
||||||
{
|
{
|
||||||
self.client
|
let vesting_contract_address = &self
|
||||||
.query_contract_smart(self.vesting_contract_address(), &query)
|
.vesting_contract_address()
|
||||||
|
.ok_or_else(|| NyxdError::unavailable_contract_address("vesting contract"))?;
|
||||||
|
self.query_contract_smart(vesting_contract_address, &query)
|
||||||
.await
|
.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: VestingQueryClient + Send + Sync>(
|
||||||
|
client: C,
|
||||||
|
msg: VestingQueryMsg,
|
||||||
|
) {
|
||||||
|
match msg {
|
||||||
|
VestingQueryMsg::GetContractVersion {} => {
|
||||||
|
client.get_vesting_contract_version().ignore()
|
||||||
|
}
|
||||||
|
VestingQueryMsg::GetCW2ContractVersion {} => {
|
||||||
|
client.get_vesting_contract_cw2_version().ignore()
|
||||||
|
}
|
||||||
|
VestingQueryMsg::GetAccountsPaged {
|
||||||
|
start_next_after,
|
||||||
|
limit,
|
||||||
|
} => client
|
||||||
|
.get_all_accounts_paged(start_next_after, limit)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::GetAccountsVestingCoinsPaged {
|
||||||
|
start_next_after,
|
||||||
|
limit,
|
||||||
|
} => client
|
||||||
|
.get_all_accounts_vesting_coins_paged(start_next_after, limit)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::LockedCoins {
|
||||||
|
vesting_account_address,
|
||||||
|
block_time,
|
||||||
|
} => client
|
||||||
|
.locked_coins(&vesting_account_address, block_time)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::SpendableCoins {
|
||||||
|
vesting_account_address,
|
||||||
|
block_time,
|
||||||
|
} => client
|
||||||
|
.spendable_coins(&vesting_account_address, block_time)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::GetVestedCoins {
|
||||||
|
vesting_account_address,
|
||||||
|
block_time,
|
||||||
|
} => client
|
||||||
|
.vested_coins(&vesting_account_address, block_time)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::GetVestingCoins {
|
||||||
|
vesting_account_address,
|
||||||
|
block_time,
|
||||||
|
} => client
|
||||||
|
.vesting_coins(&vesting_account_address, block_time)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::GetStartTime {
|
||||||
|
vesting_account_address,
|
||||||
|
} => client.vesting_start_time(&vesting_account_address).ignore(),
|
||||||
|
VestingQueryMsg::GetEndTime {
|
||||||
|
vesting_account_address,
|
||||||
|
} => client.vesting_end_time(&vesting_account_address).ignore(),
|
||||||
|
VestingQueryMsg::GetOriginalVesting {
|
||||||
|
vesting_account_address,
|
||||||
|
} => client.original_vesting(&vesting_account_address).ignore(),
|
||||||
|
VestingQueryMsg::GetHistoricalVestingStakingReward {
|
||||||
|
vesting_account_address,
|
||||||
|
} => client
|
||||||
|
.get_historical_vesting_staking_reward(&vesting_account_address)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::GetSpendableVestedCoins {
|
||||||
|
vesting_account_address,
|
||||||
|
} => client
|
||||||
|
.get_spendable_vested_coins(&vesting_account_address)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::GetSpendableRewardCoins {
|
||||||
|
vesting_account_address,
|
||||||
|
} => client
|
||||||
|
.get_spendable_reward_coins(&vesting_account_address)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::GetDelegatedCoins {
|
||||||
|
vesting_account_address,
|
||||||
|
} => client
|
||||||
|
.get_delegated_coins(&vesting_account_address)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::GetPledgedCoins {
|
||||||
|
vesting_account_address,
|
||||||
|
} => client.get_pledged_coins(&vesting_account_address).ignore(),
|
||||||
|
VestingQueryMsg::GetStakedCoins {
|
||||||
|
vesting_account_address,
|
||||||
|
} => client.get_staked_coins(&vesting_account_address).ignore(),
|
||||||
|
VestingQueryMsg::GetWithdrawnCoins {
|
||||||
|
vesting_account_address,
|
||||||
|
} => client
|
||||||
|
.get_withdrawn_coins(&vesting_account_address)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::GetAccount { address } => client.get_account(&address).ignore(),
|
||||||
|
VestingQueryMsg::GetMixnode { address } => client.get_mixnode_pledge(&address).ignore(),
|
||||||
|
VestingQueryMsg::GetGateway { address } => client.get_gateway_pledge(&address).ignore(),
|
||||||
|
VestingQueryMsg::GetCurrentVestingPeriod { address } => {
|
||||||
|
client.get_current_vesting_period(&address).ignore()
|
||||||
|
}
|
||||||
|
VestingQueryMsg::GetDelegation {
|
||||||
|
address,
|
||||||
|
mix_id,
|
||||||
|
block_timestamp_secs,
|
||||||
|
} => client
|
||||||
|
.get_vesting_delegation(&address, mix_id, block_timestamp_secs)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::GetTotalDelegationAmount { address, mix_id } => client
|
||||||
|
.get_total_delegation_amount(&address, mix_id)
|
||||||
|
.ignore(),
|
||||||
|
VestingQueryMsg::GetDelegationTimes { address, mix_id } => {
|
||||||
|
client.get_delegation_timestamps(&address, mix_id).ignore()
|
||||||
|
}
|
||||||
|
VestingQueryMsg::GetAllDelegations { start_after, limit } => client
|
||||||
|
.get_all_vesting_delegations_paged(start_after, limit)
|
||||||
|
.ignore(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
+327
-313
@@ -1,10 +1,11 @@
|
|||||||
// Copyright 2021 - 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
|
||||||
|
|
||||||
pub use crate::nyxd::cosmwasm_client::signing_client::SigningCosmWasmClient;
|
use crate::nyxd::contract_traits::NymContractsProvider;
|
||||||
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
|
use crate::nyxd::cosmwasm_client::types::ExecuteResult;
|
||||||
use crate::nyxd::error::NyxdError;
|
use crate::nyxd::error::NyxdError;
|
||||||
use crate::nyxd::{Coin, Fee, NyxdClient};
|
use crate::nyxd::{Coin, Fee, SigningCosmWasmClient};
|
||||||
|
use crate::signing::signer::OfflineSigner;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use cosmrs::AccountId;
|
use cosmrs::AccountId;
|
||||||
use nym_contracts_common::signing::MessageSignature;
|
use nym_contracts_common::signing::MessageSignature;
|
||||||
@@ -15,7 +16,8 @@ use nym_mixnet_contract_common::{Gateway, MixId, MixNode};
|
|||||||
use nym_vesting_contract_common::messages::ExecuteMsg as VestingExecuteMsg;
|
use nym_vesting_contract_common::messages::ExecuteMsg as VestingExecuteMsg;
|
||||||
use nym_vesting_contract_common::{PledgeCap, VestingSpecification};
|
use nym_vesting_contract_common::{PledgeCap, VestingSpecification};
|
||||||
|
|
||||||
#[async_trait]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
pub trait VestingSigningClient {
|
pub trait VestingSigningClient {
|
||||||
async fn execute_vesting_contract(
|
async fn execute_vesting_contract(
|
||||||
&self,
|
&self,
|
||||||
@@ -28,25 +30,64 @@ pub trait VestingSigningClient {
|
|||||||
&self,
|
&self,
|
||||||
new_costs: MixNodeCostParams,
|
new_costs: MixNodeCostParams,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_vesting_contract(
|
||||||
|
fee,
|
||||||
|
VestingExecuteMsg::UpdateMixnodeCostParams { new_costs },
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn vesting_update_mixnode_config(
|
async fn vesting_update_mixnode_config(
|
||||||
&self,
|
&self,
|
||||||
new_config: MixNodeConfigUpdate,
|
new_config: MixNodeConfigUpdate,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
let req = VestingExecuteMsg::UpdateMixnodeConfig { new_config };
|
||||||
|
self.execute_vesting_contract(fee, req, vec![]).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn vesting_update_gateway_config(
|
async fn vesting_update_gateway_config(
|
||||||
&self,
|
&self,
|
||||||
new_config: GatewayConfigUpdate,
|
new_config: GatewayConfigUpdate,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_vesting_contract(
|
||||||
|
fee,
|
||||||
|
VestingExecuteMsg::UpdateGatewayConfig { new_config },
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_mixnet_address(
|
async fn update_mixnet_address(
|
||||||
&self,
|
&self,
|
||||||
address: &str,
|
address: &str,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
let req = VestingExecuteMsg::UpdateMixnetAddress {
|
||||||
|
address: address.to_string(),
|
||||||
|
};
|
||||||
|
self.execute_vesting_contract(fee, req, vec![]).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn vesting_track_decrease_pledge(
|
||||||
|
&self,
|
||||||
|
owner: String,
|
||||||
|
amount: Coin,
|
||||||
|
fee: Option<Fee>,
|
||||||
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_vesting_contract(
|
||||||
|
fee,
|
||||||
|
VestingExecuteMsg::TrackDecreasePledge {
|
||||||
|
owner,
|
||||||
|
amount: amount.into(),
|
||||||
|
},
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn vesting_bond_gateway(
|
async fn vesting_bond_gateway(
|
||||||
&self,
|
&self,
|
||||||
@@ -54,16 +95,31 @@ pub trait VestingSigningClient {
|
|||||||
owner_signature: MessageSignature,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
let req = VestingExecuteMsg::BondGateway {
|
||||||
async fn vesting_unbond_gateway(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError>;
|
gateway,
|
||||||
|
owner_signature,
|
||||||
|
amount: pledge.into(),
|
||||||
|
};
|
||||||
|
self.execute_vesting_contract(fee, req, vec![]).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn vesting_unbond_gateway(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
let req = VestingExecuteMsg::UnbondGateway {};
|
||||||
|
self.execute_vesting_contract(fee, req, vec![]).await
|
||||||
|
}
|
||||||
async fn vesting_track_unbond_gateway(
|
async fn vesting_track_unbond_gateway(
|
||||||
&self,
|
&self,
|
||||||
owner: &str,
|
owner: &str,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
let req = VestingExecuteMsg::TrackUnbondGateway {
|
||||||
|
owner: owner.to_string(),
|
||||||
|
amount: amount.into(),
|
||||||
|
};
|
||||||
|
self.execute_vesting_contract(fee, req, vec![]).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn vesting_bond_mixnode(
|
async fn vesting_bond_mixnode(
|
||||||
&self,
|
&self,
|
||||||
@@ -72,7 +128,19 @@ pub trait VestingSigningClient {
|
|||||||
owner_signature: MessageSignature,
|
owner_signature: MessageSignature,
|
||||||
pledge: Coin,
|
pledge: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_vesting_contract(
|
||||||
|
fee,
|
||||||
|
VestingExecuteMsg::BondMixnode {
|
||||||
|
mix_node,
|
||||||
|
cost_params,
|
||||||
|
owner_signature,
|
||||||
|
amount: pledge.into(),
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn vesting_pledge_more(
|
async fn vesting_pledge_more(
|
||||||
&self,
|
&self,
|
||||||
@@ -104,20 +172,34 @@ pub trait VestingSigningClient {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError>;
|
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
let req = VestingExecuteMsg::UnbondMixnode {};
|
||||||
|
self.execute_vesting_contract(fee, req, vec![]).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn vesting_track_unbond_mixnode(
|
async fn vesting_track_unbond_mixnode(
|
||||||
&self,
|
&self,
|
||||||
owner: &str,
|
owner: &str,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
let req = VestingExecuteMsg::TrackUnbondMixnode {
|
||||||
|
owner: owner.to_string(),
|
||||||
|
amount: amount.into(),
|
||||||
|
};
|
||||||
|
self.execute_vesting_contract(fee, req, vec![]).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn withdraw_vested_coins(
|
async fn withdraw_vested_coins(
|
||||||
&self,
|
&self,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
let req = VestingExecuteMsg::WithdrawVestedCoins {
|
||||||
|
amount: amount.into(),
|
||||||
|
};
|
||||||
|
self.execute_vesting_contract(fee, req, vec![]).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn vesting_track_undelegation(
|
async fn vesting_track_undelegation(
|
||||||
&self,
|
&self,
|
||||||
@@ -125,7 +207,18 @@ pub trait VestingSigningClient {
|
|||||||
mix_id: MixId,
|
mix_id: MixId,
|
||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_vesting_contract(
|
||||||
|
fee,
|
||||||
|
VestingExecuteMsg::TrackUndelegation {
|
||||||
|
owner: address.to_string(),
|
||||||
|
mix_id,
|
||||||
|
amount: amount.into(),
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn vesting_delegate_to_mixnode(
|
async fn vesting_delegate_to_mixnode(
|
||||||
&self,
|
&self,
|
||||||
@@ -133,14 +226,35 @@ pub trait VestingSigningClient {
|
|||||||
amount: Coin,
|
amount: Coin,
|
||||||
on_behalf_of: Option<String>,
|
on_behalf_of: Option<String>,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_vesting_contract(
|
||||||
|
fee,
|
||||||
|
VestingExecuteMsg::DelegateToMixnode {
|
||||||
|
mix_id,
|
||||||
|
amount: amount.into(),
|
||||||
|
on_behalf_of,
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn vesting_undelegate_from_mixnode(
|
async fn vesting_undelegate_from_mixnode(
|
||||||
&self,
|
&self,
|
||||||
mix_id: MixId,
|
mix_id: MixId,
|
||||||
on_behalf_of: Option<String>,
|
on_behalf_of: Option<String>,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_vesting_contract(
|
||||||
|
fee,
|
||||||
|
VestingExecuteMsg::UndelegateFromMixnode {
|
||||||
|
mix_id,
|
||||||
|
on_behalf_of,
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn create_periodic_vesting_account(
|
async fn create_periodic_vesting_account(
|
||||||
&self,
|
&self,
|
||||||
@@ -150,7 +264,32 @@ pub trait VestingSigningClient {
|
|||||||
amount: Coin,
|
amount: Coin,
|
||||||
cap: Option<PledgeCap>,
|
cap: Option<PledgeCap>,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
) -> Result<ExecuteResult, NyxdError>;
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
let req = VestingExecuteMsg::CreateAccount {
|
||||||
|
owner_address: owner_address.to_string(),
|
||||||
|
staking_address,
|
||||||
|
vesting_spec,
|
||||||
|
cap,
|
||||||
|
};
|
||||||
|
self.execute_vesting_contract(fee, req, vec![amount]).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn vesting_track_reward(
|
||||||
|
&self,
|
||||||
|
amount: Coin,
|
||||||
|
address: String,
|
||||||
|
fee: Option<Fee>,
|
||||||
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_vesting_contract(
|
||||||
|
fee,
|
||||||
|
VestingExecuteMsg::TrackReward {
|
||||||
|
amount: amount.into(),
|
||||||
|
address,
|
||||||
|
},
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn vesting_withdraw_operator_reward(
|
async fn vesting_withdraw_operator_reward(
|
||||||
&self,
|
&self,
|
||||||
@@ -173,6 +312,32 @@ pub trait VestingSigningClient {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn vesting_transfer_ownership(
|
||||||
|
&self,
|
||||||
|
to_address: String,
|
||||||
|
fee: Option<Fee>,
|
||||||
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_vesting_contract(
|
||||||
|
fee,
|
||||||
|
VestingExecuteMsg::TransferOwnership { to_address },
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_staking_address(
|
||||||
|
&self,
|
||||||
|
to_address: Option<String>,
|
||||||
|
fee: Option<Fee>,
|
||||||
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
|
self.execute_vesting_contract(
|
||||||
|
fee,
|
||||||
|
VestingExecuteMsg::UpdateStakingAddress { to_address },
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_locked_pledge_cap(
|
async fn update_locked_pledge_cap(
|
||||||
&self,
|
&self,
|
||||||
address: AccountId,
|
address: AccountId,
|
||||||
@@ -235,317 +400,166 @@ pub trait VestingSigningClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NyxdClient<C> {
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
|
impl<C> VestingSigningClient for C
|
||||||
|
where
|
||||||
|
C: SigningCosmWasmClient + NymContractsProvider + Sync,
|
||||||
|
NyxdError: From<<Self as OfflineSigner>::Error>,
|
||||||
|
{
|
||||||
async fn execute_vesting_contract(
|
async fn execute_vesting_contract(
|
||||||
&self,
|
&self,
|
||||||
fee: Option<Fee>,
|
fee: Option<Fee>,
|
||||||
msg: VestingExecuteMsg,
|
msg: VestingExecuteMsg,
|
||||||
funds: Vec<Coin>,
|
funds: Vec<Coin>,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
) -> Result<ExecuteResult, NyxdError> {
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
let vesting_contract_address = &self
|
||||||
|
.vesting_contract_address()
|
||||||
|
.ok_or_else(|| NyxdError::unavailable_contract_address("vesting contract"))?;
|
||||||
|
|
||||||
|
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
|
||||||
let memo = msg.name().to_string();
|
let memo = msg.name().to_string();
|
||||||
self.client
|
|
||||||
.execute(
|
|
||||||
self.address(),
|
|
||||||
self.vesting_contract_address(),
|
|
||||||
&msg,
|
|
||||||
fee,
|
|
||||||
memo,
|
|
||||||
funds,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn vesting_update_mixnode_cost_params(
|
let signer_address = &self.signer_addresses()?[0];
|
||||||
&self,
|
self.execute(
|
||||||
new_costs: MixNodeCostParams,
|
signer_address,
|
||||||
fee: Option<Fee>,
|
vesting_contract_address,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
&msg,
|
||||||
self.execute_vesting_contract(
|
|
||||||
fee,
|
fee,
|
||||||
VestingExecuteMsg::UpdateMixnodeCostParams { new_costs },
|
memo,
|
||||||
vec![],
|
funds,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn vesting_update_mixnode_config(
|
#[cfg(test)]
|
||||||
&self,
|
mod tests {
|
||||||
new_config: MixNodeConfigUpdate,
|
use super::*;
|
||||||
fee: Option<Fee>,
|
use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue};
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
|
||||||
let req = VestingExecuteMsg::UpdateMixnodeConfig { new_config };
|
|
||||||
self.client
|
|
||||||
.execute(
|
|
||||||
self.address(),
|
|
||||||
self.vesting_contract_address(),
|
|
||||||
&req,
|
|
||||||
fee,
|
|
||||||
"VestingContract::UpdateMixnetConfig",
|
|
||||||
vec![],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn vesting_update_gateway_config(
|
// it's enough that this compiles and clippy is happy about it
|
||||||
&self,
|
#[allow(dead_code)]
|
||||||
new_config: GatewayConfigUpdate,
|
fn all_execute_variants_are_covered<C: VestingSigningClient + Send + Sync>(
|
||||||
fee: Option<Fee>,
|
client: C,
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
msg: VestingExecuteMsg,
|
||||||
self.execute_vesting_contract(
|
) {
|
||||||
fee,
|
match msg {
|
||||||
VestingExecuteMsg::UpdateGatewayConfig { new_config },
|
VestingExecuteMsg::CreateFamily { label } => {
|
||||||
vec![],
|
client.vesting_create_family(label, None).ignore()
|
||||||
)
|
}
|
||||||
.await
|
VestingExecuteMsg::JoinFamily {
|
||||||
}
|
join_permit,
|
||||||
|
family_head,
|
||||||
async fn update_mixnet_address(
|
} => client
|
||||||
&self,
|
.vesting_join_family(join_permit, family_head, None)
|
||||||
address: &str,
|
.ignore(),
|
||||||
fee: Option<Fee>,
|
VestingExecuteMsg::LeaveFamily { family_head } => {
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
client.vesting_leave_family(family_head, None).ignore()
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
}
|
||||||
let req = VestingExecuteMsg::UpdateMixnetAddress {
|
VestingExecuteMsg::KickFamilyMember { member } => {
|
||||||
address: address.to_string(),
|
client.vesting_kick_family_member(member, None).ignore()
|
||||||
};
|
}
|
||||||
self.client
|
VestingExecuteMsg::TrackReward { amount, address } => client
|
||||||
.execute(
|
.vesting_track_reward(amount.into(), address, None)
|
||||||
self.address(),
|
.ignore(),
|
||||||
self.vesting_contract_address(),
|
VestingExecuteMsg::ClaimOperatorReward {} => {
|
||||||
&req,
|
client.vesting_withdraw_operator_reward(None).ignore()
|
||||||
fee,
|
}
|
||||||
"VestingContract::UpdateMixnetAddress",
|
VestingExecuteMsg::ClaimDelegatorReward { mix_id } => client
|
||||||
vec![],
|
.vesting_withdraw_delegator_reward(mix_id, None)
|
||||||
)
|
.ignore(),
|
||||||
.await
|
VestingExecuteMsg::UpdateMixnodeCostParams { new_costs } => client
|
||||||
}
|
.vesting_update_mixnode_cost_params(new_costs, None)
|
||||||
|
.ignore(),
|
||||||
async fn vesting_bond_gateway(
|
VestingExecuteMsg::UpdateMixnodeConfig { new_config } => client
|
||||||
&self,
|
.vesting_update_mixnode_config(new_config, None)
|
||||||
gateway: Gateway,
|
.ignore(),
|
||||||
owner_signature: MessageSignature,
|
VestingExecuteMsg::UpdateMixnetAddress { address } => {
|
||||||
pledge: Coin,
|
client.update_mixnet_address(&address, None).ignore()
|
||||||
fee: Option<Fee>,
|
}
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
VestingExecuteMsg::DelegateToMixnode {
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
mix_id,
|
||||||
let req = VestingExecuteMsg::BondGateway {
|
amount,
|
||||||
gateway,
|
on_behalf_of,
|
||||||
owner_signature,
|
} => client
|
||||||
amount: pledge.into(),
|
.vesting_delegate_to_mixnode(mix_id, amount.into(), on_behalf_of, None)
|
||||||
};
|
.ignore(),
|
||||||
self.client
|
VestingExecuteMsg::UndelegateFromMixnode {
|
||||||
.execute(
|
mix_id,
|
||||||
self.address(),
|
on_behalf_of,
|
||||||
self.vesting_contract_address(),
|
} => client
|
||||||
&req,
|
.vesting_undelegate_from_mixnode(mix_id, on_behalf_of, None)
|
||||||
fee,
|
.ignore(),
|
||||||
"VestingContract::BondGateway",
|
VestingExecuteMsg::CreateAccount {
|
||||||
vec![],
|
owner_address,
|
||||||
)
|
staking_address,
|
||||||
.await
|
vesting_spec,
|
||||||
}
|
cap,
|
||||||
|
} => client
|
||||||
async fn vesting_unbond_gateway(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
.create_periodic_vesting_account(
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
&owner_address,
|
||||||
let req = VestingExecuteMsg::UnbondGateway {};
|
staking_address,
|
||||||
self.client
|
vesting_spec,
|
||||||
.execute(
|
mock_coin(),
|
||||||
self.address(),
|
cap,
|
||||||
self.vesting_contract_address(),
|
None,
|
||||||
&req,
|
)
|
||||||
fee,
|
.ignore(),
|
||||||
"VestingContract::UnbondGateway",
|
VestingExecuteMsg::WithdrawVestedCoins { amount } => {
|
||||||
vec![],
|
client.withdraw_vested_coins(amount.into(), None).ignore()
|
||||||
)
|
}
|
||||||
.await
|
VestingExecuteMsg::TrackUndelegation {
|
||||||
}
|
owner,
|
||||||
|
mix_id,
|
||||||
async fn vesting_track_unbond_gateway(
|
amount,
|
||||||
&self,
|
} => client
|
||||||
owner: &str,
|
.vesting_track_undelegation(&owner, mix_id, amount.into(), None)
|
||||||
amount: Coin,
|
.ignore(),
|
||||||
fee: Option<Fee>,
|
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
|
||||||
let req = VestingExecuteMsg::TrackUnbondGateway {
|
|
||||||
owner: owner.to_string(),
|
|
||||||
amount: amount.into(),
|
|
||||||
};
|
|
||||||
self.client
|
|
||||||
.execute(
|
|
||||||
self.address(),
|
|
||||||
self.vesting_contract_address(),
|
|
||||||
&req,
|
|
||||||
fee,
|
|
||||||
"VestingContract::TrackUnbondGateway",
|
|
||||||
vec![],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn vesting_bond_mixnode(
|
|
||||||
&self,
|
|
||||||
mix_node: MixNode,
|
|
||||||
cost_params: MixNodeCostParams,
|
|
||||||
owner_signature: MessageSignature,
|
|
||||||
pledge: Coin,
|
|
||||||
fee: Option<Fee>,
|
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
|
||||||
self.execute_vesting_contract(
|
|
||||||
fee,
|
|
||||||
VestingExecuteMsg::BondMixnode {
|
VestingExecuteMsg::BondMixnode {
|
||||||
mix_node,
|
mix_node,
|
||||||
cost_params,
|
cost_params,
|
||||||
owner_signature,
|
owner_signature,
|
||||||
amount: pledge.into(),
|
amount,
|
||||||
},
|
} => client
|
||||||
vec![],
|
.vesting_bond_mixnode(mix_node, cost_params, owner_signature, amount.into(), None)
|
||||||
)
|
.ignore(),
|
||||||
.await
|
VestingExecuteMsg::PledgeMore { amount } => {
|
||||||
}
|
client.vesting_pledge_more(amount.into(), None).ignore()
|
||||||
|
}
|
||||||
async fn vesting_unbond_mixnode(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
VestingExecuteMsg::DecreasePledge { amount } => {
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
client.vesting_decrease_pledge(amount.into(), None).ignore()
|
||||||
let req = VestingExecuteMsg::UnbondMixnode {};
|
}
|
||||||
self.client
|
VestingExecuteMsg::UnbondMixnode {} => client.vesting_unbond_mixnode(None).ignore(),
|
||||||
.execute(
|
VestingExecuteMsg::TrackUnbondMixnode { owner, amount } => client
|
||||||
self.address(),
|
.vesting_track_unbond_mixnode(&owner, amount.into(), None)
|
||||||
self.vesting_contract_address(),
|
.ignore(),
|
||||||
&req,
|
VestingExecuteMsg::TrackDecreasePledge { owner, amount } => client
|
||||||
fee,
|
.vesting_track_decrease_pledge(owner, amount.into(), None)
|
||||||
"VestingContract::UnbondMixnode",
|
.ignore(),
|
||||||
vec![],
|
VestingExecuteMsg::BondGateway {
|
||||||
)
|
gateway,
|
||||||
.await
|
owner_signature,
|
||||||
}
|
amount,
|
||||||
|
} => client
|
||||||
async fn vesting_track_unbond_mixnode(
|
.vesting_bond_gateway(gateway, owner_signature, amount.into(), None)
|
||||||
&self,
|
.ignore(),
|
||||||
owner: &str,
|
VestingExecuteMsg::UnbondGateway {} => client.vesting_unbond_gateway(None).ignore(),
|
||||||
amount: Coin,
|
VestingExecuteMsg::TrackUnbondGateway { owner, amount } => client
|
||||||
fee: Option<Fee>,
|
.vesting_track_unbond_gateway(&owner, amount.into(), None)
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
.ignore(),
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
VestingExecuteMsg::UpdateGatewayConfig { new_config } => client
|
||||||
let req = VestingExecuteMsg::TrackUnbondMixnode {
|
.vesting_update_gateway_config(new_config, None)
|
||||||
owner: owner.to_string(),
|
.ignore(),
|
||||||
amount: amount.into(),
|
VestingExecuteMsg::TransferOwnership { to_address } => {
|
||||||
|
client.vesting_transfer_ownership(to_address, None).ignore()
|
||||||
|
}
|
||||||
|
VestingExecuteMsg::UpdateStakingAddress { to_address } => {
|
||||||
|
client.update_staking_address(to_address, None).ignore()
|
||||||
|
}
|
||||||
|
VestingExecuteMsg::UpdateLockedPledgeCap { address, cap } => client
|
||||||
|
.update_locked_pledge_cap(address.parse().unwrap(), cap, None)
|
||||||
|
.ignore(),
|
||||||
};
|
};
|
||||||
self.client
|
|
||||||
.execute(
|
|
||||||
self.address(),
|
|
||||||
self.vesting_contract_address(),
|
|
||||||
&req,
|
|
||||||
fee,
|
|
||||||
"VestingContract::TrackUnbondMixnode",
|
|
||||||
vec![],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn withdraw_vested_coins(
|
|
||||||
&self,
|
|
||||||
amount: Coin,
|
|
||||||
fee: Option<Fee>,
|
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
|
||||||
let req = VestingExecuteMsg::WithdrawVestedCoins {
|
|
||||||
amount: amount.into(),
|
|
||||||
};
|
|
||||||
self.client
|
|
||||||
.execute(
|
|
||||||
self.address(),
|
|
||||||
self.vesting_contract_address(),
|
|
||||||
&req,
|
|
||||||
fee,
|
|
||||||
"VestingContract::WithdrawVested",
|
|
||||||
vec![],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn vesting_track_undelegation(
|
|
||||||
&self,
|
|
||||||
address: &str,
|
|
||||||
mix_id: MixId,
|
|
||||||
amount: Coin,
|
|
||||||
fee: Option<Fee>,
|
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
|
||||||
self.execute_vesting_contract(
|
|
||||||
fee,
|
|
||||||
VestingExecuteMsg::TrackUndelegation {
|
|
||||||
owner: address.to_string(),
|
|
||||||
mix_id,
|
|
||||||
amount: amount.into(),
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn vesting_delegate_to_mixnode(
|
|
||||||
&self,
|
|
||||||
mix_id: MixId,
|
|
||||||
amount: Coin,
|
|
||||||
on_behalf_of: Option<String>,
|
|
||||||
fee: Option<Fee>,
|
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
|
||||||
self.execute_vesting_contract(
|
|
||||||
fee,
|
|
||||||
VestingExecuteMsg::DelegateToMixnode {
|
|
||||||
mix_id,
|
|
||||||
amount: amount.into(),
|
|
||||||
on_behalf_of,
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn vesting_undelegate_from_mixnode(
|
|
||||||
&self,
|
|
||||||
mix_id: MixId,
|
|
||||||
on_behalf_of: Option<String>,
|
|
||||||
fee: Option<Fee>,
|
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
|
||||||
self.execute_vesting_contract(
|
|
||||||
fee,
|
|
||||||
VestingExecuteMsg::UndelegateFromMixnode {
|
|
||||||
mix_id,
|
|
||||||
on_behalf_of,
|
|
||||||
},
|
|
||||||
vec![],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_periodic_vesting_account(
|
|
||||||
&self,
|
|
||||||
owner_address: &str,
|
|
||||||
staking_address: Option<String>,
|
|
||||||
vesting_spec: Option<VestingSpecification>,
|
|
||||||
amount: Coin,
|
|
||||||
cap: Option<PledgeCap>,
|
|
||||||
fee: Option<Fee>,
|
|
||||||
) -> Result<ExecuteResult, NyxdError> {
|
|
||||||
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier)));
|
|
||||||
let req = VestingExecuteMsg::CreateAccount {
|
|
||||||
owner_address: owner_address.to_string(),
|
|
||||||
staking_address,
|
|
||||||
vesting_spec,
|
|
||||||
cap,
|
|
||||||
};
|
|
||||||
self.client
|
|
||||||
.execute(
|
|
||||||
self.address(),
|
|
||||||
self.vesting_contract_address(),
|
|
||||||
&req,
|
|
||||||
fee,
|
|
||||||
"VestingContract::CreatePeriodicVestingAccount",
|
|
||||||
vec![amount],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// 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;
|
||||||
+42
-26
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::nyxd;
|
use crate::nyxd;
|
||||||
@@ -8,7 +8,7 @@ use crate::nyxd::cosmwasm_client::types::{
|
|||||||
Account, CodeDetails, Contract, ContractCodeId, SequenceResponse, SimulateResponse,
|
Account, CodeDetails, Contract, ContractCodeId, SequenceResponse, SimulateResponse,
|
||||||
};
|
};
|
||||||
use crate::nyxd::error::NyxdError;
|
use crate::nyxd::error::NyxdError;
|
||||||
use crate::nyxd::TendermintClient;
|
use crate::rpc::TendermintRpcClient;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use cosmrs::cosmwasm::{CodeInfoResponse, ContractCodeHistoryEntry};
|
use cosmrs::cosmwasm::{CodeInfoResponse, ContractCodeHistoryEntry};
|
||||||
use cosmrs::proto::cosmos::auth::v1beta1::{QueryAccountRequest, QueryAccountResponse};
|
use cosmrs::proto::cosmos::auth::v1beta1::{QueryAccountRequest, QueryAccountResponse};
|
||||||
@@ -28,6 +28,7 @@ use cosmrs::proto::cosmwasm::wasm::v1::{
|
|||||||
};
|
};
|
||||||
use cosmrs::tendermint::{block, chain, Hash};
|
use cosmrs::tendermint::{block, chain, Hash};
|
||||||
use cosmrs::{AccountId, Coin as CosmosCoin, Tx};
|
use cosmrs::{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;
|
||||||
@@ -38,25 +39,26 @@ use tendermint_rpc::{
|
|||||||
Order,
|
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")]
|
#[cfg(feature = "http-client")]
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CosmWasmClient for cosmrs::rpc::HttpClient {
|
impl CosmWasmClient for cosmrs::rpc::HttpClient {}
|
||||||
fn broadcast_polling_rate(&self) -> Duration {
|
|
||||||
Duration::from_secs(4)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn broadcast_timeout(&self) -> Duration {
|
|
||||||
Duration::from_secs(60)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait CosmWasmClient: TendermintClient {
|
|
||||||
// this should probably get redesigned, but I'm leaving those like that temporarily to fix
|
|
||||||
// the underlying issue more quickly
|
|
||||||
fn broadcast_polling_rate(&self) -> Duration;
|
|
||||||
fn broadcast_timeout(&self) -> Duration;
|
|
||||||
|
|
||||||
|
#[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?
|
||||||
@@ -69,6 +71,9 @@ pub trait CosmWasmClient: TendermintClient {
|
|||||||
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)?;
|
||||||
|
|
||||||
@@ -242,7 +247,7 @@ pub trait CosmWasmClient: TendermintClient {
|
|||||||
where
|
where
|
||||||
T: Into<Vec<u8>> + Send,
|
T: Into<Vec<u8>> + Send,
|
||||||
{
|
{
|
||||||
Ok(tendermint_rpc::client::Client::broadcast_tx_async(self, tx).await?)
|
Ok(TendermintRpcClient::broadcast_tx_async(self, tx).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Broadcast a transaction, returning the response from `CheckTx`.
|
/// Broadcast a transaction, returning the response from `CheckTx`.
|
||||||
@@ -250,7 +255,7 @@ pub trait CosmWasmClient: TendermintClient {
|
|||||||
where
|
where
|
||||||
T: Into<Vec<u8>> + Send,
|
T: Into<Vec<u8>> + Send,
|
||||||
{
|
{
|
||||||
Ok(tendermint_rpc::client::Client::broadcast_tx_sync(self, tx).await?)
|
Ok(TendermintRpcClient::broadcast_tx_sync(self, tx).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Broadcast a transaction, returning the response from `DeliverTx`.
|
/// Broadcast a transaction, returning the response from `DeliverTx`.
|
||||||
@@ -261,13 +266,23 @@ pub trait CosmWasmClient: TendermintClient {
|
|||||||
where
|
where
|
||||||
T: Into<Vec<u8>> + Send,
|
T: Into<Vec<u8>> + Send,
|
||||||
{
|
{
|
||||||
Ok(tendermint_rpc::client::Client::broadcast_tx_commit(self, tx).await?)
|
Ok(TendermintRpcClient::broadcast_tx_commit(self, tx).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn broadcast_tx<T>(&self, tx: T) -> Result<TxResponse, NyxdError>
|
async fn broadcast_tx<T>(
|
||||||
|
&self,
|
||||||
|
tx: T,
|
||||||
|
timeout: impl Into<Option<Duration>> + Send,
|
||||||
|
poll_interval: impl Into<Option<Duration>> + Send,
|
||||||
|
) -> Result<TxResponse, NyxdError>
|
||||||
where
|
where
|
||||||
T: Into<Vec<u8>> + Send,
|
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() {
|
||||||
@@ -282,16 +297,16 @@ pub trait CosmWasmClient: TendermintClient {
|
|||||||
|
|
||||||
let tx_hash = broadcasted.hash;
|
let tx_hash = broadcasted.hash;
|
||||||
|
|
||||||
let start = tokio::time::Instant::now();
|
let start = 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 tokio::time::Instant::now().duration_since(start) >= self.broadcast_timeout() {
|
if Instant::now().duration_since(start) >= timeout {
|
||||||
return Err(NyxdError::BroadcastTimeout {
|
return Err(NyxdError::BroadcastTimeout {
|
||||||
hash: tx_hash,
|
hash: tx_hash,
|
||||||
timeout: self.broadcast_timeout(),
|
timeout,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +314,7 @@ pub trait CosmWasmClient: TendermintClient {
|
|||||||
return Ok(poll_res);
|
return Ok(poll_res);
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::time::sleep(self.broadcast_polling_rate()).await;
|
sleep(poll_interval).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,6 +492,7 @@ pub trait CosmWasmClient: TendermintClient {
|
|||||||
.make_abci_query::<_, QuerySmartContractStateResponse>(path, req)
|
.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)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
+25
-184
@@ -1,7 +1,7 @@
|
|||||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 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::CosmWasmClient;
|
use crate::nyxd::cosmwasm_client::client_traits::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::*;
|
||||||
@@ -9,6 +9,7 @@ use crate::nyxd::error::NyxdError;
|
|||||||
use crate::nyxd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER};
|
use crate::nyxd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER};
|
||||||
use crate::nyxd::{Coin, GasAdjustable, GasPrice, TxResponse};
|
use crate::nyxd::{Coin, GasAdjustable, GasPrice, TxResponse};
|
||||||
use crate::signing::signer::OfflineSigner;
|
use crate::signing::signer::OfflineSigner;
|
||||||
|
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::abci::GasInfo;
|
||||||
@@ -26,21 +27,9 @@ 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::{Duration, SystemTime};
|
use std::time::SystemTime;
|
||||||
use tendermint_rpc::endpoint::broadcast;
|
use tendermint_rpc::endpoint::broadcast;
|
||||||
|
|
||||||
#[cfg(feature = "http-client")]
|
|
||||||
use crate::signing::tx_signer::TxSigner;
|
|
||||||
|
|
||||||
#[cfg(feature = "http-client")]
|
|
||||||
use tendermint_rpc::{Error as TendermintRpcError, SimpleRequest};
|
|
||||||
|
|
||||||
#[cfg(feature = "http-client")]
|
|
||||||
use cosmrs::rpc::{HttpClient, HttpClientUrl};
|
|
||||||
|
|
||||||
pub const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4);
|
|
||||||
pub const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60);
|
|
||||||
|
|
||||||
fn empty_fee() -> tx::Fee {
|
fn empty_fee() -> tx::Fee {
|
||||||
tx::Fee {
|
tx::Fee {
|
||||||
amount: vec![],
|
amount: vec![],
|
||||||
@@ -64,18 +53,17 @@ fn single_unspecified_signer_auth(
|
|||||||
.auth_info(empty_fee())
|
.auth_info(empty_fee())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
pub trait SigningCosmWasmClient: CosmWasmClient {
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
type Signer: OfflineSigner + Send + Sync;
|
pub trait SigningCosmWasmClient: CosmWasmClient + TxSigner
|
||||||
|
where
|
||||||
fn signer(&self) -> &Self::Signer;
|
NyxdError: From<<Self as OfflineSigner>::Error>,
|
||||||
|
{
|
||||||
|
// TODO: would it somehow be possible to get rid of this method and allow for
|
||||||
|
// blanket implementation for anything that provides CosmWasmClient + TxSigner?
|
||||||
fn gas_price(&self) -> &GasPrice;
|
fn gas_price(&self) -> &GasPrice;
|
||||||
|
|
||||||
fn signer_public_key(&self, signer_address: &AccountId) -> Option<tx::SignerPublicKey> {
|
fn simulated_gas_multiplier(&self) -> f32;
|
||||||
let account = self.signer().find_account(signer_address).ok()?;
|
|
||||||
Some(account.public_key().into())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn simulate(
|
async fn simulate(
|
||||||
&self,
|
&self,
|
||||||
@@ -587,9 +575,9 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
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))
|
||||||
@@ -687,7 +675,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.to_bytes()
|
.to_bytes()
|
||||||
.map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?;
|
.map_err(|_| NyxdError::SerializationError("Tx".to_owned()))?;
|
||||||
|
|
||||||
self.broadcast_tx(tx_bytes).await
|
self.broadcast_tx(tx_bytes, None, None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn sign(
|
async fn sign(
|
||||||
@@ -710,161 +698,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.sign_direct(signer_address, messages, fee, memo, signer_data)
|
Ok(<Self as TxSigner>::sign_direct(
|
||||||
}
|
self,
|
||||||
|
signer_address,
|
||||||
fn sign_amino(
|
messages,
|
||||||
&self,
|
fee,
|
||||||
signer_address: &AccountId,
|
memo,
|
||||||
messages: Vec<Any>,
|
signer_data,
|
||||||
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>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "http-client")]
|
|
||||||
#[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,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "http-client")]
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "http-client")]
|
|
||||||
#[async_trait]
|
|
||||||
impl<S> tendermint_rpc::client::Client for Client<S>
|
|
||||||
where
|
|
||||||
S: Send + Sync,
|
|
||||||
{
|
|
||||||
async fn perform<R>(&self, request: R) -> Result<R::Output, tendermint_rpc::Error>
|
|
||||||
where
|
|
||||||
R: SimpleRequest,
|
|
||||||
{
|
|
||||||
self.rpc_client.perform(request).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "http-client")]
|
|
||||||
#[async_trait]
|
|
||||||
impl<S> CosmWasmClient for Client<S>
|
|
||||||
where
|
|
||||||
S: Send + Sync,
|
|
||||||
{
|
|
||||||
fn broadcast_polling_rate(&self) -> Duration {
|
|
||||||
self.broadcast_polling_rate
|
|
||||||
}
|
|
||||||
|
|
||||||
fn broadcast_timeout(&self) -> Duration {
|
|
||||||
self.broadcast_timeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "http-client")]
|
|
||||||
#[async_trait]
|
|
||||||
impl<S> SigningCosmWasmClient for Client<S>
|
|
||||||
where
|
|
||||||
S: OfflineSigner + Send + Sync,
|
|
||||||
NyxdError: From<S::Error>,
|
|
||||||
{
|
|
||||||
type Signer = S;
|
|
||||||
|
|
||||||
fn signer(&self) -> &Self::Signer {
|
|
||||||
self.tx_signer.signer()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gas_price(&self) -> &GasPrice {
|
|
||||||
&self.gas_price
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign_amino(
|
|
||||||
&self,
|
|
||||||
signer_address: &AccountId,
|
|
||||||
messages: Vec<Any>,
|
|
||||||
fee: tx::Fee,
|
|
||||||
memo: impl Into<String> + Send + 'static,
|
|
||||||
signer_data: SignerData,
|
|
||||||
) -> Result<tx::Raw, NyxdError> {
|
|
||||||
Ok(self
|
|
||||||
.tx_signer
|
|
||||||
.sign_amino(signer_address, messages, fee, memo, signer_data)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
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> {
|
|
||||||
Ok(self
|
|
||||||
.tx_signer
|
|
||||||
.sign_direct(signer_address, messages, fee, memo, signer_data)?)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +48,6 @@ impl CheckResponse for crate::nyxd::TxResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "signing")]
|
|
||||||
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::write::GzEncoder;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
|
|||||||
@@ -1,37 +1,375 @@
|
|||||||
// 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
|
||||||
|
|
||||||
#[cfg(feature = "http-client")]
|
use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient};
|
||||||
use crate::nyxd::error::NyxdError;
|
use crate::nyxd::error::NyxdError;
|
||||||
#[cfg(feature = "http-client")]
|
use crate::nyxd::{Config, GasPrice, Hash, Height};
|
||||||
use cosmrs::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl};
|
use crate::rpc::TendermintRpcClient;
|
||||||
#[cfg(feature = "http-client")]
|
use crate::signing::{
|
||||||
use std::convert::TryInto;
|
signer::{NoSigner, OfflineSigner},
|
||||||
|
AccountData,
|
||||||
|
};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use cosmrs::tendermint::{abci, evidence::Evidence, Genesis};
|
||||||
|
use cosmrs::tx::{Raw, SignDoc};
|
||||||
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use tendermint_rpc::endpoint::*;
|
||||||
|
use tendermint_rpc::query::Query;
|
||||||
|
use tendermint_rpc::{Error as TendermintRpcError, Order, Paging, SimpleRequest};
|
||||||
|
|
||||||
pub mod client;
|
#[cfg(feature = "http-client")]
|
||||||
|
use crate::http_client;
|
||||||
|
#[cfg(feature = "http-client")]
|
||||||
|
use cosmrs::rpc::{HttpClient, HttpClientUrl};
|
||||||
|
|
||||||
|
pub mod client_traits;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
pub mod logs;
|
pub mod logs;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
#[cfg(feature = "signing")]
|
#[derive(Debug)]
|
||||||
pub mod signing_client;
|
pub(crate) struct SigningClientOptions {
|
||||||
|
gas_price: GasPrice,
|
||||||
|
simulated_gas_multiplier: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Config> for SigningClientOptions {
|
||||||
|
fn from(value: &'a Config) -> Self {
|
||||||
|
SigningClientOptions {
|
||||||
|
gas_price: value.gas_price.clone(),
|
||||||
|
simulated_gas_multiplier: value.simulated_gas_multiplier,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convenience wrapper around query client to allow for optional signing
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct MaybeSigningClient<C, S = NoSigner> {
|
||||||
|
client: C,
|
||||||
|
signer: S,
|
||||||
|
opts: SigningClientOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
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")]
|
#[cfg(feature = "http-client")]
|
||||||
pub fn connect<U>(endpoint: U) -> Result<HttpClient, NyxdError>
|
impl<S> MaybeSigningClient<HttpClient, S> {
|
||||||
where
|
pub(crate) fn change_endpoint<U>(&mut self, new_endpoint: U) -> Result<(), NyxdError>
|
||||||
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
where
|
||||||
{
|
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
||||||
Ok(HttpClient::new(endpoint)?)
|
{
|
||||||
|
self.client = http_client(new_endpoint)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "signing", feature = "http-client"))]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
pub fn connect_with_signer<S, U: Clone>(
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
endpoint: U,
|
impl<C, S> TendermintRpcClient for MaybeSigningClient<C, S>
|
||||||
signer: S,
|
|
||||||
gas_price: crate::nyxd::GasPrice,
|
|
||||||
) -> Result<signing_client::Client<S>, NyxdError>
|
|
||||||
where
|
where
|
||||||
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
C: TendermintRpcClient + Send + Sync,
|
||||||
|
S: Send + Sync,
|
||||||
{
|
{
|
||||||
signing_client::Client::connect_with_signer(endpoint, signer, gas_price)
|
async fn abci_info(&self) -> Result<abci::response::Info, TendermintRpcError> {
|
||||||
|
self.client.abci_info().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn abci_query<V>(
|
||||||
|
&self,
|
||||||
|
path: Option<String>,
|
||||||
|
data: V,
|
||||||
|
height: Option<Height>,
|
||||||
|
prove: bool,
|
||||||
|
) -> Result<abci_query::AbciQuery, TendermintRpcError>
|
||||||
|
where
|
||||||
|
V: Into<Vec<u8>> + Send,
|
||||||
|
{
|
||||||
|
self.client.abci_query(path, data, height, prove).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn block<H>(&self, height: H) -> Result<block::Response, TendermintRpcError>
|
||||||
|
where
|
||||||
|
H: Into<Height> + Send,
|
||||||
|
{
|
||||||
|
self.client.block(height).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn block_by_hash(
|
||||||
|
&self,
|
||||||
|
hash: Hash,
|
||||||
|
) -> Result<block_by_hash::Response, TendermintRpcError> {
|
||||||
|
self.client.block_by_hash(hash).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn latest_block(&self) -> Result<block::Response, TendermintRpcError> {
|
||||||
|
self.client.latest_block().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn header<H>(&self, height: H) -> Result<header::Response, TendermintRpcError>
|
||||||
|
where
|
||||||
|
H: Into<Height> + Send,
|
||||||
|
{
|
||||||
|
self.client.header(height).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn header_by_hash(
|
||||||
|
&self,
|
||||||
|
hash: Hash,
|
||||||
|
) -> Result<header_by_hash::Response, TendermintRpcError> {
|
||||||
|
self.client.header_by_hash(hash).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn block_results<H>(
|
||||||
|
&self,
|
||||||
|
height: H,
|
||||||
|
) -> Result<block_results::Response, TendermintRpcError>
|
||||||
|
where
|
||||||
|
H: Into<Height> + Send,
|
||||||
|
{
|
||||||
|
self.client.block_results(height).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn latest_block_results(&self) -> Result<block_results::Response, TendermintRpcError> {
|
||||||
|
self.client.latest_block_results().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn block_search(
|
||||||
|
&self,
|
||||||
|
query: Query,
|
||||||
|
page: u32,
|
||||||
|
per_page: u8,
|
||||||
|
order: Order,
|
||||||
|
) -> Result<block_search::Response, TendermintRpcError> {
|
||||||
|
self.client.block_search(query, page, per_page, order).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn blockchain<H>(
|
||||||
|
&self,
|
||||||
|
min: H,
|
||||||
|
max: H,
|
||||||
|
) -> Result<blockchain::Response, TendermintRpcError>
|
||||||
|
where
|
||||||
|
H: Into<Height> + Send,
|
||||||
|
{
|
||||||
|
self.client.blockchain(min, max).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn broadcast_tx_async<T>(
|
||||||
|
&self,
|
||||||
|
tx: T,
|
||||||
|
) -> Result<broadcast::tx_async::Response, TendermintRpcError>
|
||||||
|
where
|
||||||
|
T: Into<Vec<u8>> + Send,
|
||||||
|
{
|
||||||
|
self.client.broadcast_tx_async(tx).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn broadcast_tx_sync<T>(
|
||||||
|
&self,
|
||||||
|
tx: T,
|
||||||
|
) -> Result<broadcast::tx_sync::Response, TendermintRpcError>
|
||||||
|
where
|
||||||
|
T: Into<Vec<u8>> + Send,
|
||||||
|
{
|
||||||
|
self.client.broadcast_tx_sync(tx).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn broadcast_tx_commit<T>(
|
||||||
|
&self,
|
||||||
|
tx: T,
|
||||||
|
) -> Result<broadcast::tx_commit::Response, TendermintRpcError>
|
||||||
|
where
|
||||||
|
T: Into<Vec<u8>> + Send,
|
||||||
|
{
|
||||||
|
self.client.broadcast_tx_commit(tx).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn commit<H>(&self, height: H) -> Result<commit::Response, TendermintRpcError>
|
||||||
|
where
|
||||||
|
H: Into<Height> + Send,
|
||||||
|
{
|
||||||
|
self.client.commit(height).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn consensus_params<H>(
|
||||||
|
&self,
|
||||||
|
height: H,
|
||||||
|
) -> Result<consensus_params::Response, TendermintRpcError>
|
||||||
|
where
|
||||||
|
H: Into<Height> + Send,
|
||||||
|
{
|
||||||
|
self.client.consensus_params(height).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn consensus_state(&self) -> Result<consensus_state::Response, TendermintRpcError> {
|
||||||
|
self.client.consensus_state().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn validators<H>(
|
||||||
|
&self,
|
||||||
|
height: H,
|
||||||
|
paging: Paging,
|
||||||
|
) -> Result<validators::Response, TendermintRpcError>
|
||||||
|
where
|
||||||
|
H: Into<Height> + Send,
|
||||||
|
{
|
||||||
|
self.client.validators(height, paging).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn latest_consensus_params(
|
||||||
|
&self,
|
||||||
|
) -> Result<consensus_params::Response, TendermintRpcError> {
|
||||||
|
self.client.latest_consensus_params().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn latest_commit(&self) -> Result<commit::Response, TendermintRpcError> {
|
||||||
|
self.client.latest_commit().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn health(&self) -> Result<(), TendermintRpcError> {
|
||||||
|
self.client.health().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn genesis<AppState>(&self) -> Result<Genesis<AppState>, TendermintRpcError>
|
||||||
|
where
|
||||||
|
AppState: Debug + Serialize + DeserializeOwned + Send,
|
||||||
|
{
|
||||||
|
self.client.genesis().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn net_info(&self) -> Result<net_info::Response, TendermintRpcError> {
|
||||||
|
self.client.net_info().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn status(&self) -> Result<status::Response, TendermintRpcError> {
|
||||||
|
self.client.status().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn broadcast_evidence(
|
||||||
|
&self,
|
||||||
|
e: Evidence,
|
||||||
|
) -> Result<evidence::Response, TendermintRpcError> {
|
||||||
|
self.client.broadcast_evidence(e).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn tx(&self, hash: Hash, prove: bool) -> Result<tx::Response, TendermintRpcError> {
|
||||||
|
self.client.tx(hash, prove).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn tx_search(
|
||||||
|
&self,
|
||||||
|
query: Query,
|
||||||
|
prove: bool,
|
||||||
|
page: u32,
|
||||||
|
per_page: u8,
|
||||||
|
order: Order,
|
||||||
|
) -> Result<tx_search::Response, TendermintRpcError> {
|
||||||
|
self.client
|
||||||
|
.tx_search(query, prove, page, per_page, order)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "tendermint-rpc/http-client",
|
||||||
|
feature = "tendermint-rpc/websocket-client"
|
||||||
|
))]
|
||||||
|
async fn wait_until_healthy<T>(&self, timeout: T) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
T: Into<core::time::Duration> + Send,
|
||||||
|
{
|
||||||
|
self.client.wait_until_healthy(timeout).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn perform<R>(&self, request: R) -> Result<R::Output, TendermintRpcError>
|
||||||
|
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)
|
||||||
|
// }
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user